1// Copyright (c) 2011 The Chromium Authors. All rights reserved. 2// Use of this source code is governed by a BSD-style license that can be 3// found in the LICENSE file. 4 5cr.define('cr.ui', function() { 6 const Event = cr.Event; 7 const EventTarget = cr.EventTarget; 8 9 /** 10 * Creates a new selection model that is to be used with lists. This only 11 * allows a single index to be selected. 12 * 13 * @param {number=} opt_length The number items in the selection. 14 * 15 * @constructor 16 * @extends {!cr.EventTarget} 17 */ 18 function ListSingleSelectionModel(opt_length) { 19 this.length_ = opt_length || 0; 20 this.selectedIndex = -1; 21 } 22 23 ListSingleSelectionModel.prototype = { 24 __proto__: EventTarget.prototype, 25 26 /** 27 * The number of items in the model. 28 * @type {number} 29 */ 30 get length() { 31 return this.length_; 32 }, 33 34 /** 35 * @type {!Array} The selected indexes. 36 */ 37 get selectedIndexes() { 38 var i = this.selectedIndex; 39 return i != -1 ? [this.selectedIndex] : []; 40 }, 41 42 /** 43 * Convenience getter which returns the first selected index. 44 * @type {number} 45 */ 46 get selectedIndex() { 47 return this.selectedIndex_; 48 }, 49 set selectedIndex(selectedIndex) { 50 var oldSelectedIndex = this.selectedIndex; 51 var i = Math.max(-1, Math.min(this.length_ - 1, selectedIndex)); 52 53 if (i != oldSelectedIndex) { 54 this.beginChange(); 55 this.selectedIndex_ = i 56 this.endChange(); 57 } 58 }, 59 60 /** 61 * Selects a range of indexes, starting with {@code start} and ends with 62 * {@code end}. 63 * @param {number} start The first index to select. 64 * @param {number} end The last index to select. 65 */ 66 selectRange: function(start, end) { 67 // Only select first index. 68 this.selectedIndex = Math.min(start, end); 69 }, 70 71 /** 72 * Selects all indexes. 73 */ 74 selectAll: function() { 75 // Select all is not allowed on a single selection model 76 }, 77 78 /** 79 * Clears the selection 80 */ 81 clear: function() { 82 this.beginChange(); 83 this.length_ = 0; 84 this.selectedIndex = this.anchorIndex = this.leadIndex = -1; 85 this.endChange(); 86 }, 87 88 /** 89 * Unselects all selected items. 90 */ 91 unselectAll: function() { 92 this.selectedIndex = -1; 93 }, 94 95 /** 96 * Sets the selected state for an index. 97 * @param {number} index The index to set the selected state for. 98 * @param {boolean} b Whether to select the index or not. 99 */ 100 setIndexSelected: function(index, b) { 101 // Only allow selection 102 var oldSelected = index == this.selectedIndex_; 103 if (oldSelected == b) 104 return; 105 106 if (b) 107 this.selectedIndex = index; 108 else if (index == this.selectedIndex_) 109 this.selectedIndex = -1; 110 }, 111 112 /** 113 * Whether a given index is selected or not. 114 * @param {number} index The index to check. 115 * @return {boolean} Whether an index is selected. 116 */ 117 getIndexSelected: function(index) { 118 return index == this.selectedIndex_; 119 }, 120 121 /** 122 * This is used to begin batching changes. Call {@code endChange} when you 123 * are done making changes. 124 */ 125 beginChange: function() { 126 if (!this.changeCount_) { 127 this.changeCount_ = 0; 128 this.selectedIndexBefore_ = this.selectedIndex_; 129 } 130 this.changeCount_++; 131 }, 132 133 /** 134 * Call this after changes are done and it will dispatch a change event if 135 * any changes were actually done. 136 */ 137 endChange: function() { 138 this.changeCount_--; 139 if (!this.changeCount_) { 140 if (this.selectedIndexBefore_ != this.selectedIndex_) { 141 var e = new Event('change'); 142 var indexes = [this.selectedIndexBefore_, this.selectedIndex_]; 143 e.changes = indexes.filter(function(index) { 144 return index != -1; 145 }).map(function(index) { 146 return { 147 index: index, 148 selected: index == this.selectedIndex_ 149 }; 150 }, this); 151 this.dispatchEvent(e); 152 } 153 } 154 }, 155 156 leadIndex_: -1, 157 158 /** 159 * The leadIndex is used with multiple selection and it is the index that 160 * the user is moving using the arrow keys. 161 * @type {number} 162 */ 163 get leadIndex() { 164 return this.leadIndex_; 165 }, 166 set leadIndex(leadIndex) { 167 var li = Math.max(-1, Math.min(this.length_ - 1, leadIndex)); 168 if (li != this.leadIndex_) { 169 var oldLeadIndex = this.leadIndex_; 170 this.leadIndex_ = li; 171 cr.dispatchPropertyChange(this, 'leadIndex', li, oldLeadIndex); 172 cr.dispatchPropertyChange(this, 'anchorIndex', li, oldLeadIndex); 173 } 174 }, 175 176 /** 177 * The anchorIndex is used with multiple selection. 178 * @type {number} 179 */ 180 get anchorIndex() { 181 return this.leadIndex; 182 }, 183 set anchorIndex(anchorIndex) { 184 this.leadIndex = anchorIndex; 185 }, 186 187 /** 188 * Whether the selection model supports multiple selected items. 189 * @type {boolean} 190 */ 191 get multiple() { 192 return false; 193 }, 194 195 /** 196 * Adjusts the selection after reordering of items in the table. 197 * @param {!Array.<number>} permutation The reordering permutation. 198 */ 199 adjustToReordering: function(permutation) { 200 }, 201 202 /** 203 * Adjust the selection by adding or removing a certain numbers of items. 204 * This should be called by the owner of the selection model as items are 205 * added and removed from the underlying data model. 206 * @param {number} index The index of the first change. 207 * @param {number} itemsRemoved Number of items removed. 208 * @param {number} itemsAdded Number of items added. 209 */ 210 adjust: function(index, itemsRemoved, itemsAdded) { 211 function getNewAdjustedIndex(i) { 212 if (i >= index && i < index + itemsRemoved) { 213 return index; 214 } else if (i >= index) { 215 return i + itemsAdded - itemsRemoved; 216 } 217 return i; 218 } 219 220 this.length_ += itemsAdded - itemsRemoved; 221 222 var i = this.selectedIndex; 223 if (itemsRemoved > 0 && i >= index && i < index + itemsRemoved) 224 this.selectedIndex = -1; 225 else if (i >= index) 226 this.selectedIndex = i + itemsAdded - itemsRemoved; 227 228 this.leadIndex = getNewAdjustedIndex(this.leadIndex); 229 } 230 }; 231 232 return { 233 ListSingleSelectionModel: ListSingleSelectionModel 234 }; 235}); 236