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
@@ -2,7 +2,10 @@ import { ModelContract } from "src/ts/types/core/base/model.type";
2
2
  import { RecyclerView } from "./recyclerview";
3
3
  import { AdapterContract } from "src/ts/types/core/base/adapter.type";
4
4
  import { Libs } from "src/ts/utils/libs";
5
- import { VirtualOptions, VirtualRecyclerViewTags } from "src/ts/types/core/base/virtual-recyclerview.type";
5
+ import {
6
+ VirtualOptions,
7
+ VirtualRecyclerViewTags,
8
+ } from "src/ts/types/core/base/virtual-recyclerview.type";
6
9
  import { Lifecycle } from "./lifecycle";
7
10
  import { LifecycleState } from "src/ts/types/core/base/lifecycle.type";
8
11
  import { Fenwick } from "./fenwick";
@@ -58,7 +61,7 @@ import { Fenwick } from "./fenwick";
58
61
  */
59
62
  export class VirtualRecyclerView<
60
63
  TItem extends ModelContract<any, any>,
61
- TAdapter extends AdapterContract<TItem>
64
+ TAdapter extends AdapterContract<TItem>,
62
65
  > extends RecyclerView<TItem, TAdapter> {
63
66
  /**
64
67
  * Virtualization settings (materialized to `Required<VirtualOptions>`).
@@ -109,8 +112,8 @@ export class VirtualRecyclerView<
109
112
  private resizeObs?: ResizeObserver;
110
113
 
111
114
  /** Pending animation frame ids for window and measurement. */
112
- private rafId: number | null = null;
113
- private measureRaf: number | null = null;
115
+ private rafId?: number;
116
+ private measureRaf?: number;
114
117
 
115
118
  /** Re-entrancy/suspension flags used to prevent feedback loops. */
116
119
  private updating = false;
@@ -119,6 +122,12 @@ export class VirtualRecyclerView<
119
122
  private suspended = false;
120
123
  private boundOnScroll?: () => void;
121
124
  private resumeResizeAfter = false;
125
+ /**
126
+ * When set, scrollToIndex() will be called after the next measureVisibleAndUpdate()
127
+ * completes and Fenwick has been updated with real heights.
128
+ * Set by ensureRendered() and cleared after the corrective scroll fires.
129
+ */
130
+ private pendingScrollToIndex: number | null = null;
122
131
 
123
132
  /** Small cache for sticky header height (≈16ms TTL) to limit layout reads. */
124
133
  private stickyCacheTick = 0;
@@ -138,9 +147,9 @@ export class VirtualRecyclerView<
138
147
  *
139
148
  * Note: The virtualization scaffold is built when an adapter is set via {@link setAdapter}.
140
149
  *
141
- * @param {HTMLDivElement | null} [viewElement=null] - Optional root container for the recycler view.
150
+ * @param {HTMLDivElement} [viewElement=null] - Optional root container for the recycler view.
142
151
  */
143
- constructor(viewElement: HTMLDivElement | null = null) {
152
+ constructor(viewElement?: HTMLDivElement) {
144
153
  super(viewElement);
145
154
  }
146
155
 
@@ -187,24 +196,37 @@ export class VirtualRecyclerView<
187
196
 
188
197
  this.viewElement.replaceChildren();
189
198
 
190
- const nodeMounted = Libs.mountNode({
191
- PadTop: { tag: { node: "div", classList: "seui-virtual-pad-top" } },
192
- ItemsHost:{ tag: { node: "div", classList: "seui-virtual-items" } },
193
- PadBottom:{ tag: { node: "div", classList: "seui-virtual-pad-bottom" } },
194
- }, this.viewElement) as VirtualRecyclerViewTags;
199
+ const nodeMounted = Libs.mountNode<VirtualRecyclerViewTags>(
200
+ {
201
+ PadTop: {
202
+ tag: { node: "div", classList: "seui-virtual-pad-top" },
203
+ },
204
+ ItemsHost: {
205
+ tag: { node: "div", classList: "seui-virtual-items" },
206
+ },
207
+ PadBottom: {
208
+ tag: { node: "div", classList: "seui-virtual-pad-bottom" },
209
+ },
210
+ },
211
+ this.viewElement,
212
+ );
195
213
 
196
214
  this.PadTop = nodeMounted.PadTop;
197
215
  this.ItemsHost = nodeMounted.ItemsHost;
198
216
  this.PadBottom = nodeMounted.PadBottom;
199
217
 
200
- this.scrollEl = this.opts.scrollEl
201
- ?? (this.viewElement.closest(".seui-popup") as HTMLElement)
202
- ?? (this.viewElement.parentElement as HTMLElement);
218
+ this.scrollEl =
219
+ this.opts.scrollEl ??
220
+ (this.viewElement.closest(".seui-popup") as HTMLElement) ??
221
+ (this.viewElement.parentElement as HTMLElement);
203
222
 
204
- if (!this.scrollEl) throw new Error("VirtualRecyclerView: scrollEl not found");
223
+ if (!this.scrollEl)
224
+ throw new Error("VirtualRecyclerView: scrollEl not found");
205
225
 
206
226
  this.boundOnScroll = this.onScroll.bind(this);
207
- this.scrollEl.addEventListener("scroll", this.boundOnScroll, { passive: true });
227
+ this.scrollEl.addEventListener("scroll", this.boundOnScroll, {
228
+ passive: true,
229
+ });
208
230
 
209
231
  this.refresh(false);
210
232
  this.attachResizeObserverOnce();
@@ -249,7 +271,9 @@ export class VirtualRecyclerView<
249
271
  this.suspended = false;
250
272
 
251
273
  if (this.scrollEl && this.boundOnScroll) {
252
- this.scrollEl.addEventListener("scroll", this.boundOnScroll, { passive: true });
274
+ this.scrollEl.addEventListener("scroll", this.boundOnScroll, {
275
+ passive: true,
276
+ });
253
277
  }
254
278
 
255
279
  if (this.resumeResizeAfter) {
@@ -310,9 +334,25 @@ export class VirtualRecyclerView<
310
334
  * @param {{ scrollIntoView?: boolean }} [opt] - Optional behavior controls.
311
335
  * @returns {void}
312
336
  */
313
- public ensureRendered(index: number, opt?: { scrollIntoView?: boolean }): void {
314
- this.mountRange(index, index);
315
- if (opt?.scrollIntoView) this.scrollToIndex(index);
337
+ public ensureRendered(
338
+ index: number,
339
+ opt?: { scrollIntoView?: boolean },
340
+ ): void {
341
+ if (!opt?.scrollIntoView) {
342
+ // No scroll requested — mount only (legacy path, used by probes).
343
+ this.mountRange(index, index);
344
+ return;
345
+ }
346
+
347
+ // Pass 1: instant — brings window to vicinity, triggers measure.
348
+ // Must be instant so Pass 2 smooth scroll isn't interrupted mid-animation.
349
+ this.scrollToIndex(index, "instant");
350
+
351
+ // Pass 2: measureVisibleAndUpdate() will consume this and fire a corrective
352
+ // smooth scroll after Fenwick has been updated with real heights.
353
+ // rv.resume() is guaranteed to run before this callback (popup.open onComplete
354
+ // calls rv.resume() first), so the window is already rendered when we arrive here.
355
+ this.pendingScrollToIndex = index;
316
356
  }
317
357
 
318
358
  /**
@@ -327,16 +367,32 @@ export class VirtualRecyclerView<
327
367
  * @param {number} index - Item index to bring into view.
328
368
  * @returns {void}
329
369
  */
330
- public scrollToIndex(index: number): void {
370
+ public scrollToIndex(index: number, behavior: ScrollBehavior = "smooth"): void {
331
371
  const count = this.adapter?.itemCount?.() ?? 0;
332
372
  if (count <= 0) return;
333
373
 
334
374
  const topInContainer = this.offsetTopOf(index);
335
375
  const containerTop = this.containerTopInScroll();
336
- const target = containerTop + topInContainer;
337
- const maxScroll = Math.max(0, this.scrollEl.scrollHeight - this.scrollEl.clientHeight);
376
+ const stickyH = this.stickyTopHeight();
377
+ const viewportH = Math.max(0, this.scrollEl.clientHeight - stickyH);
378
+
379
+ // item height from cache, or current estimate for unmeasured items
380
+ const est = this.getEstimate();
381
+ const itemH = this.heightCache[index] ?? est;
338
382
 
339
- this.scrollEl.scrollTop = Math.min(Math.max(0, target), maxScroll);
383
+ // Align item center to viewport center (below any sticky header).
384
+ // viewportH already excludes stickyH, so no further offset needed.
385
+ // Equivalent to scrollIntoView({ block: "center" }).
386
+ const centeredTarget = containerTop + topInContainer
387
+ - (viewportH - itemH) / 3;
388
+
389
+ const maxScroll = Math.max(
390
+ 0,
391
+ this.scrollEl.scrollHeight - this.scrollEl.clientHeight,
392
+ );
393
+
394
+ const clamped = Math.min(Math.max(0, centeredTarget), maxScroll);
395
+ this.scrollEl.scrollTo({ top: clamped, behavior });
340
396
  }
341
397
 
342
398
  /**
@@ -358,7 +414,7 @@ export class VirtualRecyclerView<
358
414
  }
359
415
 
360
416
  this.resizeObs?.disconnect();
361
- this.created.forEach(el => el.remove());
417
+ this.created.forEach((el) => el.remove());
362
418
  this.created.clear();
363
419
  }
364
420
 
@@ -411,9 +467,24 @@ export class VirtualRecyclerView<
411
467
  if (count <= 0) return;
412
468
 
413
469
  this.suspend();
414
- this.resetState();
470
+ this.pendingScrollToIndex = null;
471
+
472
+ // When visibility changes (search filter applied or cleared), heightCache may
473
+ // contain heights measured while only a subset of items was visible. Re-using
474
+ // these partial measurements in rebuildFenwick() causes incorrect prefix sums
475
+ // (e.g. items measured while scrolled into a filtered window have real heights,
476
+ // while surrounding items still use estimates — creating an uneven Fenwick).
477
+ //
478
+ // Safe fix: clear heightCache entirely on visibility change. The adaptive
479
+ // estimator will re-seed from probeInitialHeight() on the next render, and
480
+ // items will be re-measured as they scroll into view.
481
+ this.heightCache = [];
482
+ this.measuredSum = 0;
483
+ this.measuredCount = 0;
484
+ this.firstMeasured = false;
485
+
486
+ this.resetDOM();
415
487
  this.cleanupInvisibleItems();
416
- this.recomputeMeasuredStats(count);
417
488
  this.rebuildFenwick(count);
418
489
  this.start = 0;
419
490
  this.end = -1;
@@ -433,7 +504,31 @@ export class VirtualRecyclerView<
433
504
  }
434
505
 
435
506
  /**
436
- * Resets internal state: mounted elements, caches, Fenwick sums, padding, and estimator stats.
507
+ * Resets DOM nodes, Fenwick sums, padding, and estimator stats — but preserves {@link heightCache}.
508
+ *
509
+ * Use this inside {@link refreshItem} so that {@link recomputeMeasuredStats} can still
510
+ * read previously measured heights before the Fenwick tree is rebuilt.
511
+ *
512
+ * DOM side effects:
513
+ * - Removes all currently mounted item elements tracked in {@link created}.
514
+ * - Resets pad heights to `0px`.
515
+ *
516
+ * @returns {void}
517
+ */
518
+ private resetDOM(): void {
519
+ this.created.forEach((el) => el.remove());
520
+ this.created.clear();
521
+ this.fenwick.reset(0);
522
+ this.PadTop.style.height = "0px";
523
+ this.PadBottom.style.height = "0px";
524
+ this.firstMeasured = false;
525
+ }
526
+
527
+ /**
528
+ * Full reset: clears DOM nodes, Fenwick sums, padding, estimator stats, AND {@link heightCache}.
529
+ *
530
+ * Use this for complete teardown (e.g., adapter swap, destroy sequence) where all
531
+ * cached measurements should be discarded.
437
532
  *
438
533
  * DOM side effects:
439
534
  * - Removes all currently mounted item elements tracked in {@link created}.
@@ -442,7 +537,7 @@ export class VirtualRecyclerView<
442
537
  * @returns {void}
443
538
  */
444
539
  private resetState(): void {
445
- this.created.forEach(el => el.remove());
540
+ this.created.forEach((el) => el.remove());
446
541
  this.created.clear();
447
542
  this.heightCache = [];
448
543
  this.fenwick.reset(0);
@@ -557,7 +652,9 @@ export class VirtualRecyclerView<
557
652
  const now = performance.now();
558
653
  if (now - this.stickyCacheTick < 16) return this.stickyCacheVal;
559
654
 
560
- const sticky = this.scrollEl.querySelector(".seui-option-handle:not(.hide)") as HTMLElement | null;
655
+ const sticky = this.scrollEl.querySelector(
656
+ ".seui-option-handle:not(.hide)",
657
+ ) as HTMLElement | null;
561
658
  this.stickyCacheVal = sticky?.offsetHeight ?? 0;
562
659
  this.stickyCacheTick = now;
563
660
  return this.stickyCacheVal;
@@ -623,7 +720,7 @@ export class VirtualRecyclerView<
623
720
  private rebuildFenwick(count: number): void {
624
721
  const est = this.getEstimate();
625
722
  const arr = Array.from({ length: count }, (_, i) =>
626
- this.isIndexVisible(i) ? (this.heightCache[i] ?? est) : 0
723
+ this.isIndexVisible(i) ? (this.heightCache[i] ?? est) : 0,
627
724
  );
628
725
  this.fenwick.buildFrom(arr);
629
726
  }
@@ -738,8 +835,12 @@ export class VirtualRecyclerView<
738
835
  const next = el.nextElementSibling as HTMLElement | null;
739
836
 
740
837
  const needsReorder =
741
- (prev && Number(prev.getAttribute(VirtualRecyclerView.ATTR_INDEX)) > index) ||
742
- (next && Number(next.getAttribute(VirtualRecyclerView.ATTR_INDEX)) < index);
838
+ (prev &&
839
+ Number(prev.getAttribute(VirtualRecyclerView.ATTR_INDEX)) >
840
+ index) ||
841
+ (next &&
842
+ Number(next.getAttribute(VirtualRecyclerView.ATTR_INDEX)) <
843
+ index);
743
844
 
744
845
  if (needsReorder) {
745
846
  el.remove();
@@ -763,7 +864,13 @@ export class VirtualRecyclerView<
763
864
  if (this.resizeObs) return;
764
865
 
765
866
  this.resizeObs = new ResizeObserver(() => {
766
- if (this.suppressResize || this.suspended || !this.adapter || this.measureRaf != null) return;
867
+ if (
868
+ this.suppressResize ||
869
+ this.suspended ||
870
+ !this.adapter ||
871
+ this.measureRaf != null
872
+ )
873
+ return;
767
874
 
768
875
  this.measureRaf = requestAnimationFrame(() => {
769
876
  this.measureRaf = null;
@@ -794,7 +901,9 @@ export class VirtualRecyclerView<
794
901
  if (!this.isIndexVisible(i)) continue;
795
902
 
796
903
  const item = this.adapter.items[i];
797
- const el = (item as any)?.view?.getView?.() as HTMLElement | undefined;
904
+ const el = (item as any)?.view?.getView?.() as
905
+ | HTMLElement
906
+ | undefined;
798
907
  if (!el) continue;
799
908
 
800
909
  const newH = this.measureOuterHeight(el);
@@ -805,6 +914,16 @@ export class VirtualRecyclerView<
805
914
  if (this.opts.adaptiveEstimate) this.rebuildFenwick(count);
806
915
  this.scheduleUpdateWindow();
807
916
  }
917
+
918
+ // Corrective scroll: if ensureRendered() registered a target index, fire
919
+ // scrollToIndex() now that real heights are in Fenwick. Clear the target
920
+ // first to prevent infinite re-triggering (scrollToIndex may cause another
921
+ // measure cycle, but heights won't change so changed === false next time).
922
+ if (this.pendingScrollToIndex !== null) {
923
+ const target = this.pendingScrollToIndex;
924
+ this.pendingScrollToIndex = null;
925
+ this.scrollToIndex(target, "smooth");
926
+ }
808
927
  }
809
928
 
810
929
  /**
@@ -857,7 +976,8 @@ export class VirtualRecyclerView<
857
976
 
858
977
  const anchorIndex = this.findFirstVisibleIndex(stRel, count);
859
978
  const anchorTop = this.offsetTopOf(anchorIndex);
860
- const anchorDelta = containerTop + anchorTop - this.scrollEl.scrollTop;
979
+ const anchorDelta =
980
+ containerTop + anchorTop - this.scrollEl.scrollTop;
861
981
 
862
982
  const firstVis = this.findFirstVisibleIndex(stRel, count);
863
983
  if (firstVis === -1) {
@@ -868,12 +988,21 @@ export class VirtualRecyclerView<
868
988
  const est = this.getEstimate();
869
989
  const overscanPx = this.opts.overscan * est;
870
990
 
871
- let startIndex = this.nextVisibleFrom(
872
- Math.min(count - 1, this.fenwick.lowerBoundPrefix(Math.max(0, stRel - overscanPx))),
873
- count
874
- ) ?? firstVis;
875
-
876
- let endIndex = Math.min(count - 1, this.fenwick.lowerBoundPrefix(stRel + vhEff + overscanPx));
991
+ let startIndex =
992
+ this.nextVisibleFrom(
993
+ Math.min(
994
+ count - 1,
995
+ this.fenwick.lowerBoundPrefix(
996
+ Math.max(0, stRel - overscanPx),
997
+ ),
998
+ ),
999
+ count,
1000
+ ) ?? firstVis;
1001
+
1002
+ let endIndex = Math.min(
1003
+ count - 1,
1004
+ this.fenwick.lowerBoundPrefix(stRel + vhEff + overscanPx),
1005
+ );
877
1006
 
878
1007
  if (startIndex === this.start && endIndex === this.end) return;
879
1008
 
@@ -900,8 +1029,12 @@ export class VirtualRecyclerView<
900
1029
 
901
1030
  // Keep anchor item stable to prevent scroll jump
902
1031
  const anchorTopNew = this.offsetTopOf(anchorIndex);
903
- const targetScroll = this.containerTopInScroll() + anchorTopNew - anchorDelta;
904
- const maxScroll = Math.max(0, this.scrollEl.scrollHeight - this.scrollEl.clientHeight);
1032
+ const targetScroll =
1033
+ this.containerTopInScroll() + anchorTopNew - anchorDelta;
1034
+ const maxScroll = Math.max(
1035
+ 0,
1036
+ this.scrollEl.scrollHeight - this.scrollEl.clientHeight,
1037
+ );
905
1038
  const clamped = Math.min(Math.max(0, targetScroll), maxScroll);
906
1039
 
907
1040
  const heightChanged = Math.abs(anchorTopNew - anchorTop) > 1;
@@ -1036,4 +1169,4 @@ export class VirtualRecyclerView<
1036
1169
  private totalHeight(count: number): number {
1037
1170
  return this.fenwick.sum(count);
1038
1171
  }
1039
- }
1172
+ }
@@ -50,17 +50,19 @@ import { Lifecycle } from "./base/lifecycle";
50
50
  */
51
51
  export class ModelManager<
52
52
  TModel extends MixedItem,
53
- TAdapter extends Adapter<MixedItem, ViewContract<any>>
53
+ TAdapter extends Adapter<MixedItem, ViewContract<any>>,
54
54
  > extends Lifecycle {
55
55
  private privModelList: Array<MixedItem> = [];
56
56
 
57
57
  private privAdapter!: new (...args: any[]) => TAdapter;
58
58
 
59
- private privAdapterHandle: TAdapter | null = null;
59
+ private privAdapterHandle?: TAdapter;
60
60
 
61
- private privRecyclerView!: new (...args: any[]) => RecyclerViewContract<TAdapter>;
61
+ private privRecyclerView!: new (
62
+ ...args: any[]
63
+ ) => RecyclerViewContract<TAdapter>;
62
64
 
63
- private privRecyclerViewHandle: RecyclerViewContract<TAdapter> | null = null;
65
+ private privRecyclerViewHandle?: RecyclerViewContract<TAdapter>;
64
66
 
65
67
  private options: SelectiveOptions = null;
66
68
 
@@ -97,7 +99,9 @@ export class ModelManager<
97
99
  * @param {new (...args: any[]) => RecyclerViewContract<TAdapter>} recyclerView - The recycler view constructor.
98
100
  * @returns {void}
99
101
  */
100
- public setupRecyclerView(recyclerView: new (...args: any[]) => RecyclerViewContract<TAdapter>): void {
102
+ public setupRecyclerView(
103
+ recyclerView: new (...args: any[]) => RecyclerViewContract<TAdapter>,
104
+ ): void {
101
105
  this.privRecyclerView = recyclerView;
102
106
  }
103
107
 
@@ -113,7 +117,9 @@ export class ModelManager<
113
117
  * @param {Array<HTMLOptGroupElement | HTMLOptionElement>} modelData - Parsed DOM elements from the source `<select>`.
114
118
  * @returns {Array<GroupModel | OptionModel>} The ordered list of group and option models.
115
119
  */
116
- public createModelResources(modelData: Array<HTMLOptGroupElement | HTMLOptionElement>): Array<GroupModel | OptionModel> {
120
+ public createModelResources(
121
+ modelData: Array<HTMLOptGroupElement | HTMLOptionElement>,
122
+ ): Array<GroupModel | OptionModel> {
117
123
  if (this.is(LifecycleState.INITIALIZED)) {
118
124
  this.mount();
119
125
  }
@@ -123,14 +129,26 @@ export class ModelManager<
123
129
 
124
130
  modelData.forEach((data) => {
125
131
  if (data.tagName === "OPTGROUP") {
126
- currentGroup = new GroupModel(this.options, data as HTMLOptGroupElement);
132
+ currentGroup = new GroupModel(
133
+ this.options,
134
+ data as HTMLOptGroupElement,
135
+ );
127
136
  this.privModelList.push(currentGroup);
128
137
  } else if (data.tagName === "OPTION") {
129
- const optionModel = new OptionModel(this.options, data as HTMLOptionElement);
130
-
131
- const parentGroup = data["__parentGroup"] as HTMLOptGroupElement | undefined;
132
-
133
- if (parentGroup && currentGroup && parentGroup === currentGroup.targetElement) {
138
+ const optionModel = new OptionModel(
139
+ this.options,
140
+ data as HTMLOptionElement,
141
+ );
142
+
143
+ const parentGroup = data["__parentGroup"] as
144
+ | HTMLOptGroupElement
145
+ | undefined;
146
+
147
+ if (
148
+ parentGroup &&
149
+ currentGroup &&
150
+ parentGroup === currentGroup.targetElement
151
+ ) {
134
152
  currentGroup.addItem(optionModel);
135
153
  optionModel.group = currentGroup;
136
154
  } else {
@@ -155,7 +173,9 @@ export class ModelManager<
155
173
  * @returns {Promise<void>} Resolves when the adapter (if any) completes syncing.
156
174
  * @see Adapter#syncFromSource
157
175
  */
158
- public async replace(modelData: Array<HTMLOptGroupElement | HTMLOptionElement>): Promise<void> {
176
+ public async replace(
177
+ modelData: Array<HTMLOptGroupElement | HTMLOptionElement>,
178
+ ): Promise<void> {
159
179
  this.createModelResources(modelData);
160
180
 
161
181
  if (this.privAdapterHandle) {
@@ -199,9 +219,9 @@ export class ModelManager<
199
219
  public load<TExtra extends object = {}>(
200
220
  viewElement: HTMLElement,
201
221
  adapterOpt: Partial<TAdapter> = {},
202
- recyclerViewOpt: Partial<RecyclerViewContract<TAdapter>> & TExtra = {} as any
222
+ recyclerViewOpt: Partial<RecyclerViewContract<TAdapter>> &
223
+ TExtra = {} as any,
203
224
  ): void {
204
-
205
225
  this.privAdapterHandle = new this.privAdapter(this.privModelList);
206
226
  Object.assign(this.privAdapterHandle, adapterOpt);
207
227
 
@@ -230,7 +250,9 @@ export class ModelManager<
230
250
  * @returns {void}
231
251
  * @see Adapter#updateData
232
252
  */
233
- public updateModel(modelData: Array<HTMLOptGroupElement | HTMLOptionElement>): void {
253
+ public updateModel(
254
+ modelData: Array<HTMLOptGroupElement | HTMLOptionElement>,
255
+ ): void {
234
256
  const oldModels = this.privModelList;
235
257
  const newModels: Array<MixedItem> = [];
236
258
 
@@ -256,9 +278,10 @@ export class ModelManager<
256
278
 
257
279
  if (existingGroup) {
258
280
  // Label is used as key; keep original behavior.
259
- const hasLabelChange = existingGroup.label !== dataVset.label;
281
+ const hasLabelChange =
282
+ existingGroup.label !== dataVset.label;
260
283
  if (hasLabelChange) {
261
- existingGroup.updateTarget(dataVset)
284
+ existingGroup.updateTarget(dataVset);
262
285
  }
263
286
 
264
287
  existingGroup.position = position;
@@ -283,7 +306,9 @@ export class ModelManager<
283
306
  existingOption.updateTarget(dataVset);
284
307
  existingOption.position = position;
285
308
 
286
- const parentGroup = dataVset["__parentGroup"] as HTMLOptGroupElement;
309
+ const parentGroup = dataVset[
310
+ "__parentGroup"
311
+ ] as HTMLOptGroupElement;
287
312
 
288
313
  if (parentGroup && currentGroup) {
289
314
  currentGroup.addItem(existingOption);
@@ -298,7 +323,9 @@ export class ModelManager<
298
323
  const newOption = new OptionModel(this.options, dataVset);
299
324
  newOption.position = position;
300
325
 
301
- const parentGroup = dataVset["__parentGroup"] as HTMLOptGroupElement;
326
+ const parentGroup = dataVset[
327
+ "__parentGroup"
328
+ ] as HTMLOptGroupElement;
302
329
 
303
330
  if (parentGroup && currentGroup) {
304
331
  currentGroup.addItem(newOption);
@@ -438,4 +465,4 @@ export class ModelManager<
438
465
  public triggerChanged(event_name: string): Promise<void> {
439
466
  return this.privAdapterHandle?.changeProp(event_name);
440
467
  }
441
- }
468
+ }