selective-ui 1.4.0 → 1.4.2

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.
Files changed (71) hide show
  1. package/dist/selective-ui.css +2 -2
  2. package/dist/selective-ui.css.map +1 -1
  3. package/dist/selective-ui.esm.js +407 -573
  4. package/dist/selective-ui.esm.js.map +1 -1
  5. package/dist/selective-ui.esm.min.js +2 -2
  6. package/dist/selective-ui.esm.min.js.br +0 -0
  7. package/dist/selective-ui.min.css +1 -1
  8. package/dist/selective-ui.min.css.br +0 -0
  9. package/dist/selective-ui.min.js +2 -2
  10. package/dist/selective-ui.min.js.br +0 -0
  11. package/dist/selective-ui.umd.js +409 -575
  12. package/dist/selective-ui.umd.js.map +1 -1
  13. package/package.json +12 -12
  14. package/src/css/views/option-view.css +2 -2
  15. package/src/ts/adapter/mixed-adapter.ts +149 -71
  16. package/src/ts/components/accessorybox.ts +14 -11
  17. package/src/ts/components/directive.ts +1 -1
  18. package/src/ts/components/option-handle.ts +12 -9
  19. package/src/ts/components/placeholder.ts +5 -5
  20. package/src/ts/components/popup/empty-state.ts +5 -5
  21. package/src/ts/components/popup/loading-state.ts +5 -5
  22. package/src/ts/components/popup/popup.ts +138 -76
  23. package/src/ts/components/searchbox.ts +17 -13
  24. package/src/ts/components/selectbox.ts +260 -84
  25. package/src/ts/core/base/adapter.ts +61 -14
  26. package/src/ts/core/base/fenwick.ts +3 -2
  27. package/src/ts/core/base/lifecycle.ts +14 -4
  28. package/src/ts/core/base/model.ts +17 -15
  29. package/src/ts/core/base/recyclerview.ts +7 -5
  30. package/src/ts/core/base/view.ts +10 -5
  31. package/src/ts/core/base/virtual-recyclerview.ts +178 -45
  32. package/src/ts/core/model-manager.ts +48 -21
  33. package/src/ts/core/search-controller.ts +174 -56
  34. package/src/ts/global.ts +5 -8
  35. package/src/ts/index.ts +2 -2
  36. package/src/ts/models/group-model.ts +33 -8
  37. package/src/ts/models/option-model.ts +88 -20
  38. package/src/ts/services/dataset-observer.ts +6 -3
  39. package/src/ts/services/ea-observer.ts +1 -1
  40. package/src/ts/services/effector.ts +22 -12
  41. package/src/ts/services/refresher.ts +14 -4
  42. package/src/ts/services/resize-observer.ts +24 -11
  43. package/src/ts/services/select-observer.ts +2 -2
  44. package/src/ts/types/components/popup.type.ts +18 -1
  45. package/src/ts/types/components/searchbox.type.ts +43 -30
  46. package/src/ts/types/components/state.box.type.ts +1 -1
  47. package/src/ts/types/core/base/adapter.type.ts +13 -5
  48. package/src/ts/types/core/base/lifecycle.type.ts +1 -2
  49. package/src/ts/types/core/base/model.type.ts +3 -3
  50. package/src/ts/types/core/base/recyclerview.type.ts +7 -5
  51. package/src/ts/types/core/base/view.type.ts +6 -6
  52. package/src/ts/types/core/base/virtual-recyclerview.type.ts +45 -46
  53. package/src/ts/types/core/search-controller.type.ts +18 -2
  54. package/src/ts/types/css.d.ts +1 -0
  55. package/src/ts/types/plugins/plugin.type.ts +2 -2
  56. package/src/ts/types/services/effector.type.ts +25 -25
  57. package/src/ts/types/services/resize-observer.type.ts +23 -12
  58. package/src/ts/types/utils/callback-scheduler.type.ts +2 -2
  59. package/src/ts/types/utils/ievents.type.ts +1 -1
  60. package/src/ts/types/utils/istorage.type.ts +62 -60
  61. package/src/ts/types/utils/libs.type.ts +19 -17
  62. package/src/ts/types/utils/selective.type.ts +6 -3
  63. package/src/ts/types/views/view.group.type.ts +9 -5
  64. package/src/ts/types/views/view.option.type.ts +39 -17
  65. package/src/ts/utils/callback-scheduler.ts +12 -7
  66. package/src/ts/utils/ievents.ts +12 -5
  67. package/src/ts/utils/istorage.ts +5 -3
  68. package/src/ts/utils/libs.ts +122 -43
  69. package/src/ts/utils/selective.ts +15 -8
  70. package/src/ts/views/group-view.ts +11 -9
  71. package/src/ts/views/option-view.ts +37 -18
@@ -4,7 +4,12 @@ import { GroupModel } from "../models/group-model";
4
4
  import { OptionModel } from "../models/option-model";
5
5
  import { LifecycleState } from "../types/core/base/lifecycle.type";
6
6
  import { MixedItem } from "../types/core/base/mixed-adapter.type";
7
- import { AjaxConfig, NormalizedAjaxItem, PaginationState, ParseResponseResult } from "../types/core/search-controller.type";
7
+ import {
8
+ AjaxConfig,
9
+ NormalizedAjaxItem,
10
+ PaginationState,
11
+ ParseResponseResult,
12
+ } from "../types/core/search-controller.type";
8
13
  import { Libs } from "../utils/libs";
9
14
  import { Lifecycle } from "./base/lifecycle";
10
15
  import { ModelManager } from "./model-manager";
@@ -52,13 +57,13 @@ export class SearchController extends Lifecycle {
52
57
  * AJAX configuration; when `null`, {@link search} falls back to local filtering.
53
58
  * @see {@link setAjax}
54
59
  */
55
- private ajaxConfig: AjaxConfig | null = null;
60
+ private ajaxConfig?: AjaxConfig;
56
61
 
57
62
  /** Abort handle used to cancel an in-flight AJAX request when a newer request starts. */
58
- private abortController: AbortController | null = null;
63
+ private abortController?: AbortController;
59
64
 
60
65
  /** Optional popup handle used for showing/hiding loading UI during remote operations. */
61
- private popup: Popup | null = null;
66
+ private popup?: Popup;
62
67
 
63
68
  /**
64
69
  * SelectBox handle used by custom data builder functions that require Selective context.
@@ -88,7 +93,11 @@ export class SearchController extends Lifecycle {
88
93
  * @param {ModelManager<MixedItem, any>} modelManager - Manager responsible for model resources and rendering refresh.
89
94
  * @param {SelectBox} selectBox - SelectBox handle used by configured AJAX data builders.
90
95
  */
91
- public constructor(selectElement: HTMLSelectElement, modelManager: ModelManager<MixedItem, any>, selectBox: SelectBox) {
96
+ public constructor(
97
+ selectElement: HTMLSelectElement,
98
+ modelManager: ModelManager<MixedItem, any>,
99
+ selectBox: SelectBox,
100
+ ) {
92
101
  super();
93
102
  this.initialize(selectElement, modelManager, selectBox);
94
103
  }
@@ -102,7 +111,11 @@ export class SearchController extends Lifecycle {
102
111
  * @param {SelectBox} selectBox - SelectBox handle.
103
112
  * @returns {void}
104
113
  */
105
- private initialize(selectElement: HTMLSelectElement, modelManager: ModelManager<MixedItem, any>, selectBox: SelectBox): void {
114
+ private initialize(
115
+ selectElement: HTMLSelectElement,
116
+ modelManager: ModelManager<MixedItem, any>,
117
+ selectBox: SelectBox,
118
+ ): void {
106
119
  this.select = selectElement;
107
120
  this.modelManager = modelManager;
108
121
  this.selectBox = selectBox;
@@ -137,9 +150,17 @@ export class SearchController extends Lifecycle {
137
150
  * - When AJAX is not configured, resolves with `{ success: false, ... }`.
138
151
  * - This method does not mutate the `<select>`; it only returns normalized items.
139
152
  */
140
- async loadByValues(values: string | string[]): Promise<{ success: boolean; items: NormalizedAjaxItem[]; message?: string }> {
153
+ async loadByValues(values: string | string[]): Promise<{
154
+ success: boolean;
155
+ items: NormalizedAjaxItem[];
156
+ message?: string;
157
+ }> {
141
158
  if (!this.ajaxConfig) {
142
- return { success: false, items: [], message: "Ajax not configured" };
159
+ return {
160
+ success: false,
161
+ items: [],
162
+ message: "Ajax not configured",
163
+ };
143
164
  }
144
165
 
145
166
  const valuesArray = Array.isArray(values) ? values : [values];
@@ -156,8 +177,12 @@ export class SearchController extends Lifecycle {
156
177
  values: valuesArray.join(","),
157
178
  load_by_values: "1",
158
179
  ...(typeof cfg.data === "function"
159
- ? cfg.data.bind(this.selectBox.Selective.find(this.selectBox.container.targetElement))("", 0)
160
- : cfg.data ?? {}),
180
+ ? cfg.data.bind(
181
+ this.selectBox.Selective.find(
182
+ this.selectBox.container.targetElement,
183
+ ),
184
+ )("", 0)
185
+ : (cfg.data ?? {})),
161
186
  };
162
187
  }
163
188
 
@@ -165,18 +190,23 @@ export class SearchController extends Lifecycle {
165
190
 
166
191
  if ((cfg.method ?? "GET") === "POST") {
167
192
  const formData = new URLSearchParams();
168
- Object.keys(payload).forEach((key) => formData.append(key, String(payload[key])));
193
+ Object.keys(payload).forEach((key) =>
194
+ formData.append(key, String(payload[key])),
195
+ );
169
196
  response = await fetch(cfg.url, {
170
197
  method: "POST",
171
198
  body: formData,
172
- headers: { "Content-Type": "application/x-www-form-urlencoded" },
199
+ headers: {
200
+ "Content-Type": "application/x-www-form-urlencoded",
201
+ },
173
202
  });
174
203
  } else {
175
204
  const params = new URLSearchParams(payload).toString();
176
205
  response = await fetch(`${cfg.url}?${params}`);
177
206
  }
178
207
 
179
- if (!response.ok) throw new Error(`HTTP error! status: ${response.status}`);
208
+ if (!response.ok)
209
+ throw new Error(`HTTP error! status: ${response.status}`);
180
210
 
181
211
  const data = await response.json();
182
212
  const result = this.parseResponse(data);
@@ -197,7 +227,10 @@ export class SearchController extends Lifecycle {
197
227
  * @param {string[]} values - Values to check.
198
228
  * @returns {{ existing: string[]; missing: string[] }} Partitioned result.
199
229
  */
200
- public checkMissingValues(values: string[]): { existing: string[]; missing: string[] } {
230
+ public checkMissingValues(values: string[]): {
231
+ existing: string[];
232
+ missing: string[];
233
+ } {
201
234
  const allOptions = Array.from(this.select.options);
202
235
  const existingValues = allOptions.map((opt) => opt.value);
203
236
 
@@ -211,10 +244,10 @@ export class SearchController extends Lifecycle {
211
244
  * Configures AJAX settings used for remote searching and pagination.
212
245
  * Setting `null` disables AJAX mode and causes {@link search} to use local filtering.
213
246
  *
214
- * @param {AjaxConfig | null} config - AJAX configuration (endpoint, method, data builders, keepSelected, ...).
247
+ * @param {AjaxConfig} config - AJAX configuration (endpoint, method, data builders, keepSelected, ...).
215
248
  * @returns {void}
216
249
  */
217
- public setAjax(config: AjaxConfig | null): void {
250
+ public setAjax(config?: AjaxConfig): void {
218
251
  this.ajaxConfig = config;
219
252
  }
220
253
 
@@ -272,7 +305,8 @@ export class SearchController extends Lifecycle {
272
305
 
273
306
  for (const m of modelList) {
274
307
  if (m instanceof OptionModel) flatOptions.push(m);
275
- else if (m instanceof GroupModel && Array.isArray(m.items)) flatOptions.push(...m.items);
308
+ else if (m instanceof GroupModel && Array.isArray(m.items))
309
+ flatOptions.push(...m.items);
276
310
  }
277
311
 
278
312
  flatOptions.forEach((opt) => {
@@ -289,7 +323,10 @@ export class SearchController extends Lifecycle {
289
323
  * @param {boolean} [append=false] - AJAX mode only: append results (next page) instead of replacing.
290
324
  * @returns {Promise<any>} Implementation-specific result object from the underlying strategy.
291
325
  */
292
- public async search(keyword: string, append: boolean = false): Promise<any> {
326
+ public async search(
327
+ keyword: string,
328
+ append: boolean = false,
329
+ ): Promise<any> {
293
330
  if (this.ajaxConfig) return this.ajaxSearch(keyword, append);
294
331
  return this.localSearch(keyword);
295
332
  }
@@ -305,10 +342,14 @@ export class SearchController extends Lifecycle {
305
342
  * @returns {Promise<any>} Result of the paginated request, or an error object when not applicable.
306
343
  */
307
344
  public async loadMore(): Promise<any> {
308
- if (!this.ajaxConfig) return { success: false, message: "Ajax not enabled" };
309
- if (this.paginationState.isLoading) return { success: false, message: "Already loading" };
310
- if (!this.paginationState.isPaginationEnabled) return { success: false, message: "Pagination not enabled" };
311
- if (!this.paginationState.hasMore) return { success: false, message: "No more data" };
345
+ if (!this.ajaxConfig)
346
+ return { success: false, message: "Ajax not enabled" };
347
+ if (this.paginationState.isLoading)
348
+ return { success: false, message: "Already loading" };
349
+ if (!this.paginationState.isPaginationEnabled)
350
+ return { success: false, message: "Pagination not enabled" };
351
+ if (!this.paginationState.hasMore)
352
+ return { success: false, message: "No more data" };
312
353
 
313
354
  this.paginationState.currentPage++;
314
355
  return this.ajaxSearch(this.paginationState.currentKeyword, true);
@@ -329,8 +370,11 @@ export class SearchController extends Lifecycle {
329
370
  * @returns {Promise<{ success: boolean; hasResults: boolean; isEmpty: boolean }>}
330
371
  * Summary result for UI consumers.
331
372
  */
332
- private async localSearch(keyword: string): Promise<{ success: boolean; hasResults: boolean; isEmpty: boolean }> {
333
- if (this.compareSearchTrigger(keyword)) this.paginationState.currentKeyword = keyword;
373
+ private async localSearch(
374
+ keyword: string,
375
+ ): Promise<{ success: boolean; hasResults: boolean; isEmpty: boolean }> {
376
+ if (this.compareSearchTrigger(keyword))
377
+ this.paginationState.currentKeyword = keyword;
334
378
 
335
379
  const lower = String(keyword ?? "").toLowerCase();
336
380
  const lowerNA = Libs.string2normalize(lower);
@@ -340,7 +384,8 @@ export class SearchController extends Lifecycle {
340
384
  const flatOptions: OptionModel[] = [];
341
385
  for (const m of modelList) {
342
386
  if (m instanceof OptionModel) flatOptions.push(m);
343
- else if (m instanceof GroupModel && Array.isArray(m.items)) flatOptions.push(...m.items);
387
+ else if (m instanceof GroupModel && Array.isArray(m.items))
388
+ flatOptions.push(...m.items);
344
389
  }
345
390
 
346
391
  let hasVisibleItems = false;
@@ -388,7 +433,10 @@ export class SearchController extends Lifecycle {
388
433
  * @param {boolean} [append=false] - Whether to append results (true = next page).
389
434
  * @returns {Promise<any>} Implementation-specific result object with pagination flags.
390
435
  */
391
- private async ajaxSearch(keyword: string, append: boolean = false): Promise<any> {
436
+ private async ajaxSearch(
437
+ keyword: string,
438
+ append: boolean = false,
439
+ ): Promise<any> {
392
440
  const cfg = this.ajaxConfig!;
393
441
  if (this.compareSearchTrigger(keyword)) {
394
442
  this.resetPagination();
@@ -410,11 +458,19 @@ export class SearchController extends Lifecycle {
410
458
 
411
459
  let payload: Record<string, any>;
412
460
  if (typeof cfg.data === "function") {
413
- const selectiveInstance = this.selectBox?.Selective?.find(this.selectBox?.container?.targetElement);
461
+ const selectiveInstance = this.selectBox?.Selective?.find(
462
+ this.selectBox?.container?.targetElement,
463
+ );
414
464
  payload = cfg.data.call(selectiveInstance, keyword, page);
415
- if (payload && typeof payload.selectedValue === "undefined") payload.selectedValue = selectedValues;
465
+ if (payload && typeof payload.selectedValue === "undefined")
466
+ payload.selectedValue = selectedValues;
416
467
  } else {
417
- payload = { search: keyword, page, selectedValue: selectedValues, ...(cfg.data ?? {}) };
468
+ payload = {
469
+ search: keyword,
470
+ page,
471
+ selectedValue: selectedValues,
472
+ ...(cfg.data ?? {}),
473
+ };
418
474
  }
419
475
 
420
476
  try {
@@ -422,16 +478,22 @@ export class SearchController extends Lifecycle {
422
478
 
423
479
  if ((cfg.method ?? "GET") === "POST") {
424
480
  const formData = new URLSearchParams();
425
- Object.keys(payload).forEach((key) => formData.append(key, String(payload[key])));
481
+ Object.keys(payload).forEach((key) =>
482
+ formData.append(key, String(payload[key])),
483
+ );
426
484
  response = await fetch(cfg.url, {
427
485
  method: "POST",
428
486
  body: formData,
429
- headers: { "Content-Type": "application/x-www-form-urlencoded" },
487
+ headers: {
488
+ "Content-Type": "application/x-www-form-urlencoded",
489
+ },
430
490
  signal: this.abortController.signal,
431
491
  });
432
492
  } else {
433
493
  const params = new URLSearchParams(payload).toString();
434
- response = await fetch(`${cfg.url}?${params}`, { signal: this.abortController.signal });
494
+ response = await fetch(`${cfg.url}?${params}`, {
495
+ signal: this.abortController.signal,
496
+ });
435
497
  }
436
498
 
437
499
  const data = await response.json();
@@ -466,7 +528,8 @@ export class SearchController extends Lifecycle {
466
528
  this.paginationState.isLoading = false;
467
529
  this.popup?.hideLoading();
468
530
 
469
- if (error?.name === "AbortError") return { success: false, message: "Request aborted" };
531
+ if (error?.name === "AbortError")
532
+ return { success: false, message: "Request aborted" };
470
533
 
471
534
  console.error("Ajax search error:", error);
472
535
  return { success: false, message: error?.message };
@@ -502,7 +565,10 @@ export class SearchController extends Lifecycle {
502
565
  if (typeof data.page !== "undefined") {
503
566
  hasPagination = true;
504
567
  page = parseInt(data.page ?? 0, 10);
505
- totalPages = parseInt(data.totalPages ?? data.total_page ?? 1, 10);
568
+ totalPages = parseInt(
569
+ data.totalPages ?? data.total_page ?? 1,
570
+ 10,
571
+ );
506
572
  hasMore = page < totalPages - 1;
507
573
  }
508
574
  } else if (data.data && Array.isArray(data.data)) {
@@ -510,8 +576,11 @@ export class SearchController extends Lifecycle {
510
576
  if (typeof data.page !== "undefined") {
511
577
  hasPagination = true;
512
578
  page = parseInt(data.page ?? 0, 10);
513
- totalPages = parseInt(data.totalPages ?? data.total_page ?? 1, 10);
514
- hasMore = data.hasMore ?? (page < totalPages - 1);
579
+ totalPages = parseInt(
580
+ data.totalPages ?? data.total_page ?? 1,
581
+ 10,
582
+ );
583
+ hasMore = data.hasMore ?? page < totalPages - 1;
515
584
  }
516
585
  } else if (Array.isArray(data)) {
517
586
  items = data;
@@ -520,25 +589,53 @@ export class SearchController extends Lifecycle {
520
589
  if (data.pagination) {
521
590
  hasPagination = true;
522
591
  page = parseInt(data.pagination.page ?? 0, 10);
523
- totalPages = parseInt(data.pagination.totalPages ?? data.pagination.total_page ?? 1, 10);
524
- hasMore = data.pagination.hasMore ?? (page < totalPages - 1);
592
+ totalPages = parseInt(
593
+ data.pagination.totalPages ??
594
+ data.pagination.total_page ??
595
+ 1,
596
+ 10,
597
+ );
598
+ hasMore = data.pagination.hasMore ?? page < totalPages - 1;
525
599
  }
526
600
  }
527
601
 
528
602
  const normalized: NormalizedAjaxItem[] = items.map((item: any) => {
529
- if (item instanceof HTMLOptionElement || item instanceof HTMLOptGroupElement) return item;
530
-
531
- if (item.type === "optgroup" || item.isGroup || item.group || item.label) {
603
+ if (
604
+ item instanceof HTMLOptionElement ||
605
+ item instanceof HTMLOptGroupElement
606
+ )
607
+ return item;
608
+
609
+ if (
610
+ item.type === "optgroup" ||
611
+ item.isGroup ||
612
+ item.group ||
613
+ item.label
614
+ ) {
532
615
  const label = item.label ?? item.name ?? item.title ?? "";
533
616
  const dataObj = item.data ?? {};
534
- const opts = (item.options ?? item.items ?? []).map((opt: any) => ({
535
- value: opt.value ?? opt.id ?? opt.key ?? "",
536
- text: opt.text ?? opt.label ?? opt.name ?? opt.title ?? "",
537
- selected: opt.selected ?? false,
538
- data: opt.data ?? (opt.imgsrc ? { imgsrc: opt.imgsrc } : {}),
539
- }));
540
-
541
- return { type: "optgroup", label, data: dataObj, options: opts };
617
+ const opts = (item.options ?? item.items ?? []).map(
618
+ (opt: any) => ({
619
+ value: opt.value ?? opt.id ?? opt.key ?? "",
620
+ text:
621
+ opt.text ??
622
+ opt.label ??
623
+ opt.name ??
624
+ opt.title ??
625
+ "",
626
+ selected: opt.selected ?? false,
627
+ data:
628
+ opt.data ??
629
+ (opt.imgsrc ? { imgsrc: opt.imgsrc } : {}),
630
+ }),
631
+ );
632
+
633
+ return {
634
+ type: "optgroup",
635
+ label,
636
+ data: dataObj,
637
+ options: opts,
638
+ };
542
639
  }
543
640
 
544
641
  const dataObj = item.data ?? {};
@@ -574,19 +671,34 @@ export class SearchController extends Lifecycle {
574
671
  * @param {boolean} [append=false] - Append to existing options instead of replacing.
575
672
  * @returns {void}
576
673
  */
577
- public applyAjaxResult(items: NormalizedAjaxItem[], keepSelected: boolean, append: boolean = false): void {
674
+ public applyAjaxResult(
675
+ items: NormalizedAjaxItem[],
676
+ keepSelected: boolean,
677
+ append: boolean = false,
678
+ ): void {
578
679
  const select = this.select;
579
680
 
580
681
  let oldSelected: string[] = [];
581
- if (keepSelected) oldSelected = Array.from(select.selectedOptions).map((o) => o.value);
682
+ if (keepSelected)
683
+ oldSelected = Array.from(select.selectedOptions).map(
684
+ (o) => o.value,
685
+ );
582
686
 
583
687
  if (!append) select.innerHTML = "";
584
688
 
585
689
  items.forEach((item: any) => {
586
690
  // Skip empty item (defensive guard)
587
- if ((item["type"] === "option" || !item["type"]) && item["value"] === "" && item["text"] === "") return;
691
+ if (
692
+ (item["type"] === "option" || !item["type"]) &&
693
+ item["value"] === "" &&
694
+ item["text"] === ""
695
+ )
696
+ return;
588
697
 
589
- if (item instanceof HTMLOptionElement || item instanceof HTMLOptGroupElement) {
698
+ if (
699
+ item instanceof HTMLOptionElement ||
700
+ item instanceof HTMLOptGroupElement
701
+ ) {
590
702
  select.appendChild(item);
591
703
  return;
592
704
  }
@@ -613,7 +725,10 @@ export class SearchController extends Lifecycle {
613
725
  });
614
726
  }
615
727
 
616
- if (opt.selected || (keepSelected && oldSelected.includes(option.value))) {
728
+ if (
729
+ opt.selected ||
730
+ (keepSelected && oldSelected.includes(option.value))
731
+ ) {
617
732
  option.selected = true;
618
733
  }
619
734
 
@@ -633,7 +748,10 @@ export class SearchController extends Lifecycle {
633
748
  });
634
749
  }
635
750
 
636
- if (item.selected || (keepSelected && oldSelected.includes(option.value))) {
751
+ if (
752
+ item.selected ||
753
+ (keepSelected && oldSelected.includes(option.value))
754
+ ) {
637
755
  option.selected = true;
638
756
  }
639
757
 
@@ -667,4 +785,4 @@ export class SearchController extends Lifecycle {
667
785
 
668
786
  super.destroy();
669
787
  }
670
- }
788
+ }
package/src/ts/global.ts CHANGED
@@ -57,7 +57,7 @@ if (typeof globalThis.GLOBAL_SEUI == "undefined") {
57
57
  effector: Effector.bind(Effector),
58
58
  rebind: SECLASS.rebind.bind(SECLASS),
59
59
  registerPlugin: SECLASS.registerPlugin.bind(SECLASS),
60
- unregisterPlugin: SECLASS.unregisterPlugin.bind(SECLASS)
60
+ unregisterPlugin: SECLASS.unregisterPlugin.bind(SECLASS),
61
61
  } as SelectiveUIGlobal;
62
62
 
63
63
  let domInitialized = false;
@@ -68,9 +68,7 @@ if (typeof globalThis.GLOBAL_SEUI == "undefined") {
68
68
  document.addEventListener("mousedown", () => {
69
69
  const sels = Libs.getBindedCommand();
70
70
  if (sels.length > 0) {
71
- const actionApi = SECLASS.find(
72
- sels.join(", ")
73
- );
71
+ const actionApi = SECLASS.find(sels.join(", "));
74
72
  if (!actionApi.isEmpty) actionApi.close();
75
73
  }
76
74
  });
@@ -86,11 +84,10 @@ if (typeof globalThis.GLOBAL_SEUI == "undefined") {
86
84
  }
87
85
  }
88
86
  console.log(`[${__LIB_NAME__}] v${__LIB_VERSION__} loaded successfully`);
89
- }
90
- else {
87
+ } else {
91
88
  console.warn(
92
89
  `[${globalThis.GLOBAL_SEUI.name}] Already loaded (v${globalThis.GLOBAL_SEUI.version}). ` +
93
- `Using existing instance. Please remove duplicate <script> tags.`
90
+ `Using existing instance. Please remove duplicate <script> tags.`,
94
91
  );
95
92
  }
96
93
 
@@ -149,7 +146,7 @@ export function find(query: string): SelectiveActionApi {
149
146
  * // Destroy all instances
150
147
  * destroy();
151
148
  */
152
- export function destroy(query: string | null = null): void {
149
+ export function destroy(query?: string): void {
153
150
  globalThis.GLOBAL_SEUI.destroy(query);
154
151
  }
155
152
 
package/src/ts/index.ts CHANGED
@@ -98,7 +98,7 @@ export function find(query: string): SelectiveActionApi {
98
98
  * // Destroy all instances
99
99
  * destroy();
100
100
  */
101
- export function destroy(query: string | null = null): void {
101
+ export function destroy(query?: string): void {
102
102
  SECLASS.destroy(query);
103
103
  }
104
104
 
@@ -161,7 +161,7 @@ function init(): void {
161
161
  const sels = Libs.getBindedCommand();
162
162
  if (sels.length > 0) {
163
163
  const actionApi = SECLASS.find(
164
- sels.join(", ")
164
+ sels.join(", "),
165
165
  ) as SelectiveActionApi;
166
166
  if (!actionApi.isEmpty) actionApi.close();
167
167
  }
@@ -43,7 +43,12 @@ import { LifecycleState } from "../types/core/base/lifecycle.type";
43
43
  * @see {@link OptionModel}
44
44
  * @see {@link GroupView}
45
45
  */
46
- export class GroupModel extends Model<HTMLOptGroupElement, GroupViewTags, GroupView, SelectiveOptions> {
46
+ export class GroupModel extends Model<
47
+ HTMLOptGroupElement,
48
+ GroupViewTags,
49
+ GroupView,
50
+ SelectiveOptions
51
+ > {
47
52
  /** Group label (mirrors `HTMLSelectOptGroupElement.label`). */
48
53
  public label = "";
49
54
 
@@ -67,7 +72,13 @@ export class GroupModel extends Model<HTMLOptGroupElement, GroupViewTags, GroupV
67
72
  * Subscribers invoked when collapsed state changes.
68
73
  * Callbacks are invoked through {@link iEvents.callEvent}.
69
74
  */
70
- private privOnCollapsedChanged: Array<(evtToken: IEventCallback, model: GroupModel, collapsed: boolean) => void> = [];
75
+ private privOnCollapsedChanged: Array<
76
+ (
77
+ evtToken: IEventCallback,
78
+ model: GroupModel,
79
+ collapsed: boolean,
80
+ ) => void
81
+ > = [];
71
82
 
72
83
  /**
73
84
  * Creates a group model from configuration and an optional `<optgroup>` element.
@@ -75,10 +86,15 @@ export class GroupModel extends Model<HTMLOptGroupElement, GroupViewTags, GroupV
75
86
  * @param {SelectiveOptions} options - Shared configuration for models/views.
76
87
  * @param {HTMLOptGroupElement} [targetElement] - Backing `<optgroup>` element (when available).
77
88
  */
78
- public constructor(options: SelectiveOptions, targetElement?: HTMLOptGroupElement) {
89
+ public constructor(
90
+ options: SelectiveOptions,
91
+ targetElement?: HTMLOptGroupElement,
92
+ ) {
79
93
  super(options, targetElement ?? null, null);
80
94
  this.label = this.targetElement.label;
81
- this.collapsed = Libs.string2Boolean(this.targetElement.dataset?.collapsed);
95
+ this.collapsed = Libs.string2Boolean(
96
+ this.targetElement.dataset?.collapsed,
97
+ );
82
98
  }
83
99
 
84
100
  /**
@@ -189,7 +205,7 @@ export class GroupModel extends Model<HTMLOptGroupElement, GroupViewTags, GroupV
189
205
  return;
190
206
  }
191
207
 
192
- this.items.forEach(item => {
208
+ this.items.forEach((item) => {
193
209
  item.destroy();
194
210
  });
195
211
 
@@ -206,7 +222,13 @@ export class GroupModel extends Model<HTMLOptGroupElement, GroupViewTags, GroupV
206
222
  * Listener invoked with `(evtToken, model, collapsed)`.
207
223
  * @returns {void}
208
224
  */
209
- public onCollapsedChanged(callback: (evtToken: IEventCallback, model: GroupModel, collapsed: boolean) => void): void {
225
+ public onCollapsedChanged(
226
+ callback: (
227
+ evtToken: IEventCallback,
228
+ model: GroupModel,
229
+ collapsed: boolean,
230
+ ) => void,
231
+ ): void {
210
232
  this.privOnCollapsedChanged.push(callback);
211
233
  }
212
234
 
@@ -224,7 +246,10 @@ export class GroupModel extends Model<HTMLOptGroupElement, GroupViewTags, GroupV
224
246
  this.collapsed = !this.collapsed;
225
247
  this.view?.setCollapsed(this.collapsed);
226
248
 
227
- iEvents.callEvent<[GroupModel, boolean]>([this, this.collapsed], ...this.privOnCollapsedChanged);
249
+ iEvents.callEvent<[GroupModel, boolean]>(
250
+ [this, this.collapsed],
251
+ ...this.privOnCollapsedChanged,
252
+ );
228
253
  }
229
254
 
230
255
  /**
@@ -267,4 +292,4 @@ export class GroupModel extends Model<HTMLOptGroupElement, GroupViewTags, GroupV
267
292
  public updateVisibility(): void {
268
293
  this.view?.updateVisibility();
269
294
  }
270
- }
295
+ }