selective-ui 1.2.1 → 1.2.3

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 (48) hide show
  1. package/dist/selective-ui.css +3 -1
  2. package/dist/selective-ui.css.map +1 -1
  3. package/dist/selective-ui.esm.js +498 -467
  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 +499 -468
  12. package/dist/selective-ui.umd.js.map +1 -1
  13. package/package.json +1 -1
  14. package/src/css/components/popup.css +3 -1
  15. package/src/ts/adapter/mixed-adapter.ts +50 -54
  16. package/src/ts/components/accessorybox.ts +51 -25
  17. package/src/ts/components/directive.ts +3 -3
  18. package/src/ts/components/empty-state.ts +7 -7
  19. package/src/ts/components/loading-state.ts +7 -7
  20. package/src/ts/components/option-handle.ts +17 -17
  21. package/src/ts/components/placeholder.ts +12 -12
  22. package/src/ts/components/popup.ts +94 -109
  23. package/src/ts/components/searchbox.ts +14 -14
  24. package/src/ts/components/selectbox.ts +25 -29
  25. package/src/ts/core/base/adapter.ts +19 -19
  26. package/src/ts/core/base/model.ts +12 -13
  27. package/src/ts/core/base/recyclerview.ts +7 -7
  28. package/src/ts/core/base/view.ts +6 -6
  29. package/src/ts/core/base/virtual-recyclerview.ts +55 -52
  30. package/src/ts/core/model-manager.ts +61 -54
  31. package/src/ts/core/search-controller.ts +69 -71
  32. package/src/ts/models/group-model.ts +21 -21
  33. package/src/ts/models/option-model.ts +30 -30
  34. package/src/ts/services/dataset-observer.ts +17 -17
  35. package/src/ts/services/ea-observer.ts +21 -21
  36. package/src/ts/services/effector.ts +27 -27
  37. package/src/ts/services/refresher.ts +1 -1
  38. package/src/ts/services/resize-observer.ts +29 -29
  39. package/src/ts/services/select-observer.ts +27 -34
  40. package/src/ts/types/components/popup.type.ts +15 -0
  41. package/src/ts/types/utils/istorage.type.ts +1 -1
  42. package/src/ts/utils/callback-scheduler.ts +41 -21
  43. package/src/ts/utils/ievents.ts +4 -4
  44. package/src/ts/utils/istorage.ts +5 -5
  45. package/src/ts/utils/libs.ts +38 -29
  46. package/src/ts/utils/selective.ts +11 -11
  47. package/src/ts/views/group-view.ts +7 -7
  48. package/src/ts/views/option-view.ts +51 -51
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "selective-ui",
3
- "version": "1.2.1",
3
+ "version": "1.2.3",
4
4
  "description": "An overlay for the HTML select element.",
5
5
  "author": "Huỳnh Công Xuân Mai",
6
6
  "license": "MIT",
@@ -10,7 +10,7 @@
10
10
  display: none;
11
11
  position: fixed;
12
12
  z-index: 9999;
13
- overflow: none;
13
+ overflow: hidden;
14
14
  flex-direction: column;
15
15
 
16
16
  height: 0px;
@@ -22,6 +22,8 @@
22
22
  border-style: var(--seui-popup-border-style);
23
23
  border-width: var(--seui-popup-border-width);
24
24
  border-radius: var(--seui-popup-border-radius);
25
+
26
+ transition: none;
25
27
  }
26
28
 
27
29
  .selective-ui-options-container {
@@ -12,26 +12,26 @@ import { Libs } from "../utils/libs";
12
12
  * @extends {Adapter<GroupModel|OptionModel>}
13
13
  */
14
14
  export class MixedAdapter extends Adapter<MixedItem, GroupView | OptionView> {
15
- isMultiple = false;
15
+ public isMultiple = false;
16
16
 
17
- private _visibilityChangedCallbacks: Array<(stats: VisibilityStats) => void> = [];
18
- private _currentHighlightIndex = -1;
17
+ private visibilityChangedCallbacks: Array<(stats: VisibilityStats) => void> = [];
18
+ private currentHighlightIndex = -1;
19
19
 
20
- private _selectedItemSingle: OptionModel | null = null;
20
+ private selectedItemSingle: OptionModel | null = null;
21
21
 
22
- groups: GroupModel[] = [];
22
+ public groups: GroupModel[] = [];
23
23
 
24
- flatOptions: OptionModel[] = [];
24
+ public flatOptions: OptionModel[] = [];
25
25
 
26
- constructor(items: MixedItem[] = []) {
26
+ public constructor(items: MixedItem[] = []) {
27
27
  super(items);
28
- this._buildFlatStructure();
28
+ this.buildFlatStructure();
29
29
 
30
30
  Libs.callbackScheduler.on(`sche_vis_${this.adapterKey}`, () => {
31
31
  const visibleCount = this.flatOptions.filter((item) => item.visible).length;
32
32
  const totalCount = this.flatOptions.length;
33
33
 
34
- this._visibilityChangedCallbacks.forEach((callback) => {
34
+ this.visibilityChangedCallbacks.forEach((callback) => {
35
35
  callback({
36
36
  visibleCount,
37
37
  totalCount,
@@ -46,7 +46,7 @@ export class MixedAdapter extends Adapter<MixedItem, GroupView | OptionView> {
46
46
  /**
47
47
  * Build flat list of all options for navigation
48
48
  */
49
- private _buildFlatStructure(): void {
49
+ private buildFlatStructure(): void {
50
50
  this.flatOptions = [];
51
51
  this.groups = [];
52
52
 
@@ -84,9 +84,9 @@ export class MixedAdapter extends Adapter<MixedItem, GroupView | OptionView> {
84
84
  item.position = position;
85
85
 
86
86
  if (item instanceof GroupModel) {
87
- this._handleGroupView(item, viewer as GroupView, position);
87
+ this.handleGroupView(item, viewer as GroupView, position);
88
88
  } else if (item instanceof OptionModel) {
89
- this._handleOptionView(item, viewer as OptionView, position);
89
+ this.handleOptionView(item, viewer as OptionView, position);
90
90
  }
91
91
 
92
92
  item.isInit = true;
@@ -100,7 +100,7 @@ export class MixedAdapter extends Adapter<MixedItem, GroupView | OptionView> {
100
100
  * @param {GroupView} groupView - The view instance that renders the group in the UI.
101
101
  * @param {number} position - The position (index) of the group within a list.
102
102
  */
103
- private _handleGroupView(groupModel: GroupModel, groupView: GroupView, position: number): void {
103
+ private handleGroupView(groupModel: GroupModel, groupView: GroupView, position: number): void {
104
104
  super.onViewHolder(groupModel, groupView, position);
105
105
  groupModel.view = groupView;
106
106
 
@@ -134,7 +134,7 @@ export class MixedAdapter extends Adapter<MixedItem, GroupView | OptionView> {
134
134
  optionViewer = new OptionView(itemsContainer);
135
135
  }
136
136
 
137
- this._handleOptionView(optionModel, optionViewer, idx);
137
+ this.handleOptionView(optionModel, optionViewer, idx);
138
138
  optionModel.isInit = true;
139
139
  });
140
140
 
@@ -150,7 +150,7 @@ export class MixedAdapter extends Adapter<MixedItem, GroupView | OptionView> {
150
150
  * @param {OptionView} optionViewer - The view instance that renders the option in the UI.
151
151
  * @param {number} position - The index of this option within its group's item list.
152
152
  */
153
- private _handleOptionView(optionModel: OptionModel, optionViewer: OptionView, position: number): void {
153
+ private handleOptionView(optionModel: OptionModel, optionViewer: OptionView, position: number): void {
154
154
  optionViewer.isMultiple = this.isMultiple;
155
155
  optionViewer.hasImage = optionModel.hasImage;
156
156
 
@@ -180,23 +180,19 @@ export class MixedAdapter extends Adapter<MixedItem, GroupView | OptionView> {
180
180
  optionViewer.view.tags.LabelContent.innerHTML = optionModel.text;
181
181
 
182
182
  if (!optionModel.isInit) {
183
- optionViewer.view.tags.OptionView.addEventListener("click", (ev: MouseEvent) => {
183
+ optionViewer.view.tags.OptionView.addEventListener("click", async (ev: MouseEvent) => {
184
184
  ev.stopPropagation();
185
185
  ev.preventDefault();
186
186
 
187
187
  if (this.isSkipEvent) return;
188
188
 
189
189
  if (this.isMultiple) {
190
- this.changingProp("select");
191
- setTimeout(() => {
192
- optionModel.selected = !optionModel.selected;
193
- }, 5);
190
+ await this.changingProp("select");
191
+ optionModel.selected = !optionModel.selected;
194
192
  } else if (optionModel.selected !== true) {
195
- this.changingProp("select");
196
- setTimeout(() => {
197
- if (this._selectedItemSingle) this._selectedItemSingle.selected = false;
198
- optionModel.selected = true;
199
- }, 5);
193
+ await this.changingProp("select");
194
+ if (this.selectedItemSingle) this.selectedItemSingle.selected = false;
195
+ optionModel.selected = true;
200
196
  }
201
197
  });
202
198
 
@@ -212,18 +208,18 @@ export class MixedAdapter extends Adapter<MixedItem, GroupView | OptionView> {
212
208
  });
213
209
 
214
210
  optionModel.onInternalSelected((_evtToken: IEventCallback, _el: OptionModel, selected: boolean) => {
215
- if (selected) this._selectedItemSingle = optionModel;
211
+ if (selected) this.selectedItemSingle = optionModel;
216
212
  this.changeProp("selected_internal");
217
213
  });
218
214
 
219
215
  optionModel.onVisibilityChanged((_evtToken: IEventCallback, model: OptionModel, _visible: boolean) => {
220
216
  model.group?.updateVisibility();
221
- this._notifyVisibilityChanged();
217
+ this.notifyVisibilityChanged();
222
218
  });
223
219
  }
224
220
 
225
221
  if (optionModel.selected) {
226
- this._selectedItemSingle = optionModel;
222
+ this.selectedItemSingle = optionModel;
227
223
  optionModel.selectedNonTrigger = true;
228
224
  }
229
225
  }
@@ -233,11 +229,11 @@ export class MixedAdapter extends Adapter<MixedItem, GroupView | OptionView> {
233
229
  *
234
230
  * @param {Array<GroupModel|OptionModel>} items - The new collection of items to be displayed.
235
231
  */
236
- override setItems(items: MixedItem[]): void {
237
- this.changingProp("items", items);
232
+ override async setItems(items: MixedItem[]): Promise<void> {
233
+ await this.changingProp("items", items);
238
234
  this.items = items;
239
- this._buildFlatStructure();
240
- this.changeProp("items", items);
235
+ this.buildFlatStructure();
236
+ await this.changeProp("items", items);
241
237
  }
242
238
 
243
239
  /**
@@ -245,8 +241,8 @@ export class MixedAdapter extends Adapter<MixedItem, GroupView | OptionView> {
245
241
  *
246
242
  * @param {Array<GroupModel|OptionModel>} items - The new collection of items to sync.
247
243
  */
248
- override syncFromSource(items: MixedItem[]): void {
249
- this.setItems(items);
244
+ override async syncFromSource(items: MixedItem[]): Promise<void> {
245
+ await this.setItems(items);
250
246
  }
251
247
 
252
248
  /**
@@ -257,7 +253,7 @@ export class MixedAdapter extends Adapter<MixedItem, GroupView | OptionView> {
257
253
  */
258
254
  override updateData(items: MixedItem[]): void {
259
255
  this.items = items;
260
- this._buildFlatStructure();
256
+ this.buildFlatStructure();
261
257
  }
262
258
 
263
259
  /**
@@ -265,7 +261,7 @@ export class MixedAdapter extends Adapter<MixedItem, GroupView | OptionView> {
265
261
  *
266
262
  * @returns {OptionModel[]} - An array of selected option items from the flat list.
267
263
  */
268
- getSelectedItems(): OptionModel[] {
264
+ public getSelectedItems(): OptionModel[] {
269
265
  return this.flatOptions.filter((item) => item.selected);
270
266
  }
271
267
 
@@ -274,7 +270,7 @@ export class MixedAdapter extends Adapter<MixedItem, GroupView | OptionView> {
274
270
  *
275
271
  * @returns {OptionModel|undefined} - The first selected option or undefined if none are selected.
276
272
  */
277
- getSelectedItem(): OptionModel | undefined {
273
+ public getSelectedItem(): OptionModel | undefined {
278
274
  return this.flatOptions.find((item) => item.selected);
279
275
  }
280
276
 
@@ -283,7 +279,7 @@ export class MixedAdapter extends Adapter<MixedItem, GroupView | OptionView> {
283
279
  *
284
280
  * @param {boolean} isChecked - If true, select all; if false, deselect all.
285
281
  */
286
- checkAll(isChecked: boolean): void {
282
+ public checkAll(isChecked: boolean): void {
287
283
  if (!this.isMultiple) return;
288
284
  this.flatOptions.forEach((item) => {
289
285
  item.selected = isChecked;
@@ -296,22 +292,22 @@ export class MixedAdapter extends Adapter<MixedItem, GroupView | OptionView> {
296
292
  * @param {(stats: {visibleCount:number,totalCount:number,hasVisible:boolean,isEmpty:boolean}) => void} callback
297
293
  * - Function to invoke when visibility stats change.
298
294
  */
299
- onVisibilityChanged(callback: (stats: VisibilityStats) => void): void {
300
- this._visibilityChangedCallbacks.push(callback);
295
+ public onVisibilityChanged(callback: (stats: VisibilityStats) => void): void {
296
+ this.visibilityChangedCallbacks.push(callback);
301
297
  }
302
298
 
303
299
  /**
304
300
  * Notifies all registered visibility-change callbacks with up-to-date statistics.
305
301
  * Computes visible and total counts, then emits aggregated state.
306
302
  */
307
- private _notifyVisibilityChanged(): void {
303
+ private notifyVisibilityChanged(): void {
308
304
  Libs.callbackScheduler.run(`sche_vis_${this.adapterKey}`);
309
305
  }
310
306
 
311
307
  /**
312
308
  * Computes and returns current visibility statistics for options.
313
309
  */
314
- getVisibilityStats(): VisibilityStats {
310
+ public getVisibilityStats(): VisibilityStats {
315
311
  const visibleCount = this.flatOptions.filter((item) => item.visible).length;
316
312
  const totalCount = this.flatOptions.length;
317
313
 
@@ -326,7 +322,7 @@ export class MixedAdapter extends Adapter<MixedItem, GroupView | OptionView> {
326
322
  /**
327
323
  * Resets the highlight to the first visible option (index 0).
328
324
  */
329
- resetHighlight(): void {
325
+ public resetHighlight(): void {
330
326
  this.setHighlight(0);
331
327
  }
332
328
 
@@ -336,12 +332,12 @@ export class MixedAdapter extends Adapter<MixedItem, GroupView | OptionView> {
336
332
  * @param {number} direction - Increment (+1) or decrement (-1) of the current visible index.
337
333
  * @param {boolean} [isScrollToView=true] - Whether to scroll the highlighted item into view.
338
334
  */
339
- navigate(direction: number, isScrollToView: boolean = true): void {
335
+ public navigate(direction: number, isScrollToView: boolean = true): void {
340
336
  const visibleOptions = this.flatOptions.filter((opt) => opt.visible);
341
337
  if (visibleOptions.length === 0) return;
342
338
 
343
339
  let currentVisibleIndex = visibleOptions.findIndex(
344
- (opt) => opt === this.flatOptions[this._currentHighlightIndex]
340
+ (opt) => opt === this.flatOptions[this.currentHighlightIndex]
345
341
  );
346
342
  if (currentVisibleIndex === -1) currentVisibleIndex = -1;
347
343
 
@@ -359,9 +355,9 @@ export class MixedAdapter extends Adapter<MixedItem, GroupView | OptionView> {
359
355
  * Triggers a click on the currently highlighted and visible option to select it.
360
356
  * No-op if nothing is highlighted or the highlighted item is not visible.
361
357
  */
362
- selectHighlighted(): void {
363
- if (this._currentHighlightIndex > -1 && this.flatOptions[this._currentHighlightIndex]) {
364
- const item = this.flatOptions[this._currentHighlightIndex];
358
+ public selectHighlighted(): void {
359
+ if (this.currentHighlightIndex > -1 && this.flatOptions[this.currentHighlightIndex]) {
360
+ const item = this.flatOptions[this.currentHighlightIndex];
365
361
  if (item.visible) {
366
362
  const viewEl = item.view?.getView?.();
367
363
  if (viewEl) viewEl.click();
@@ -376,7 +372,7 @@ export class MixedAdapter extends Adapter<MixedItem, GroupView | OptionView> {
376
372
  * @param {number|OptionModel} target - Flat index or the specific OptionModel to highlight.
377
373
  * @param {boolean} [isScrollToView=true] - Whether to scroll the highlighted item into view.
378
374
  */
379
- setHighlight(target: number | OptionModel, isScrollToView: boolean = true): void {
375
+ public setHighlight(target: number | OptionModel, isScrollToView: boolean = true): void {
380
376
  let index = 0;
381
377
 
382
378
  if (typeof target === "number") {
@@ -388,8 +384,8 @@ export class MixedAdapter extends Adapter<MixedItem, GroupView | OptionView> {
388
384
  index = 0;
389
385
  }
390
386
 
391
- if (this._currentHighlightIndex > -1 && this.flatOptions[this._currentHighlightIndex]) {
392
- this.flatOptions[this._currentHighlightIndex].highlighted = false;
387
+ if (this.currentHighlightIndex > -1 && this.flatOptions[this.currentHighlightIndex]) {
388
+ this.flatOptions[this.currentHighlightIndex].highlighted = false;
393
389
  }
394
390
 
395
391
  for (let i = index; i < this.flatOptions.length; i++) {
@@ -397,7 +393,7 @@ export class MixedAdapter extends Adapter<MixedItem, GroupView | OptionView> {
397
393
  if (!item?.visible) continue;
398
394
 
399
395
  item.highlighted = true;
400
- this._currentHighlightIndex = i;
396
+ this.currentHighlightIndex = i;
401
397
 
402
398
  if (isScrollToView) {
403
399
  const el = item.view?.getView?.();
@@ -417,11 +413,11 @@ export class MixedAdapter extends Adapter<MixedItem, GroupView | OptionView> {
417
413
  * Hook invoked whenever the highlight changes.
418
414
  * Override to handle UI side effects (e.g., ARIA announcement, focus sync).
419
415
  */
420
- onHighlightChange(index: number, id?: string): void { }
416
+ public onHighlightChange(index: number, id?: string): void { }
421
417
 
422
418
  /**
423
419
  * Hook invoked when a group's collapsed state changes.
424
420
  * Override to handle side effects like analytics or layout adjustments.
425
421
  */
426
- onCollapsedChange(model: GroupModel, collapsed: boolean): void { }
422
+ public onCollapsedChange(model: GroupModel, collapsed: boolean): void { }
427
423
  }
@@ -11,24 +11,26 @@ import { Libs } from "../utils/libs";
11
11
  * @class
12
12
  */
13
13
  export class AccessoryBox {
14
- nodeMounted: MountViewResult<any> | null = null;
14
+ private nodeMounted: MountViewResult<any> | null = null;
15
15
 
16
- node: HTMLDivElement | null = null;
16
+ private node: HTMLDivElement | null = null;
17
17
 
18
- options: SelectiveOptions | null = null;
18
+ private options: SelectiveOptions | null = null;
19
19
 
20
- selectUIMask: HTMLDivElement | null = null;
20
+ private selectUIMask: HTMLDivElement | null = null;
21
21
 
22
- parentMask: HTMLDivElement | null = null;
22
+ private parentMask: HTMLDivElement | null = null;
23
23
 
24
- modelManager: ModelManager<MixedItem, MixedAdapter> | null = null;
24
+ private modelManager: ModelManager<MixedItem, MixedAdapter> | null = null;
25
+
26
+ private modelDatas: OptionModel[] = [];
25
27
 
26
28
  /**
27
29
  * Initializes the accessory box with optional configuration and immediately calls init() if provided.
28
30
  *
29
31
  * @param {object|null} options - Configuration options for the accessory box (e.g., layout and behavior).
30
32
  */
31
- constructor(options: SelectiveOptions | null = null) {
33
+ public constructor(options: SelectiveOptions | null = null) {
32
34
  if (options) this.init(options);
33
35
  }
34
36
 
@@ -38,7 +40,7 @@ export class AccessoryBox {
38
40
  *
39
41
  * @param {SelectiveOptions} options - Configuration object for the accessory box.
40
42
  */
41
- init(options: SelectiveOptions): void {
43
+ private init(options: SelectiveOptions): void {
42
44
  this.nodeMounted = Libs.mountNode({
43
45
  AccessoryBox: {
44
46
  tag: {
@@ -60,7 +62,7 @@ export class AccessoryBox {
60
62
  *
61
63
  * @param {HTMLDivElement} selectUIMask - The overlay/mask element of the main Select UI.
62
64
  */
63
- setRoot(selectUIMask: HTMLDivElement): void {
65
+ public setRoot(selectUIMask: HTMLDivElement): void {
64
66
  this.selectUIMask = selectUIMask;
65
67
  this.parentMask = selectUIMask.parentElement as HTMLDivElement | null;
66
68
 
@@ -71,8 +73,14 @@ export class AccessoryBox {
71
73
  * Inserts the accessory box before or after the Select UI mask depending on the configured accessoryStyle.
72
74
  * Keeps the accessory box aligned relative to the parent mask.
73
75
  */
74
- refreshLocation(): void {
75
- if (!this.parentMask || !this.node || !this.selectUIMask || !this.options) return;
76
+ public refreshLocation(): void {
77
+ if (
78
+ !this.parentMask ||
79
+ !this.node ||
80
+ !this.selectUIMask ||
81
+ !this.options
82
+ )
83
+ return;
76
84
 
77
85
  const ref =
78
86
  this.options.accessoryStyle === "top"
@@ -87,7 +95,9 @@ export class AccessoryBox {
87
95
  *
88
96
  * @param {ModelManager} modelManager - The model manager controlling option state.
89
97
  */
90
- setModelManager(modelManager: ModelManager<MixedItem, MixedAdapter> | null): void {
98
+ public setModelManager(
99
+ modelManager: ModelManager<MixedItem, MixedAdapter> | null,
100
+ ): void {
91
101
  this.modelManager = modelManager;
92
102
  }
93
103
 
@@ -97,14 +107,11 @@ export class AccessoryBox {
97
107
  *
98
108
  * @param {OptionModel[]} modelDatas - List of option models to render as accessory items.
99
109
  */
100
- setModelData(modelDatas: OptionModel[]): void {
110
+ public setModelData(modelDatas: OptionModel[]): void {
101
111
  if (!this.node || !this.options) return;
102
-
103
112
  this.node.replaceChildren();
104
113
 
105
114
  if (modelDatas.length > 0 && this.options.multiple) {
106
- this.node.classList.remove("hide");
107
-
108
115
  modelDatas.forEach((modelData) => {
109
116
  Libs.mountNode(
110
117
  {
@@ -118,12 +125,12 @@ export class AccessoryBox {
118
125
  role: "button",
119
126
  ariaLabel: `${this.options!.textAccessoryDeselect}${modelData.textContent}`,
120
127
  title: `${this.options!.textAccessoryDeselect}${modelData.textContent}`,
121
- onclick: (evt: MouseEvent) => {
128
+ onclick: async (evt: MouseEvent) => {
122
129
  evt.preventDefault();
123
- this.modelManager?.triggerChanging?.("select");
124
- setTimeout(() => {
125
- modelData.selected = false;
126
- }, 10);
130
+ await this.modelManager?.triggerChanging?.(
131
+ "select",
132
+ );
133
+ modelData.selected = false;
127
134
  },
128
135
  },
129
136
  },
@@ -137,13 +144,32 @@ export class AccessoryBox {
137
144
  },
138
145
  },
139
146
  },
140
- this.node
147
+ this.node,
141
148
  );
142
149
  });
143
- } else {
144
- this.node.classList.add("hide");
150
+ }
151
+ else {
152
+ modelDatas = [];
145
153
  }
146
154
 
155
+ this.modelDatas = modelDatas;
156
+ this.refreshDisplay();
147
157
  iEvents.trigger(window, "resize");
148
158
  }
149
- }
159
+
160
+ private refreshDisplay(): void {
161
+ if (this.options?.accessoryVisible && this.modelDatas.length > 0 && this.options.multiple) {
162
+ this.show();
163
+ } else {
164
+ this.hide();
165
+ }
166
+ }
167
+
168
+ private show(): void {
169
+ this.node.classList.remove("hide");
170
+ }
171
+
172
+ private hide(): void {
173
+ this.node.classList.add("hide");
174
+ }
175
+ }
@@ -8,14 +8,14 @@ export class Directive {
8
8
  node: HTMLElement;
9
9
 
10
10
  constructor() {
11
- this.node = this._init();
11
+ this.node = this.init();
12
12
  }
13
13
 
14
14
  /**
15
15
  * Represents a directive button element used to toggle dropdown state.
16
16
  * Initializes a clickable node with appropriate ARIA attributes for accessibility.
17
17
  */
18
- private _init(): HTMLElement {
18
+ private init(): HTMLElement {
19
19
  // Libs.nodeCreator returns Element, but this node is always an HTMLElement in practice.
20
20
  return Libs.nodeCreator({
21
21
  node: "div",
@@ -30,7 +30,7 @@ export class Directive {
30
30
  *
31
31
  * @param {boolean} value - If true, adds the "drop-down" class; otherwise removes it.
32
32
  */
33
- setDropdown(value: boolean): void {
33
+ public setDropdown(value: boolean): void {
34
34
  this.node.classList.toggle("drop-down", !!value);
35
35
  }
36
36
  }
@@ -6,15 +6,15 @@ import { Libs } from "../utils/libs";
6
6
  * @class
7
7
  */
8
8
  export class EmptyState {
9
- node: HTMLDivElement | null = null;
9
+ public node: HTMLDivElement | null = null;
10
10
 
11
- options: SelectiveOptions | null = null;
11
+ public options: SelectiveOptions | null = null;
12
12
 
13
13
  /**
14
14
  * Represents an empty state component that displays a message when no data or search results are available.
15
15
  * Provides methods to show/hide the state and check its visibility.
16
16
  */
17
- constructor(options: SelectiveOptions | null = null) {
17
+ public constructor(options: SelectiveOptions | null = null) {
18
18
  if (options) this.init(options);
19
19
  }
20
20
 
@@ -23,7 +23,7 @@ export class EmptyState {
23
23
  *
24
24
  * @param {object} options - Configuration object containing text for "no data" and "not found" states.
25
25
  */
26
- init(options: SelectiveOptions): void {
26
+ private init(options: SelectiveOptions): void {
27
27
  this.options = options;
28
28
 
29
29
  this.node = Libs.nodeCreator({
@@ -40,7 +40,7 @@ export class EmptyState {
40
40
  * @param {"notfound" | "nodata"} [type="nodata"] - Determines which message to show:
41
41
  * "notfound" for search results not found, "nodata" for no available data.
42
42
  */
43
- show(type: EmptyStateType = "nodata"): void {
43
+ public show(type: EmptyStateType = "nodata"): void {
44
44
  if (!this.node || !this.options) return;
45
45
 
46
46
  const text = type === "notfound" ? this.options.textNotFound : this.options.textNoData;
@@ -52,7 +52,7 @@ export class EmptyState {
52
52
  /**
53
53
  * Hides the empty state element by adding the "hide" class.
54
54
  */
55
- hide(): void {
55
+ public hide(): void {
56
56
  if (!this.node) return;
57
57
  this.node.classList.add("hide");
58
58
  }
@@ -62,7 +62,7 @@ export class EmptyState {
62
62
  *
63
63
  * @returns {boolean} - True if visible, false otherwise.
64
64
  */
65
- get isVisible(): boolean {
65
+ public get isVisible(): boolean {
66
66
  return !!this.node && !this.node.classList.contains("hide");
67
67
  }
68
68
  }
@@ -5,15 +5,15 @@ import { Libs } from "../utils/libs";
5
5
  * @class
6
6
  */
7
7
  export class LoadingState {
8
- node: HTMLDivElement | null = null;
8
+ public node: HTMLDivElement | null = null;
9
9
 
10
- options: SelectiveOptions | null = null;
10
+ public options: SelectiveOptions | null = null;
11
11
 
12
12
  /**
13
13
  * Represents a loading state component that displays a loading message during data fetch or processing.
14
14
  * Provides methods to show/hide the state and check its visibility.
15
15
  */
16
- constructor(options: SelectiveOptions | null = null) {
16
+ public constructor(options: SelectiveOptions | null = null) {
17
17
  if (options) this.init(options);
18
18
  }
19
19
 
@@ -22,7 +22,7 @@ export class LoadingState {
22
22
  *
23
23
  * @param {object} options - Configuration object containing text for the loading message.
24
24
  */
25
- init(options: SelectiveOptions): void {
25
+ private init(options: SelectiveOptions): void {
26
26
  this.options = options;
27
27
 
28
28
  this.node = Libs.nodeCreator({
@@ -39,7 +39,7 @@ export class LoadingState {
39
39
  *
40
40
  * @param {boolean} hasItems - If true, applies a "small" style for compact display.
41
41
  */
42
- show(hasItems: boolean): void {
42
+ public show(hasItems: boolean): void {
43
43
  if (!this.node || !this.options) return;
44
44
 
45
45
  this.node.textContent = this.options.textLoading;
@@ -50,7 +50,7 @@ export class LoadingState {
50
50
  /**
51
51
  * Hides the loading state element by adding the "hide" class.
52
52
  */
53
- hide(): void {
53
+ public hide(): void {
54
54
  if (!this.node) return;
55
55
  this.node.classList.add("hide");
56
56
  }
@@ -60,7 +60,7 @@ export class LoadingState {
60
60
  *
61
61
  * @returns {boolean} - True if visible, false otherwise.
62
62
  */
63
- get isVisible(): boolean {
63
+ public get isVisible(): boolean {
64
64
  return !!this.node && !this.node.classList.contains("hide");
65
65
  }
66
66
  }