selective-ui 1.3.1 → 1.4.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.
Files changed (70) hide show
  1. package/dist/selective-ui.css +0 -6
  2. package/dist/selective-ui.css.map +1 -1
  3. package/dist/selective-ui.esm.js +271 -559
  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 +273 -561
  12. package/dist/selective-ui.umd.js.map +1 -1
  13. package/package.json +12 -12
  14. package/src/ts/adapter/mixed-adapter.ts +147 -68
  15. package/src/ts/components/accessorybox.ts +14 -11
  16. package/src/ts/components/directive.ts +1 -1
  17. package/src/ts/components/option-handle.ts +12 -9
  18. package/src/ts/components/placeholder.ts +5 -5
  19. package/src/ts/components/popup/empty-state.ts +5 -5
  20. package/src/ts/components/popup/loading-state.ts +5 -5
  21. package/src/ts/components/popup/popup.ts +138 -76
  22. package/src/ts/components/searchbox.ts +17 -13
  23. package/src/ts/components/selectbox.ts +258 -83
  24. package/src/ts/core/base/adapter.ts +39 -14
  25. package/src/ts/core/base/fenwick.ts +3 -2
  26. package/src/ts/core/base/lifecycle.ts +14 -4
  27. package/src/ts/core/base/model.ts +17 -15
  28. package/src/ts/core/base/recyclerview.ts +7 -5
  29. package/src/ts/core/base/view.ts +10 -5
  30. package/src/ts/core/base/virtual-recyclerview.ts +89 -37
  31. package/src/ts/core/model-manager.ts +48 -21
  32. package/src/ts/core/search-controller.ts +174 -56
  33. package/src/ts/global.ts +5 -8
  34. package/src/ts/index.ts +2 -2
  35. package/src/ts/models/group-model.ts +33 -8
  36. package/src/ts/models/option-model.ts +60 -19
  37. package/src/ts/services/dataset-observer.ts +6 -3
  38. package/src/ts/services/ea-observer.ts +1 -1
  39. package/src/ts/services/effector.ts +22 -12
  40. package/src/ts/services/refresher.ts +7 -3
  41. package/src/ts/services/resize-observer.ts +24 -11
  42. package/src/ts/services/select-observer.ts +2 -2
  43. package/src/ts/types/components/popup.type.ts +18 -1
  44. package/src/ts/types/components/searchbox.type.ts +43 -30
  45. package/src/ts/types/components/state.box.type.ts +1 -1
  46. package/src/ts/types/core/base/adapter.type.ts +13 -5
  47. package/src/ts/types/core/base/lifecycle.type.ts +1 -2
  48. package/src/ts/types/core/base/model.type.ts +3 -3
  49. package/src/ts/types/core/base/recyclerview.type.ts +7 -5
  50. package/src/ts/types/core/base/view.type.ts +6 -6
  51. package/src/ts/types/core/base/virtual-recyclerview.type.ts +45 -46
  52. package/src/ts/types/core/search-controller.type.ts +18 -2
  53. package/src/ts/types/css.d.ts +1 -0
  54. package/src/ts/types/plugins/plugin.type.ts +2 -2
  55. package/src/ts/types/services/effector.type.ts +25 -25
  56. package/src/ts/types/services/resize-observer.type.ts +23 -12
  57. package/src/ts/types/utils/callback-scheduler.type.ts +2 -2
  58. package/src/ts/types/utils/ievents.type.ts +1 -1
  59. package/src/ts/types/utils/istorage.type.ts +62 -60
  60. package/src/ts/types/utils/libs.type.ts +19 -17
  61. package/src/ts/types/utils/selective.type.ts +6 -3
  62. package/src/ts/types/views/view.group.type.ts +9 -5
  63. package/src/ts/types/views/view.option.type.ts +39 -17
  64. package/src/ts/utils/callback-scheduler.ts +12 -7
  65. package/src/ts/utils/ievents.ts +12 -5
  66. package/src/ts/utils/istorage.ts +5 -3
  67. package/src/ts/utils/libs.ts +122 -43
  68. package/src/ts/utils/selective.ts +15 -8
  69. package/src/ts/views/group-view.ts +11 -9
  70. package/src/ts/views/option-view.ts +37 -18
@@ -42,7 +42,7 @@ export class EmptyState extends Lifecycle {
42
42
  * - Intended to be appended by the parent container (component does not auto-attach).
43
43
  * - Removed from DOM during {@link destroy}.
44
44
  */
45
- public node: HTMLDivElement | null = null;
45
+ public node?: HTMLDivElement;
46
46
 
47
47
  /**
48
48
  * Configuration source for empty state messages.
@@ -51,7 +51,7 @@ export class EmptyState extends Lifecycle {
51
51
  * - `textNoData` (for `"nodata"`)
52
52
  * - `textNotFound` (for `"notfound"`)
53
53
  */
54
- public options: SelectiveOptions | null = null;
54
+ public options?: SelectiveOptions;
55
55
 
56
56
  /**
57
57
  * Creates a new {@link EmptyState}.
@@ -59,9 +59,9 @@ export class EmptyState extends Lifecycle {
59
59
  * If `options` are provided, initialization runs immediately (creates {@link node} and
60
60
  * transitions to `INITIALIZED`).
61
61
  *
62
- * @param {SelectiveOptions | null} [options=null] - Configuration containing empty state messages.
62
+ * @param {SelectiveOptions} [options=null] - Configuration containing empty state messages.
63
63
  */
64
- public constructor(options: SelectiveOptions | null = null) {
64
+ public constructor(options?: SelectiveOptions) {
65
65
  super();
66
66
  if (options) this.initialize(options);
67
67
  }
@@ -159,4 +159,4 @@ export class EmptyState extends Lifecycle {
159
159
 
160
160
  super.destroy();
161
161
  }
162
- }
162
+ }
@@ -41,7 +41,7 @@ export class LoadingState extends Lifecycle {
41
41
  * - Intended to be appended by the parent container (component does not auto-attach).
42
42
  * - Removed from DOM during {@link destroy}.
43
43
  */
44
- public node: HTMLDivElement | null = null;
44
+ public node?: HTMLDivElement;
45
45
 
46
46
  /**
47
47
  * Configuration source for loading message text.
@@ -49,7 +49,7 @@ export class LoadingState extends Lifecycle {
49
49
  * Expected to provide:
50
50
  * - `textLoading` (displayed while loading is active)
51
51
  */
52
- public options: SelectiveOptions | null = null;
52
+ public options?: SelectiveOptions;
53
53
 
54
54
  /**
55
55
  * Creates a new {@link LoadingState}.
@@ -57,9 +57,9 @@ export class LoadingState extends Lifecycle {
57
57
  * If `options` are provided, initialization runs immediately (creates {@link node} and
58
58
  * transitions to `INITIALIZED`).
59
59
  *
60
- * @param {SelectiveOptions | null} [options=null] - Configuration containing the loading message text.
60
+ * @param {SelectiveOptions} [options=null] - Configuration containing the loading message text.
61
61
  */
62
- public constructor(options: SelectiveOptions | null = null) {
62
+ public constructor(options?: SelectiveOptions) {
63
63
  super();
64
64
  if (options) this.initialize(options);
65
65
  }
@@ -157,4 +157,4 @@ export class LoadingState extends Lifecycle {
157
157
 
158
158
  super.destroy();
159
159
  }
160
- }
160
+ }
@@ -1,6 +1,9 @@
1
1
  import { Libs } from "../../utils/libs";
2
2
  import { OptionHandle } from "../option-handle";
3
- import type { EffectorInterface, DimensionObject } from "../../types/services/effector.type";
3
+ import type {
4
+ EffectorInterface,
5
+ DimensionObject,
6
+ } from "../../types/services/effector.type";
4
7
  import { ModelManager } from "../../core/model-manager";
5
8
  import { EmptyState } from "./empty-state";
6
9
  import { LoadingState } from "./loading-state";
@@ -8,9 +11,17 @@ import { MixedAdapter } from "../../adapter/mixed-adapter";
8
11
  import type { RecyclerViewContract } from "../../types/core/base/recyclerview.type";
9
12
  import { ResizeObserverService } from "../../services/resize-observer";
10
13
  import { ElementMetrics } from "../../types/services/resize-observer.type";
11
- import { MixedItem, VisibilityStats } from "../../types/core/base/mixed-adapter.type";
14
+ import {
15
+ MixedItem,
16
+ VisibilityStats,
17
+ } from "../../types/core/base/mixed-adapter.type";
12
18
  import { SelectiveOptions } from "../../types/utils/selective.type";
13
- import { ParentBinderMapLike, VirtualRecyclerOptions } from "../../types/components/popup.type";
19
+ import {
20
+ ParentBinderMapLike,
21
+ PopupLocaltion,
22
+ PopupPosition,
23
+ VirtualRecyclerOptions,
24
+ } from "../../types/components/popup.type";
14
25
  import { Lifecycle } from "../../core/base/lifecycle";
15
26
  import { LifecycleState } from "../../types/core/base/lifecycle.type";
16
27
  import { MountViewResult } from "src/ts/types/utils/libs.type";
@@ -36,49 +47,50 @@ import { MountViewResult } from "src/ts/types/utils/libs.type";
36
47
  */
37
48
  export class Popup extends Lifecycle {
38
49
  /** ModelManager reference used to provide adapter and recycler view resources */
39
- private modelManager: ModelManager<MixedItem, MixedAdapter> | null;
40
-
41
- /** Active configuration for the popup behavior and text labels */
42
- private options: SelectiveOptions | null = null;
50
+ private modelManager?: ModelManager<
51
+ MixedItem,
52
+ MixedAdapter
53
+ >; /** Active configuration for the popup behavior and text labels */
54
+ private options?: SelectiveOptions;
43
55
 
44
56
  /** Indicates whether the popup DOM has been attached to the document body at least once */
45
57
  private isCreated = false;
46
58
 
47
59
  /** Mixed adapter handling items/models and visibility stats */
48
- public optionAdapter: MixedAdapter | null = null;
60
+ public optionAdapter?: MixedAdapter;
49
61
 
50
62
  /** Root popup container (the floating panel) */
51
- public node: HTMLDivElement | null = null;
63
+ public node?: HTMLDivElement;
52
64
 
53
65
  /** Effector service used to measure/animate the popup */
54
- private effSvc: EffectorInterface | null = null;
66
+ private effSvc?: EffectorInterface;
55
67
 
56
68
  /** Resize observer to react to parent panel size changes */
57
- private resizeObser: ResizeObserverService | null = null;
69
+ private resizeObser?: ResizeObserverService;
58
70
 
59
71
  /** Binder map for parent elements (anchors to compute placement from) */
60
- private parent: ParentBinderMapLike | null = null;
72
+ private parent?: ParentBinderMapLike;
61
73
 
62
74
  /** Header control exposing "Select All / Deselect All" actions */
63
- public optionHandle: OptionHandle | null = null;
75
+ public optionHandle?: OptionHandle;
64
76
 
65
77
  /** "Empty / Not found" feedback component */
66
- public emptyState: EmptyState | null = null;
78
+ public emptyState?: EmptyState;
67
79
 
68
80
  /** Loading indicator component */
69
- public loadingState: LoadingState | null = null;
81
+ public loadingState?: LoadingState;
70
82
 
71
83
  /** Virtualized recycler view for performant lists */
72
- public recyclerView: RecyclerViewContract<MixedAdapter> | null = null;
84
+ public recyclerView?: RecyclerViewContract<MixedAdapter>;
73
85
 
74
86
  /** Container that holds the list of options */
75
- private optionsContainer: HTMLDivElement | null = null;
87
+ private optionsContainer?: HTMLDivElement;
76
88
 
77
89
  /** Scroll handler used by infinite scroll */
78
- private scrollListener: (() => Promise<void>) | null = null;
90
+ private scrollListener?: () => Promise<void>;
79
91
 
80
92
  /** Handle to defer hiding the loading indicator */
81
- private hideLoadHandle: ReturnType<typeof setTimeout> | null = null;
93
+ private hideLoadHandle?: ReturnType<typeof setTimeout>;
82
94
 
83
95
  /** Default virtual scroll configuration (tuned for typical option heights) */
84
96
  private virtualScrollConfig = {
@@ -87,7 +99,7 @@ export class Popup extends Lifecycle {
87
99
  /** Number of extra items to render above/below the viewport */
88
100
  overscan: 8,
89
101
  /** Whether the list contains items with dynamic (non-uniform) heights */
90
- dynamicHeights: true
102
+ dynamicHeights: true,
91
103
  };
92
104
 
93
105
  /**
@@ -100,9 +112,9 @@ export class Popup extends Lifecycle {
100
112
  * @param modelManager - Model manager that supplies the adapter and recycler view.
101
113
  */
102
114
  public constructor(
103
- select: HTMLSelectElement | null = null,
104
- options: SelectiveOptions | null = null,
105
- modelManager: ModelManager<MixedItem, MixedAdapter> | null = null
115
+ select?: HTMLSelectElement,
116
+ options?: SelectiveOptions,
117
+ modelManager?: ModelManager<MixedItem, MixedAdapter>,
106
118
  ) {
107
119
  super();
108
120
  this.modelManager = modelManager;
@@ -123,8 +135,12 @@ export class Popup extends Lifecycle {
123
135
  * @param options - Panel configuration (dimensions, IDs, labels, flags).
124
136
  * @throws Error if a ModelManager is not provided.
125
137
  */
126
- private initialize(select: HTMLSelectElement, options: SelectiveOptions): void {
127
- if (!this.modelManager) throw new Error("Popup requires a ModelManager instance.");
138
+ private initialize(
139
+ select: HTMLSelectElement,
140
+ options: SelectiveOptions,
141
+ ): void {
142
+ if (!this.modelManager)
143
+ throw new Error("Popup requires a ModelManager instance.");
128
144
 
129
145
  this.optionHandle = new OptionHandle(options);
130
146
  this.emptyState = new EmptyState(options);
@@ -153,27 +169,32 @@ export class Popup extends Lifecycle {
153
169
  },
154
170
  },
155
171
  },
156
- null
172
+ null,
157
173
  );
158
174
 
159
175
  this.node = nodeMounted.view as HTMLDivElement;
160
- this.optionsContainer = nodeMounted.tags.OptionsContainer as HTMLDivElement;
176
+ this.optionsContainer = nodeMounted.tags
177
+ .OptionsContainer as HTMLDivElement;
161
178
 
162
179
  this.parent = Libs.getBinderMap<ParentBinderMapLike>(select);
163
180
  this.options = options;
164
181
  this.init();
165
-
166
- const recyclerViewOpt = options.virtualScroll
167
- ? {
168
- scrollEl: this.node,
169
- estimateItemHeight: this.virtualScrollConfig.estimateItemHeight,
170
- overscan: this.virtualScrollConfig.overscan,
171
- dynamicHeights: this.virtualScrollConfig.dynamicHeights }
172
- : {}
173
- ;
174
182
 
183
+ const recyclerViewOpt = options.virtualScroll
184
+ ? {
185
+ scrollEl: this.node,
186
+ estimateItemHeight:
187
+ this.virtualScrollConfig.estimateItemHeight,
188
+ overscan: this.virtualScrollConfig.overscan,
189
+ dynamicHeights: this.virtualScrollConfig.dynamicHeights,
190
+ }
191
+ : {};
175
192
  // Load ModelManager resources into the list container
176
- this.modelManager.load<VirtualRecyclerOptions>(this.optionsContainer, { isMultiple: options.multiple, options: options }, recyclerViewOpt);
193
+ this.modelManager.load<VirtualRecyclerOptions>(
194
+ this.optionsContainer,
195
+ { isMultiple: options.multiple, options: options },
196
+ recyclerViewOpt,
197
+ );
177
198
 
178
199
  const MMResources = this.modelManager.getResources();
179
200
 
@@ -203,7 +224,14 @@ export class Popup extends Lifecycle {
203
224
  * - Triggers a resize to accommodate layout changes
204
225
  */
205
226
  public async showLoading(): Promise<void> {
206
- if (!this.options || !this.loadingState || !this.optionHandle || !this.optionAdapter || !this.modelManager) return;
227
+ if (
228
+ !this.options ||
229
+ !this.loadingState ||
230
+ !this.optionHandle ||
231
+ !this.optionAdapter ||
232
+ !this.modelManager
233
+ )
234
+ return;
207
235
 
208
236
  if (this.hideLoadHandle) clearTimeout(this.hideLoadHandle);
209
237
  this.modelManager.skipEvent(true);
@@ -211,7 +239,9 @@ export class Popup extends Lifecycle {
211
239
  if (Libs.string2Boolean(this.options.loadingfield) === false) return;
212
240
 
213
241
  this.emptyState.hide();
214
- this.loadingState.show(this.optionAdapter.getVisibilityStats().hasVisible);
242
+ this.loadingState.show(
243
+ this.optionAdapter.getVisibilityStats().hasVisible,
244
+ );
215
245
  this.triggerResize();
216
246
  }
217
247
 
@@ -222,7 +252,13 @@ export class Popup extends Lifecycle {
222
252
  * Debounce: Uses `animationtime` as a short delay before hiding the loading indicator.
223
253
  */
224
254
  public async hideLoading(): Promise<void> {
225
- if (!this.options || !this.loadingState || !this.optionAdapter || !this.modelManager) return;
255
+ if (
256
+ !this.options ||
257
+ !this.loadingState ||
258
+ !this.optionAdapter ||
259
+ !this.modelManager
260
+ )
261
+ return;
226
262
 
227
263
  if (this.hideLoadHandle) clearTimeout(this.hideLoadHandle);
228
264
 
@@ -268,7 +304,13 @@ export class Popup extends Lifecycle {
268
304
  * @param stats - Optionally provide precomputed visibility stats.
269
305
  */
270
306
  private updateEmptyState(stats?: VisibilityStats): void {
271
- if (!this.optionAdapter || !this.emptyState || !this.optionHandle || !this.optionsContainer) return;
307
+ if (
308
+ !this.optionAdapter ||
309
+ !this.emptyState ||
310
+ !this.optionHandle ||
311
+ !this.optionsContainer
312
+ )
313
+ return;
272
314
 
273
315
  const s = stats ?? this.optionAdapter.getVisibilityStats();
274
316
 
@@ -293,7 +335,10 @@ export class Popup extends Lifecycle {
293
335
  * @param propName - Adapter property name to observe.
294
336
  * @param callback - Handler invoked before the property changes.
295
337
  */
296
- public onAdapterPropChanging(propName: string, callback: (...args: unknown[]) => void): void {
338
+ public onAdapterPropChanging(
339
+ propName: string,
340
+ callback: (...args: unknown[]) => void,
341
+ ): void {
297
342
  this.optionAdapter?.onPropChanging(propName, callback);
298
343
  }
299
344
 
@@ -303,7 +348,10 @@ export class Popup extends Lifecycle {
303
348
  * @param propName - Adapter property name to observe.
304
349
  * @param callback - Handler invoked after the property changes.
305
350
  */
306
- public onAdapterPropChanged(propName: string, callback: (...args: unknown[]) => void): void {
351
+ public onAdapterPropChanged(
352
+ propName: string,
353
+ callback: (...args: unknown[]) => void,
354
+ ): void {
307
355
  this.optionAdapter?.onPropChanged(propName, callback);
308
356
  }
309
357
 
@@ -357,8 +405,15 @@ export class Popup extends Lifecycle {
357
405
  * @param callback - Optional callback invoked when the opening animation completes.
358
406
  * @param isShowEmptyState - If true, applies the empty/not-found state before animation.
359
407
  */
360
- public open(callback: (() => void) | null = null, isShowEmptyState: boolean): void {
361
- if (!this.node || !this.options || !this.optionHandle || !this.parent || !this.effSvc) return;
408
+ public open(callback?: () => void, isShowEmptyState?: boolean): void {
409
+ if (
410
+ !this.node ||
411
+ !this.options ||
412
+ !this.optionHandle ||
413
+ !this.parent ||
414
+ !this.effSvc
415
+ )
416
+ return;
362
417
 
363
418
  // Ensure one-time initialization
364
419
  this.load();
@@ -373,7 +428,8 @@ export class Popup extends Lifecycle {
373
428
 
374
429
  // Compute placement based on parent anchor
375
430
  const location = this.getParentLocation();
376
- const { position, top, maxHeight, realHeight } = this.calculatePosition(location);
431
+ const { position, top, maxHeight, realHeight } =
432
+ this.calculatePosition(location);
377
433
 
378
434
  // Run expand animation
379
435
  this.effSvc.expand({
@@ -415,8 +471,14 @@ export class Popup extends Lifecycle {
415
471
  *
416
472
  * @param callback - Optional callback invoked when the closing animation completes.
417
473
  */
418
- public close(callback: (() => void) | null = null): void {
419
- if (!this.isCreated || !this.options || !this.resizeObser || !this.effSvc) return;
474
+ public close(callback?: () => void): void {
475
+ if (
476
+ !this.isCreated ||
477
+ !this.options ||
478
+ !this.resizeObser ||
479
+ !this.effSvc
480
+ )
481
+ return;
420
482
  const rv: any = this.recyclerView;
421
483
  rv?.suspend?.();
422
484
 
@@ -445,10 +507,14 @@ export class Popup extends Lifecycle {
445
507
  */
446
508
  public setupInfiniteScroll(
447
509
  searchController: {
448
- getPaginationState(): { isPaginationEnabled: boolean; isLoading: boolean; hasMore: boolean };
510
+ getPaginationState(): {
511
+ isPaginationEnabled: boolean;
512
+ isLoading: boolean;
513
+ hasMore: boolean;
514
+ };
449
515
  loadMore(): Promise<{ success: boolean; message?: string }>;
450
516
  },
451
- _options?: SelectiveOptions
517
+ _options?: SelectiveOptions,
452
518
  ): void {
453
519
  if (!this.node) return;
454
520
 
@@ -475,7 +541,7 @@ export class Popup extends Lifecycle {
475
541
 
476
542
  this.node.addEventListener("scroll", this.scrollListener);
477
543
  }
478
-
544
+
479
545
  /**
480
546
  * Completely tears down the popup and releases all resources.
481
547
  *
@@ -522,7 +588,7 @@ export class Popup extends Lifecycle {
522
588
  this.node.remove();
523
589
  }
524
590
  }
525
-
591
+
526
592
  this.node = null;
527
593
  this.optionsContainer = null;
528
594
  this.modelManager = null;
@@ -544,14 +610,7 @@ export class Popup extends Lifecycle {
544
610
  * Computes the parent panel's location and box metrics
545
611
  * (size, position, padding, border). Accounts for iOS visual viewport offsets.
546
612
  */
547
- private getParentLocation(): {
548
- width: number;
549
- height: number;
550
- top: number;
551
- left: number;
552
- padding: { top: number; right: number; bottom: number; left: number };
553
- border: { top: number; right: number; bottom: number; left: number };
554
- } {
613
+ private getParentLocation(): PopupLocaltion {
555
614
  const viewPanel = this.parent!.container.tags.ViewPanel;
556
615
  const rect = viewPanel.getBoundingClientRect();
557
616
  const style = window.getComputedStyle(viewPanel);
@@ -584,13 +643,7 @@ export class Popup extends Lifecycle {
584
643
  *
585
644
  * Returns the final placement, top offset, and computed heights.
586
645
  */
587
- private calculatePosition(location: { width: number; height: number; top: number; left: number }): {
588
- position: "top" | "bottom";
589
- top: number;
590
- maxHeight: number;
591
- realHeight: number;
592
- contentHeight: number;
593
- } {
646
+ private calculatePosition(location: PopupLocaltion): PopupPosition {
594
647
  const vv = window.visualViewport;
595
648
  const is_ios = Libs.IsIOS();
596
649
 
@@ -599,13 +652,17 @@ export class Popup extends Lifecycle {
599
652
  const gap = 3;
600
653
  const safeMargin = 15;
601
654
 
602
- const dimensions: DimensionObject = this.effSvc!.getHiddenDimensions("flex");
655
+ const dimensions: DimensionObject =
656
+ this.effSvc!.getHiddenDimensions("flex");
603
657
  const contentHeight = dimensions.scrollHeight;
604
658
 
605
- const configMaxHeight = parseFloat(this.options?.panelHeight ?? "220") || 220;
606
- const configMinHeight = parseFloat(this.options?.panelMinHeight ?? "100") || 100;
659
+ const configMaxHeight =
660
+ parseFloat(this.options?.panelHeight ?? "220") || 220;
661
+ const configMinHeight =
662
+ parseFloat(this.options?.panelMinHeight ?? "100") || 100;
607
663
 
608
- const spaceBelow = viewportHeight - (location.top + location.height) - gap;
664
+ const spaceBelow =
665
+ viewportHeight - (location.top + location.height) - gap;
609
666
  const spaceAbove = location.top - gap;
610
667
 
611
668
  let position: "top" | "bottom" = "bottom";
@@ -614,7 +671,11 @@ export class Popup extends Lifecycle {
614
671
 
615
672
  const heightOri = spaceBelow - safeMargin;
616
673
 
617
- if (realHeight >= configMinHeight ? heightOri >= configMinHeight : heightOri >= realHeight) {
674
+ if (
675
+ realHeight >= configMinHeight
676
+ ? heightOri >= configMinHeight
677
+ : heightOri >= realHeight
678
+ ) {
618
679
  position = "bottom";
619
680
  maxHeight = Math.min(spaceBelow - safeMargin, configMaxHeight);
620
681
  } else if (spaceAbove >= Math.max(realHeight, configMinHeight)) {
@@ -633,7 +694,7 @@ export class Popup extends Lifecycle {
633
694
  realHeight = Math.min(contentHeight, maxHeight);
634
695
 
635
696
  const viewportOffsetY = vv && is_ios ? vv.offsetTop : 0;
636
-
697
+
637
698
  const top =
638
699
  position === "bottom"
639
700
  ? location.top + location.height + gap + viewportOffsetY
@@ -646,10 +707,11 @@ export class Popup extends Lifecycle {
646
707
  * Handles parent resize by recalculating placement and dimensions,
647
708
  * then animates the popup to the new size and position.
648
709
  */
649
- private handleResize(location: { width: number; height: number; top: number; left: number }): void {
710
+ private handleResize(location: PopupLocaltion): void {
650
711
  if (!this.options || !this.effSvc) return;
651
712
 
652
- const { position, top, maxHeight, realHeight } = this.calculatePosition(location);
713
+ const { position, top, maxHeight, realHeight } =
714
+ this.calculatePosition(location);
653
715
 
654
716
  this.effSvc.resize({
655
717
  duration: this.options.animationtime,
@@ -662,4 +724,4 @@ export class Popup extends Lifecycle {
662
724
  animate: true,
663
725
  });
664
726
  }
665
- }
727
+ }
@@ -1,6 +1,10 @@
1
1
  import { Libs } from "../utils/libs";
2
2
  import { MountViewResult } from "../types/utils/libs.type";
3
- import { NavigateHandler, SearchBoxTags, SearchHandler } from "../types/components/searchbox.type";
3
+ import {
4
+ NavigateHandler,
5
+ SearchBoxTags,
6
+ SearchHandler,
7
+ } from "../types/components/searchbox.type";
4
8
  import { SelectiveOptions } from "../types/utils/selective.type";
5
9
  import { Lifecycle } from "../core/base/lifecycle";
6
10
  import { LifecycleState } from "../types/core/base/lifecycle.type";
@@ -59,7 +63,7 @@ export class SearchBox extends Lifecycle {
59
63
  *
60
64
  * @param options - Configuration such as placeholder, accessibility IDs, and flags.
61
65
  */
62
- constructor(options: SelectiveOptions | null = null) {
66
+ constructor(options?: SelectiveOptions) {
63
67
  super();
64
68
  this.options = options;
65
69
  if (options) this.initialize(options);
@@ -73,7 +77,7 @@ export class SearchBox extends Lifecycle {
73
77
  *
74
78
  * @internal
75
79
  */
76
- private nodeMounted: MountViewResult<SearchBoxTags> | null = null;
80
+ private nodeMounted?: MountViewResult<SearchBoxTags>;
77
81
 
78
82
  /**
79
83
  * Root container node of this component.
@@ -81,7 +85,7 @@ export class SearchBox extends Lifecycle {
81
85
  * Created during {@link initialize} and removed during {@link destroy}.
82
86
  * Visibility is controlled by adding/removing the `hide` class.
83
87
  */
84
- public node: HTMLDivElement | null = null;
88
+ public node?: HTMLDivElement;
85
89
 
86
90
  /**
87
91
  * The `<input type="search">` element used to capture user queries.
@@ -91,7 +95,7 @@ export class SearchBox extends Lifecycle {
91
95
  *
92
96
  * @internal
93
97
  */
94
- private SearchInput: HTMLInputElement | null = null;
98
+ private SearchInput?: HTMLInputElement;
95
99
 
96
100
  /**
97
101
  * External "search changed" hook.
@@ -102,7 +106,7 @@ export class SearchBox extends Lifecycle {
102
106
  * Ownership:
103
107
  * - Implementations typically filter adapter/model state and refresh the list.
104
108
  */
105
- public onSearch: SearchHandler | null = null;
109
+ public onSearch?: SearchHandler;
106
110
 
107
111
  /**
108
112
  * Options snapshot used for behavior toggles and attributes.
@@ -116,7 +120,7 @@ export class SearchBox extends Lifecycle {
116
120
  *
117
121
  * @internal
118
122
  */
119
- private options: SelectiveOptions | null = null;
123
+ private options?: SelectiveOptions;
120
124
 
121
125
  /**
122
126
  * External navigation hook for list traversal.
@@ -127,21 +131,21 @@ export class SearchBox extends Lifecycle {
127
131
  *
128
132
  * Typical consumers update highlight/active option in Adapter/RecyclerView.
129
133
  */
130
- public onNavigate: NavigateHandler | null = null;
134
+ public onNavigate?: NavigateHandler;
131
135
 
132
136
  /**
133
137
  * External "commit" hook (Enter key).
134
138
  *
135
139
  * Typical consumers confirm selection of the highlighted option or submit the current state.
136
140
  */
137
- public onEnter: (() => void) | null = null;
138
-
141
+ public onEnter?: () => void;
142
+
139
143
  /**
140
144
  * External "cancel" hook (Escape key).
141
145
  *
142
146
  * Typical consumers close the popup, clear highlight, or reset interaction mode.
143
147
  */
144
- public onEsc: (() => void) | null = null;
148
+ public onEsc?: () => void;
145
149
 
146
150
  /**
147
151
  * Initializes DOM, ARIA attributes, and interaction listeners.
@@ -353,7 +357,7 @@ export class SearchBox extends Lifecycle {
353
357
  if (this.is(LifecycleState.DESTROYED)) {
354
358
  return;
355
359
  }
356
-
360
+
357
361
  this.node?.remove();
358
362
  this.nodeMounted = null;
359
363
  this.node = null;
@@ -366,4 +370,4 @@ export class SearchBox extends Lifecycle {
366
370
 
367
371
  super.destroy();
368
372
  }
369
- }
373
+ }