selective-ui 1.2.4 → 1.2.6

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 (67) hide show
  1. package/README.md +7 -0
  2. package/dist/selective-ui.css +64 -58
  3. package/dist/selective-ui.css.map +1 -1
  4. package/dist/selective-ui.esm.js +4396 -1344
  5. package/dist/selective-ui.esm.js.map +1 -1
  6. package/dist/selective-ui.esm.min.js +2 -2
  7. package/dist/selective-ui.esm.min.js.br +0 -0
  8. package/dist/selective-ui.min.css +1 -1
  9. package/dist/selective-ui.min.css.br +0 -0
  10. package/dist/selective-ui.min.js +2 -2
  11. package/dist/selective-ui.min.js.br +0 -0
  12. package/dist/selective-ui.umd.js +4401 -1345
  13. package/dist/selective-ui.umd.js.map +1 -1
  14. package/package.json +3 -3
  15. package/src/css/components/accessorybox.css +1 -1
  16. package/src/css/components/directive.css +2 -2
  17. package/src/css/components/option-handle.css +4 -4
  18. package/src/css/components/placeholder.css +1 -1
  19. package/src/css/components/popup/empty-state.css +3 -3
  20. package/src/css/components/popup/loading-state.css +3 -3
  21. package/src/css/components/popup/popup.css +5 -5
  22. package/src/css/components/searchbox.css +2 -2
  23. package/src/css/components/selectbox.css +7 -7
  24. package/src/css/views/group-view.css +8 -8
  25. package/src/css/views/option-view.css +22 -22
  26. package/src/ts/adapter/mixed-adapter.ts +248 -92
  27. package/src/ts/components/accessorybox.ts +170 -73
  28. package/src/ts/components/directive.ts +55 -26
  29. package/src/ts/components/option-handle.ts +127 -60
  30. package/src/ts/components/placeholder.ts +73 -35
  31. package/src/ts/components/popup/empty-state.ts +71 -35
  32. package/src/ts/components/popup/loading-state.ts +73 -33
  33. package/src/ts/components/popup/popup.ts +19 -39
  34. package/src/ts/components/searchbox.ts +189 -50
  35. package/src/ts/components/selectbox.ts +401 -40
  36. package/src/ts/core/base/adapter.ts +160 -79
  37. package/src/ts/core/base/fenwick.ts +147 -0
  38. package/src/ts/core/base/lifecycle.ts +118 -35
  39. package/src/ts/core/base/model.ts +94 -36
  40. package/src/ts/core/base/recyclerview.ts +0 -1
  41. package/src/ts/core/base/view.ts +54 -23
  42. package/src/ts/core/base/virtual-recyclerview.ts +365 -283
  43. package/src/ts/core/model-manager.ts +172 -92
  44. package/src/ts/core/search-controller.ts +166 -93
  45. package/src/ts/global.ts +26 -5
  46. package/src/ts/index.ts +22 -3
  47. package/src/ts/models/group-model.ts +138 -32
  48. package/src/ts/models/option-model.ts +197 -53
  49. package/src/ts/services/dataset-observer.ts +72 -10
  50. package/src/ts/services/ea-observer.ts +87 -10
  51. package/src/ts/services/effector.ts +181 -32
  52. package/src/ts/services/refresher.ts +32 -7
  53. package/src/ts/services/resize-observer.ts +136 -19
  54. package/src/ts/services/select-observer.ts +115 -50
  55. package/src/ts/types/core/base/view.type.ts +3 -3
  56. package/src/ts/types/core/base/virtual-recyclerview.type.ts +1 -1
  57. package/src/ts/types/plugins/plugin.type.ts +46 -0
  58. package/src/ts/types/utils/ievents.type.ts +6 -1
  59. package/src/ts/types/utils/istorage.type.ts +8 -4
  60. package/src/ts/types/utils/libs.type.ts +2 -2
  61. package/src/ts/types/utils/selective.type.ts +14 -1
  62. package/src/ts/utils/callback-scheduler.ts +115 -37
  63. package/src/ts/utils/ievents.ts +91 -29
  64. package/src/ts/utils/libs.ts +41 -65
  65. package/src/ts/utils/selective.ts +412 -79
  66. package/src/ts/views/group-view.ts +142 -31
  67. package/src/ts/views/option-view.ts +272 -60
@@ -6,58 +6,98 @@ import { iEvents } from "../utils/ievents";
6
6
  import { Libs } from "../utils/libs";
7
7
 
8
8
  /**
9
- * UI control that exposes "Select All" / "Deselect All" actions
10
- * for multiple-selection lists.
9
+ * OptionHandle
11
10
  *
12
- * Responsibilities:
13
- * - Renders two action controls (links/buttons)
14
- * - Shows/hides itself based on configuration flags
15
- * - Allows registration of callbacks for both actions
16
- * - Participates in the standard `Lifecycle`
11
+ * Headless-friendly, DOM-driven UI control that exposes bulk selection actions
12
+ * ("Select all" / "Deselect all") for multiple-selection experiences.
17
13
  *
18
- * Visibility rule:
19
- * - Visible only when `options.multiple` and `options.selectall` are truthy.
14
+ * ### Responsibility
15
+ * - Creates and owns a small DOM subtree (root + two action elements).
16
+ * - Exposes registration APIs for action callbacks (`onSelectAll`, `onDeSelectAll`).
17
+ * - Reflects feature flags from {@link SelectiveOptions} by showing/hiding itself.
18
+ * - Participates in the library {@link Lifecycle} finite-state machine (FSM).
19
+ *
20
+ * ### Lifecycle (Strict FSM)
21
+ * - Constructed in `NEW`.
22
+ * - {@link initialize} builds DOM and calls `init()` → transitions to `INITIALIZED`.
23
+ * - {@link update} is safe to call repeatedly; it re-evaluates visibility and then
24
+ * delegates to `super.update()` (idempotent once in `UPDATED`).
25
+ * - {@link destroy} is a terminal transition; subsequent calls are no-ops.
26
+ *
27
+ * ### Event / Callback Flow
28
+ * - User interaction is handled via DOM `onclick` handlers bound during initialization.
29
+ * - On click, this component dispatches callbacks through {@link iEvents.callFunctions}.
30
+ * - This class does not own selection state; it only emits intent via callbacks.
31
+ *
32
+ * ### Visibility Contract
33
+ * Visible only when BOTH flags are enabled:
34
+ * - `options.multiple` truthy (after {@link Libs.string2Boolean} coercion)
35
+ * - `options.selectall` truthy (after {@link Libs.string2Boolean} coercion)
36
+ *
37
+ * ### DOM / a11y Notes
38
+ * - Uses `<a>` elements as action triggers. This is a DOM-side effect and may have
39
+ * accessibility implications depending on `href`, keyboard handling, and ARIA.
20
40
  *
21
41
  * @extends Lifecycle
22
42
  */
23
43
  export class OptionHandle extends Lifecycle {
24
-
25
44
  /**
26
- * Internal reference to the mounted node structure returned by `Libs.mountNode`.
27
- * Used to access typed tags if needed. Null before initialization.
45
+ * Result returned by {@link Libs.mountNode}.
46
+ *
47
+ * Stores the mounted view structure so the component can keep a stable reference
48
+ * to its created DOM nodes. `null` before {@link initialize}.
49
+ *
50
+ * @internal
28
51
  */
29
- private nodeMounted: MountViewResult<any> | null = null;
52
+ private nodeMounted: MountViewResult | null = null;
30
53
 
31
54
  /**
32
- * Root DOM element of the option handle component.
33
- * Created during initialization and removed on destroy.
55
+ * Root element of this control.
56
+ *
57
+ * Created during {@link initialize}. This node is used by {@link show}/{@link hide}
58
+ * and removed during {@link destroy}.
34
59
  */
35
60
  public node: HTMLDivElement | null = null;
36
61
 
37
62
  /**
38
- * Configuration options controlling labels and feature flags.
39
- * (e.g., textSelectAll, textDeselectAll, multiple, selectall)
63
+ * Configuration snapshot used for:
64
+ * - labels (`textSelectAll`, `textDeselectAll`)
65
+ * - feature flags (`multiple`, `selectall`)
66
+ *
67
+ * Treated as read-only after initialization; cleared on {@link destroy}.
68
+ *
69
+ * @internal
40
70
  */
41
71
  private options: SelectiveOptions | null = null;
42
72
 
43
73
  /**
44
- * Registered callbacks executed when "Select All" is activated.
74
+ * Callback list invoked when the "Select all" control is activated.
75
+ *
76
+ * Callbacks are invoked via {@link iEvents.callFunctions}. This component does not
77
+ * interpret arguments; it delegates invocation semantics to the dispatcher helper.
78
+ *
79
+ * @internal
45
80
  */
46
81
  private actionOnSelectAll: Array<(...args: unknown[]) => unknown> = [];
47
82
 
48
83
  /**
49
- * Registered callbacks executed when "Deselect All" is activated.
84
+ * Callback list invoked when the "Deselect all" control is activated.
85
+ *
86
+ * Callbacks are invoked via {@link iEvents.callFunctions}. This component does not
87
+ * interpret arguments; it delegates invocation semantics to the dispatcher helper.
88
+ *
89
+ * @internal
50
90
  */
51
91
  private actionOnDeSelectAll: Array<(...args: unknown[]) => unknown> = [];
52
92
 
53
93
  /**
54
- * Creates a new OptionHandle control.
94
+ * Creates an {@link OptionHandle}.
55
95
  *
56
- * If `options` are provided, the component is initialized immediately and
57
- * enters the lifecycle (init). Otherwise, call a custom initializer later
58
- * to set it up.
96
+ * If `options` is provided, the instance immediately performs {@link initialize}
97
+ * and enters the {@link Lifecycle} (calls `init()` internally).
98
+ * If `options` is `null`, the instance stays in `NEW` until initialized elsewhere.
59
99
  *
60
- * @param options - Configuration with texts and feature flags.
100
+ * @param options - Feature flags and labels for the two actions.
61
101
  */
62
102
  public constructor(options: SelectiveOptions | null = null) {
63
103
  super();
@@ -65,26 +105,33 @@ export class OptionHandle extends Lifecycle {
65
105
  }
66
106
 
67
107
  /**
68
- * Initializes the option handle UI.
108
+ * Initializes DOM and binds event handlers.
109
+ *
110
+ * DOM structure (conceptually):
111
+ * - Root: `div.seui-option-handle.hide`
112
+ * - Child: `a.seui-option-handle-item` ("Select all")
113
+ * - Child: `a.seui-option-handle-item` ("Deselect all")
69
114
  *
70
- * Builds the DOM:
71
- * - Root: `.selective-ui-option-handle.hide`
72
- * - Children: "Select All" and "Deselect All" controls
115
+ * Click handlers:
116
+ * - "Select all" → dispatches {@link actionOnSelectAll} via {@link iEvents.callFunctions}
117
+ * - "Deselect all" dispatches {@link actionOnDeSelectAll} via {@link iEvents.callFunctions}
73
118
  *
74
- * Wires their click handlers to invoke registered callbacks
75
- * via the `iEvents.callFunctions` helper.
119
+ * Side effects:
120
+ * - Creates DOM nodes (via {@link Libs.mountNode})
121
+ * - Transitions lifecycle by calling `init()` at the end
76
122
  *
77
123
  * @param options - Configuration providing labels and feature flags.
124
+ * @internal
78
125
  */
79
126
  private initialize(options: SelectiveOptions): void {
80
- this.nodeMounted = Libs.mountNode({
127
+ this.nodeMounted = Libs.mountNode<MountViewResult>({
81
128
  OptionHandle: {
82
- tag: { node: "div", classList: ["selective-ui-option-handle", "hide"] },
129
+ tag: { node: "div", classList: ["seui-option-handle", "hide"] },
83
130
  child: {
84
131
  SelectAll: {
85
132
  tag: {
86
133
  node: "a",
87
- classList: "selective-ui-option-handle-item",
134
+ classList: "seui-option-handle-item",
88
135
  textContent: options.textSelectAll,
89
136
  onclick: () => {
90
137
  iEvents.callFunctions(this.actionOnSelectAll);
@@ -94,7 +141,7 @@ export class OptionHandle extends Lifecycle {
94
141
  DeSelectAll: {
95
142
  tag: {
96
143
  node: "a",
97
- classList: "selective-ui-option-handle-item",
144
+ classList: "seui-option-handle-item",
98
145
  textContent: options.textDeselectAll,
99
146
  onclick: () => {
100
147
  iEvents.callFunctions(this.actionOnDeSelectAll);
@@ -103,7 +150,7 @@ export class OptionHandle extends Lifecycle {
103
150
  },
104
151
  },
105
152
  },
106
- }) as MountViewResult<any>;
153
+ });
107
154
 
108
155
  this.node = this.nodeMounted.view as HTMLDivElement;
109
156
  this.options = options;
@@ -112,14 +159,16 @@ export class OptionHandle extends Lifecycle {
112
159
  }
113
160
 
114
161
  /**
115
- * Returns whether the handle should be available (and thus visible)
116
- * based on current configuration flags.
162
+ * Computes whether this control is enabled/available under current configuration.
117
163
  *
118
- * Availability requires:
119
- * - `multiple` is truthy
120
- * - `selectall` is truthy
164
+ * This method performs a boolean coercion using {@link Libs.string2Boolean} to
165
+ * support string-like flags in {@link SelectiveOptions}.
121
166
  *
122
- * @returns True if both features are enabled; otherwise false.
167
+ * No-ops:
168
+ * - Returns `false` when {@link options} has not been set.
169
+ *
170
+ * @returns `true` when both `multiple` and `selectall` are enabled; otherwise `false`.
171
+ * @internal
123
172
  */
124
173
  private available(): boolean {
125
174
  if (!this.options) return false;
@@ -127,12 +176,17 @@ export class OptionHandle extends Lifecycle {
127
176
  }
128
177
 
129
178
  /**
130
- * Refreshes the visibility based on `available()` and emits the update lifecycle.
179
+ * Re-evaluates visibility and advances the lifecycle update step.
180
+ *
181
+ * Behavior:
182
+ * - If {@link node} exists, toggles the `hide` class based on {@link available}.
183
+ * - Always delegates to `super.update()` to participate in the FSM transition.
131
184
  *
132
- * - Shows the handle when available
133
- * - Hides it otherwise
185
+ * Idempotency:
186
+ * - Repeated calls remain safe; DOM class toggling is stable and the underlying
187
+ * {@link Lifecycle} update is expected to be idempotent after the first transition.
134
188
  *
135
- * Note: `super.update()` transitions lifecycle to `UPDATED` (idempotent after first call).
189
+ * @override
136
190
  */
137
191
  public override update(): void {
138
192
  if (this.node) {
@@ -147,9 +201,9 @@ export class OptionHandle extends Lifecycle {
147
201
  }
148
202
 
149
203
  /**
150
- * Makes the option handle visible.
204
+ * Shows the control by removing the `hide` CSS class on the root node.
151
205
  *
152
- * Removes the `hide` class from the root node.
206
+ * No-ops when {@link node} is `null`.
153
207
  */
154
208
  public show(): void {
155
209
  if (!this.node) return;
@@ -157,9 +211,9 @@ export class OptionHandle extends Lifecycle {
157
211
  }
158
212
 
159
213
  /**
160
- * Hides the option handle.
214
+ * Hides the control by adding the `hide` CSS class on the root node.
161
215
  *
162
- * Adds the `hide` class to the root node.
216
+ * No-ops when {@link node} is `null`.
163
217
  */
164
218
  public hide(): void {
165
219
  if (!this.node) return;
@@ -167,12 +221,15 @@ export class OptionHandle extends Lifecycle {
167
221
  }
168
222
 
169
223
  /**
170
- * Registers a callback for the "Select All" action.
224
+ * Registers a callback for the external "Select all" intent.
171
225
  *
172
- * The callback will be invoked with the arguments provided
173
- * by the action dispatcher (if any).
226
+ * Notes:
227
+ * - This is an "external event" hook: it notifies the host/controller layer that a
228
+ * bulk action was requested. This component does not mutate selection state itself.
229
+ * - Callbacks are executed by {@link iEvents.callFunctions} when the corresponding
230
+ * DOM control is activated.
174
231
  *
175
- * @param action - Function to execute when "Select All" is triggered.
232
+ * @param action - Callback invoked on activation; ignored when not a function.
176
233
  */
177
234
  public onSelectAll(action: ((...args: unknown[]) => unknown) | null = null): void {
178
235
  if (typeof action === "function") {
@@ -181,12 +238,15 @@ export class OptionHandle extends Lifecycle {
181
238
  }
182
239
 
183
240
  /**
184
- * Registers a callback for the "Deselect All" action.
241
+ * Registers a callback for the external "Deselect all" intent.
185
242
  *
186
- * The callback will be invoked with the arguments provided
187
- * by the action dispatcher (if any).
243
+ * Notes:
244
+ * - This is an "external event" hook: it notifies the host/controller layer that a
245
+ * bulk deselection was requested. This component does not mutate selection state itself.
246
+ * - Callbacks are executed by {@link iEvents.callFunctions} when the corresponding
247
+ * DOM control is activated.
188
248
  *
189
- * @param action - Function to execute when "Deselect All" is triggered.
249
+ * @param action - Callback invoked on activation; ignored when not a function.
190
250
  */
191
251
  public onDeSelectAll(action: ((...args: unknown[]) => unknown) | null = null): void {
192
252
  if (typeof action === "function") {
@@ -195,10 +255,17 @@ export class OptionHandle extends Lifecycle {
195
255
  }
196
256
 
197
257
  /**
198
- * Destroys the option handle component.
258
+ * Tears down DOM resources and terminates the lifecycle.
259
+ *
260
+ * Strict FSM / idempotency:
261
+ * - If already in {@link LifecycleState.DESTROYED}, this method returns immediately.
262
+ *
263
+ * Side effects:
264
+ * - Removes the root DOM node from the document (if present).
265
+ * - Clears references to options and callback lists to allow GC.
266
+ * - Delegates to `super.destroy()` to finalize the lifecycle transition.
199
267
  *
200
- * Removes the DOM node, clears stored options,
201
- * and terminates the lifecycle.
268
+ * @override
202
269
  */
203
270
  public override destroy(): void {
204
271
  if (this.is(LifecycleState.DESTROYED)) {
@@ -4,37 +4,63 @@ import { SelectiveOptions } from "../types/utils/selective.type";
4
4
  import { Libs } from "../utils/libs";
5
5
 
6
6
  /**
7
- * UI component representing a placeholder for the Select UI.
7
+ * PlaceHolder
8
8
  *
9
- * The placeholder displays contextual guidance when no value is selected.
10
- * It supports dynamic updates and optional HTML content, depending on configuration.
9
+ * DOM-driven placeholder view for the Select UI when no value is selected.
10
+ * This component is intentionally minimal: it owns a single DOM node and exposes
11
+ * getter/setter APIs for the placeholder content.
11
12
  *
12
- * The component manages a single DOM node and participates
13
- * in the standard `Lifecycle`.
13
+ * ### Responsibility
14
+ * - Create and own the placeholder DOM element (`.seui-placeholder`).
15
+ * - Render placeholder content from {@link SelectiveOptions.placeholder}.
16
+ * - Support runtime updates via {@link set}, optionally persisting into options.
17
+ * - Participate in the shared {@link Lifecycle} FSM.
18
+ *
19
+ * ### Lifecycle (Strict FSM)
20
+ * - Constructed in `NEW`.
21
+ * - {@link initialize} creates DOM and calls `init()` → transitions to `INITIALIZED`.
22
+ * - Updates are data-driven via {@link set}; this class does not override `update()`.
23
+ * - {@link destroy} removes the DOM node and clears references; repeat calls are no-ops
24
+ * once {@link LifecycleState.DESTROYED}.
25
+ *
26
+ * ### DOM / Rendering Notes
27
+ * - Content is written through `innerHTML` (DOM side effect).
28
+ * - {@link Libs.tagTranslate} is applied to the incoming value before rendering.
29
+ * - When `options.allowHtml` is falsy, HTML is stripped via {@link Libs.stripHtml}
30
+ * to reduce injection risk. When truthy, translated HTML is rendered as-is.
14
31
  *
15
32
  * @extends Lifecycle
16
33
  */
17
34
  export class PlaceHolder extends Lifecycle {
18
-
19
35
  /**
20
- * Root DOM element of the placeholder component.
21
- * Created during initialization and removed on destroy.
36
+ * Root DOM element for the placeholder.
37
+ *
38
+ * Created during {@link initialize}. Removed from the DOM during {@link destroy}.
39
+ * `null` before initialization and after destruction.
22
40
  */
23
41
  public node: HTMLElement | null = null;
24
42
 
25
43
  /**
26
- * Configuration options containing placeholder text
27
- * and rendering preferences (e.g., allowHtml).
44
+ * Configuration snapshot used to render and optionally persist placeholder content.
45
+ *
46
+ * Key fields used by this component:
47
+ * - `placeholder`: initial/current placeholder text/markup
48
+ * - `allowHtml`: controls whether HTML is rendered or stripped
49
+ *
50
+ * Cleared during {@link destroy}.
51
+ *
52
+ * @internal
28
53
  */
29
54
  private options: SelectiveOptions | null = null;
30
55
 
31
56
  /**
32
- * Creates a new PlaceHolder instance.
57
+ * Creates a new {@link PlaceHolder}.
33
58
  *
34
- * If options are provided, the component is initialized immediately.
59
+ * If `options` is provided, the component initializes immediately and enters the
60
+ * {@link Lifecycle} by calling `init()` internally. If `options` is `null`, the
61
+ * instance remains in `NEW` until initialized elsewhere (by design).
35
62
  *
36
- * @param options - Configuration object containing placeholder text
37
- * and HTML rendering settings.
63
+ * @param options - Select UI options containing placeholder content and rendering flags.
38
64
  */
39
65
  constructor(options: SelectiveOptions | null) {
40
66
  super();
@@ -42,20 +68,22 @@ export class PlaceHolder extends Lifecycle {
42
68
  }
43
69
 
44
70
  /**
45
- * Initializes the placeholder component.
71
+ * Builds the placeholder DOM node and starts the lifecycle.
46
72
  *
47
- * Creates the DOM node, applies base styling,
48
- * renders the initial placeholder content,
49
- * stores configuration options, and starts the lifecycle.
73
+ * Side effects:
74
+ * - Creates a `div.seui-placeholder` node via {@link Libs.nodeCreator}.
75
+ * - Writes initial placeholder content into `innerHTML`.
76
+ * - Transitions the lifecycle by calling `init()`.
50
77
  *
51
- * @param options - Configuration object containing placeholder settings.
78
+ * @param options - Options providing placeholder content and rendering behavior.
79
+ * @internal
52
80
  */
53
81
  private initialize(options: SelectiveOptions): void {
54
- this.node = Libs.nodeCreator({
82
+ this.node = Libs.nodeCreator<HTMLElement>({
55
83
  node: "div",
56
- classList: "selective-ui-placeholder",
84
+ classList: "seui-placeholder",
57
85
  innerHTML: options.placeholder,
58
- }) as HTMLElement;
86
+ });
59
87
 
60
88
  this.options = options;
61
89
 
@@ -63,25 +91,29 @@ export class PlaceHolder extends Lifecycle {
63
91
  }
64
92
 
65
93
  /**
66
- * Returns the current placeholder text from the configuration.
94
+ * Returns the current placeholder content as stored in {@link options}.
95
+ *
96
+ * This method does not read from the DOM; it returns the configuration value.
67
97
  *
68
- * @returns The current placeholder value, or an empty string if not set.
98
+ * @returns The configured placeholder string, or an empty string if uninitialized.
69
99
  */
70
100
  public get(): string {
71
101
  return this.options?.placeholder ?? "";
72
102
  }
73
103
 
74
104
  /**
75
- * Updates the placeholder content.
105
+ * Updates the rendered placeholder content.
76
106
  *
77
- * The value can optionally be persisted back into the configuration.
78
- * HTML rendering is controlled by the `allowHtml` option:
79
- * - When enabled, translated HTML is rendered
80
- * - When disabled, HTML tags are stripped for safety
107
+ * Behavior:
108
+ * - No-ops if the component is not initialized (`node`/`options` missing).
109
+ * - Optionally persists the new value back into {@link options.placeholder}.
110
+ * - Applies {@link Libs.tagTranslate} before rendering.
111
+ * - Renders using `innerHTML`:
112
+ * - If `options.allowHtml` is truthy, renders translated HTML.
113
+ * - Otherwise, strips HTML via {@link Libs.stripHtml} and renders safe text/markup.
81
114
  *
82
- * @param value - The new placeholder content.
83
- * @param isSave - Whether to persist the value in the configuration.
84
- * Defaults to `true`.
115
+ * @param value - New placeholder content to render.
116
+ * @param isSave - When `true` (default), also updates {@link options.placeholder}.
85
117
  */
86
118
  public set(value: string, isSave: boolean = true): void {
87
119
  if (!this.node || !this.options) return;
@@ -97,10 +129,16 @@ export class PlaceHolder extends Lifecycle {
97
129
  }
98
130
 
99
131
  /**
100
- * Destroys the placeholder component.
132
+ * Disposes the placeholder DOM and terminates the lifecycle.
133
+ *
134
+ * Strict FSM / idempotency:
135
+ * - If already {@link LifecycleState.DESTROYED}, returns immediately.
136
+ *
137
+ * Side effects:
138
+ * - Removes {@link node} from the DOM (if present).
139
+ * - Clears references to allow garbage collection.
101
140
  *
102
- * Removes the DOM node, clears stored options,
103
- * and terminates the lifecycle.
141
+ * @override
104
142
  */
105
143
  public override destroy(): void {
106
144
  if (this.is(LifecycleState.DESTROYED)) {
@@ -5,37 +5,61 @@ import { SelectiveOptions } from "../../types/utils/selective.type";
5
5
  import { Libs } from "../../utils/libs";
6
6
 
7
7
  /**
8
- * UI component that represents an empty state.
8
+ * Lightweight UI state box that renders contextual "empty" feedback.
9
9
  *
10
- * The empty state is used to display contextual feedback when:
11
- * - No data is available
12
- * - A search yields no matching results
10
+ * ### Responsibility
11
+ * - Owns a single DOM node that can be shown/hidden to communicate:
12
+ * - **No data** (no options available)
13
+ * - **Not found** (search produced zero visible results)
14
+ * - Exposes a minimal imperative API (`show`, `hide`, `isVisible`) used by higher-level
15
+ * controllers/components (e.g., popup/search flows).
13
16
  *
14
- * It manages a single DOM node and participates in the standard lifecycle.
17
+ * ### Lifecycle (Strict FSM)
18
+ * - Constructed in `NEW`. When `options` are provided, {@link initialize} is called and the
19
+ * instance transitions to `INITIALIZED` via {@link Lifecycle.init}.
20
+ * - This component does not automatically mount itself into a container; consumers are expected
21
+ * to append {@link node} where appropriate.
22
+ * - {@link destroy} removes the node and transitions to `DESTROYED`.
23
+ *
24
+ * ### Idempotency / No-ops
25
+ * - {@link show} and {@link hide} are **no-ops** until {@link node} exists.
26
+ * - {@link destroy} is idempotent once in {@link LifecycleState.DESTROYED}.
27
+ *
28
+ * ### Accessibility / DOM side effects
29
+ * - Uses `role="status"` and `aria-live="polite"` so screen readers announce changes without
30
+ * interrupting the user.
31
+ * - Visibility is controlled via the `"hide"` CSS class; hiding does not remove the element.
15
32
  *
16
33
  * @extends Lifecycle
34
+ * @see {@link LifecycleState}
35
+ * @see {@link EmptyStateType}
17
36
  */
18
37
  export class EmptyState extends Lifecycle {
19
-
20
38
  /**
21
- * Root DOM element of the empty state component.
22
- * Created during initialization and removed on destroy.
39
+ * Root DOM element for the empty state UI.
40
+ *
41
+ * - Created during {@link initialize}.
42
+ * - Intended to be appended by the parent container (component does not auto-attach).
43
+ * - Removed from DOM during {@link destroy}.
23
44
  */
24
45
  public node: HTMLDivElement | null = null;
25
46
 
26
47
  /**
27
- * Configuration options providing display text
28
- * for different empty state scenarios.
48
+ * Configuration source for empty state messages.
49
+ *
50
+ * Expected to provide at least:
51
+ * - `textNoData` (for `"nodata"`)
52
+ * - `textNotFound` (for `"notfound"`)
29
53
  */
30
54
  public options: SelectiveOptions | null = null;
31
55
 
32
56
  /**
33
- * Creates a new EmptyState instance.
57
+ * Creates a new {@link EmptyState}.
34
58
  *
35
- * If options are provided, the component is initialized immediately.
59
+ * If `options` are provided, initialization runs immediately (creates {@link node} and
60
+ * transitions to `INITIALIZED`).
36
61
  *
37
- * @param options - Configuration containing messages for
38
- * "no data" and "not found" states.
62
+ * @param {SelectiveOptions | null} [options=null] - Configuration containing empty state messages.
39
63
  */
40
64
  public constructor(options: SelectiveOptions | null = null) {
41
65
  super();
@@ -43,35 +67,39 @@ export class EmptyState extends Lifecycle {
43
67
  }
44
68
 
45
69
  /**
46
- * Initializes the empty state component.
70
+ * Initializes internal resources for this component.
47
71
  *
48
- * Creates the root DOM element, applies accessibility attributes,
49
- * stores configuration options, and starts the lifecycle.
72
+ * Side effects:
73
+ * - Creates the root `div` node with `role="status"` and `aria-live="polite"`.
74
+ * - Applies base CSS classes: `"seui-empty-state"` and `"hide"`.
75
+ * - Stores the options reference and calls {@link Lifecycle.init}.
50
76
  *
51
- * @param options - Configuration object containing empty state messages.
77
+ * @param {SelectiveOptions} options - Configuration object containing empty state messages.
78
+ * @returns {void}
52
79
  */
53
80
  private initialize(options: SelectiveOptions): void {
54
81
  this.options = options;
55
82
 
56
- this.node = Libs.nodeCreator({
83
+ this.node = Libs.nodeCreator<HTMLDivElement>({
57
84
  node: "div",
58
- classList: ["selective-ui-empty-state", "hide"],
85
+ classList: ["seui-empty-state", "hide"],
59
86
  role: "status",
60
87
  ariaLive: "polite",
61
- }) as HTMLDivElement;
88
+ });
62
89
 
63
90
  this.init();
64
91
  }
65
92
 
66
93
  /**
67
- * Displays the empty state message.
94
+ * Shows the empty state message for the given scenario.
68
95
  *
69
- * The message content depends on the provided type:
70
- * - `"nodata"`: no data available
71
- * - `"notfound"`: no matching search results
96
+ * - `"nodata"`: uses `options.textNoData`
97
+ * - `"notfound"`: uses `options.textNotFound`
72
98
  *
73
- * @param type - Type of empty state to display.
74
- * Defaults to `"nodata"`.
99
+ * No-op if {@link node} or {@link options} are not initialized.
100
+ *
101
+ * @param {EmptyStateType} [type="nodata"] - Which empty state message to display.
102
+ * @returns {void}
75
103
  */
76
104
  public show(type: EmptyStateType = "nodata"): void {
77
105
  if (!this.node || !this.options) return;
@@ -86,10 +114,12 @@ export class EmptyState extends Lifecycle {
86
114
  }
87
115
 
88
116
  /**
89
- * Hides the empty state component.
117
+ * Hides the empty state node by applying the `"hide"` CSS class.
118
+ *
119
+ * This does not remove the element from the DOM.
120
+ * No-op if {@link node} is not initialized.
90
121
  *
91
- * This does not remove the element from the DOM;
92
- * it only updates its visibility via CSS.
122
+ * @returns {void}
93
123
  */
94
124
  public hide(): void {
95
125
  if (!this.node) return;
@@ -97,19 +127,25 @@ export class EmptyState extends Lifecycle {
97
127
  }
98
128
 
99
129
  /**
100
- * Indicates whether the empty state is currently visible.
130
+ * Whether the empty state is currently visible.
101
131
  *
102
- * @returns True if the empty state is shown; otherwise false.
132
+ * @returns {boolean} `true` when {@link node} exists and does not have the `"hide"` class.
103
133
  */
104
134
  public get isVisible(): boolean {
105
135
  return !!this.node && !this.node.classList.contains("hide");
106
136
  }
107
137
 
108
138
  /**
109
- * Destroys the empty state component.
139
+ * Releases resources owned by this component.
140
+ *
141
+ * - Removes the root DOM node (if present).
142
+ * - Clears stored options and internal references.
143
+ * - Transitions to `DESTROYED`.
144
+ *
145
+ * Idempotent: returns early if already in {@link LifecycleState.DESTROYED}.
110
146
  *
111
- * Removes the DOM node, clears stored options,
112
- * and terminates the lifecycle.
147
+ * @returns {void}
148
+ * @override
113
149
  */
114
150
  public override destroy(): void {
115
151
  if (this.is(LifecycleState.DESTROYED)) {