@danielgindi/selectbox 1.0.147 → 2.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/css/droplist.css +1 -1
- package/css/droplist.css.map +1 -1
- package/dist/lib.cjs.js +853 -338
- package/dist/lib.cjs.js.map +1 -1
- package/dist/lib.cjs.min.js +2 -2
- package/dist/lib.cjs.min.js.map +1 -1
- package/dist/lib.es6.js +853 -338
- package/dist/lib.es6.js.map +1 -1
- package/dist/lib.es6.min.js +2 -2
- package/dist/lib.es6.min.js.map +1 -1
- package/dist/lib.umd.js +853 -338
- package/dist/lib.umd.js.map +1 -1
- package/dist/lib.umd.min.js +2 -2
- package/dist/lib.umd.min.js.map +1 -1
- package/lib/DropList.js +726 -104
- package/lib/SelectBox.js +80 -187
- package/package.json +19 -20
- package/scss/droplist.scss +141 -110
- package/vue/DropList.vue +93 -8
- package/vue/SelectBox.vue +13 -13
package/lib/DropList.js
CHANGED
|
@@ -28,10 +28,12 @@ import {
|
|
|
28
28
|
VALUE_UP,
|
|
29
29
|
} from 'keycode-js';
|
|
30
30
|
import mitt from 'mitt';
|
|
31
|
+
import throttle from './utils/throttle';
|
|
31
32
|
|
|
32
33
|
const ItemSymbol = Symbol('item');
|
|
33
34
|
const DestroyedSymbol = Symbol('destroyed');
|
|
34
35
|
const GhostSymbol = Symbol('ghost');
|
|
36
|
+
const NoResultsItemSymbol = Symbol('no_results_items');
|
|
35
37
|
|
|
36
38
|
const hasOwnProperty = Object.prototype.hasOwnProperty;
|
|
37
39
|
|
|
@@ -57,7 +59,17 @@ const hasOwnProperty = Object.prototype.hasOwnProperty;
|
|
|
57
59
|
* @property {string} [valueProp='value']
|
|
58
60
|
* @property {function(item: DropList.ItemBase, itemEl: Element):(*|false)} [renderItem] Function to call when rendering an item element
|
|
59
61
|
* @property {function(item: DropList.ItemBase, itemEl: Element)} [unrenderItem] Function to call when rendering an item element
|
|
62
|
+
* @property {function(item: DropList.ItemBase, itemEl: Element):(*|false)} [renderNoResultsItem]
|
|
63
|
+
* @property {function(item: DropList.ItemBase, itemEl: Element)} [unrenderNoResultsItem]
|
|
60
64
|
* @property {function(name: string, data: *)} [on]
|
|
65
|
+
* @property {boolean} [searchable=false] include inline search box
|
|
66
|
+
* @property {string} [noResultsText='No matching results'] text for no results (empty for none)
|
|
67
|
+
* @property {number} [filterThrottleWindow=300] throttle time (milliseconds) for filtering
|
|
68
|
+
* @property {boolean} [filterOnEmptyTerm=false] call the filter function on empty search term too
|
|
69
|
+
* @property {boolean} [filterGroups=false] should groups be filtered?
|
|
70
|
+
* @property {boolean} [filterEmptyGroups=false] should empty groups be filtered out?
|
|
71
|
+
* @property {function(items: DropList.ItemBase[], term: string):(DropList.ItemBase[]|null)} [filterFn]
|
|
72
|
+
* @property {function(dropList: DropList):DropList.PositionOptions} [positionOptionsProvider]
|
|
61
73
|
* */
|
|
62
74
|
/** */
|
|
63
75
|
|
|
@@ -121,6 +133,13 @@ let defaultOptions = {
|
|
|
121
133
|
valueProp: 'value',
|
|
122
134
|
|
|
123
135
|
on: null,
|
|
136
|
+
|
|
137
|
+
searchable: false,
|
|
138
|
+
noResultsText: 'No matching results',
|
|
139
|
+
filterThrottleWindow: 300,
|
|
140
|
+
filterOnEmptyTerm: false,
|
|
141
|
+
filterGroups: false,
|
|
142
|
+
filterEmptyGroups: false,
|
|
124
143
|
};
|
|
125
144
|
|
|
126
145
|
/*
|
|
@@ -181,17 +200,35 @@ class DropList {
|
|
|
181
200
|
valueProp: o.valueProp,
|
|
182
201
|
renderItem: o.renderItem,
|
|
183
202
|
unrenderItem: o.unrenderItem,
|
|
203
|
+
renderNoResultsItem: o.renderNoResultsItem,
|
|
204
|
+
unrenderNoResultsItem: o.unrenderNoResultsItem,
|
|
184
205
|
on: o.on || null,
|
|
206
|
+
positionOptionsProvider: o.positionOptionsProvider ?? null,
|
|
207
|
+
|
|
208
|
+
searchable: o.searchable,
|
|
209
|
+
|
|
185
210
|
silenceEvents: true,
|
|
186
211
|
mitt: mitt(),
|
|
187
212
|
|
|
213
|
+
filterThrottleWindow: o.filterThrottleWindow,
|
|
214
|
+
filterOnEmptyTerm: o.filterOnEmptyTerm,
|
|
215
|
+
filterGroups: o.filterGroups,
|
|
216
|
+
filterEmptyGroups: o.filterEmptyGroups,
|
|
217
|
+
filterFn: o.filterFn,
|
|
218
|
+
filteredItems: null,
|
|
219
|
+
filterTerm: '',
|
|
220
|
+
needsRefilter: false,
|
|
221
|
+
throttledRefilterItems: null,
|
|
222
|
+
|
|
188
223
|
focusItemIndex: -1,
|
|
189
224
|
focusItemEl: null,
|
|
190
225
|
|
|
191
226
|
sink: new DomEventsSink(),
|
|
192
227
|
};
|
|
193
228
|
|
|
194
|
-
|
|
229
|
+
const baseClass = p.baseClassName + '_wrapper';
|
|
230
|
+
|
|
231
|
+
let classes = [baseClass];
|
|
195
232
|
|
|
196
233
|
if (p.additionalClasses) {
|
|
197
234
|
classes = classes.concat((p.additionalClasses + '').split(' ').filter(x => x));
|
|
@@ -202,31 +239,54 @@ class DropList {
|
|
|
202
239
|
top: '-9999px',
|
|
203
240
|
};
|
|
204
241
|
|
|
205
|
-
let
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
setCssProps(/**@type ElementCSSInlineStyle*/
|
|
242
|
+
let wrapperEl = o.el;
|
|
243
|
+
|
|
244
|
+
if (wrapperEl instanceof Element) {
|
|
245
|
+
p.elOriginalDisplay = wrapperEl.style.display || '';
|
|
246
|
+
wrapperEl.classList.add(...classes);
|
|
247
|
+
setCssProps(/**@type ElementCSSInlineStyle*/wrapperEl, initialCss);
|
|
211
248
|
p.ownsEl = false;
|
|
212
249
|
} else {
|
|
213
|
-
|
|
250
|
+
wrapperEl = createElement('div', {
|
|
214
251
|
class: classes.join(' '),
|
|
215
|
-
role: 'menu',
|
|
216
252
|
css: initialCss,
|
|
217
253
|
});
|
|
218
254
|
}
|
|
219
255
|
|
|
220
|
-
|
|
256
|
+
let menuEl = createElement('ul');
|
|
257
|
+
menuEl.role = 'menu';
|
|
221
258
|
|
|
222
|
-
|
|
259
|
+
if (o.searchable) {
|
|
260
|
+
p.headerEl = createElement('div', {
|
|
261
|
+
class: p.baseClassName + '_header',
|
|
262
|
+
});
|
|
223
263
|
|
|
224
|
-
|
|
264
|
+
p.searchInput = createElement('input', {
|
|
265
|
+
type: 'search',
|
|
266
|
+
role: 'searchbox',
|
|
267
|
+
tabindex: '0',
|
|
268
|
+
autocorrect: 'off',
|
|
269
|
+
autocomplete: 'off',
|
|
270
|
+
autocapitalize: 'off',
|
|
271
|
+
spellcheck: 'false',
|
|
272
|
+
'aria-autocomplete': 'list',
|
|
273
|
+
});
|
|
274
|
+
|
|
275
|
+
p.headerEl.appendChild(p.searchInput);
|
|
276
|
+
wrapperEl.appendChild(p.headerEl);
|
|
277
|
+
}
|
|
278
|
+
|
|
279
|
+
wrapperEl.appendChild(menuEl);
|
|
280
|
+
|
|
281
|
+
p.el = wrapperEl;
|
|
282
|
+
p.menuEl = menuEl;
|
|
225
283
|
|
|
284
|
+
p.items = [];
|
|
285
|
+
p.groupCount = 0; // This will keep state of how many `group` items we have
|
|
226
286
|
p.mouseHandled = false;
|
|
227
287
|
|
|
228
288
|
p.virtualListHelper = new VirtualListHelper({
|
|
229
|
-
list: p.
|
|
289
|
+
list: p.menuEl,
|
|
230
290
|
virtual: true,
|
|
231
291
|
buffer: 5,
|
|
232
292
|
estimatedItemHeight: o.estimatedItemHeight || 20,
|
|
@@ -253,7 +313,19 @@ class DropList {
|
|
|
253
313
|
};
|
|
254
314
|
itemEl.setAttribute('aria-hidden', 'true');
|
|
255
315
|
} else {
|
|
256
|
-
|
|
316
|
+
const items = p.filteredItems ?? p.items;
|
|
317
|
+
if (items.length === 0 && p.noResultsText) {
|
|
318
|
+
item = {
|
|
319
|
+
value: NoResultsItemSymbol,
|
|
320
|
+
label: p.noResultsText,
|
|
321
|
+
_nointeraction: true,
|
|
322
|
+
_nocheck: true,
|
|
323
|
+
|
|
324
|
+
[ItemSymbol]: NoResultsItemSymbol,
|
|
325
|
+
};
|
|
326
|
+
} else {
|
|
327
|
+
item = items[index];
|
|
328
|
+
}
|
|
257
329
|
}
|
|
258
330
|
|
|
259
331
|
if (!item) {
|
|
@@ -299,36 +371,20 @@ class DropList {
|
|
|
299
371
|
},
|
|
300
372
|
});
|
|
301
373
|
|
|
302
|
-
|
|
303
|
-
const fn = p.unrenderItem;
|
|
304
|
-
p.virtualListHelper.setOnItemUnrender(el => {
|
|
305
|
-
try {
|
|
306
|
-
fn(el[ItemSymbol][ItemSymbol], el);
|
|
307
|
-
} catch (err) {
|
|
308
|
-
console.error(err); // eslint-disable-line no-console
|
|
309
|
-
}
|
|
310
|
-
delete el[ItemSymbol];
|
|
311
|
-
|
|
312
|
-
if (p.focusItemEl === el)
|
|
313
|
-
p.focusItemEl = null;
|
|
314
|
-
});
|
|
315
|
-
} else {
|
|
316
|
-
p.virtualListHelper.setOnItemUnrender(el => {
|
|
317
|
-
delete el[ItemSymbol];
|
|
318
|
-
|
|
319
|
-
if (p.focusItemEl === el)
|
|
320
|
-
p.focusItemEl = null;
|
|
321
|
-
});
|
|
322
|
-
}
|
|
374
|
+
this._setupUnrenderFunction();
|
|
323
375
|
|
|
324
376
|
if (p.capturesFocus) {
|
|
325
|
-
|
|
377
|
+
wrapperEl.tabIndex = 0;
|
|
326
378
|
}
|
|
327
379
|
|
|
380
|
+
this.setFilterThrottleWindow(o.filterThrottleWindow);
|
|
381
|
+
this.setNoResultsText(o.noResultsText);
|
|
382
|
+
|
|
328
383
|
this._hookMouseEvents();
|
|
329
384
|
this._hookTouchEvents();
|
|
330
385
|
this._hookFocusEvents();
|
|
331
386
|
this._hookKeyEvents();
|
|
387
|
+
this._hookSearchEvents();
|
|
332
388
|
|
|
333
389
|
this.silenceEvents = false;
|
|
334
390
|
}
|
|
@@ -346,7 +402,7 @@ class DropList {
|
|
|
346
402
|
p.sink.remove();
|
|
347
403
|
p.virtualListHelper.destroy();
|
|
348
404
|
|
|
349
|
-
if (p.el) {
|
|
405
|
+
if (p.el?.parentNode) {
|
|
350
406
|
remove(p.el);
|
|
351
407
|
}
|
|
352
408
|
|
|
@@ -355,13 +411,15 @@ class DropList {
|
|
|
355
411
|
p.currentSubDropList = null;
|
|
356
412
|
}
|
|
357
413
|
|
|
414
|
+
if (p.throttledRefilterItems)
|
|
415
|
+
p.throttledRefilterItems.cancel();
|
|
416
|
+
|
|
358
417
|
if (!p.ownsEl) {
|
|
359
418
|
for (let name of Array.from(p.el.classList)) {
|
|
360
419
|
if (name.startsWith(p.baseClassName)) {
|
|
361
420
|
p.el.classList.remove(name);
|
|
362
421
|
}
|
|
363
422
|
}
|
|
364
|
-
p.el.removeAttribute('role');
|
|
365
423
|
for (let key of ['position', 'left', 'top', 'right', 'bottom', 'z-index']) {
|
|
366
424
|
p.el.style[key] = '';
|
|
367
425
|
}
|
|
@@ -376,6 +434,8 @@ class DropList {
|
|
|
376
434
|
delete p.lastPositionTarget;
|
|
377
435
|
}
|
|
378
436
|
|
|
437
|
+
delete p.lastPositionOptions;
|
|
438
|
+
|
|
379
439
|
this._p = null;
|
|
380
440
|
}
|
|
381
441
|
|
|
@@ -459,16 +519,47 @@ class DropList {
|
|
|
459
519
|
*/
|
|
460
520
|
setUnrenderItem(fn) {
|
|
461
521
|
const p = this._p;
|
|
462
|
-
|
|
463
522
|
p.unrenderItem = fn;
|
|
523
|
+
this._setupUnrenderFunction();
|
|
524
|
+
return this;
|
|
525
|
+
}
|
|
526
|
+
|
|
527
|
+
/**
|
|
528
|
+
* @param {(function(item: DropList.ItemBase, itemEl: Element):(*|false))|null} render
|
|
529
|
+
* @param {(function(item: DropList.ItemBase, itemEl: Element))|null} unrender
|
|
530
|
+
* @returns {DropList}
|
|
531
|
+
*/
|
|
532
|
+
setRenderNoResultsItem(render, unrender) {
|
|
533
|
+
const p = this._p;
|
|
534
|
+
p.renderNoResultsItem = render;
|
|
535
|
+
p.unrenderNoResultsItem = unrender;
|
|
536
|
+
this._setupUnrenderFunction();
|
|
537
|
+
return this;
|
|
538
|
+
}
|
|
464
539
|
|
|
465
|
-
|
|
540
|
+
/**
|
|
541
|
+
* @private
|
|
542
|
+
*/
|
|
543
|
+
_setupUnrenderFunction() {
|
|
544
|
+
const p = this._p;
|
|
545
|
+
|
|
546
|
+
if (typeof p.unrenderItem === 'function' || typeof p.unrenderNoResultsItem === 'function') {
|
|
466
547
|
const fn = p.unrenderItem;
|
|
548
|
+
const fnNoResults = p.unrenderNoResultsItem;
|
|
467
549
|
p.virtualListHelper.setOnItemUnrender(el => {
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
550
|
+
const item = el[ItemSymbol];
|
|
551
|
+
if (item === NoResultsItemSymbol) {
|
|
552
|
+
try {
|
|
553
|
+
fnNoResults(item, el);
|
|
554
|
+
} catch (err) {
|
|
555
|
+
console.error(err); // eslint-disable-line no-console
|
|
556
|
+
}
|
|
557
|
+
} else {
|
|
558
|
+
try {
|
|
559
|
+
fn(item[ItemSymbol], el);
|
|
560
|
+
} catch (err) {
|
|
561
|
+
console.error(err); // eslint-disable-line no-console
|
|
562
|
+
}
|
|
472
563
|
}
|
|
473
564
|
delete el[ItemSymbol];
|
|
474
565
|
|
|
@@ -503,7 +594,8 @@ class DropList {
|
|
|
503
594
|
if (!el)
|
|
504
595
|
return;
|
|
505
596
|
|
|
506
|
-
|
|
597
|
+
const baseClass = p.baseClassName + '_wrapper';
|
|
598
|
+
let classes = [baseClass];
|
|
507
599
|
|
|
508
600
|
if (p.direction === 'ltr' || p.direction === 'rtl')
|
|
509
601
|
classes.push(`${p.baseClassName}__` + p.direction);
|
|
@@ -530,7 +622,8 @@ class DropList {
|
|
|
530
622
|
p.focusItemEl = null;
|
|
531
623
|
}
|
|
532
624
|
|
|
533
|
-
const
|
|
625
|
+
const items = p.filteredItems ?? p.items;
|
|
626
|
+
const item = items[p.focusItemIndex];
|
|
534
627
|
p.focusItemIndex = -1;
|
|
535
628
|
|
|
536
629
|
if (!item) {
|
|
@@ -681,9 +774,24 @@ class DropList {
|
|
|
681
774
|
}
|
|
682
775
|
}
|
|
683
776
|
|
|
684
|
-
p.
|
|
685
|
-
|
|
686
|
-
.
|
|
777
|
+
if (!p.filteredItems) {
|
|
778
|
+
let hadNoResultItem = p.hasNoResultsItem;
|
|
779
|
+
p.hasNoResultsItem = p.items.length === 0 && !!p.noResultsText;
|
|
780
|
+
if (p.hasNoResultsItem) {
|
|
781
|
+
p.virtualListHelper
|
|
782
|
+
.setCount(1)
|
|
783
|
+
.render();
|
|
784
|
+
} else {
|
|
785
|
+
if (hadNoResultItem)
|
|
786
|
+
p.virtualListHelper.removeItemsAt(1, 0);
|
|
787
|
+
|
|
788
|
+
p.virtualListHelper
|
|
789
|
+
.addItemsAt(itemsToAdd.length, atIndex === -1 ? atIndex : (atIndex - itemsToAdd.length))
|
|
790
|
+
.render();
|
|
791
|
+
}
|
|
792
|
+
} else {
|
|
793
|
+
p.needsRefilter = true;
|
|
794
|
+
}
|
|
687
795
|
|
|
688
796
|
return this;
|
|
689
797
|
}
|
|
@@ -697,9 +805,11 @@ class DropList {
|
|
|
697
805
|
const p = this._p;
|
|
698
806
|
|
|
699
807
|
p.items.length = 0;
|
|
808
|
+
p.filteredItems = null;
|
|
700
809
|
p.groupCount = 0;
|
|
701
810
|
|
|
702
|
-
p.
|
|
811
|
+
p.hasNoResultsItem = !!p.noResultsText;
|
|
812
|
+
p.virtualListHelper.setCount(p.hasNoResultsItem ? 1 : 0);
|
|
703
813
|
|
|
704
814
|
this.addItems(items);
|
|
705
815
|
this.updateSublist();
|
|
@@ -750,9 +860,18 @@ class DropList {
|
|
|
750
860
|
if (hasOwnProperty.call(newItem, '_child'))
|
|
751
861
|
item._child = !!newItem._child;
|
|
752
862
|
|
|
753
|
-
|
|
863
|
+
let virtualItemIndex = itemIndex;
|
|
864
|
+
if (p.filteredItems) {
|
|
865
|
+
virtualItemIndex = p.filteredItems.indexOf(item);
|
|
866
|
+
if (virtualItemIndex !== -1) {
|
|
867
|
+
p.filteredItems.splice(virtualItemIndex, 1);
|
|
868
|
+
}
|
|
869
|
+
}
|
|
870
|
+
|
|
871
|
+
if (virtualItemIndex !== -1 &&
|
|
872
|
+
p.virtualListHelper.isItemRendered(virtualItemIndex)) {
|
|
754
873
|
p.virtualListHelper
|
|
755
|
-
.refreshItemAt(
|
|
874
|
+
.refreshItemAt(virtualItemIndex)
|
|
756
875
|
.render();
|
|
757
876
|
}
|
|
758
877
|
|
|
@@ -771,9 +890,27 @@ class DropList {
|
|
|
771
890
|
p.groupCount--;
|
|
772
891
|
}
|
|
773
892
|
|
|
774
|
-
|
|
775
|
-
|
|
776
|
-
.
|
|
893
|
+
let virtualItemIndex = itemIndex;
|
|
894
|
+
if (p.filteredItems) {
|
|
895
|
+
virtualItemIndex = p.filteredItems.indexOf(spliced[0]);
|
|
896
|
+
if (virtualItemIndex !== -1) {
|
|
897
|
+
p.filteredItems.splice(virtualItemIndex, 1);
|
|
898
|
+
}
|
|
899
|
+
}
|
|
900
|
+
|
|
901
|
+
if (virtualItemIndex !== -1) {
|
|
902
|
+
p.hasNoResultsItem = (p.filteredItems ?? p.items).length === 0 && !!p.noResultsText;
|
|
903
|
+
|
|
904
|
+
if (p.hasNoResultsItem) {
|
|
905
|
+
p.virtualListHelper
|
|
906
|
+
.setCount(1)
|
|
907
|
+
.render();
|
|
908
|
+
} else {
|
|
909
|
+
p.virtualListHelper
|
|
910
|
+
.removeItemsAt(virtualItemIndex, 1)
|
|
911
|
+
.render();
|
|
912
|
+
}
|
|
913
|
+
}
|
|
777
914
|
|
|
778
915
|
return this;
|
|
779
916
|
}
|
|
@@ -782,10 +919,13 @@ class DropList {
|
|
|
782
919
|
const p = this._p;
|
|
783
920
|
|
|
784
921
|
p.items.length = 0;
|
|
922
|
+
p.filteredItems = null;
|
|
785
923
|
p.groupCount = 0;
|
|
786
924
|
|
|
925
|
+
p.hasNoResultsItem = !!p.noResultsText;
|
|
926
|
+
|
|
787
927
|
p.virtualListHelper
|
|
788
|
-
.setCount(0)
|
|
928
|
+
.setCount(p.hasNoResultsItem ? 1 : 0)
|
|
789
929
|
.render();
|
|
790
930
|
|
|
791
931
|
return this;
|
|
@@ -821,8 +961,23 @@ class DropList {
|
|
|
821
961
|
itemIndexByValue(value) {
|
|
822
962
|
const p = this._p;
|
|
823
963
|
|
|
824
|
-
|
|
825
|
-
|
|
964
|
+
const items = p.items;
|
|
965
|
+
for (let i = 0, count = items.length; i < count; i++) {
|
|
966
|
+
let item = items[i];
|
|
967
|
+
if (item.value === value) {
|
|
968
|
+
return i;
|
|
969
|
+
}
|
|
970
|
+
}
|
|
971
|
+
|
|
972
|
+
return -1;
|
|
973
|
+
}
|
|
974
|
+
|
|
975
|
+
filteredItemIndexByValue(value) {
|
|
976
|
+
const p = this._p;
|
|
977
|
+
|
|
978
|
+
const items = p.filteredItems ?? p.items;
|
|
979
|
+
for (let i = 0, count = items.length; i < count; i++) {
|
|
980
|
+
let item = items[i];
|
|
826
981
|
if (item.value === value) {
|
|
827
982
|
return i;
|
|
828
983
|
}
|
|
@@ -834,8 +989,23 @@ class DropList {
|
|
|
834
989
|
itemIndexByValueOrLabel(value, label) {
|
|
835
990
|
const p = this._p;
|
|
836
991
|
|
|
837
|
-
|
|
838
|
-
|
|
992
|
+
const items = p.items;
|
|
993
|
+
for (let i = 0, count = items.length; i < count; i++) {
|
|
994
|
+
let item = items[i];
|
|
995
|
+
if (item.value === value || item.label === label) {
|
|
996
|
+
return i;
|
|
997
|
+
}
|
|
998
|
+
}
|
|
999
|
+
|
|
1000
|
+
return -1;
|
|
1001
|
+
}
|
|
1002
|
+
|
|
1003
|
+
filteredItemIndexByValueOrLabel(value, label) {
|
|
1004
|
+
const p = this._p;
|
|
1005
|
+
|
|
1006
|
+
const items = p.filteredItems ?? p.items;
|
|
1007
|
+
for (let i = 0, count = items.length; i < count; i++) {
|
|
1008
|
+
let item = items[i];
|
|
839
1009
|
if (item.value === value || item.label === label) {
|
|
840
1010
|
return i;
|
|
841
1011
|
}
|
|
@@ -844,6 +1014,36 @@ class DropList {
|
|
|
844
1014
|
return -1;
|
|
845
1015
|
}
|
|
846
1016
|
|
|
1017
|
+
itemIndexByItem(item) {
|
|
1018
|
+
const p = this._p;
|
|
1019
|
+
|
|
1020
|
+
const items = p.items;
|
|
1021
|
+
|
|
1022
|
+
for (let i = 0, count = items.length; i < count; i++) {
|
|
1023
|
+
let it = items[i];
|
|
1024
|
+
if (it[ItemSymbol] === item) {
|
|
1025
|
+
return i;
|
|
1026
|
+
}
|
|
1027
|
+
}
|
|
1028
|
+
|
|
1029
|
+
return -1;
|
|
1030
|
+
}
|
|
1031
|
+
|
|
1032
|
+
filteredItemIndexByItem(item) {
|
|
1033
|
+
const p = this._p;
|
|
1034
|
+
|
|
1035
|
+
const items = p.filteredItems ?? p.items;
|
|
1036
|
+
|
|
1037
|
+
for (let i = 0, count = items.length; i < count; i++) {
|
|
1038
|
+
let it = items[i];
|
|
1039
|
+
if (it[ItemSymbol] === item) {
|
|
1040
|
+
return i;
|
|
1041
|
+
}
|
|
1042
|
+
}
|
|
1043
|
+
|
|
1044
|
+
return -1;
|
|
1045
|
+
}
|
|
1046
|
+
|
|
847
1047
|
items() {
|
|
848
1048
|
return this._p.items.map(x => x[ItemSymbol]);
|
|
849
1049
|
}
|
|
@@ -860,19 +1060,320 @@ class DropList {
|
|
|
860
1060
|
return this._p.items[index]?.[ItemSymbol];
|
|
861
1061
|
}
|
|
862
1062
|
|
|
1063
|
+
filteredItemAtIndex(index) {
|
|
1064
|
+
const p = this._p;
|
|
1065
|
+
const items = p.filteredItems ?? p.items;
|
|
1066
|
+
return items[index]?.[ItemSymbol];
|
|
1067
|
+
}
|
|
1068
|
+
|
|
1069
|
+
/**
|
|
1070
|
+
* @param {function(dropList: DropList):DropList.PositionOptions} fn
|
|
1071
|
+
* @returns {DropList}
|
|
1072
|
+
*/
|
|
1073
|
+
setPositionOptionsProvider(fn) {
|
|
1074
|
+
const p = this._p;
|
|
1075
|
+
if (p.positionOptionsProvider === fn)
|
|
1076
|
+
return this;
|
|
1077
|
+
p.positionOptionsProvider = fn ?? null;
|
|
1078
|
+
this.relayout();
|
|
1079
|
+
return this;
|
|
1080
|
+
}
|
|
1081
|
+
|
|
1082
|
+
/**
|
|
1083
|
+
* @returns {function(dropList: DropList):DropList.PositionOptions|null}
|
|
1084
|
+
*/
|
|
1085
|
+
getPositionOptionsProvider() {
|
|
1086
|
+
const p = this._p;
|
|
1087
|
+
return p.positionOptionsProvider;
|
|
1088
|
+
}
|
|
1089
|
+
|
|
1090
|
+
/**
|
|
1091
|
+
* @param {string} term
|
|
1092
|
+
* @param {boolean} [performSearch=false] should actually perform the search, or just set the input's text?
|
|
1093
|
+
* @returns {DropList}
|
|
1094
|
+
*/
|
|
1095
|
+
setSearchTerm(term, performSearch = false) {
|
|
1096
|
+
const p = this._p;
|
|
1097
|
+
|
|
1098
|
+
if (p.searchInput) {
|
|
1099
|
+
p.searchInput.value = term;
|
|
1100
|
+
}
|
|
1101
|
+
|
|
1102
|
+
if (performSearch) {
|
|
1103
|
+
p.filterTerm = term.trim();
|
|
1104
|
+
p.filteredItems = null;
|
|
1105
|
+
|
|
1106
|
+
this._trigger('search', { value: term });
|
|
1107
|
+
p.throttledRefilterItems();
|
|
1108
|
+
}
|
|
1109
|
+
|
|
1110
|
+
return this;
|
|
1111
|
+
}
|
|
1112
|
+
|
|
1113
|
+
isFilterPending() {
|
|
1114
|
+
const p = this._p;
|
|
1115
|
+
|
|
1116
|
+
return !!(p.throttledRefilterItems.isScheduled() ||
|
|
1117
|
+
(!p.filteredItems && (p.filterTerm || (p.filterOnEmptyTerm && p.filterFn))));
|
|
1118
|
+
}
|
|
1119
|
+
|
|
1120
|
+
/** @private */
|
|
1121
|
+
_refilterItems() {
|
|
1122
|
+
const p = this._p;
|
|
1123
|
+
|
|
1124
|
+
const term = p.filterTerm;
|
|
1125
|
+
const filterGroups = p.filterGroups;
|
|
1126
|
+
const filterEmptyGroups = p.filterEmptyGroups;
|
|
1127
|
+
|
|
1128
|
+
if (term || (p.filterOnEmptyTerm && p.filterFn)) {
|
|
1129
|
+
let fn = p.filterFn;
|
|
1130
|
+
|
|
1131
|
+
let filteredItems;
|
|
1132
|
+
|
|
1133
|
+
if (typeof fn === 'function') {
|
|
1134
|
+
filteredItems = p.filterFn(p.items, term);
|
|
1135
|
+
}
|
|
1136
|
+
|
|
1137
|
+
// If there was no filter function, or it gave up on filtering.
|
|
1138
|
+
if (!Array.isArray(filteredItems)) {
|
|
1139
|
+
if (term) {
|
|
1140
|
+
const matcher = new RegExp(escapeRegex(term), 'i');
|
|
1141
|
+
const labelProp = p.labelProp;
|
|
1142
|
+
|
|
1143
|
+
filteredItems = p.items.filter(x => {
|
|
1144
|
+
if (!filterGroups && x._group) return true;
|
|
1145
|
+
return matcher.test(x[labelProp]);
|
|
1146
|
+
});
|
|
1147
|
+
} else {
|
|
1148
|
+
filteredItems = null;
|
|
1149
|
+
}
|
|
1150
|
+
}
|
|
1151
|
+
|
|
1152
|
+
p.filteredItems = filteredItems;
|
|
1153
|
+
|
|
1154
|
+
if (filteredItems && filterEmptyGroups) {
|
|
1155
|
+
// Clean up groups without children
|
|
1156
|
+
|
|
1157
|
+
let lastGroup = -1;
|
|
1158
|
+
let len = filteredItems.length;
|
|
1159
|
+
|
|
1160
|
+
for (let i = 0; i < len; i++) {
|
|
1161
|
+
let item = filteredItems[i];
|
|
1162
|
+
|
|
1163
|
+
if (item._group) {
|
|
1164
|
+
if (lastGroup !== -1 && lastGroup === i - 1) {
|
|
1165
|
+
// It was an empty group
|
|
1166
|
+
filteredItems.splice(lastGroup, 1);
|
|
1167
|
+
i--;
|
|
1168
|
+
len--;
|
|
1169
|
+
}
|
|
1170
|
+
|
|
1171
|
+
lastGroup = i;
|
|
1172
|
+
}
|
|
1173
|
+
}
|
|
1174
|
+
|
|
1175
|
+
if (lastGroup !== -1) {
|
|
1176
|
+
if (lastGroup === len - 1) {
|
|
1177
|
+
// It was an empty group
|
|
1178
|
+
filteredItems.splice(lastGroup, 1);
|
|
1179
|
+
}
|
|
1180
|
+
}
|
|
1181
|
+
}
|
|
1182
|
+
} else {
|
|
1183
|
+
p.filteredItems = null;
|
|
1184
|
+
}
|
|
1185
|
+
|
|
1186
|
+
this.needsRefilter = false;
|
|
1187
|
+
|
|
1188
|
+
const items = p.filteredItems ?? p.items;
|
|
1189
|
+
p.hasNoResultsItem = items.length === 0 && !!p.noResultsText;
|
|
1190
|
+
p.virtualListHelper
|
|
1191
|
+
.setCount(items.length + (p.hasNoResultsItem ? 1 : 0))
|
|
1192
|
+
.render();
|
|
1193
|
+
|
|
1194
|
+
this._trigger('itemschanged', { term: term, mutated: false, count: this.getFilteredItemCount() });
|
|
1195
|
+
this.relayout();
|
|
1196
|
+
}
|
|
1197
|
+
|
|
1198
|
+
invokeRefilter() {
|
|
1199
|
+
const p = this._p;
|
|
1200
|
+
if (!p.filterTerm && !p.filterOnEmptyTerm && !p.filteredItems)
|
|
1201
|
+
return this;
|
|
1202
|
+
p.filteredItems = null;
|
|
1203
|
+
p.throttledRefilterItems();
|
|
1204
|
+
return this;
|
|
1205
|
+
}
|
|
1206
|
+
|
|
1207
|
+
getFilteredItemCount() {
|
|
1208
|
+
const p = this._p;
|
|
1209
|
+
|
|
1210
|
+
if (p.needsRefilter)
|
|
1211
|
+
this._refilterItems();
|
|
1212
|
+
|
|
1213
|
+
if (p.filteredItems)
|
|
1214
|
+
return p.filteredItems.length;
|
|
1215
|
+
|
|
1216
|
+
if (p.items)
|
|
1217
|
+
return p.items.length;
|
|
1218
|
+
|
|
1219
|
+
return 0;
|
|
1220
|
+
}
|
|
1221
|
+
|
|
1222
|
+
/**
|
|
1223
|
+
* @param {string} noResultsText
|
|
1224
|
+
* @returns {DropList}
|
|
1225
|
+
*/
|
|
1226
|
+
setNoResultsText(noResultsText) {
|
|
1227
|
+
this._p.noResultsText = noResultsText;
|
|
1228
|
+
return this;
|
|
1229
|
+
}
|
|
1230
|
+
|
|
1231
|
+
/**
|
|
1232
|
+
* @returns {string}
|
|
1233
|
+
*/
|
|
1234
|
+
getNoResultsText() {
|
|
1235
|
+
return this._p.noResultsText;
|
|
1236
|
+
}
|
|
1237
|
+
|
|
1238
|
+
/**
|
|
1239
|
+
* @param {number} window
|
|
1240
|
+
* @returns {DropList}
|
|
1241
|
+
*/
|
|
1242
|
+
setFilterThrottleWindow(window) {
|
|
1243
|
+
const p = this._p;
|
|
1244
|
+
p.filterThrottleWindow = window;
|
|
1245
|
+
|
|
1246
|
+
let isScheduled = p.throttledRefilterItems ? p.throttledRefilterItems.isScheduled() : false;
|
|
1247
|
+
|
|
1248
|
+
if (p.throttledRefilterItems)
|
|
1249
|
+
p.throttledRefilterItems.cancel();
|
|
1250
|
+
|
|
1251
|
+
p.throttledRefilterItems = throttle(() => this._refilterItems(), p.filterThrottleWindow, true);
|
|
1252
|
+
|
|
1253
|
+
if (isScheduled)
|
|
1254
|
+
p.throttledRefilterItems();
|
|
1255
|
+
|
|
1256
|
+
return this;
|
|
1257
|
+
}
|
|
1258
|
+
|
|
1259
|
+
/**
|
|
1260
|
+
* @returns {number}
|
|
1261
|
+
*/
|
|
1262
|
+
getFilterThrottleWindow() {
|
|
1263
|
+
return this._p.filterThrottleWindow;
|
|
1264
|
+
}
|
|
1265
|
+
|
|
1266
|
+
/**
|
|
1267
|
+
* @param {boolean} value
|
|
1268
|
+
* @returns {DropList}
|
|
1269
|
+
*/
|
|
1270
|
+
setFilterOnEmptyTerm(value) {
|
|
1271
|
+
const p = this._p;
|
|
1272
|
+
if (p.filterOnEmptyTerm === value)
|
|
1273
|
+
return this;
|
|
1274
|
+
p.filterOnEmptyTerm = value;
|
|
1275
|
+
p.throttledRefilterItems();
|
|
1276
|
+
return this;
|
|
1277
|
+
}
|
|
1278
|
+
|
|
1279
|
+
/**
|
|
1280
|
+
* @returns {boolean}
|
|
1281
|
+
*/
|
|
1282
|
+
getFilterOnEmptyTerm() {
|
|
1283
|
+
return this._p.filterOnEmptyTerm;
|
|
1284
|
+
}
|
|
1285
|
+
|
|
1286
|
+
/**
|
|
1287
|
+
* @param {boolean} value
|
|
1288
|
+
* @returns {DropList}
|
|
1289
|
+
*/
|
|
1290
|
+
setFilterGroups(value) {
|
|
1291
|
+
const p = this._p;
|
|
1292
|
+
if (p.filterGroups === value)
|
|
1293
|
+
return this;
|
|
1294
|
+
p.filterGroups = value;
|
|
1295
|
+
p.throttledRefilterItems();
|
|
1296
|
+
return this;
|
|
1297
|
+
}
|
|
1298
|
+
|
|
1299
|
+
/**
|
|
1300
|
+
* @returns {boolean}
|
|
1301
|
+
*/
|
|
1302
|
+
getFilterGroups() {
|
|
1303
|
+
return this._p.filterGroups;
|
|
1304
|
+
}
|
|
1305
|
+
|
|
1306
|
+
/**
|
|
1307
|
+
* @param {boolean} value
|
|
1308
|
+
* @returns {DropList}
|
|
1309
|
+
*/
|
|
1310
|
+
setFilterEmptyGroups(value) {
|
|
1311
|
+
const p = this._p;
|
|
1312
|
+
if (p.filterEmptyGroups === value)
|
|
1313
|
+
return this;
|
|
1314
|
+
p.filterEmptyGroups = value;
|
|
1315
|
+
p.throttledRefilterItems();
|
|
1316
|
+
return this;
|
|
1317
|
+
}
|
|
1318
|
+
|
|
1319
|
+
/**
|
|
1320
|
+
* @returns {boolean}
|
|
1321
|
+
*/
|
|
1322
|
+
getFilterEmptyGroups() {
|
|
1323
|
+
return this._p.filterEmptyGroups;
|
|
1324
|
+
}
|
|
1325
|
+
|
|
1326
|
+
/**
|
|
1327
|
+
* @param {function(items: DropList.ItemBase[], term: string):(DropList.ItemBase[]|null)} fn
|
|
1328
|
+
* @returns {DropList}
|
|
1329
|
+
*/
|
|
1330
|
+
setFilterFn(fn) {
|
|
1331
|
+
const p = this._p;
|
|
1332
|
+
if (p.filterFn === fn)
|
|
1333
|
+
return this;
|
|
1334
|
+
p.filterFn = fn;
|
|
1335
|
+
p.throttledRefilterItems();
|
|
1336
|
+
return this;
|
|
1337
|
+
}
|
|
1338
|
+
|
|
1339
|
+
/**
|
|
1340
|
+
* @returns {function(items: DropList.ItemBase[], term: string):(DropList.ItemBase[]|null)}
|
|
1341
|
+
*/
|
|
1342
|
+
getFilterFn() {
|
|
1343
|
+
return this._p.filterFn;
|
|
1344
|
+
}
|
|
1345
|
+
|
|
863
1346
|
/**
|
|
864
1347
|
*
|
|
865
|
-
* @param {DropList.PositionOptions} positionOptions
|
|
1348
|
+
* @param {DropList.PositionOptions} [positionOptions]
|
|
866
1349
|
* @returns {DropList}
|
|
867
1350
|
* @public
|
|
868
1351
|
*/
|
|
869
1352
|
relayout(positionOptions) {
|
|
870
|
-
const p = this._p, el = p.el;
|
|
1353
|
+
const p = this._p, el = p.el, menuEl = p.menuEl;
|
|
871
1354
|
|
|
872
1355
|
if (!this.isVisible()) return this;
|
|
873
1356
|
|
|
874
1357
|
let w = window;
|
|
875
1358
|
|
|
1359
|
+
if (!positionOptions)
|
|
1360
|
+
positionOptions = p.positionOptionsProvider?.() ?? p.lastPositionOptions;
|
|
1361
|
+
|
|
1362
|
+
// Supply some default for extreme cases, no crashing
|
|
1363
|
+
if (!positionOptions) {
|
|
1364
|
+
positionOptions = {
|
|
1365
|
+
targetOffset: {
|
|
1366
|
+
left: window.innerWidth / 2,
|
|
1367
|
+
top: window.innerHeight / 2,
|
|
1368
|
+
},
|
|
1369
|
+
targetWidth: 0,
|
|
1370
|
+
targetHeight: 0,
|
|
1371
|
+
position: { x: 'center', y: 'center' },
|
|
1372
|
+
anchor: { x: 'center', y: 'center' },
|
|
1373
|
+
targetRtl: getComputedStyle(document.body).direction === 'rtl',
|
|
1374
|
+
};
|
|
1375
|
+
}
|
|
1376
|
+
|
|
876
1377
|
let targetBox = {};
|
|
877
1378
|
|
|
878
1379
|
let offset = positionOptions.targetOffset || getElementOffset(positionOptions.target);
|
|
@@ -935,18 +1436,25 @@ class DropList {
|
|
|
935
1436
|
let verticalBorderWidth = (parseFloat(elComputedStyle.borderTopWidth) || 0) +
|
|
936
1437
|
(parseFloat(elComputedStyle.borderBottomWidth) || 0);
|
|
937
1438
|
|
|
1439
|
+
let headerHeight = 0;
|
|
1440
|
+
if (p.headerEl) {
|
|
1441
|
+
headerHeight = getElementHeight(p.headerEl, true, true);
|
|
1442
|
+
}
|
|
1443
|
+
|
|
938
1444
|
if (p.virtualListHelper.isVirtual()) {
|
|
939
1445
|
maxViewHeight =
|
|
940
1446
|
p.virtualListHelper.estimateFullHeight() +
|
|
941
1447
|
verticalPadding +
|
|
942
|
-
verticalBorderWidth
|
|
1448
|
+
verticalBorderWidth +
|
|
1449
|
+
headerHeight;
|
|
943
1450
|
} else {
|
|
944
1451
|
// Another method to calculate height is measuring the whole thing at once.
|
|
945
1452
|
// This causes relayout of course.
|
|
946
1453
|
el.style.height = '';
|
|
1454
|
+
menuEl.style.height = '';
|
|
947
1455
|
el.style.top = '-9999px';
|
|
948
1456
|
|
|
949
|
-
maxViewHeight = Math.max(getElementHeight(p.el),
|
|
1457
|
+
maxViewHeight = Math.max(getElementHeight(p.el), menuEl.scrollHeight);
|
|
950
1458
|
maxViewHeight += verticalPadding + verticalBorderWidth;
|
|
951
1459
|
}
|
|
952
1460
|
|
|
@@ -1081,6 +1589,11 @@ class DropList {
|
|
|
1081
1589
|
setCssProps(el, viewCss);
|
|
1082
1590
|
setElementHeight(el, viewSize.height, true, true);
|
|
1083
1591
|
|
|
1592
|
+
if (menuEl !== el) {
|
|
1593
|
+
let menuHeight = viewSize.height - headerHeight - verticalBorderWidth - verticalPadding;
|
|
1594
|
+
setElementHeight(menuEl, menuHeight, true, true);
|
|
1595
|
+
}
|
|
1596
|
+
|
|
1084
1597
|
// Update the scroll position for virtual lists
|
|
1085
1598
|
p.virtualListHelper.render();
|
|
1086
1599
|
|
|
@@ -1218,7 +1731,7 @@ class DropList {
|
|
|
1218
1731
|
|
|
1219
1732
|
/**
|
|
1220
1733
|
*
|
|
1221
|
-
* @param {DropList.PositionOptions?} positionOptions
|
|
1734
|
+
* @param {DropList.PositionOptions?} [positionOptions]
|
|
1222
1735
|
* @returns {DropList}
|
|
1223
1736
|
* @public
|
|
1224
1737
|
*/
|
|
@@ -1245,6 +1758,10 @@ class DropList {
|
|
|
1245
1758
|
}
|
|
1246
1759
|
});
|
|
1247
1760
|
|
|
1761
|
+
if (p.needsRefilter) {
|
|
1762
|
+
this._refilterItems();
|
|
1763
|
+
}
|
|
1764
|
+
|
|
1248
1765
|
const el = p.el;
|
|
1249
1766
|
el.style.position = 'absolute';
|
|
1250
1767
|
el.classList.remove(`${p.baseClassName}__is-hiding`);
|
|
@@ -1256,6 +1773,14 @@ class DropList {
|
|
|
1256
1773
|
if (getComputedStyle(p.el).display === 'none')
|
|
1257
1774
|
p.el.style.display = 'block';
|
|
1258
1775
|
|
|
1776
|
+
p.lastPositionOptions = null;
|
|
1777
|
+
|
|
1778
|
+
if (positionOptions === undefined) {
|
|
1779
|
+
positionOptions = p.positionOptionsProvider?.() ?? p.lastPositionOptions;
|
|
1780
|
+
} else {
|
|
1781
|
+
p.lastPositionOptions = positionOptions;
|
|
1782
|
+
}
|
|
1783
|
+
|
|
1259
1784
|
if (positionOptions) {
|
|
1260
1785
|
const elComputedStyle = getComputedStyle(el);
|
|
1261
1786
|
|
|
@@ -1302,6 +1827,11 @@ class DropList {
|
|
|
1302
1827
|
|
|
1303
1828
|
p.hiding = true;
|
|
1304
1829
|
|
|
1830
|
+
if (this.isFilterPending()) {
|
|
1831
|
+
p.throttledRefilterItems.cancel();
|
|
1832
|
+
p.needsRefilter = true;
|
|
1833
|
+
}
|
|
1834
|
+
|
|
1305
1835
|
if (el) {
|
|
1306
1836
|
|
|
1307
1837
|
el.classList.add(`${p.baseClassName}__is-hiding`);
|
|
@@ -1322,7 +1852,8 @@ class DropList {
|
|
|
1322
1852
|
}
|
|
1323
1853
|
});
|
|
1324
1854
|
} else {
|
|
1325
|
-
|
|
1855
|
+
if (el.parentNode)
|
|
1856
|
+
remove(el);
|
|
1326
1857
|
el.classList.remove(`${p.baseClassName}__is-hiding`);
|
|
1327
1858
|
}
|
|
1328
1859
|
}
|
|
@@ -1371,15 +1902,22 @@ class DropList {
|
|
|
1371
1902
|
setFocusedItemAtIndex(itemIndex) {
|
|
1372
1903
|
const p = this._p;
|
|
1373
1904
|
|
|
1905
|
+
if (p.filteredItems) {
|
|
1906
|
+
const item = p.items[itemIndex];
|
|
1907
|
+
itemIndex = p.items.indexOf(item);
|
|
1908
|
+
}
|
|
1909
|
+
|
|
1374
1910
|
p.focusItemIndex = itemIndex;
|
|
1375
1911
|
|
|
1912
|
+
const items = p.filteredItems ?? p.items;
|
|
1913
|
+
|
|
1376
1914
|
let item = null;
|
|
1377
1915
|
if (itemIndex > -1)
|
|
1378
|
-
item =
|
|
1916
|
+
item = items[itemIndex];
|
|
1379
1917
|
if (item && item._nointeraction)
|
|
1380
1918
|
item = null;
|
|
1381
1919
|
if (itemIndex > -1) {
|
|
1382
|
-
this.
|
|
1920
|
+
this._scrollItemIndexIntoView(itemIndex);
|
|
1383
1921
|
}
|
|
1384
1922
|
let itemElement = item ? p.virtualListHelper.getItemElementAt(itemIndex) : null;
|
|
1385
1923
|
|
|
@@ -1434,6 +1972,7 @@ class DropList {
|
|
|
1434
1972
|
valueProp: p.valueProp,
|
|
1435
1973
|
renderItem: p.renderItem,
|
|
1436
1974
|
unrenderItem: p.unrenderItem,
|
|
1975
|
+
positionOptionsProvider: () => p.currentSubDropList.showOptions,
|
|
1437
1976
|
});
|
|
1438
1977
|
|
|
1439
1978
|
let onBlur = event => {
|
|
@@ -1481,7 +2020,7 @@ class DropList {
|
|
|
1481
2020
|
},
|
|
1482
2021
|
};
|
|
1483
2022
|
|
|
1484
|
-
droplist.show(
|
|
2023
|
+
droplist.show();
|
|
1485
2024
|
|
|
1486
2025
|
droplist.el.focus();
|
|
1487
2026
|
}
|
|
@@ -1535,7 +2074,7 @@ class DropList {
|
|
|
1535
2074
|
|
|
1536
2075
|
if (itemElement) {
|
|
1537
2076
|
p.currentSubDropList.showOptions.target = itemElement;
|
|
1538
|
-
p.currentSubDropList.droplist.relayout(
|
|
2077
|
+
p.currentSubDropList.droplist.relayout();
|
|
1539
2078
|
}
|
|
1540
2079
|
}
|
|
1541
2080
|
}
|
|
@@ -1546,7 +2085,8 @@ class DropList {
|
|
|
1546
2085
|
|
|
1547
2086
|
let itemIndex = item._nointeraction ? -1 : this._getItemIndex(item);
|
|
1548
2087
|
|
|
1549
|
-
|
|
2088
|
+
const items = p.items;
|
|
2089
|
+
if (itemIndex > -1 && items[itemIndex]._nointeraction)
|
|
1550
2090
|
itemIndex = -1;
|
|
1551
2091
|
|
|
1552
2092
|
return this.setFocusedItemAtIndex(itemIndex);
|
|
@@ -1562,7 +2102,14 @@ class DropList {
|
|
|
1562
2102
|
let itemEl = null;
|
|
1563
2103
|
|
|
1564
2104
|
if (itemIndex > -1 && !p.items[itemIndex]._nointeraction) {
|
|
1565
|
-
|
|
2105
|
+
if (p.filteredItems) {
|
|
2106
|
+
const item = p.items[itemIndex];
|
|
2107
|
+
itemIndex = p.filteredItems.indexOf(item);
|
|
2108
|
+
}
|
|
2109
|
+
|
|
2110
|
+
if (itemIndex > -1) {
|
|
2111
|
+
itemEl = p.virtualListHelper.getItemElementAt(itemIndex);
|
|
2112
|
+
}
|
|
1566
2113
|
}
|
|
1567
2114
|
|
|
1568
2115
|
this._setSingleSelectedItemEl(itemEl);
|
|
@@ -1575,7 +2122,8 @@ class DropList {
|
|
|
1575
2122
|
|
|
1576
2123
|
let itemIndex = item._nointeraction ? -1 : this._getItemIndex(item);
|
|
1577
2124
|
|
|
1578
|
-
|
|
2125
|
+
const items = p.items;
|
|
2126
|
+
if (itemIndex > -1 && items[itemIndex]._nointeraction)
|
|
1579
2127
|
itemIndex = -1;
|
|
1580
2128
|
|
|
1581
2129
|
return this.setSingleSelectedItemAtIndex(itemIndex);
|
|
@@ -1595,24 +2143,42 @@ class DropList {
|
|
|
1595
2143
|
|
|
1596
2144
|
isFirstItem() {
|
|
1597
2145
|
const p = this._p;
|
|
1598
|
-
|
|
2146
|
+
const items = p.filteredItems ?? p.items;
|
|
2147
|
+
return p.focusItemIndex === 0 && p.focusItemIndex < items.length;
|
|
1599
2148
|
}
|
|
1600
2149
|
|
|
1601
2150
|
isLastItem() {
|
|
1602
2151
|
const p = this._p;
|
|
1603
|
-
|
|
2152
|
+
const items = p.filteredItems ?? p.items;
|
|
2153
|
+
return p.focusItemIndex > -1 && p.focusItemIndex === items.length - 1;
|
|
1604
2154
|
}
|
|
1605
2155
|
|
|
1606
2156
|
scrollItemIndexIntoView(itemIndex) {
|
|
1607
2157
|
const p = this._p;
|
|
1608
2158
|
|
|
2159
|
+
if (this._hasScroll()) {
|
|
2160
|
+
if (p.filteredItems) {
|
|
2161
|
+
const item = p.items[itemIndex];
|
|
2162
|
+
itemIndex = p.items.indexOf(item);
|
|
2163
|
+
}
|
|
2164
|
+
|
|
2165
|
+
if (itemIndex !== -1) {
|
|
2166
|
+
this._scrollItemIndexIntoView(itemIndex);
|
|
2167
|
+
}
|
|
2168
|
+
}
|
|
2169
|
+
|
|
2170
|
+
return this;
|
|
2171
|
+
}
|
|
2172
|
+
|
|
2173
|
+
_scrollItemIndexIntoView(itemIndex) {
|
|
2174
|
+
const p = this._p;
|
|
2175
|
+
|
|
1609
2176
|
if (this._hasScroll()) {
|
|
1610
2177
|
const el = p.el, scrollTop = el.scrollTop;
|
|
1611
2178
|
|
|
1612
2179
|
let itemPos, previousPos = -1;
|
|
1613
2180
|
let maxIterations = 30; // Some zoom/scroll issues can make it so that it takes almost forever
|
|
1614
2181
|
|
|
1615
|
-
|
|
1616
2182
|
while (maxIterations-- > 0) {
|
|
1617
2183
|
itemPos = p.virtualListHelper.getItemPosition(itemIndex);
|
|
1618
2184
|
|
|
@@ -1635,8 +2201,6 @@ class DropList {
|
|
|
1635
2201
|
p.virtualListHelper.render();
|
|
1636
2202
|
}
|
|
1637
2203
|
}
|
|
1638
|
-
|
|
1639
|
-
return this;
|
|
1640
2204
|
}
|
|
1641
2205
|
|
|
1642
2206
|
/**
|
|
@@ -1697,7 +2261,8 @@ class DropList {
|
|
|
1697
2261
|
let itemIndex = -1;
|
|
1698
2262
|
|
|
1699
2263
|
if (item) {
|
|
1700
|
-
|
|
2264
|
+
const items = p.filteredItems ?? p.items;
|
|
2265
|
+
itemIndex = items.indexOf(item);
|
|
1701
2266
|
if (itemIndex === -1) {
|
|
1702
2267
|
let value = (item && item.value !== undefined) ? item.value : item;
|
|
1703
2268
|
let label = (item && item.label) ? item.label : value;
|
|
@@ -1845,12 +2410,25 @@ class DropList {
|
|
|
1845
2410
|
|
|
1846
2411
|
p.sink
|
|
1847
2412
|
.add(p.el, 'focus', event => {
|
|
2413
|
+
if (event.target === this.el && p.searchable)
|
|
2414
|
+
p.searchInput.focus();
|
|
2415
|
+
|
|
2416
|
+
if (event.relatedTarget &&
|
|
2417
|
+
this.elContains(event.relatedTarget, true) &&
|
|
2418
|
+
this.elContains(event.target, true))
|
|
2419
|
+
return;
|
|
2420
|
+
|
|
1848
2421
|
let itemEl = p.focusItemEl || // focused item
|
|
1849
2422
|
p.el.firstChild; // or the first item
|
|
1850
2423
|
|
|
1851
2424
|
this._focus(event, itemEl, null, false);
|
|
1852
|
-
})
|
|
2425
|
+
}, true)
|
|
1853
2426
|
.add(p.el, 'blur', event => {
|
|
2427
|
+
if (event.relatedTarget &&
|
|
2428
|
+
this.elContains(event.relatedTarget, true) &&
|
|
2429
|
+
this.elContains(event.target, true))
|
|
2430
|
+
return;
|
|
2431
|
+
|
|
1854
2432
|
setTimeout(() => {
|
|
1855
2433
|
if (this[DestroyedSymbol]) return;
|
|
1856
2434
|
|
|
@@ -1861,7 +2439,7 @@ class DropList {
|
|
|
1861
2439
|
this._delayBlurItemOnBlur();
|
|
1862
2440
|
this._trigger('blur', event);
|
|
1863
2441
|
});
|
|
1864
|
-
});
|
|
2442
|
+
}, true);
|
|
1865
2443
|
}
|
|
1866
2444
|
|
|
1867
2445
|
_hookKeyEvents() {
|
|
@@ -1870,6 +2448,21 @@ class DropList {
|
|
|
1870
2448
|
p.sink.add(p.el, 'keydown', evt => this._keydown(evt));
|
|
1871
2449
|
}
|
|
1872
2450
|
|
|
2451
|
+
_hookSearchEvents() {
|
|
2452
|
+
const p = this._p;
|
|
2453
|
+
|
|
2454
|
+
if (!p.searchInput)
|
|
2455
|
+
return;
|
|
2456
|
+
|
|
2457
|
+
p.sink.add(p.searchInput, 'input', () => {
|
|
2458
|
+
p.filterTerm = p.searchInput.value.trim();
|
|
2459
|
+
p.filteredItems = null;
|
|
2460
|
+
|
|
2461
|
+
this._trigger('search', { value: p.searchInput.value });
|
|
2462
|
+
p.throttledRefilterItems();
|
|
2463
|
+
});
|
|
2464
|
+
}
|
|
2465
|
+
|
|
1873
2466
|
_keydown(event) {
|
|
1874
2467
|
const p = this._p;
|
|
1875
2468
|
|
|
@@ -1914,7 +2507,8 @@ class DropList {
|
|
|
1914
2507
|
case VALUE_RIGHT:
|
|
1915
2508
|
if (event.key === VALUE_RIGHT && getComputedStyle(event.target).direction !== 'rtl' ||
|
|
1916
2509
|
event.key === VALUE_LEFT && getComputedStyle(event.target).direction === 'rtl') {
|
|
1917
|
-
|
|
2510
|
+
const items = p.filteredItems ?? p.items;
|
|
2511
|
+
let item = items[p.focusItemIndex];
|
|
1918
2512
|
if (p.focusItemIndex > -1 && item._subitems)
|
|
1919
2513
|
this._showSublist(item, p.focusItemEl);
|
|
1920
2514
|
} else {
|
|
@@ -1944,7 +2538,11 @@ class DropList {
|
|
|
1944
2538
|
|
|
1945
2539
|
default: {
|
|
1946
2540
|
if (event.type === 'keydown') return;
|
|
1947
|
-
|
|
2541
|
+
|
|
2542
|
+
// Inline search box not available, then support typing to focus by first letters
|
|
2543
|
+
if (!p.searchable)
|
|
2544
|
+
this._keydownFreeType(event);
|
|
2545
|
+
|
|
1948
2546
|
preventDefault = false;
|
|
1949
2547
|
}
|
|
1950
2548
|
}
|
|
@@ -1969,12 +2567,14 @@ class DropList {
|
|
|
1969
2567
|
|
|
1970
2568
|
let focusItemIndex = p.focusItemIndex;
|
|
1971
2569
|
|
|
2570
|
+
const items = p.filteredItems ?? p.items;
|
|
2571
|
+
|
|
1972
2572
|
// These are all the possible matches for the text typed in so far
|
|
1973
|
-
for (let i = 0, count =
|
|
2573
|
+
for (let i = 0, count = items.length; i < count; i++) {
|
|
1974
2574
|
if (matchIndex !== -1 && i < focusItemIndex)
|
|
1975
2575
|
continue; // We are only interested in first match + match after the focused item
|
|
1976
2576
|
|
|
1977
|
-
item =
|
|
2577
|
+
item = items[i];
|
|
1978
2578
|
if (regex.test(item.label)) {
|
|
1979
2579
|
matchIndex = i;
|
|
1980
2580
|
if (focusItemIndex === -1 || i >= focusItemIndex)
|
|
@@ -1988,11 +2588,11 @@ class DropList {
|
|
|
1988
2588
|
keyword = character;
|
|
1989
2589
|
regex = new RegExp(`^${escapeRegex(keyword)}`, 'i');
|
|
1990
2590
|
|
|
1991
|
-
for (let i = 0, count =
|
|
2591
|
+
for (let i = 0, count = items.length; i < count; i++) {
|
|
1992
2592
|
if (matchIndex !== -1 && i < focusItemIndex)
|
|
1993
2593
|
continue; // We are only interested in first match + match after the focused item
|
|
1994
2594
|
|
|
1995
|
-
item =
|
|
2595
|
+
item = items[i];
|
|
1996
2596
|
if (regex.test(item.label)) {
|
|
1997
2597
|
matchIndex = i;
|
|
1998
2598
|
if (focusItemIndex === -1 || i >= focusItemIndex)
|
|
@@ -2006,7 +2606,7 @@ class DropList {
|
|
|
2006
2606
|
this._focus(evt, next || null, matchIndex, true);
|
|
2007
2607
|
|
|
2008
2608
|
if (!this.isVisible()) {
|
|
2009
|
-
this.triggerItemSelection(next ? null :
|
|
2609
|
+
this.triggerItemSelection(next ? null : items[matchIndex], evt);
|
|
2010
2610
|
}
|
|
2011
2611
|
|
|
2012
2612
|
// Record the last filter used
|
|
@@ -2033,6 +2633,10 @@ class DropList {
|
|
|
2033
2633
|
itemIndex = p.virtualListHelper.getItemIndexFromElement(itemEl);
|
|
2034
2634
|
}
|
|
2035
2635
|
|
|
2636
|
+
if (itemIndex > -1 && itemEl?.[ItemSymbol]?.[ItemSymbol] === NoResultsItemSymbol) {
|
|
2637
|
+
itemIndex = undefined;
|
|
2638
|
+
}
|
|
2639
|
+
|
|
2036
2640
|
if (itemIndex > -1) {
|
|
2037
2641
|
this.scrollItemIndexIntoView(itemIndex);
|
|
2038
2642
|
} else if (itemIndex === undefined) {
|
|
@@ -2058,7 +2662,8 @@ class DropList {
|
|
|
2058
2662
|
p.focusItemEl = focusItemEl;
|
|
2059
2663
|
p.focusItemIndex = itemIndex;
|
|
2060
2664
|
|
|
2061
|
-
const
|
|
2665
|
+
const items = p.filteredItems ?? p.items;
|
|
2666
|
+
const item = items[itemIndex];
|
|
2062
2667
|
this._trigger('itemfocus', {
|
|
2063
2668
|
value: item.value,
|
|
2064
2669
|
item: item[ItemSymbol] ?? item,
|
|
@@ -2093,12 +2698,13 @@ class DropList {
|
|
|
2093
2698
|
const p = this._p;
|
|
2094
2699
|
|
|
2095
2700
|
let next, nextIndex, directionUp = false;
|
|
2701
|
+
const items = p.filteredItems ?? p.items;
|
|
2096
2702
|
|
|
2097
2703
|
if (direction === 'first') {
|
|
2098
2704
|
nextIndex = 0;
|
|
2099
2705
|
directionUp = false;
|
|
2100
2706
|
} else if (direction === 'last') {
|
|
2101
|
-
nextIndex =
|
|
2707
|
+
nextIndex = items.length - 1;
|
|
2102
2708
|
directionUp = true;
|
|
2103
2709
|
} else if (direction === 'prev') {
|
|
2104
2710
|
if (!this.hasFocusedItem())
|
|
@@ -2106,7 +2712,7 @@ class DropList {
|
|
|
2106
2712
|
|
|
2107
2713
|
nextIndex = p.focusItemIndex - 1;
|
|
2108
2714
|
if (nextIndex === -1) {
|
|
2109
|
-
nextIndex =
|
|
2715
|
+
nextIndex = items.length - 1;
|
|
2110
2716
|
}
|
|
2111
2717
|
|
|
2112
2718
|
directionUp = true;
|
|
@@ -2115,7 +2721,7 @@ class DropList {
|
|
|
2115
2721
|
return this._move('first', event);
|
|
2116
2722
|
|
|
2117
2723
|
nextIndex = p.focusItemIndex + 1;
|
|
2118
|
-
if (nextIndex ===
|
|
2724
|
+
if (nextIndex === items.length) {
|
|
2119
2725
|
nextIndex = 0;
|
|
2120
2726
|
}
|
|
2121
2727
|
|
|
@@ -2144,8 +2750,8 @@ class DropList {
|
|
|
2144
2750
|
|
|
2145
2751
|
if (nextIndex < 0) {
|
|
2146
2752
|
nextIndex = 0;
|
|
2147
|
-
} else if (nextIndex >=
|
|
2148
|
-
nextIndex =
|
|
2753
|
+
} else if (nextIndex >= items.length) {
|
|
2754
|
+
nextIndex = items.length;
|
|
2149
2755
|
}
|
|
2150
2756
|
} else if (p.focusItemEl) {
|
|
2151
2757
|
let base = getElementOffset(p.focusItemEl).top;
|
|
@@ -2182,13 +2788,13 @@ class DropList {
|
|
|
2182
2788
|
return;
|
|
2183
2789
|
}
|
|
2184
2790
|
|
|
2185
|
-
let itemCount =
|
|
2791
|
+
let itemCount = items.length;
|
|
2186
2792
|
|
|
2187
2793
|
if (nextIndex >= itemCount) {
|
|
2188
2794
|
return;
|
|
2189
2795
|
}
|
|
2190
2796
|
|
|
2191
|
-
let item =
|
|
2797
|
+
let item = items[nextIndex];
|
|
2192
2798
|
// noinspection UnnecessaryLocalVariableJS
|
|
2193
2799
|
let startedAtIndex = nextIndex;
|
|
2194
2800
|
|
|
@@ -2205,7 +2811,7 @@ class DropList {
|
|
|
2205
2811
|
}
|
|
2206
2812
|
}
|
|
2207
2813
|
|
|
2208
|
-
item =
|
|
2814
|
+
item = items[nextIndex];
|
|
2209
2815
|
|
|
2210
2816
|
if (nextIndex === startedAtIndex) {
|
|
2211
2817
|
break;
|
|
@@ -2221,7 +2827,9 @@ class DropList {
|
|
|
2221
2827
|
}
|
|
2222
2828
|
|
|
2223
2829
|
_hasScroll() {
|
|
2224
|
-
|
|
2830
|
+
const p = this._p;
|
|
2831
|
+
const menuEl = p.menuEl;
|
|
2832
|
+
return menuEl.clientHeight < menuEl.scrollHeight;
|
|
2225
2833
|
}
|
|
2226
2834
|
|
|
2227
2835
|
_updateGroupStateForItem(item) {
|
|
@@ -2236,7 +2844,7 @@ class DropList {
|
|
|
2236
2844
|
let affectedItems = 0;
|
|
2237
2845
|
|
|
2238
2846
|
if (p.autoCheckGroupChildren) {
|
|
2239
|
-
let items = p.items;
|
|
2847
|
+
let items = p.filteredItems ?? p.items;
|
|
2240
2848
|
let groupIndex = items.indexOf(item);
|
|
2241
2849
|
|
|
2242
2850
|
for (let i = groupIndex + 1, len = items.length; i < len; i++) {
|
|
@@ -2279,7 +2887,7 @@ class DropList {
|
|
|
2279
2887
|
affectedItems: affectedItems,
|
|
2280
2888
|
});
|
|
2281
2889
|
} else if (p.groupCount > 0 && p.autoCheckGroupChildren) {
|
|
2282
|
-
let items = p.items;
|
|
2890
|
+
let items = p.filteredItems ?? p.items;
|
|
2283
2891
|
let itemIndex = items.indexOf(item);
|
|
2284
2892
|
let groupIndex = -1;
|
|
2285
2893
|
|
|
@@ -2305,7 +2913,7 @@ class DropList {
|
|
|
2305
2913
|
if (!(p.multi && p.autoCheckGroupChildren && groupIndex > -1))
|
|
2306
2914
|
return this;
|
|
2307
2915
|
|
|
2308
|
-
let items = p.items;
|
|
2916
|
+
let items = p.filteredItems ?? p.items;
|
|
2309
2917
|
let groupItem = items[groupIndex];
|
|
2310
2918
|
|
|
2311
2919
|
if (!groupItem || !groupItem._group) return this;
|
|
@@ -2431,7 +3039,7 @@ class DropList {
|
|
|
2431
3039
|
_determineVirtualMode(targetItemCount) {
|
|
2432
3040
|
const p = this._p;
|
|
2433
3041
|
|
|
2434
|
-
let items = p.items;
|
|
3042
|
+
let items = p.filteredItems ?? p.items;
|
|
2435
3043
|
if (targetItemCount === undefined) {
|
|
2436
3044
|
targetItemCount = items.length;
|
|
2437
3045
|
}
|
|
@@ -2451,7 +3059,21 @@ class DropList {
|
|
|
2451
3059
|
// NOTE: a "measure" item will not have full data of original item.
|
|
2452
3060
|
// so for a custom renderer - we try to send original item, and fallback to our private list item.
|
|
2453
3061
|
|
|
2454
|
-
|
|
3062
|
+
const originalItem = item[ItemSymbol];
|
|
3063
|
+
|
|
3064
|
+
if (originalItem === NoResultsItemSymbol) {
|
|
3065
|
+
if (p.renderNoResultsItem && p.renderNoResultsItem(item, itemEl) !== false) {
|
|
3066
|
+
return true;
|
|
3067
|
+
}
|
|
3068
|
+
|
|
3069
|
+
itemEl.appendChild(createElement('div', {
|
|
3070
|
+
class: 'droplist-no-results-content',
|
|
3071
|
+
textContent: p.noResultsText,
|
|
3072
|
+
}));
|
|
3073
|
+
return;
|
|
3074
|
+
}
|
|
3075
|
+
|
|
3076
|
+
if (!p.renderItem || p.renderItem(originalItem || item, itemEl) === false) {
|
|
2455
3077
|
itemEl.appendChild(createElement('span', {
|
|
2456
3078
|
class: `${p.baseClassName}__item_label`,
|
|
2457
3079
|
textContent: item.label,
|
|
@@ -2493,7 +3115,7 @@ class DropList {
|
|
|
2493
3115
|
}
|
|
2494
3116
|
|
|
2495
3117
|
let autoWidth = 0;
|
|
2496
|
-
if (!p.useExactTargetWidth) {
|
|
3118
|
+
if (!p.useExactTargetWidth || !targetWidth) {
|
|
2497
3119
|
if (p.estimateWidth || p.virtualListHelper.isVirtual()) {
|
|
2498
3120
|
autoWidth = p.lastMeasureItemWidth;
|
|
2499
3121
|
} else {
|