UNPKG

17.6 kBJavaScriptView Raw
1import { SELECTION_CHANGE, SELECTION_ITEMS_CHANGE, SelectionMode } from './Selection.types';
2import { EventGroup } from '../EventGroup';
3/**
4 * {@docCategory Selection}
5 */
6var Selection = /** @class */ (function () {
7 /**
8 * Create a new Selection. If `TItem` does not have a `key` property, you must provide an options
9 * object with a `getKey` implementation. Providing options is optional otherwise.
10 * (At most one `options` object is accepted.)
11 */
12 function Selection() {
13 var options = []; // Otherwise, arguments require options with `getKey`.
14 for (var _i = 0 // Otherwise, arguments require options with `getKey`.
15 ; _i < arguments.length // Otherwise, arguments require options with `getKey`.
16 ; _i++ // Otherwise, arguments require options with `getKey`.
17 ) {
18 options[_i] = arguments[_i]; // Otherwise, arguments require options with `getKey`.
19 }
20 var _a = options[0] || {}, onSelectionChanged = _a.onSelectionChanged, onItemsChanged = _a.onItemsChanged, getKey = _a.getKey, _b = _a.canSelectItem, canSelectItem = _b === void 0 ? function () { return true; } : _b, items = _a.items, _c = _a.selectionMode, selectionMode = _c === void 0 ? SelectionMode.multiple : _c;
21 this.mode = selectionMode;
22 this._getKey = getKey || defaultGetKey;
23 this._changeEventSuppressionCount = 0;
24 this._exemptedCount = 0;
25 this._anchoredIndex = 0;
26 this._unselectableCount = 0;
27 this._onSelectionChanged = onSelectionChanged;
28 this._onItemsChanged = onItemsChanged;
29 this._canSelectItem = canSelectItem;
30 this._keyToIndexMap = {};
31 this._isModal = false;
32 this.setItems(items || [], true);
33 this.count = this.getSelectedCount();
34 }
35 Selection.prototype.canSelectItem = function (item, index) {
36 if (typeof index === 'number' && index < 0) {
37 return false;
38 }
39 return this._canSelectItem(item, index);
40 };
41 Selection.prototype.getKey = function (item, index) {
42 var key = this._getKey(item, index);
43 return typeof key === 'number' || key ? "" + key : '';
44 };
45 Selection.prototype.setChangeEvents = function (isEnabled, suppressChange) {
46 this._changeEventSuppressionCount += isEnabled ? -1 : 1;
47 if (this._changeEventSuppressionCount === 0 && this._hasChanged) {
48 this._hasChanged = false;
49 if (!suppressChange) {
50 this._change();
51 }
52 }
53 };
54 Selection.prototype.isModal = function () {
55 return this._isModal;
56 };
57 Selection.prototype.setModal = function (isModal) {
58 if (this._isModal !== isModal) {
59 this.setChangeEvents(false);
60 this._isModal = isModal;
61 if (!isModal) {
62 this.setAllSelected(false);
63 }
64 this._change();
65 this.setChangeEvents(true);
66 }
67 };
68 /**
69 * Selection needs the items, call this method to set them. If the set
70 * of items is the same, this will re-evaluate selection and index maps.
71 * Otherwise, shouldClear should be set to true, so that selection is
72 * cleared.
73 */
74 Selection.prototype.setItems = function (items, shouldClear) {
75 if (shouldClear === void 0) { shouldClear = true; }
76 var newKeyToIndexMap = {};
77 var newUnselectableIndices = {};
78 var hasSelectionChanged = false;
79 this.setChangeEvents(false);
80 // Reset the unselectable count.
81 this._unselectableCount = 0;
82 var haveItemsChanged = false;
83 // Build lookup table for quick selection evaluation.
84 for (var i = 0; i < items.length; i++) {
85 var item = items[i];
86 if (item) {
87 var key = this.getKey(item, i);
88 if (key) {
89 if (!haveItemsChanged && (!(key in this._keyToIndexMap) || this._keyToIndexMap[key] !== i)) {
90 haveItemsChanged = true;
91 }
92 newKeyToIndexMap[key] = i;
93 }
94 }
95 newUnselectableIndices[i] = item && !this.canSelectItem(item);
96 if (newUnselectableIndices[i]) {
97 this._unselectableCount++;
98 }
99 }
100 if (shouldClear || items.length === 0) {
101 this._setAllSelected(false, true);
102 }
103 // Check the exemption list for discrepencies.
104 var newExemptedIndicies = {};
105 var newExemptedCount = 0;
106 for (var indexProperty in this._exemptedIndices) {
107 if (this._exemptedIndices.hasOwnProperty(indexProperty)) {
108 var index = Number(indexProperty);
109 var item = this._items[index];
110 var exemptKey = item ? this.getKey(item, Number(index)) : undefined;
111 var newIndex = exemptKey ? newKeyToIndexMap[exemptKey] : index;
112 if (newIndex === undefined) {
113 // The item has likely been replaced or removed.
114 hasSelectionChanged = true;
115 }
116 else {
117 // We know the new index of the item. update the existing exemption table.
118 newExemptedIndicies[newIndex] = true;
119 newExemptedCount++;
120 hasSelectionChanged = hasSelectionChanged || newIndex !== index;
121 }
122 }
123 }
124 if (this._items && this._exemptedCount === 0 && items.length !== this._items.length && this._isAllSelected) {
125 // If everything was selected but the number of items has changed, selection has changed.
126 hasSelectionChanged = true;
127 }
128 if (!haveItemsChanged) {
129 for (var _i = 0, _a = Object.keys(this._keyToIndexMap); _i < _a.length; _i++) {
130 var key = _a[_i];
131 if (!(key in newKeyToIndexMap)) {
132 haveItemsChanged = true;
133 break;
134 }
135 }
136 }
137 this._exemptedIndices = newExemptedIndicies;
138 this._exemptedCount = newExemptedCount;
139 this._keyToIndexMap = newKeyToIndexMap;
140 this._unselectableIndices = newUnselectableIndices;
141 this._items = items;
142 this._selectedItems = null;
143 if (hasSelectionChanged) {
144 this._updateCount();
145 }
146 if (haveItemsChanged) {
147 EventGroup.raise(this, SELECTION_ITEMS_CHANGE);
148 if (this._onItemsChanged) {
149 this._onItemsChanged();
150 }
151 }
152 if (hasSelectionChanged) {
153 this._change();
154 }
155 this.setChangeEvents(true);
156 };
157 Selection.prototype.getItems = function () {
158 return this._items;
159 };
160 Selection.prototype.getSelection = function () {
161 if (!this._selectedItems) {
162 this._selectedItems = [];
163 var items = this._items;
164 if (items) {
165 for (var i = 0; i < items.length; i++) {
166 if (this.isIndexSelected(i)) {
167 this._selectedItems.push(items[i]);
168 }
169 }
170 }
171 }
172 return this._selectedItems;
173 };
174 Selection.prototype.getSelectedCount = function () {
175 return this._isAllSelected
176 ? this._items.length - this._exemptedCount - this._unselectableCount
177 : this._exemptedCount;
178 };
179 Selection.prototype.getSelectedIndices = function () {
180 if (!this._selectedIndices) {
181 this._selectedIndices = [];
182 var items = this._items;
183 if (items) {
184 for (var i = 0; i < items.length; i++) {
185 if (this.isIndexSelected(i)) {
186 this._selectedIndices.push(i);
187 }
188 }
189 }
190 }
191 return this._selectedIndices;
192 };
193 Selection.prototype.getItemIndex = function (key) {
194 var index = this._keyToIndexMap[key];
195 return (index !== null && index !== void 0 ? index : -1);
196 };
197 Selection.prototype.isRangeSelected = function (fromIndex, count) {
198 if (count === 0) {
199 return false;
200 }
201 var endIndex = fromIndex + count;
202 for (var i = fromIndex; i < endIndex; i++) {
203 if (!this.isIndexSelected(i)) {
204 return false;
205 }
206 }
207 return true;
208 };
209 Selection.prototype.isAllSelected = function () {
210 var selectableCount = this._items.length - this._unselectableCount;
211 // In single mode, we can only have a max of 1 item.
212 if (this.mode === SelectionMode.single) {
213 selectableCount = Math.min(selectableCount, 1);
214 }
215 return ((this.count > 0 && this._isAllSelected && this._exemptedCount === 0) ||
216 (!this._isAllSelected && this._exemptedCount === selectableCount && selectableCount > 0));
217 };
218 Selection.prototype.isKeySelected = function (key) {
219 var index = this._keyToIndexMap[key];
220 return this.isIndexSelected(index);
221 };
222 Selection.prototype.isIndexSelected = function (index) {
223 return !!((this.count > 0 && this._isAllSelected && !this._exemptedIndices[index] && !this._unselectableIndices[index]) ||
224 (!this._isAllSelected && this._exemptedIndices[index]));
225 };
226 Selection.prototype.setAllSelected = function (isAllSelected) {
227 if (isAllSelected && this.mode !== SelectionMode.multiple) {
228 return;
229 }
230 var selectableCount = this._items ? this._items.length - this._unselectableCount : 0;
231 this.setChangeEvents(false);
232 if (selectableCount > 0 && (this._exemptedCount > 0 || isAllSelected !== this._isAllSelected)) {
233 this._exemptedIndices = {};
234 if (isAllSelected !== this._isAllSelected || this._exemptedCount > 0) {
235 this._exemptedCount = 0;
236 this._isAllSelected = isAllSelected;
237 this._change();
238 }
239 this._updateCount();
240 }
241 this.setChangeEvents(true);
242 };
243 Selection.prototype.setKeySelected = function (key, isSelected, shouldAnchor) {
244 var index = this._keyToIndexMap[key];
245 if (index >= 0) {
246 this.setIndexSelected(index, isSelected, shouldAnchor);
247 }
248 };
249 Selection.prototype.setIndexSelected = function (index, isSelected, shouldAnchor) {
250 if (this.mode === SelectionMode.none) {
251 return;
252 }
253 // Clamp the index.
254 index = Math.min(Math.max(0, index), this._items.length - 1);
255 // No-op on out of bounds selections.
256 if (index < 0 || index >= this._items.length) {
257 return;
258 }
259 this.setChangeEvents(false);
260 var isExempt = this._exemptedIndices[index];
261 var canSelect = !this._unselectableIndices[index];
262 if (canSelect) {
263 if (isSelected && this.mode === SelectionMode.single) {
264 // If this is single-select, the previous selection should be removed.
265 this._setAllSelected(false, true);
266 }
267 // Determine if we need to remove the exemption.
268 if (isExempt && ((isSelected && this._isAllSelected) || (!isSelected && !this._isAllSelected))) {
269 delete this._exemptedIndices[index];
270 this._exemptedCount--;
271 }
272 // Determine if we need to add the exemption.
273 if (!isExempt && ((isSelected && !this._isAllSelected) || (!isSelected && this._isAllSelected))) {
274 this._exemptedIndices[index] = true;
275 this._exemptedCount++;
276 }
277 if (shouldAnchor) {
278 this._anchoredIndex = index;
279 }
280 }
281 this._updateCount();
282 this.setChangeEvents(true);
283 };
284 Selection.prototype.setRangeSelected = function (fromIndex, count, isSelected, shouldAnchor) {
285 if (this.mode === SelectionMode.none) {
286 return;
287 }
288 // Clamp the index.
289 fromIndex = Math.min(Math.max(0, fromIndex), this._items.length - 1);
290 // Clamp the range.
291 count = Math.min(Math.max(0, count), this._items.length - fromIndex);
292 // No-op on out of bounds selections.
293 if (fromIndex < 0 || fromIndex >= this._items.length || count === 0) {
294 return;
295 }
296 this.setChangeEvents(false);
297 var anchorIndex = this._anchoredIndex || 0;
298 var startIndex = fromIndex;
299 var endIndex = fromIndex + count - 1;
300 var newAnchorIndex = anchorIndex >= endIndex ? startIndex : endIndex;
301 for (; startIndex <= endIndex; startIndex++) {
302 this.setIndexSelected(startIndex, isSelected, shouldAnchor ? startIndex === newAnchorIndex : false);
303 }
304 this.setChangeEvents(true);
305 };
306 Selection.prototype.selectToKey = function (key, clearSelection) {
307 this.selectToIndex(this._keyToIndexMap[key], clearSelection);
308 };
309 Selection.prototype.selectToRange = function (fromIndex, count, clearSelection) {
310 if (this.mode === SelectionMode.none) {
311 return;
312 }
313 if (this.mode === SelectionMode.single) {
314 if (count === 1) {
315 this.setIndexSelected(fromIndex, true, true);
316 }
317 return;
318 }
319 var anchorIndex = this._anchoredIndex || 0;
320 var startIndex = Math.min(fromIndex, anchorIndex);
321 var endIndex = Math.max(fromIndex + count - 1, anchorIndex);
322 this.setChangeEvents(false);
323 if (clearSelection) {
324 this._setAllSelected(false, true);
325 }
326 for (; startIndex <= endIndex; startIndex++) {
327 this.setIndexSelected(startIndex, true, false);
328 }
329 this.setChangeEvents(true);
330 };
331 Selection.prototype.selectToIndex = function (index, clearSelection) {
332 if (this.mode === SelectionMode.none) {
333 return;
334 }
335 if (this.mode === SelectionMode.single) {
336 this.setIndexSelected(index, true, true);
337 return;
338 }
339 var anchorIndex = this._anchoredIndex || 0;
340 var startIndex = Math.min(index, anchorIndex);
341 var endIndex = Math.max(index, anchorIndex);
342 this.setChangeEvents(false);
343 if (clearSelection) {
344 this._setAllSelected(false, true);
345 }
346 for (; startIndex <= endIndex; startIndex++) {
347 this.setIndexSelected(startIndex, true, false);
348 }
349 this.setChangeEvents(true);
350 };
351 Selection.prototype.toggleAllSelected = function () {
352 this.setAllSelected(!this.isAllSelected());
353 };
354 Selection.prototype.toggleKeySelected = function (key) {
355 this.setKeySelected(key, !this.isKeySelected(key), true);
356 };
357 Selection.prototype.toggleIndexSelected = function (index) {
358 this.setIndexSelected(index, !this.isIndexSelected(index), true);
359 };
360 Selection.prototype.toggleRangeSelected = function (fromIndex, count) {
361 if (this.mode === SelectionMode.none) {
362 return;
363 }
364 var isRangeSelected = this.isRangeSelected(fromIndex, count);
365 var endIndex = fromIndex + count;
366 if (this.mode === SelectionMode.single && count > 1) {
367 return;
368 }
369 this.setChangeEvents(false);
370 for (var i = fromIndex; i < endIndex; i++) {
371 this.setIndexSelected(i, !isRangeSelected, false);
372 }
373 this.setChangeEvents(true);
374 };
375 Selection.prototype._updateCount = function (preserveModalState) {
376 if (preserveModalState === void 0) { preserveModalState = false; }
377 var count = this.getSelectedCount();
378 if (count !== this.count) {
379 this.count = count;
380 this._change();
381 }
382 if (!this.count && !preserveModalState) {
383 this.setModal(false);
384 }
385 };
386 Selection.prototype._setAllSelected = function (isAllSelected, preserveModalState) {
387 if (preserveModalState === void 0) { preserveModalState = false; }
388 if (isAllSelected && this.mode !== SelectionMode.multiple) {
389 return;
390 }
391 var selectableCount = this._items ? this._items.length - this._unselectableCount : 0;
392 this.setChangeEvents(false);
393 if (selectableCount > 0 && (this._exemptedCount > 0 || isAllSelected !== this._isAllSelected)) {
394 this._exemptedIndices = {};
395 if (isAllSelected !== this._isAllSelected || this._exemptedCount > 0) {
396 this._exemptedCount = 0;
397 this._isAllSelected = isAllSelected;
398 this._change();
399 }
400 this._updateCount(preserveModalState);
401 }
402 this.setChangeEvents(true);
403 };
404 Selection.prototype._change = function () {
405 if (this._changeEventSuppressionCount === 0) {
406 this._selectedItems = null;
407 this._selectedIndices = undefined;
408 EventGroup.raise(this, SELECTION_CHANGE);
409 if (this._onSelectionChanged) {
410 this._onSelectionChanged();
411 }
412 }
413 else {
414 this._hasChanged = true;
415 }
416 };
417 return Selection;
418}());
419export { Selection };
420function defaultGetKey(item, index) {
421 // 0 may be used as a key
422 var _a = (item || {}).key, key = _a === void 0 ? "" + index : _a;
423 return key;
424}
425//# sourceMappingURL=Selection.js.map
\No newline at end of file