@danielgindi/selectbox 1.0.147 → 2.0.1

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/dist/lib.umd.js CHANGED
@@ -1,5 +1,5 @@
1
1
  /*!
2
- * @danielgindi/selectbox 1.0.147
2
+ * @danielgindi/selectbox 2.0.1
3
3
  * git://github.com/danielgindi/selectbox.git
4
4
  */
5
5
  (function (global, factory) {
@@ -10,9 +10,57 @@
10
10
 
11
11
  var escapeRegex = (value) => value.replace(/[-[\]{}()*+?.,\\^$|#\s]/g, '\\$&');
12
12
 
13
+ const throttle = (func, wait, options) => {
14
+ let timeout, context, args, result;
15
+ let previous = 0;
16
+ if (!options) options = {};
17
+
18
+ const later = () => {
19
+ previous = options.leading === false ? 0 : Date.now();
20
+ timeout = null;
21
+ result = func.apply(context, args);
22
+ if (!timeout) context = args = null;
23
+ };
24
+
25
+ const throttled = function () {
26
+ const now = Date.now();
27
+ if (!previous && options.leading === false)
28
+ previous = now;
29
+
30
+ const remaining = wait - (now - previous);
31
+ context = this;
32
+ args = arguments;
33
+ if (remaining <= 0 || remaining > wait) {
34
+ if (timeout) {
35
+ clearTimeout(timeout);
36
+ timeout = null;
37
+ }
38
+ previous = now;
39
+ result = func.apply(context, args);
40
+ if (!timeout) context = args = null;
41
+ } else if (!timeout && options.trailing !== false) {
42
+ timeout = setTimeout(later, remaining);
43
+ }
44
+ return result;
45
+ };
46
+
47
+ throttled.cancel = () => {
48
+ clearTimeout(timeout);
49
+ previous = 0;
50
+ timeout = context = args = null;
51
+ };
52
+
53
+ throttled.isScheduled = () => {
54
+ return !!timeout;
55
+ };
56
+
57
+ return throttled;
58
+ };
59
+
13
60
  const ItemSymbol$1 = Symbol('item');
14
61
  const DestroyedSymbol$1 = Symbol('destroyed');
15
62
  const GhostSymbol = Symbol('ghost');
63
+ const NoResultsItemSymbol = Symbol('no_results_items');
16
64
 
17
65
  const hasOwnProperty = Object.prototype.hasOwnProperty;
18
66
 
@@ -38,7 +86,17 @@
38
86
  * @property {string} [valueProp='value']
39
87
  * @property {function(item: DropList.ItemBase, itemEl: Element):(*|false)} [renderItem] Function to call when rendering an item element
40
88
  * @property {function(item: DropList.ItemBase, itemEl: Element)} [unrenderItem] Function to call when rendering an item element
89
+ * @property {function(item: DropList.ItemBase, itemEl: Element):(*|false)} [renderNoResultsItem]
90
+ * @property {function(item: DropList.ItemBase, itemEl: Element)} [unrenderNoResultsItem]
41
91
  * @property {function(name: string, data: *)} [on]
92
+ * @property {boolean} [searchable=false] include inline search box
93
+ * @property {string} [noResultsText='No matching results'] text for no results (empty for none)
94
+ * @property {number} [filterThrottleWindow=300] throttle time (milliseconds) for filtering
95
+ * @property {boolean} [filterOnEmptyTerm=false] call the filter function on empty search term too
96
+ * @property {boolean} [filterGroups=false] should groups be filtered?
97
+ * @property {boolean} [filterEmptyGroups=false] should empty groups be filtered out?
98
+ * @property {function(items: DropList.ItemBase[], term: string):(DropList.ItemBase[]|null)} [filterFn]
99
+ * @property {function(dropList: DropList):DropList.PositionOptions} [positionOptionsProvider]
42
100
  * */
43
101
  /** */
44
102
 
@@ -101,7 +159,14 @@
101
159
  labelProp: 'label',
102
160
  valueProp: 'value',
103
161
 
104
- on: null
162
+ on: null,
163
+
164
+ searchable: false,
165
+ noResultsText: 'No matching results',
166
+ filterThrottleWindow: 300,
167
+ filterOnEmptyTerm: false,
168
+ filterGroups: false,
169
+ filterEmptyGroups: false
105
170
  };
106
171
 
107
172
  /*
@@ -162,17 +227,35 @@
162
227
  valueProp: o.valueProp,
163
228
  renderItem: o.renderItem,
164
229
  unrenderItem: o.unrenderItem,
230
+ renderNoResultsItem: o.renderNoResultsItem,
231
+ unrenderNoResultsItem: o.unrenderNoResultsItem,
165
232
  on: o.on || null,
233
+ positionOptionsProvider: o.positionOptionsProvider ?? null,
234
+
235
+ searchable: o.searchable,
236
+
166
237
  silenceEvents: true,
167
238
  mitt: mitt(),
168
239
 
240
+ filterThrottleWindow: o.filterThrottleWindow,
241
+ filterOnEmptyTerm: o.filterOnEmptyTerm,
242
+ filterGroups: o.filterGroups,
243
+ filterEmptyGroups: o.filterEmptyGroups,
244
+ filterFn: o.filterFn,
245
+ filteredItems: null,
246
+ filterTerm: '',
247
+ needsRefilter: false,
248
+ throttledRefilterItems: null,
249
+
169
250
  focusItemIndex: -1,
170
251
  focusItemEl: null,
171
252
 
172
253
  sink: new DomEventsSink()
173
254
  };
174
255
 
175
- let classes = [p.baseClassName];
256
+ const baseClass = p.baseClassName + '_wrapper';
257
+
258
+ let classes = [baseClass];
176
259
 
177
260
  if (p.additionalClasses) {
178
261
  classes = classes.concat((p.additionalClasses + '').split(' ').filter((x) => x));
@@ -183,31 +266,54 @@
183
266
  top: '-9999px'
184
267
  };
185
268
 
186
- let el = o.el;
187
- if (el instanceof Element) {
188
- p.elOriginalDisplay = el.style.display || '';
189
- el.classList.add(...classes);
190
- el.role = 'menu';
191
- Css.setCssProps(/**@type ElementCSSInlineStyle*/el, initialCss);
269
+ let wrapperEl = o.el;
270
+
271
+ if (wrapperEl instanceof Element) {
272
+ p.elOriginalDisplay = wrapperEl.style.display || '';
273
+ wrapperEl.classList.add(...classes);
274
+ Css.setCssProps(/**@type ElementCSSInlineStyle*/wrapperEl, initialCss);
192
275
  p.ownsEl = false;
193
276
  } else {
194
- el = Dom.createElement('ul', {
277
+ wrapperEl = Dom.createElement('div', {
195
278
  class: classes.join(' '),
196
- role: 'menu',
197
279
  css: initialCss
198
280
  });
199
281
  }
200
282
 
201
- p.el = el;
283
+ let menuEl = Dom.createElement('ul');
284
+ menuEl.role = 'menu';
202
285
 
203
- p.items = [];
286
+ if (o.searchable) {
287
+ p.headerEl = Dom.createElement('div', {
288
+ class: p.baseClassName + '_header'
289
+ });
204
290
 
205
- p.groupCount = 0; // This will keep state of how many `group` items we have
291
+ p.searchInput = Dom.createElement('input', {
292
+ type: 'search',
293
+ role: 'searchbox',
294
+ tabindex: '0',
295
+ autocorrect: 'off',
296
+ autocomplete: 'off',
297
+ autocapitalize: 'off',
298
+ spellcheck: 'false',
299
+ 'aria-autocomplete': 'list'
300
+ });
301
+
302
+ p.headerEl.appendChild(p.searchInput);
303
+ wrapperEl.appendChild(p.headerEl);
304
+ }
206
305
 
306
+ wrapperEl.appendChild(menuEl);
307
+
308
+ p.el = wrapperEl;
309
+ p.menuEl = menuEl;
310
+
311
+ p.items = [];
312
+ p.groupCount = 0; // This will keep state of how many `group` items we have
207
313
  p.mouseHandled = false;
208
314
 
209
315
  p.virtualListHelper = new VirtualListHelper({
210
- list: p.el,
316
+ list: p.menuEl,
211
317
  virtual: true,
212
318
  buffer: 5,
213
319
  estimatedItemHeight: o.estimatedItemHeight || 20,
@@ -234,7 +340,19 @@
234
340
  };
235
341
  itemEl.setAttribute('aria-hidden', 'true');
236
342
  } else {
237
- item = p.items[index];
343
+ const items = p.filteredItems ?? p.items;
344
+ if (items.length === 0 && p.noResultsText) {
345
+ item = {
346
+ value: NoResultsItemSymbol,
347
+ label: p.noResultsText,
348
+ _nointeraction: true,
349
+ _nocheck: true,
350
+
351
+ [ItemSymbol$1]: NoResultsItemSymbol
352
+ };
353
+ } else {
354
+ item = items[index];
355
+ }
238
356
  }
239
357
 
240
358
  if (!item) {
@@ -280,36 +398,20 @@
280
398
  }
281
399
  });
282
400
 
283
- if (typeof p.unrenderItem === 'function') {
284
- const fn = p.unrenderItem;
285
- p.virtualListHelper.setOnItemUnrender((el) => {
286
- try {
287
- fn(el[ItemSymbol$1][ItemSymbol$1], el);
288
- } catch (err) {
289
- console.error(err); // eslint-disable-line no-console
290
- }
291
- delete el[ItemSymbol$1];
292
-
293
- if (p.focusItemEl === el)
294
- p.focusItemEl = null;
295
- });
296
- } else {
297
- p.virtualListHelper.setOnItemUnrender((el) => {
298
- delete el[ItemSymbol$1];
299
-
300
- if (p.focusItemEl === el)
301
- p.focusItemEl = null;
302
- });
303
- }
401
+ this._setupUnrenderFunction();
304
402
 
305
403
  if (p.capturesFocus) {
306
- el.tabIndex = 0;
404
+ wrapperEl.tabIndex = 0;
307
405
  }
308
406
 
407
+ this.setFilterThrottleWindow(o.filterThrottleWindow);
408
+ this.setNoResultsText(o.noResultsText);
409
+
309
410
  this._hookMouseEvents();
310
411
  this._hookTouchEvents();
311
412
  this._hookFocusEvents();
312
413
  this._hookKeyEvents();
414
+ this._hookSearchEvents();
313
415
 
314
416
  this.silenceEvents = false;
315
417
  }
@@ -327,7 +429,7 @@
327
429
  p.sink.remove();
328
430
  p.virtualListHelper.destroy();
329
431
 
330
- if (p.el) {
432
+ if (p.el?.parentNode) {
331
433
  DomCompat.remove(p.el);
332
434
  }
333
435
 
@@ -336,13 +438,15 @@
336
438
  p.currentSubDropList = null;
337
439
  }
338
440
 
441
+ if (p.throttledRefilterItems)
442
+ p.throttledRefilterItems.cancel();
443
+
339
444
  if (!p.ownsEl) {
340
445
  for (let name of Array.from(p.el.classList)) {
341
446
  if (name.startsWith(p.baseClassName)) {
342
447
  p.el.classList.remove(name);
343
448
  }
344
449
  }
345
- p.el.removeAttribute('role');
346
450
  for (let key of ['position', 'left', 'top', 'right', 'bottom', 'z-index']) {
347
451
  p.el.style[key] = '';
348
452
  }
@@ -357,6 +461,8 @@
357
461
  delete p.lastPositionTarget;
358
462
  }
359
463
 
464
+ delete p.lastPositionOptions;
465
+
360
466
  this._p = null;
361
467
  }
362
468
 
@@ -440,16 +546,47 @@
440
546
  */
441
547
  setUnrenderItem(fn) {
442
548
  const p = this._p;
443
-
444
549
  p.unrenderItem = fn;
550
+ this._setupUnrenderFunction();
551
+ return this;
552
+ }
445
553
 
446
- if (typeof p.unrenderItem === 'function') {
554
+ /**
555
+ * @param {(function(item: DropList.ItemBase, itemEl: Element):(*|false))|null} render
556
+ * @param {(function(item: DropList.ItemBase, itemEl: Element))|null} unrender
557
+ * @returns {DropList}
558
+ */
559
+ setRenderNoResultsItem(render, unrender) {
560
+ const p = this._p;
561
+ p.renderNoResultsItem = render;
562
+ p.unrenderNoResultsItem = unrender;
563
+ this._setupUnrenderFunction();
564
+ return this;
565
+ }
566
+
567
+ /**
568
+ * @private
569
+ */
570
+ _setupUnrenderFunction() {
571
+ const p = this._p;
572
+
573
+ if (typeof p.unrenderItem === 'function' || typeof p.unrenderNoResultsItem === 'function') {
447
574
  const fn = p.unrenderItem;
575
+ const fnNoResults = p.unrenderNoResultsItem;
448
576
  p.virtualListHelper.setOnItemUnrender((el) => {
449
- try {
450
- fn(el[ItemSymbol$1][ItemSymbol$1], el);
451
- } catch (err) {
452
- console.error(err); // eslint-disable-line no-console
577
+ const item = el[ItemSymbol$1];
578
+ if (item === NoResultsItemSymbol) {
579
+ try {
580
+ fnNoResults(item, el);
581
+ } catch (err) {
582
+ console.error(err); // eslint-disable-line no-console
583
+ }
584
+ } else {
585
+ try {
586
+ fn(item[ItemSymbol$1], el);
587
+ } catch (err) {
588
+ console.error(err); // eslint-disable-line no-console
589
+ }
453
590
  }
454
591
  delete el[ItemSymbol$1];
455
592
 
@@ -484,7 +621,8 @@
484
621
  if (!el)
485
622
  return;
486
623
 
487
- let classes = [p.baseClassName];
624
+ const baseClass = p.baseClassName + '_wrapper';
625
+ let classes = [baseClass];
488
626
 
489
627
  if (p.direction === 'ltr' || p.direction === 'rtl')
490
628
  classes.push(`${p.baseClassName}__` + p.direction);
@@ -511,7 +649,8 @@
511
649
  p.focusItemEl = null;
512
650
  }
513
651
 
514
- const item = p.items[p.focusItemIndex];
652
+ const items = p.filteredItems ?? p.items;
653
+ const item = items[p.focusItemIndex];
515
654
  p.focusItemIndex = -1;
516
655
 
517
656
  if (!item) {
@@ -662,9 +801,24 @@
662
801
  }
663
802
  }
664
803
 
665
- p.virtualListHelper.
666
- addItemsAt(itemsToAdd.length, atIndex === -1 ? atIndex : atIndex - itemsToAdd.length).
667
- render();
804
+ if (!p.filteredItems) {
805
+ let hadNoResultItem = p.hasNoResultsItem;
806
+ p.hasNoResultsItem = p.items.length === 0 && !!p.noResultsText;
807
+ if (p.hasNoResultsItem) {
808
+ p.virtualListHelper.
809
+ setCount(1).
810
+ render();
811
+ } else {
812
+ if (hadNoResultItem)
813
+ p.virtualListHelper.removeItemsAt(1, 0);
814
+
815
+ p.virtualListHelper.
816
+ addItemsAt(itemsToAdd.length, atIndex === -1 ? atIndex : atIndex - itemsToAdd.length).
817
+ render();
818
+ }
819
+ } else {
820
+ p.needsRefilter = true;
821
+ }
668
822
 
669
823
  return this;
670
824
  }
@@ -678,9 +832,11 @@
678
832
  const p = this._p;
679
833
 
680
834
  p.items.length = 0;
835
+ p.filteredItems = null;
681
836
  p.groupCount = 0;
682
837
 
683
- p.virtualListHelper.setCount(0);
838
+ p.hasNoResultsItem = !!p.noResultsText;
839
+ p.virtualListHelper.setCount(p.hasNoResultsItem ? 1 : 0);
684
840
 
685
841
  this.addItems(items);
686
842
  this.updateSublist();
@@ -731,9 +887,18 @@
731
887
  if (hasOwnProperty.call(newItem, '_child'))
732
888
  item._child = !!newItem._child;
733
889
 
734
- if (p.virtualListHelper.isItemRendered(itemIndex)) {
890
+ let virtualItemIndex = itemIndex;
891
+ if (p.filteredItems) {
892
+ virtualItemIndex = p.filteredItems.indexOf(item);
893
+ if (virtualItemIndex !== -1) {
894
+ p.filteredItems.splice(virtualItemIndex, 1);
895
+ }
896
+ }
897
+
898
+ if (virtualItemIndex !== -1 &&
899
+ p.virtualListHelper.isItemRendered(virtualItemIndex)) {
735
900
  p.virtualListHelper.
736
- refreshItemAt(itemIndex).
901
+ refreshItemAt(virtualItemIndex).
737
902
  render();
738
903
  }
739
904
 
@@ -752,9 +917,27 @@
752
917
  p.groupCount--;
753
918
  }
754
919
 
755
- p.virtualListHelper.
756
- removeItemsAt(itemIndex, 1).
757
- render();
920
+ let virtualItemIndex = itemIndex;
921
+ if (p.filteredItems) {
922
+ virtualItemIndex = p.filteredItems.indexOf(spliced[0]);
923
+ if (virtualItemIndex !== -1) {
924
+ p.filteredItems.splice(virtualItemIndex, 1);
925
+ }
926
+ }
927
+
928
+ if (virtualItemIndex !== -1) {
929
+ p.hasNoResultsItem = (p.filteredItems ?? p.items).length === 0 && !!p.noResultsText;
930
+
931
+ if (p.hasNoResultsItem) {
932
+ p.virtualListHelper.
933
+ setCount(1).
934
+ render();
935
+ } else {
936
+ p.virtualListHelper.
937
+ removeItemsAt(virtualItemIndex, 1).
938
+ render();
939
+ }
940
+ }
758
941
 
759
942
  return this;
760
943
  }
@@ -763,10 +946,13 @@
763
946
  const p = this._p;
764
947
 
765
948
  p.items.length = 0;
949
+ p.filteredItems = null;
766
950
  p.groupCount = 0;
767
951
 
952
+ p.hasNoResultsItem = !!p.noResultsText;
953
+
768
954
  p.virtualListHelper.
769
- setCount(0).
955
+ setCount(p.hasNoResultsItem ? 1 : 0).
770
956
  render();
771
957
 
772
958
  return this;
@@ -802,8 +988,23 @@
802
988
  itemIndexByValue(value) {
803
989
  const p = this._p;
804
990
 
805
- for (let i = 0, count = p.items.length; i < count; i++) {
806
- let item = p.items[i];
991
+ const items = p.items;
992
+ for (let i = 0, count = items.length; i < count; i++) {
993
+ let item = items[i];
994
+ if (item.value === value) {
995
+ return i;
996
+ }
997
+ }
998
+
999
+ return -1;
1000
+ }
1001
+
1002
+ filteredItemIndexByValue(value) {
1003
+ const p = this._p;
1004
+
1005
+ const items = p.filteredItems ?? p.items;
1006
+ for (let i = 0, count = items.length; i < count; i++) {
1007
+ let item = items[i];
807
1008
  if (item.value === value) {
808
1009
  return i;
809
1010
  }
@@ -815,8 +1016,23 @@
815
1016
  itemIndexByValueOrLabel(value, label) {
816
1017
  const p = this._p;
817
1018
 
818
- for (let i = 0, count = p.items.length; i < count; i++) {
819
- let item = p.items[i];
1019
+ const items = p.items;
1020
+ for (let i = 0, count = items.length; i < count; i++) {
1021
+ let item = items[i];
1022
+ if (item.value === value || item.label === label) {
1023
+ return i;
1024
+ }
1025
+ }
1026
+
1027
+ return -1;
1028
+ }
1029
+
1030
+ filteredItemIndexByValueOrLabel(value, label) {
1031
+ const p = this._p;
1032
+
1033
+ const items = p.filteredItems ?? p.items;
1034
+ for (let i = 0, count = items.length; i < count; i++) {
1035
+ let item = items[i];
820
1036
  if (item.value === value || item.label === label) {
821
1037
  return i;
822
1038
  }
@@ -825,6 +1041,36 @@
825
1041
  return -1;
826
1042
  }
827
1043
 
1044
+ itemIndexByItem(item) {
1045
+ const p = this._p;
1046
+
1047
+ const items = p.items;
1048
+
1049
+ for (let i = 0, count = items.length; i < count; i++) {
1050
+ let it = items[i];
1051
+ if (it[ItemSymbol$1] === item) {
1052
+ return i;
1053
+ }
1054
+ }
1055
+
1056
+ return -1;
1057
+ }
1058
+
1059
+ filteredItemIndexByItem(item) {
1060
+ const p = this._p;
1061
+
1062
+ const items = p.filteredItems ?? p.items;
1063
+
1064
+ for (let i = 0, count = items.length; i < count; i++) {
1065
+ let it = items[i];
1066
+ if (it[ItemSymbol$1] === item) {
1067
+ return i;
1068
+ }
1069
+ }
1070
+
1071
+ return -1;
1072
+ }
1073
+
828
1074
  items() {
829
1075
  return this._p.items.map((x) => x[ItemSymbol$1]);
830
1076
  }
@@ -841,19 +1087,320 @@
841
1087
  return this._p.items[index]?.[ItemSymbol$1];
842
1088
  }
843
1089
 
1090
+ filteredItemAtIndex(index) {
1091
+ const p = this._p;
1092
+ const items = p.filteredItems ?? p.items;
1093
+ return items[index]?.[ItemSymbol$1];
1094
+ }
1095
+
1096
+ /**
1097
+ * @param {function(dropList: DropList):DropList.PositionOptions} fn
1098
+ * @returns {DropList}
1099
+ */
1100
+ setPositionOptionsProvider(fn) {
1101
+ const p = this._p;
1102
+ if (p.positionOptionsProvider === fn)
1103
+ return this;
1104
+ p.positionOptionsProvider = fn ?? null;
1105
+ this.relayout();
1106
+ return this;
1107
+ }
1108
+
1109
+ /**
1110
+ * @returns {function(dropList: DropList):DropList.PositionOptions|null}
1111
+ */
1112
+ getPositionOptionsProvider() {
1113
+ const p = this._p;
1114
+ return p.positionOptionsProvider;
1115
+ }
1116
+
1117
+ /**
1118
+ * @param {string} term
1119
+ * @param {boolean} [performSearch=false] should actually perform the search, or just set the input's text?
1120
+ * @returns {DropList}
1121
+ */
1122
+ setSearchTerm(term) {let performSearch = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : false;
1123
+ const p = this._p;
1124
+
1125
+ if (p.searchInput) {
1126
+ p.searchInput.value = term;
1127
+ }
1128
+
1129
+ if (performSearch) {
1130
+ p.filterTerm = term.trim();
1131
+ p.filteredItems = null;
1132
+
1133
+ this._trigger('search', { value: term });
1134
+ p.throttledRefilterItems();
1135
+ }
1136
+
1137
+ return this;
1138
+ }
1139
+
1140
+ isFilterPending() {
1141
+ const p = this._p;
1142
+
1143
+ return !!(p.throttledRefilterItems.isScheduled() ||
1144
+ !p.filteredItems && (p.filterTerm || p.filterOnEmptyTerm && p.filterFn));
1145
+ }
1146
+
1147
+ /** @private */
1148
+ _refilterItems() {
1149
+ const p = this._p;
1150
+
1151
+ const term = p.filterTerm;
1152
+ const filterGroups = p.filterGroups;
1153
+ const filterEmptyGroups = p.filterEmptyGroups;
1154
+
1155
+ if (term || p.filterOnEmptyTerm && p.filterFn) {
1156
+ let fn = p.filterFn;
1157
+
1158
+ let filteredItems;
1159
+
1160
+ if (typeof fn === 'function') {
1161
+ filteredItems = p.filterFn(p.items, term);
1162
+ }
1163
+
1164
+ // If there was no filter function, or it gave up on filtering.
1165
+ if (!Array.isArray(filteredItems)) {
1166
+ if (term) {
1167
+ const matcher = new RegExp(escapeRegex(term), 'i');
1168
+ const labelProp = p.labelProp;
1169
+
1170
+ filteredItems = p.items.filter((x) => {
1171
+ if (!filterGroups && x._group) return true;
1172
+ return matcher.test(x[labelProp]);
1173
+ });
1174
+ } else {
1175
+ filteredItems = null;
1176
+ }
1177
+ }
1178
+
1179
+ p.filteredItems = filteredItems;
1180
+
1181
+ if (filteredItems && filterEmptyGroups) {
1182
+ // Clean up groups without children
1183
+
1184
+ let lastGroup = -1;
1185
+ let len = filteredItems.length;
1186
+
1187
+ for (let i = 0; i < len; i++) {
1188
+ let item = filteredItems[i];
1189
+
1190
+ if (item._group) {
1191
+ if (lastGroup !== -1 && lastGroup === i - 1) {
1192
+ // It was an empty group
1193
+ filteredItems.splice(lastGroup, 1);
1194
+ i--;
1195
+ len--;
1196
+ }
1197
+
1198
+ lastGroup = i;
1199
+ }
1200
+ }
1201
+
1202
+ if (lastGroup !== -1) {
1203
+ if (lastGroup === len - 1) {
1204
+ // It was an empty group
1205
+ filteredItems.splice(lastGroup, 1);
1206
+ }
1207
+ }
1208
+ }
1209
+ } else {
1210
+ p.filteredItems = null;
1211
+ }
1212
+
1213
+ this.needsRefilter = false;
1214
+
1215
+ const items = p.filteredItems ?? p.items;
1216
+ p.hasNoResultsItem = items.length === 0 && !!p.noResultsText;
1217
+ p.virtualListHelper.
1218
+ setCount(items.length + (p.hasNoResultsItem ? 1 : 0)).
1219
+ render();
1220
+
1221
+ this._trigger('itemschanged', { term: term, mutated: false, count: this.getFilteredItemCount() });
1222
+ this.relayout();
1223
+ }
1224
+
1225
+ invokeRefilter() {
1226
+ const p = this._p;
1227
+ if (!p.filterTerm && !p.filterOnEmptyTerm && !p.filteredItems)
1228
+ return this;
1229
+ p.filteredItems = null;
1230
+ p.throttledRefilterItems();
1231
+ return this;
1232
+ }
1233
+
1234
+ getFilteredItemCount() {
1235
+ const p = this._p;
1236
+
1237
+ if (p.needsRefilter)
1238
+ this._refilterItems();
1239
+
1240
+ if (p.filteredItems)
1241
+ return p.filteredItems.length;
1242
+
1243
+ if (p.items)
1244
+ return p.items.length;
1245
+
1246
+ return 0;
1247
+ }
1248
+
1249
+ /**
1250
+ * @param {string} noResultsText
1251
+ * @returns {DropList}
1252
+ */
1253
+ setNoResultsText(noResultsText) {
1254
+ this._p.noResultsText = noResultsText;
1255
+ return this;
1256
+ }
1257
+
1258
+ /**
1259
+ * @returns {string}
1260
+ */
1261
+ getNoResultsText() {
1262
+ return this._p.noResultsText;
1263
+ }
1264
+
1265
+ /**
1266
+ * @param {number} window
1267
+ * @returns {DropList}
1268
+ */
1269
+ setFilterThrottleWindow(window) {
1270
+ const p = this._p;
1271
+ p.filterThrottleWindow = window;
1272
+
1273
+ let isScheduled = p.throttledRefilterItems ? p.throttledRefilterItems.isScheduled() : false;
1274
+
1275
+ if (p.throttledRefilterItems)
1276
+ p.throttledRefilterItems.cancel();
1277
+
1278
+ p.throttledRefilterItems = throttle(() => this._refilterItems(), p.filterThrottleWindow, true);
1279
+
1280
+ if (isScheduled)
1281
+ p.throttledRefilterItems();
1282
+
1283
+ return this;
1284
+ }
1285
+
1286
+ /**
1287
+ * @returns {number}
1288
+ */
1289
+ getFilterThrottleWindow() {
1290
+ return this._p.filterThrottleWindow;
1291
+ }
1292
+
1293
+ /**
1294
+ * @param {boolean} value
1295
+ * @returns {DropList}
1296
+ */
1297
+ setFilterOnEmptyTerm(value) {
1298
+ const p = this._p;
1299
+ if (p.filterOnEmptyTerm === value)
1300
+ return this;
1301
+ p.filterOnEmptyTerm = value;
1302
+ p.throttledRefilterItems();
1303
+ return this;
1304
+ }
1305
+
1306
+ /**
1307
+ * @returns {boolean}
1308
+ */
1309
+ getFilterOnEmptyTerm() {
1310
+ return this._p.filterOnEmptyTerm;
1311
+ }
1312
+
1313
+ /**
1314
+ * @param {boolean} value
1315
+ * @returns {DropList}
1316
+ */
1317
+ setFilterGroups(value) {
1318
+ const p = this._p;
1319
+ if (p.filterGroups === value)
1320
+ return this;
1321
+ p.filterGroups = value;
1322
+ p.throttledRefilterItems();
1323
+ return this;
1324
+ }
1325
+
1326
+ /**
1327
+ * @returns {boolean}
1328
+ */
1329
+ getFilterGroups() {
1330
+ return this._p.filterGroups;
1331
+ }
1332
+
1333
+ /**
1334
+ * @param {boolean} value
1335
+ * @returns {DropList}
1336
+ */
1337
+ setFilterEmptyGroups(value) {
1338
+ const p = this._p;
1339
+ if (p.filterEmptyGroups === value)
1340
+ return this;
1341
+ p.filterEmptyGroups = value;
1342
+ p.throttledRefilterItems();
1343
+ return this;
1344
+ }
1345
+
1346
+ /**
1347
+ * @returns {boolean}
1348
+ */
1349
+ getFilterEmptyGroups() {
1350
+ return this._p.filterEmptyGroups;
1351
+ }
1352
+
1353
+ /**
1354
+ * @param {function(items: DropList.ItemBase[], term: string):(DropList.ItemBase[]|null)} fn
1355
+ * @returns {DropList}
1356
+ */
1357
+ setFilterFn(fn) {
1358
+ const p = this._p;
1359
+ if (p.filterFn === fn)
1360
+ return this;
1361
+ p.filterFn = fn;
1362
+ p.throttledRefilterItems();
1363
+ return this;
1364
+ }
1365
+
1366
+ /**
1367
+ * @returns {function(items: DropList.ItemBase[], term: string):(DropList.ItemBase[]|null)}
1368
+ */
1369
+ getFilterFn() {
1370
+ return this._p.filterFn;
1371
+ }
1372
+
844
1373
  /**
845
1374
  *
846
- * @param {DropList.PositionOptions} positionOptions
1375
+ * @param {DropList.PositionOptions} [positionOptions]
847
1376
  * @returns {DropList}
848
1377
  * @public
849
1378
  */
850
1379
  relayout(positionOptions) {
851
- const p = this._p,el = p.el;
1380
+ const p = this._p,el = p.el,menuEl = p.menuEl;
852
1381
 
853
1382
  if (!this.isVisible()) return this;
854
1383
 
855
1384
  let w = window;
856
1385
 
1386
+ if (!positionOptions)
1387
+ positionOptions = p.positionOptionsProvider?.() ?? p.lastPositionOptions;
1388
+
1389
+ // Supply some default for extreme cases, no crashing
1390
+ if (!positionOptions) {
1391
+ positionOptions = {
1392
+ targetOffset: {
1393
+ left: window.innerWidth / 2,
1394
+ top: window.innerHeight / 2
1395
+ },
1396
+ targetWidth: 0,
1397
+ targetHeight: 0,
1398
+ position: { x: 'center', y: 'center' },
1399
+ anchor: { x: 'center', y: 'center' },
1400
+ targetRtl: getComputedStyle(document.body).direction === 'rtl'
1401
+ };
1402
+ }
1403
+
857
1404
  let targetBox = {};
858
1405
 
859
1406
  let offset = positionOptions.targetOffset || Css.getElementOffset(positionOptions.target);
@@ -916,18 +1463,25 @@
916
1463
  let verticalBorderWidth = (parseFloat(elComputedStyle.borderTopWidth) || 0) + (
917
1464
  parseFloat(elComputedStyle.borderBottomWidth) || 0);
918
1465
 
1466
+ let headerHeight = 0;
1467
+ if (p.headerEl) {
1468
+ headerHeight = Css.getElementHeight(p.headerEl, true, true);
1469
+ }
1470
+
919
1471
  if (p.virtualListHelper.isVirtual()) {
920
1472
  maxViewHeight =
921
1473
  p.virtualListHelper.estimateFullHeight() +
922
1474
  verticalPadding +
923
- verticalBorderWidth;
1475
+ verticalBorderWidth +
1476
+ headerHeight;
924
1477
  } else {
925
1478
  // Another method to calculate height is measuring the whole thing at once.
926
1479
  // This causes relayout of course.
927
1480
  el.style.height = '';
1481
+ menuEl.style.height = '';
928
1482
  el.style.top = '-9999px';
929
1483
 
930
- maxViewHeight = Math.max(Css.getElementHeight(p.el), el.scrollHeight);
1484
+ maxViewHeight = Math.max(Css.getElementHeight(p.el), menuEl.scrollHeight);
931
1485
  maxViewHeight += verticalPadding + verticalBorderWidth;
932
1486
  }
933
1487
 
@@ -1062,6 +1616,11 @@
1062
1616
  Css.setCssProps(el, viewCss);
1063
1617
  Css.setElementHeight(el, viewSize.height, true, true);
1064
1618
 
1619
+ if (menuEl !== el) {
1620
+ let menuHeight = viewSize.height - headerHeight - verticalBorderWidth - verticalPadding;
1621
+ Css.setElementHeight(menuEl, menuHeight, true, true);
1622
+ }
1623
+
1065
1624
  // Update the scroll position for virtual lists
1066
1625
  p.virtualListHelper.render();
1067
1626
 
@@ -1199,7 +1758,7 @@
1199
1758
 
1200
1759
  /**
1201
1760
  *
1202
- * @param {DropList.PositionOptions?} positionOptions
1761
+ * @param {DropList.PositionOptions?} [positionOptions]
1203
1762
  * @returns {DropList}
1204
1763
  * @public
1205
1764
  */
@@ -1226,6 +1785,10 @@
1226
1785
  }
1227
1786
  });
1228
1787
 
1788
+ if (p.needsRefilter) {
1789
+ this._refilterItems();
1790
+ }
1791
+
1229
1792
  const el = p.el;
1230
1793
  el.style.position = 'absolute';
1231
1794
  el.classList.remove(`${p.baseClassName}__is-hiding`);
@@ -1237,6 +1800,14 @@
1237
1800
  if (getComputedStyle(p.el).display === 'none')
1238
1801
  p.el.style.display = 'block';
1239
1802
 
1803
+ p.lastPositionOptions = null;
1804
+
1805
+ if (positionOptions === undefined) {
1806
+ positionOptions = p.positionOptionsProvider?.() ?? p.lastPositionOptions;
1807
+ } else {
1808
+ p.lastPositionOptions = positionOptions;
1809
+ }
1810
+
1240
1811
  if (positionOptions) {
1241
1812
  const elComputedStyle = getComputedStyle(el);
1242
1813
 
@@ -1283,6 +1854,11 @@
1283
1854
 
1284
1855
  p.hiding = true;
1285
1856
 
1857
+ if (this.isFilterPending()) {
1858
+ p.throttledRefilterItems.cancel();
1859
+ p.needsRefilter = true;
1860
+ }
1861
+
1286
1862
  if (el) {
1287
1863
 
1288
1864
  el.classList.add(`${p.baseClassName}__is-hiding`);
@@ -1303,6 +1879,7 @@
1303
1879
  }
1304
1880
  });
1305
1881
  } else {
1882
+ if (el.parentNode)
1306
1883
  DomCompat.remove(el);
1307
1884
  el.classList.remove(`${p.baseClassName}__is-hiding`);
1308
1885
  }
@@ -1352,15 +1929,22 @@
1352
1929
  setFocusedItemAtIndex(itemIndex) {
1353
1930
  const p = this._p;
1354
1931
 
1932
+ if (p.filteredItems) {
1933
+ const item = p.items[itemIndex];
1934
+ itemIndex = p.items.indexOf(item);
1935
+ }
1936
+
1355
1937
  p.focusItemIndex = itemIndex;
1356
1938
 
1939
+ const items = p.filteredItems ?? p.items;
1940
+
1357
1941
  let item = null;
1358
1942
  if (itemIndex > -1)
1359
- item = p.items[itemIndex];
1943
+ item = items[itemIndex];
1360
1944
  if (item && item._nointeraction)
1361
1945
  item = null;
1362
1946
  if (itemIndex > -1) {
1363
- this.scrollItemIndexIntoView(itemIndex);
1947
+ this._scrollItemIndexIntoView(itemIndex);
1364
1948
  }
1365
1949
  let itemElement = item ? p.virtualListHelper.getItemElementAt(itemIndex) : null;
1366
1950
 
@@ -1414,7 +1998,8 @@
1414
1998
  labelProp: p.labelProp,
1415
1999
  valueProp: p.valueProp,
1416
2000
  renderItem: p.renderItem,
1417
- unrenderItem: p.unrenderItem
2001
+ unrenderItem: p.unrenderItem,
2002
+ positionOptionsProvider: () => p.currentSubDropList.showOptions
1418
2003
  });
1419
2004
 
1420
2005
  let onBlur = (event) => {
@@ -1462,7 +2047,7 @@
1462
2047
  }
1463
2048
  };
1464
2049
 
1465
- droplist.show(p.currentSubDropList.showOptions);
2050
+ droplist.show();
1466
2051
 
1467
2052
  droplist.el.focus();
1468
2053
  }
@@ -1516,7 +2101,7 @@
1516
2101
 
1517
2102
  if (itemElement) {
1518
2103
  p.currentSubDropList.showOptions.target = itemElement;
1519
- p.currentSubDropList.droplist.relayout(p.currentSubDropList.showOptions);
2104
+ p.currentSubDropList.droplist.relayout();
1520
2105
  }
1521
2106
  }
1522
2107
  }
@@ -1527,7 +2112,8 @@
1527
2112
 
1528
2113
  let itemIndex = item._nointeraction ? -1 : this._getItemIndex(item);
1529
2114
 
1530
- if (itemIndex > -1 && p.items[itemIndex]._nointeraction)
2115
+ const items = p.items;
2116
+ if (itemIndex > -1 && items[itemIndex]._nointeraction)
1531
2117
  itemIndex = -1;
1532
2118
 
1533
2119
  return this.setFocusedItemAtIndex(itemIndex);
@@ -1543,7 +2129,14 @@
1543
2129
  let itemEl = null;
1544
2130
 
1545
2131
  if (itemIndex > -1 && !p.items[itemIndex]._nointeraction) {
1546
- itemEl = p.virtualListHelper.getItemElementAt(itemIndex);
2132
+ if (p.filteredItems) {
2133
+ const item = p.items[itemIndex];
2134
+ itemIndex = p.filteredItems.indexOf(item);
2135
+ }
2136
+
2137
+ if (itemIndex > -1) {
2138
+ itemEl = p.virtualListHelper.getItemElementAt(itemIndex);
2139
+ }
1547
2140
  }
1548
2141
 
1549
2142
  this._setSingleSelectedItemEl(itemEl);
@@ -1556,7 +2149,8 @@
1556
2149
 
1557
2150
  let itemIndex = item._nointeraction ? -1 : this._getItemIndex(item);
1558
2151
 
1559
- if (itemIndex > -1 && p.items[itemIndex]._nointeraction)
2152
+ const items = p.items;
2153
+ if (itemIndex > -1 && items[itemIndex]._nointeraction)
1560
2154
  itemIndex = -1;
1561
2155
 
1562
2156
  return this.setSingleSelectedItemAtIndex(itemIndex);
@@ -1576,24 +2170,42 @@
1576
2170
 
1577
2171
  isFirstItem() {
1578
2172
  const p = this._p;
1579
- return p.focusItemIndex === 0 && p.focusItemIndex < p.items.length;
2173
+ const items = p.filteredItems ?? p.items;
2174
+ return p.focusItemIndex === 0 && p.focusItemIndex < items.length;
1580
2175
  }
1581
2176
 
1582
2177
  isLastItem() {
1583
2178
  const p = this._p;
1584
- return p.focusItemIndex > -1 && p.focusItemIndex === p.items.length - 1;
2179
+ const items = p.filteredItems ?? p.items;
2180
+ return p.focusItemIndex > -1 && p.focusItemIndex === items.length - 1;
1585
2181
  }
1586
2182
 
1587
2183
  scrollItemIndexIntoView(itemIndex) {
1588
2184
  const p = this._p;
1589
2185
 
2186
+ if (this._hasScroll()) {
2187
+ if (p.filteredItems) {
2188
+ const item = p.items[itemIndex];
2189
+ itemIndex = p.items.indexOf(item);
2190
+ }
2191
+
2192
+ if (itemIndex !== -1) {
2193
+ this._scrollItemIndexIntoView(itemIndex);
2194
+ }
2195
+ }
2196
+
2197
+ return this;
2198
+ }
2199
+
2200
+ _scrollItemIndexIntoView(itemIndex) {
2201
+ const p = this._p;
2202
+
1590
2203
  if (this._hasScroll()) {
1591
2204
  const el = p.el,scrollTop = el.scrollTop;
1592
2205
 
1593
2206
  let itemPos,previousPos = -1;
1594
2207
  let maxIterations = 30; // Some zoom/scroll issues can make it so that it takes almost forever
1595
2208
 
1596
-
1597
2209
  while (maxIterations-- > 0) {
1598
2210
  itemPos = p.virtualListHelper.getItemPosition(itemIndex);
1599
2211
 
@@ -1616,8 +2228,6 @@
1616
2228
  p.virtualListHelper.render();
1617
2229
  }
1618
2230
  }
1619
-
1620
- return this;
1621
2231
  }
1622
2232
 
1623
2233
  /**
@@ -1678,7 +2288,8 @@
1678
2288
  let itemIndex = -1;
1679
2289
 
1680
2290
  if (item) {
1681
- itemIndex = p.items.indexOf(item);
2291
+ const items = p.filteredItems ?? p.items;
2292
+ itemIndex = items.indexOf(item);
1682
2293
  if (itemIndex === -1) {
1683
2294
  let value = item && item.value !== undefined ? item.value : item;
1684
2295
  let label = item && item.label ? item.label : value;
@@ -1826,12 +2437,25 @@
1826
2437
 
1827
2438
  p.sink.
1828
2439
  add(p.el, 'focus', (event) => {
2440
+ if (event.target === this.el && p.searchable)
2441
+ p.searchInput.focus();
2442
+
2443
+ if (event.relatedTarget &&
2444
+ this.elContains(event.relatedTarget, true) &&
2445
+ this.elContains(event.target, true))
2446
+ return;
2447
+
1829
2448
  let itemEl = p.focusItemEl || // focused item
1830
2449
  p.el.firstChild; // or the first item
1831
2450
 
1832
2451
  this._focus(event, itemEl, null, false);
1833
- }).
2452
+ }, true).
1834
2453
  add(p.el, 'blur', (event) => {
2454
+ if (event.relatedTarget &&
2455
+ this.elContains(event.relatedTarget, true) &&
2456
+ this.elContains(event.target, true))
2457
+ return;
2458
+
1835
2459
  setTimeout(() => {
1836
2460
  if (this[DestroyedSymbol$1]) return;
1837
2461
 
@@ -1842,7 +2466,7 @@
1842
2466
  this._delayBlurItemOnBlur();
1843
2467
  this._trigger('blur', event);
1844
2468
  });
1845
- });
2469
+ }, true);
1846
2470
  }
1847
2471
 
1848
2472
  _hookKeyEvents() {
@@ -1851,6 +2475,21 @@
1851
2475
  p.sink.add(p.el, 'keydown', (evt) => this._keydown(evt));
1852
2476
  }
1853
2477
 
2478
+ _hookSearchEvents() {
2479
+ const p = this._p;
2480
+
2481
+ if (!p.searchInput)
2482
+ return;
2483
+
2484
+ p.sink.add(p.searchInput, 'input', () => {
2485
+ p.filterTerm = p.searchInput.value.trim();
2486
+ p.filteredItems = null;
2487
+
2488
+ this._trigger('search', { value: p.searchInput.value });
2489
+ p.throttledRefilterItems();
2490
+ });
2491
+ }
2492
+
1854
2493
  _keydown(event) {
1855
2494
  const p = this._p;
1856
2495
 
@@ -1895,7 +2534,8 @@
1895
2534
  case keycodeJs.VALUE_RIGHT:
1896
2535
  if (event.key === keycodeJs.VALUE_RIGHT && getComputedStyle(event.target).direction !== 'rtl' ||
1897
2536
  event.key === keycodeJs.VALUE_LEFT && getComputedStyle(event.target).direction === 'rtl') {
1898
- let item = p.items[p.focusItemIndex];
2537
+ const items = p.filteredItems ?? p.items;
2538
+ let item = items[p.focusItemIndex];
1899
2539
  if (p.focusItemIndex > -1 && item._subitems)
1900
2540
  this._showSublist(item, p.focusItemEl);
1901
2541
  } else {
@@ -1925,7 +2565,11 @@
1925
2565
 
1926
2566
  default:{
1927
2567
  if (event.type === 'keydown') return;
2568
+
2569
+ // Inline search box not available, then support typing to focus by first letters
2570
+ if (!p.searchable)
1928
2571
  this._keydownFreeType(event);
2572
+
1929
2573
  preventDefault = false;
1930
2574
  }
1931
2575
  }
@@ -1950,12 +2594,14 @@
1950
2594
 
1951
2595
  let focusItemIndex = p.focusItemIndex;
1952
2596
 
2597
+ const items = p.filteredItems ?? p.items;
2598
+
1953
2599
  // These are all the possible matches for the text typed in so far
1954
- for (let i = 0, count = p.items.length; i < count; i++) {
2600
+ for (let i = 0, count = items.length; i < count; i++) {
1955
2601
  if (matchIndex !== -1 && i < focusItemIndex)
1956
2602
  continue; // We are only interested in first match + match after the focused item
1957
2603
 
1958
- item = p.items[i];
2604
+ item = items[i];
1959
2605
  if (regex.test(item.label)) {
1960
2606
  matchIndex = i;
1961
2607
  if (focusItemIndex === -1 || i >= focusItemIndex)
@@ -1969,11 +2615,11 @@
1969
2615
  keyword = character;
1970
2616
  regex = new RegExp(`^${escapeRegex(keyword)}`, 'i');
1971
2617
 
1972
- for (let i = 0, count = p.items.length; i < count; i++) {
2618
+ for (let i = 0, count = items.length; i < count; i++) {
1973
2619
  if (matchIndex !== -1 && i < focusItemIndex)
1974
2620
  continue; // We are only interested in first match + match after the focused item
1975
2621
 
1976
- item = p.items[i];
2622
+ item = items[i];
1977
2623
  if (regex.test(item.label)) {
1978
2624
  matchIndex = i;
1979
2625
  if (focusItemIndex === -1 || i >= focusItemIndex)
@@ -1987,7 +2633,7 @@
1987
2633
  this._focus(evt, next || null, matchIndex, true);
1988
2634
 
1989
2635
  if (!this.isVisible()) {
1990
- this.triggerItemSelection(next ? null : p.items[matchIndex], evt);
2636
+ this.triggerItemSelection(next ? null : items[matchIndex], evt);
1991
2637
  }
1992
2638
 
1993
2639
  // Record the last filter used
@@ -2014,6 +2660,10 @@
2014
2660
  itemIndex = p.virtualListHelper.getItemIndexFromElement(itemEl);
2015
2661
  }
2016
2662
 
2663
+ if (itemIndex > -1 && itemEl?.[ItemSymbol$1]?.[ItemSymbol$1] === NoResultsItemSymbol) {
2664
+ itemIndex = undefined;
2665
+ }
2666
+
2017
2667
  if (itemIndex > -1) {
2018
2668
  this.scrollItemIndexIntoView(itemIndex);
2019
2669
  } else if (itemIndex === undefined) {
@@ -2039,7 +2689,8 @@
2039
2689
  p.focusItemEl = focusItemEl;
2040
2690
  p.focusItemIndex = itemIndex;
2041
2691
 
2042
- const item = p.items[itemIndex];
2692
+ const items = p.filteredItems ?? p.items;
2693
+ const item = items[itemIndex];
2043
2694
  this._trigger('itemfocus', {
2044
2695
  value: item.value,
2045
2696
  item: item[ItemSymbol$1] ?? item,
@@ -2074,12 +2725,13 @@
2074
2725
  const p = this._p;
2075
2726
 
2076
2727
  let next,nextIndex,directionUp = false;
2728
+ const items = p.filteredItems ?? p.items;
2077
2729
 
2078
2730
  if (direction === 'first') {
2079
2731
  nextIndex = 0;
2080
2732
  directionUp = false;
2081
2733
  } else if (direction === 'last') {
2082
- nextIndex = p.items.length - 1;
2734
+ nextIndex = items.length - 1;
2083
2735
  directionUp = true;
2084
2736
  } else if (direction === 'prev') {
2085
2737
  if (!this.hasFocusedItem())
@@ -2087,7 +2739,7 @@
2087
2739
 
2088
2740
  nextIndex = p.focusItemIndex - 1;
2089
2741
  if (nextIndex === -1) {
2090
- nextIndex = p.items.length - 1;
2742
+ nextIndex = items.length - 1;
2091
2743
  }
2092
2744
 
2093
2745
  directionUp = true;
@@ -2096,7 +2748,7 @@
2096
2748
  return this._move('first', event);
2097
2749
 
2098
2750
  nextIndex = p.focusItemIndex + 1;
2099
- if (nextIndex === p.items.length) {
2751
+ if (nextIndex === items.length) {
2100
2752
  nextIndex = 0;
2101
2753
  }
2102
2754
 
@@ -2125,8 +2777,8 @@
2125
2777
 
2126
2778
  if (nextIndex < 0) {
2127
2779
  nextIndex = 0;
2128
- } else if (nextIndex >= p.items.length) {
2129
- nextIndex = p.items.length;
2780
+ } else if (nextIndex >= items.length) {
2781
+ nextIndex = items.length;
2130
2782
  }
2131
2783
  } else if (p.focusItemEl) {
2132
2784
  let base = Css.getElementOffset(p.focusItemEl).top;
@@ -2163,13 +2815,13 @@
2163
2815
  return;
2164
2816
  }
2165
2817
 
2166
- let itemCount = p.items.length;
2818
+ let itemCount = items.length;
2167
2819
 
2168
2820
  if (nextIndex >= itemCount) {
2169
2821
  return;
2170
2822
  }
2171
2823
 
2172
- let item = p.items[nextIndex];
2824
+ let item = items[nextIndex];
2173
2825
  // noinspection UnnecessaryLocalVariableJS
2174
2826
  let startedAtIndex = nextIndex;
2175
2827
 
@@ -2186,7 +2838,7 @@
2186
2838
  }
2187
2839
  }
2188
2840
 
2189
- item = p.items[nextIndex];
2841
+ item = items[nextIndex];
2190
2842
 
2191
2843
  if (nextIndex === startedAtIndex) {
2192
2844
  break;
@@ -2202,7 +2854,9 @@
2202
2854
  }
2203
2855
 
2204
2856
  _hasScroll() {
2205
- return this.el.clientHeight < this.el.scrollHeight;
2857
+ const p = this._p;
2858
+ const menuEl = p.menuEl;
2859
+ return menuEl.clientHeight < menuEl.scrollHeight;
2206
2860
  }
2207
2861
 
2208
2862
  _updateGroupStateForItem(item) {
@@ -2217,7 +2871,7 @@
2217
2871
  let affectedItems = 0;
2218
2872
 
2219
2873
  if (p.autoCheckGroupChildren) {
2220
- let items = p.items;
2874
+ let items = p.filteredItems ?? p.items;
2221
2875
  let groupIndex = items.indexOf(item);
2222
2876
 
2223
2877
  for (let i = groupIndex + 1, len = items.length; i < len; i++) {
@@ -2260,7 +2914,7 @@
2260
2914
  affectedItems: affectedItems
2261
2915
  });
2262
2916
  } else if (p.groupCount > 0 && p.autoCheckGroupChildren) {
2263
- let items = p.items;
2917
+ let items = p.filteredItems ?? p.items;
2264
2918
  let itemIndex = items.indexOf(item);
2265
2919
  let groupIndex = -1;
2266
2920
 
@@ -2286,7 +2940,7 @@
2286
2940
  if (!(p.multi && p.autoCheckGroupChildren && groupIndex > -1))
2287
2941
  return this;
2288
2942
 
2289
- let items = p.items;
2943
+ let items = p.filteredItems ?? p.items;
2290
2944
  let groupItem = items[groupIndex];
2291
2945
 
2292
2946
  if (!groupItem || !groupItem._group) return this;
@@ -2412,7 +3066,7 @@
2412
3066
  _determineVirtualMode(targetItemCount) {
2413
3067
  const p = this._p;
2414
3068
 
2415
- let items = p.items;
3069
+ let items = p.filteredItems ?? p.items;
2416
3070
  if (targetItemCount === undefined) {
2417
3071
  targetItemCount = items.length;
2418
3072
  }
@@ -2432,7 +3086,21 @@
2432
3086
  // NOTE: a "measure" item will not have full data of original item.
2433
3087
  // so for a custom renderer - we try to send original item, and fallback to our private list item.
2434
3088
 
2435
- if (!p.renderItem || p.renderItem(item[ItemSymbol$1] || item, itemEl) === false) {
3089
+ const originalItem = item[ItemSymbol$1];
3090
+
3091
+ if (originalItem === NoResultsItemSymbol) {
3092
+ if (p.renderNoResultsItem && p.renderNoResultsItem(item, itemEl) !== false) {
3093
+ return true;
3094
+ }
3095
+
3096
+ itemEl.appendChild(Dom.createElement('div', {
3097
+ class: 'droplist-no-results-content',
3098
+ textContent: p.noResultsText
3099
+ }));
3100
+ return;
3101
+ }
3102
+
3103
+ if (!p.renderItem || p.renderItem(originalItem || item, itemEl) === false) {
2436
3104
  itemEl.appendChild(Dom.createElement('span', {
2437
3105
  class: `${p.baseClassName}__item_label`,
2438
3106
  textContent: item.label
@@ -2474,7 +3142,7 @@
2474
3142
  }
2475
3143
 
2476
3144
  let autoWidth = 0;
2477
- if (!p.useExactTargetWidth) {
3145
+ if (!p.useExactTargetWidth || !targetWidth) {
2478
3146
  if (p.estimateWidth || p.virtualListHelper.isVirtual()) {
2479
3147
  autoWidth = p.lastMeasureItemWidth;
2480
3148
  } else {
@@ -2501,56 +3169,8 @@
2501
3169
  }
2502
3170
  }
2503
3171
 
2504
- const throttle = (func, wait, options) => {
2505
- let timeout, context, args, result;
2506
- let previous = 0;
2507
- if (!options) options = {};
2508
-
2509
- const later = () => {
2510
- previous = options.leading === false ? 0 : Date.now();
2511
- timeout = null;
2512
- result = func.apply(context, args);
2513
- if (!timeout) context = args = null;
2514
- };
2515
-
2516
- const throttled = function () {
2517
- const now = Date.now();
2518
- if (!previous && options.leading === false)
2519
- previous = now;
2520
-
2521
- const remaining = wait - (now - previous);
2522
- context = this;
2523
- args = arguments;
2524
- if (remaining <= 0 || remaining > wait) {
2525
- if (timeout) {
2526
- clearTimeout(timeout);
2527
- timeout = null;
2528
- }
2529
- previous = now;
2530
- result = func.apply(context, args);
2531
- if (!timeout) context = args = null;
2532
- } else if (!timeout && options.trailing !== false) {
2533
- timeout = setTimeout(later, remaining);
2534
- }
2535
- return result;
2536
- };
2537
-
2538
- throttled.cancel = () => {
2539
- clearTimeout(timeout);
2540
- previous = 0;
2541
- timeout = context = args = null;
2542
- };
2543
-
2544
- throttled.isScheduled = () => {
2545
- return !!timeout;
2546
- };
2547
-
2548
- return throttled;
2549
- };
2550
-
2551
3172
  const ItemSymbol = Symbol('item');
2552
3173
  const DestroyedSymbol = Symbol('destroyed');
2553
- const NoResultsItemSymbol = Symbol('no_results_items');
2554
3174
  const RestMultiItemsSymbol = Symbol('rest_multi_items');
2555
3175
 
2556
3176
  const hasTouchCapability = !!('ontouchstart' in window ||
@@ -2608,10 +3228,6 @@
2608
3228
  * @property {boolean} [showSelection=true] show selection? if false, the placeholder will take effect
2609
3229
  * @property {boolean} [showPlaceholderInTooltip=false] show placeholder in title attribute
2610
3230
  * @property {function(items: DropList.ItemBase[]):string} [multiPlaceholderFormatter] formatter for placeholder for multi items mode
2611
- * @property {boolean} [searchable=false] is it searchable?
2612
- * @property {string} [noResultsText='No matching results'] text for no results (empty for none)
2613
- * @property {number} [filterThrottleWindow=300] throttle time (milliseconds) for filtering
2614
- * @property {boolean} [filterOnEmptyTerm=false] call the filter function on empty search term too
2615
3231
  * @property {string} [labelProp='label']
2616
3232
  * @property {string} [valueProp='value']
2617
3233
  * @property {string} [multiItemLabelProp='short_label']
@@ -2628,6 +3244,10 @@
2628
3244
  * @property {function(item: DropList.ItemBase, itemEl: Element)} [unrenderRestMultiItem]
2629
3245
  * @property {function(item: DropList.ItemBase, itemEl: Element):(*|false)} [renderNoResultsItem]
2630
3246
  * @property {function(item: DropList.ItemBase, itemEl: Element)} [unrenderNoResultsItem]
3247
+ * @property {boolean} [searchable=false] is it searchable?
3248
+ * @property {string} [noResultsText='No matching results'] text for no results (empty for none)
3249
+ * @property {number} [filterThrottleWindow=300] throttle time (milliseconds) for filtering
3250
+ * @property {boolean} [filterOnEmptyTerm=false] call the filter function on empty search term too
2631
3251
  * @property {function(items: DropList.ItemBase[], term: string):(DropList.ItemBase[]|null)} [filterFn]
2632
3252
  * @property {function(name: string, ...args)} [on]
2633
3253
  * @property {boolean} [isLoadingMode]
@@ -2746,8 +3366,6 @@
2746
3366
  multiPlaceholderFormatter: o.multiPlaceholderFormatter,
2747
3367
  searchable: o.searchable,
2748
3368
  noResultsText: o.noResultsText,
2749
- filterThrottleWindow: o.filterThrottleWindow,
2750
- filterOnEmptyTerm: o.filterOnEmptyTerm,
2751
3369
 
2752
3370
  labelProp: o.labelProp,
2753
3371
  valueProp: o.valueProp,
@@ -2764,7 +3382,6 @@
2764
3382
  unrenderRestMultiItem: o.unrenderRestMultiItem,
2765
3383
  renderNoResultsItem: o.renderNoResultsItem,
2766
3384
  unrenderNoResultsItem: o.unrenderNoResultsItem,
2767
- filterFn: o.filterFn,
2768
3385
  on: o.on || null,
2769
3386
  silenceEvents: true,
2770
3387
  mitt: mitt(),
@@ -2772,8 +3389,6 @@
2772
3389
  isLoadingMode: !!o.isLoadingMode,
2773
3390
 
2774
3391
  items: [],
2775
- filteredItems: null,
2776
- currentItemsView: [], // contains the final version of items sent to DropList
2777
3392
  itemsChanged: true,
2778
3393
 
2779
3394
  sink: new DomEventsSink(),
@@ -2786,10 +3401,9 @@
2786
3401
  selectionChanged: true,
2787
3402
  resortBySelectionNeeded: false,
2788
3403
 
2789
- throttledUpdateListItems: throttle(() => this._updateListItems(),
2790
- o.filterThrottleWindow,
2791
- { leading: true, trailing: true }),
2792
-
3404
+ filterThrottleWindow: o.filterThrottleWindow,
3405
+ filterOnEmptyTerm: o.filterOnEmptyTerm,
3406
+ filterFn: null,
2793
3407
  filterTerm: ''
2794
3408
  };
2795
3409
 
@@ -2901,6 +3515,9 @@
2901
3515
  p.resizeObserver.observe(p.el);
2902
3516
  }
2903
3517
 
3518
+ if (o.filterFn)
3519
+ this.setFilterFn(o.filterFn);
3520
+
2904
3521
  this.setItems(o.items);
2905
3522
  delete o.items; // we do not need this in memory anymore
2906
3523
 
@@ -2934,9 +3551,6 @@
2934
3551
  p.sink.remove();
2935
3552
  p.dropList && p.dropList.destroy();
2936
3553
 
2937
- if (p.throttledUpdateListItems)
2938
- p.throttledUpdateListItems.cancel();
2939
-
2940
3554
  this._cleanupSingleWrapper();
2941
3555
 
2942
3556
  if (p.unrenderMultiItem || p.unrenderRestMultiItem) {
@@ -3065,7 +3679,6 @@
3065
3679
  items = [];
3066
3680
 
3067
3681
  p.items = items.slice(0);
3068
- p.filteredItems = null;
3069
3682
  p.itemsChanged = true;
3070
3683
 
3071
3684
  this._updateItemByValueMap();
@@ -3082,8 +3695,8 @@
3082
3695
  getFilteredItemCount() {
3083
3696
  const p = this._p;
3084
3697
 
3085
- if (p.filteredItems)
3086
- return p.filteredItems.length;
3698
+ if (p.dropList)
3699
+ return p.dropList.getFilteredItemCount();
3087
3700
 
3088
3701
  if (p.items)
3089
3702
  return p.items.length;
@@ -3093,9 +3706,7 @@
3093
3706
 
3094
3707
  isFilterPending() {
3095
3708
  const p = this._p;
3096
-
3097
- return !!(p.throttledUpdateListItems.isScheduled() ||
3098
- !p.filteredItems && (p.filterTerm || p.filterOnEmptyTerm && p.filterFn));
3709
+ return p.dropList?.isFilterPending() === true;
3099
3710
  }
3100
3711
 
3101
3712
  updateItemByValue(value, newItem) {
@@ -3195,11 +3806,7 @@
3195
3806
 
3196
3807
  if (performSearch) {
3197
3808
  p.filterTerm = p.input.value.trim();
3198
- p.filteredItems = null;
3199
- p.itemsChanged = true;
3200
-
3201
- this._trigger('search', { value: p.input.value });
3202
- p.throttledUpdateListItems();
3809
+ p.dropList?.setSearchTerm(p.filterTerm, performSearch);
3203
3810
  }
3204
3811
 
3205
3812
  return this;
@@ -3217,11 +3824,7 @@
3217
3824
 
3218
3825
  invokeRefilter() {
3219
3826
  const p = this._p;
3220
- if (!p.filterTerm && !p.filterOnEmptyTerm && !p.filteredItems)
3221
- return this;
3222
- p.filteredItems = null;
3223
- p.itemsChanged = true;
3224
- p.throttledUpdateListItems();
3827
+ p.dropList?.invokeRefilter();
3225
3828
  return this;
3226
3829
  }
3227
3830
 
@@ -3258,7 +3861,6 @@
3258
3861
  return this;
3259
3862
 
3260
3863
  p.sortListItems = sortListItems;
3261
- p.filteredItems = null;
3262
3864
  p.itemsChanged = true;
3263
3865
  this._scheduleSync('render_list');
3264
3866
  return this;
@@ -3347,6 +3949,8 @@
3347
3949
  return this;
3348
3950
 
3349
3951
  p.treatGroupSelectionAsItems = treatGroupSelectionAsItems;
3952
+ p.dropList?.setFilterGroups(treatGroupSelectionAsItems);
3953
+ p.dropList?.setFilterEmptyGroups(!treatGroupSelectionAsItems);
3350
3954
  p.itemsChanged = true;
3351
3955
  this._scheduleSync('render_list');
3352
3956
  return this;
@@ -3526,8 +4130,7 @@
3526
4130
  * @returns {SelectBox}
3527
4131
  */
3528
4132
  setNoResultsText(noResultsText) {
3529
- this._p.noResultsText = noResultsText;
3530
- this._scheduleSync('render_list');
4133
+ this._p.dropList?.setNoResultsText(noResultsText);
3531
4134
  return this;
3532
4135
  }
3533
4136
 
@@ -3545,17 +4148,7 @@
3545
4148
  setFilterThrottleWindow(window) {
3546
4149
  const p = this._p;
3547
4150
  p.filterThrottleWindow = window;
3548
-
3549
- let isScheduled = p.throttledUpdateListItems ? p.throttledUpdateListItems.isScheduled() : false;
3550
-
3551
- if (p.throttledUpdateListItems)
3552
- p.throttledUpdateListItems.cancel();
3553
-
3554
- p.throttledUpdateListItems = throttle(() => this._updateListItems(), p.filterThrottleWindow, true);
3555
-
3556
- if (isScheduled)
3557
- p.throttledUpdateListItems();
3558
-
4151
+ p.dropList?.setFilterThrottleWindow(window);
3559
4152
  return this;
3560
4153
  }
3561
4154
 
@@ -3574,8 +4167,7 @@
3574
4167
  const p = this._p;
3575
4168
  if (p.filterOnEmptyTerm === value)
3576
4169
  return this;
3577
- p.filterOnEmptyTerm = value;
3578
- p.throttledUpdateListItems();
4170
+ p.dropList?.setFilterOnEmptyTerm(value);
3579
4171
  return this;
3580
4172
  }
3581
4173
 
@@ -3717,8 +4309,21 @@
3717
4309
  const p = this._p;
3718
4310
  if (p.filterFn === fn)
3719
4311
  return this;
4312
+ if (!fn) {
4313
+ // Add search by multi-item label
4314
+ fn = (items, term) => {
4315
+ const matcher = new RegExp(escapeRegex(term), 'i');
4316
+ const labelProp = p.labelProp,
4317
+ multiItemLabelProp = p.multiItemLabelProp;
4318
+
4319
+ return p.items.filter((x) => {
4320
+ if (!p.treatGroupSelectionAsItems && x._group) return true;
4321
+ return matcher.test(x[labelProp] || x[multiItemLabelProp]);
4322
+ });
4323
+ };
4324
+ }
3720
4325
  p.filterFn = fn;
3721
- p.throttledUpdateListItems();
4326
+ p.dropList?.setFilterFn(fn);
3722
4327
  return this;
3723
4328
  }
3724
4329
 
@@ -3898,7 +4503,7 @@
3898
4503
  // Propagate direction to droplist
3899
4504
  p.dropList.setDirection(getComputedStyle(p.el).direction);
3900
4505
 
3901
- p.dropList.show(this._getDropListPositionOptions());
4506
+ p.dropList.show();
3902
4507
  this._repositionDropList();
3903
4508
 
3904
4509
  // Another one in case the droplist position messed with screen layout.
@@ -4291,31 +4896,14 @@
4291
4896
  const customUnrenderItem = (p.listOptions || {}).unrenderItem;
4292
4897
 
4293
4898
  const renderItem = renderNoResultsItem || customRenderItem ? (item, itemEl) => {
4294
- if (item && item[valueProp] === NoResultsItemSymbol) {
4295
- if (renderNoResultsItem && renderNoResultsItem(item, itemEl) !== false) {
4296
- return true;
4297
- }
4298
-
4299
- itemEl.appendChild(Dom.createElement('div', {
4300
- class: 'droplist-no-results-content',
4301
- textContent: p.noResultsText
4302
- }));
4303
- return true;
4304
- } else {
4305
- if (customRenderItem)
4306
- return customRenderItem(item, itemEl);
4307
- }
4899
+ if (customRenderItem)
4900
+ return customRenderItem(item, itemEl);
4308
4901
  return false;
4309
4902
  } : null;
4310
4903
 
4311
4904
  const unrenderItem = unrenderNoResultsItem || customRenderItem ? (item, itemEl) => {
4312
- if (item && item[valueProp] === NoResultsItemSymbol) {
4313
- if (unrenderNoResultsItem)
4314
- return unrenderNoResultsItem(item, itemEl);
4315
- } else {
4316
- if (customUnrenderItem)
4317
- return customUnrenderItem(item, itemEl);
4318
- }
4905
+ if (customUnrenderItem)
4906
+ return customUnrenderItem(item, itemEl);
4319
4907
  return false;
4320
4908
  } : null;
4321
4909
 
@@ -4333,6 +4921,16 @@
4333
4921
  labelProp: p.labelProp,
4334
4922
  valueProp: p.valueProp,
4335
4923
 
4924
+ searchable: false,
4925
+ noResultsText: p.noResultsText,
4926
+ filterThrottleWindow: p.filterThrottleWindow,
4927
+ filterOnEmptyTerm: p.filterOnEmptyTerm,
4928
+ filterGroups: p.treatGroupSelectionAsItems,
4929
+ filterEmptyGroups: p.treatGroupSelectionAsItems,
4930
+ filterFn: p.filterFn,
4931
+
4932
+ positionOptionsProvider: () => this._getDropListPositionOptions(),
4933
+
4336
4934
  on: (name, event) => {
4337
4935
  switch (name) {
4338
4936
  case 'show:before':{
@@ -4487,6 +5085,15 @@
4487
5085
 
4488
5086
  case 'blur':
4489
5087
  this._handleOnBlur();
5088
+ break;
5089
+
5090
+ case 'search':
5091
+ this._trigger('search', event);
5092
+ break;
5093
+
5094
+ case 'itemschanged':
5095
+ this._trigger('itemschanged', event);
5096
+ break;
4490
5097
  }
4491
5098
  }
4492
5099
  });
@@ -4517,8 +5124,7 @@
4517
5124
  p.dropList && this.droplistElContains(document.activeElement, true))) {
4518
5125
  return;
4519
5126
  }
4520
- if (p.throttledUpdateListItems)
4521
- p.throttledUpdateListItems.cancel();
5127
+
4522
5128
  this.closeList();
4523
5129
  });
4524
5130
  }
@@ -4665,12 +5271,9 @@
4665
5271
  if (p.disabled) return;
4666
5272
 
4667
5273
  p.filterTerm = p.input.value.trim();
4668
- p.filteredItems = null;
4669
- p.itemsChanged = true;
5274
+ p.dropList?.setSearchTerm(p.filterTerm, true);
4670
5275
 
4671
5276
  this._trigger('search', { value: p.input.value });
4672
-
4673
- p.throttledUpdateListItems();
4674
5277
  }).
4675
5278
  add(p.input, 'click.dropdown', () => {
4676
5279
  if (p.disabled) return;
@@ -4775,15 +5378,16 @@
4775
5378
  if (this.isMultiEnabled()) return;
4776
5379
 
4777
5380
  let selectedItems = this.getSelectedItems();
4778
- let items = p.filteredItems ?? p.items;
4779
- if (p.currentItemsView && p.currentItemsView.length === items.length)
4780
- items = p.currentItemsView;
4781
- if (items.length + (p.clearable ? 1 : 0) > 1) {
4782
- let nextIndex = selectedItems.length > 0 ? items.indexOf(selectedItems[0]) - 1 : items.length - 1;
5381
+ let finalItemCount = p.dropList.getFilteredItemCount();
5382
+
5383
+ if (finalItemCount + (p.clearable ? 1 : 0) > 1) {
5384
+ let nextIndex = selectedItems.length > 0 ?
5385
+ p.dropList.filteredItemIndexByItem(selectedItems[0]) - 1 :
5386
+ finalItemCount - 1;
4783
5387
  if (nextIndex === -1 && !p.clearable)
4784
- nextIndex = items.length - 1;
5388
+ nextIndex = finalItemCount - 1;
4785
5389
 
4786
- let item = nextIndex === -1 ? null : items[nextIndex];
5390
+ let item = nextIndex === -1 ? null : p.dropList.filteredItemAtIndex(nextIndex);
4787
5391
  if (item) {
4788
5392
  this._performSelectWithEvent(item, item[p.valueProp]);
4789
5393
  } else {
@@ -4798,15 +5402,16 @@
4798
5402
  if (this.isMultiEnabled()) return;
4799
5403
 
4800
5404
  let selectedItems = this.getSelectedItems();
4801
- let items = p.filteredItems ?? p.items;
4802
- if (p.currentItemsView && p.currentItemsView.length === items.length)
4803
- items = p.currentItemsView;
4804
- if (items.length + (p.clearable ? 1 : 0) > 1) {
4805
- let nextIndex = selectedItems.length > 0 ? items.indexOf(selectedItems[0]) + 1 : 0;
4806
- if (nextIndex === items.length)
5405
+ let finalItemCount = p.dropList.getFilteredItemCount();
5406
+
5407
+ if (finalItemCount + (p.clearable ? 1 : 0) > 1) {
5408
+ let nextIndex = selectedItems.length > 0 ?
5409
+ p.dropList.filteredItemIndexByItem(selectedItems[0]) + 1 :
5410
+ 0;
5411
+ if (nextIndex === finalItemCount)
4807
5412
  nextIndex = p.clearable ? -1 : 0;
4808
5413
 
4809
- let item = nextIndex === -1 ? null : items[nextIndex];
5414
+ let item = nextIndex === -1 ? null : p.dropList.filteredItemAtIndex(nextIndex);
4810
5415
  if (item) {
4811
5416
  this._performSelectWithEvent(item, item[p.valueProp]);
4812
5417
  } else {
@@ -4823,37 +5428,19 @@
4823
5428
  if (!dropList || !p.dropListVisible)
4824
5429
  return;
4825
5430
 
4826
- // For every search change, filteredItems is cleared.
4827
- // If it's non-null here, then it means we have an extra call to _updateListItems, and not need to refilter.
4828
- if (!p.filteredItems && (p.filterTerm || p.filterOnEmptyTerm && p.filterFn)) {
4829
- this._refilterItems();
4830
- }
4831
-
4832
5431
  if (p.itemsChanged || p.selectionChanged) {
4833
5432
  p.dropList._lastSerializedBox = null;
4834
5433
  }
4835
5434
 
4836
5435
  if (p.itemsChanged) {
4837
- let items = p.filteredItems || p.items;
5436
+ let items = p.items;
4838
5437
  if (p.sortListItems || p.sortListCheckedFirst && p.multi) {
4839
5438
  items = this._sortItems(items,
4840
5439
  p.sortListItems,
4841
5440
  p.sortListCheckedFirst && p.multi,
4842
5441
  p.splitListCheckedGroups);
4843
5442
  }
4844
- dropList.removeAllItems();
4845
-
4846
- if (items.length === 0 && p.noResultsText) {
4847
- items = [{
4848
- [p.labelProp]: p.noResultsText,
4849
- [p.valueProp]: NoResultsItemSymbol,
4850
- _nointeraction: true,
4851
- _nocheck: true
4852
- }];
4853
- }
4854
-
4855
- dropList.addItems(items);
4856
- p.currentItemsView = items;
5443
+ dropList.setItems(items);
4857
5444
  p.itemsChanged = false;
4858
5445
  p.selectionChanged = true;
4859
5446
  p.resortBySelectionNeeded = false;
@@ -4883,77 +5470,6 @@
4883
5470
  }
4884
5471
  }
4885
5472
 
4886
- /** @private */
4887
- _refilterItems() {
4888
- const p = this._p;
4889
-
4890
- const term = p.filterTerm;
4891
- const treatGroupSelectionAsItems = p.treatGroupSelectionAsItems;
4892
-
4893
- if (term || p.filterOnEmptyTerm && p.filterFn) {
4894
- let fn = p.filterFn;
4895
-
4896
- let filteredItems;
4897
-
4898
- if (typeof fn === 'function') {
4899
- filteredItems = p.filterFn(p.items, term);
4900
- }
4901
-
4902
- // If there was no filter function, or it gave up on filtering.
4903
- if (!Array.isArray(filteredItems)) {
4904
- if (term) {
4905
- const matcher = new RegExp(escapeRegex(term), 'i');
4906
- const labelProp = p.labelProp,
4907
- multiItemLabelProp = p.multiItemLabelProp;
4908
-
4909
- filteredItems = p.items.filter((x) => {
4910
- if (!treatGroupSelectionAsItems && x._group) return true;
4911
- return matcher.test(x[labelProp] || x[multiItemLabelProp]);
4912
- });
4913
- } else {
4914
- filteredItems = null;
4915
- }
4916
- }
4917
-
4918
- p.filteredItems = filteredItems;
4919
-
4920
- if (filteredItems && !treatGroupSelectionAsItems) {
4921
- // Clean up groups without children
4922
-
4923
- let lastGroup = -1;
4924
- let len = filteredItems.length;
4925
-
4926
- for (let i = 0; i < len; i++) {
4927
- let item = filteredItems[i];
4928
-
4929
- if (item._group) {
4930
- if (lastGroup !== -1 && lastGroup === i - 1) {
4931
- // It was an empty group
4932
- filteredItems.splice(lastGroup, 1);
4933
- i--;
4934
- len--;
4935
- }
4936
-
4937
- lastGroup = i;
4938
- }
4939
- }
4940
-
4941
- if (lastGroup !== -1) {
4942
- if (lastGroup === len - 1) {
4943
- // It was an empty group
4944
- filteredItems.splice(lastGroup, 1);
4945
- }
4946
- }
4947
- }
4948
- } else {
4949
- p.filteredItems = null;
4950
- }
4951
-
4952
- this._trigger('itemschanged', { term: term, mutated: false, count: this.getFilteredItemCount() });
4953
-
4954
- p.itemsChanged = true;
4955
- }
4956
-
4957
5473
  /** @private */
4958
5474
  _setSelectedItems(items) {
4959
5475
  const p = this._p,valueProp = p.valueProp;
@@ -5592,8 +6108,7 @@
5592
6108
  p.input.value = value == null ? '' : String(value);
5593
6109
 
5594
6110
  p.filterTerm = '';
5595
- p.filteredItems = null;
5596
- p.itemsChanged = true;
6111
+ p.dropList?.setSearchTerm('', true);
5597
6112
  }
5598
6113
 
5599
6114
  /**
@@ -5714,7 +6229,7 @@
5714
6229
  const serialized = box.left + ',' + box.top + ',' + box.right + ',' + box.bottom;
5715
6230
 
5716
6231
  if (p.dropList._lastSerializedBox !== serialized) {
5717
- p.dropList.relayout(this._getDropListPositionOptions());
6232
+ p.dropList.relayout();
5718
6233
  p.dropList._lastSerializedBox = serialized;
5719
6234
  }
5720
6235