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.
- package/README.md +7 -0
- package/dist/selective-ui.css +64 -58
- package/dist/selective-ui.css.map +1 -1
- package/dist/selective-ui.esm.js +4396 -1344
- package/dist/selective-ui.esm.js.map +1 -1
- package/dist/selective-ui.esm.min.js +2 -2
- package/dist/selective-ui.esm.min.js.br +0 -0
- package/dist/selective-ui.min.css +1 -1
- package/dist/selective-ui.min.css.br +0 -0
- package/dist/selective-ui.min.js +2 -2
- package/dist/selective-ui.min.js.br +0 -0
- package/dist/selective-ui.umd.js +4401 -1345
- package/dist/selective-ui.umd.js.map +1 -1
- package/package.json +3 -3
- package/src/css/components/accessorybox.css +1 -1
- package/src/css/components/directive.css +2 -2
- package/src/css/components/option-handle.css +4 -4
- package/src/css/components/placeholder.css +1 -1
- package/src/css/components/popup/empty-state.css +3 -3
- package/src/css/components/popup/loading-state.css +3 -3
- package/src/css/components/popup/popup.css +5 -5
- package/src/css/components/searchbox.css +2 -2
- package/src/css/components/selectbox.css +7 -7
- package/src/css/views/group-view.css +8 -8
- package/src/css/views/option-view.css +22 -22
- package/src/ts/adapter/mixed-adapter.ts +248 -92
- package/src/ts/components/accessorybox.ts +170 -73
- package/src/ts/components/directive.ts +55 -26
- package/src/ts/components/option-handle.ts +127 -60
- package/src/ts/components/placeholder.ts +73 -35
- package/src/ts/components/popup/empty-state.ts +71 -35
- package/src/ts/components/popup/loading-state.ts +73 -33
- package/src/ts/components/popup/popup.ts +19 -39
- package/src/ts/components/searchbox.ts +189 -50
- package/src/ts/components/selectbox.ts +401 -40
- package/src/ts/core/base/adapter.ts +160 -79
- package/src/ts/core/base/fenwick.ts +147 -0
- package/src/ts/core/base/lifecycle.ts +118 -35
- package/src/ts/core/base/model.ts +94 -36
- package/src/ts/core/base/recyclerview.ts +0 -1
- package/src/ts/core/base/view.ts +54 -23
- package/src/ts/core/base/virtual-recyclerview.ts +365 -283
- package/src/ts/core/model-manager.ts +172 -92
- package/src/ts/core/search-controller.ts +166 -93
- package/src/ts/global.ts +26 -5
- package/src/ts/index.ts +22 -3
- package/src/ts/models/group-model.ts +138 -32
- package/src/ts/models/option-model.ts +197 -53
- package/src/ts/services/dataset-observer.ts +72 -10
- package/src/ts/services/ea-observer.ts +87 -10
- package/src/ts/services/effector.ts +181 -32
- package/src/ts/services/refresher.ts +32 -7
- package/src/ts/services/resize-observer.ts +136 -19
- package/src/ts/services/select-observer.ts +115 -50
- package/src/ts/types/core/base/view.type.ts +3 -3
- package/src/ts/types/core/base/virtual-recyclerview.type.ts +1 -1
- package/src/ts/types/plugins/plugin.type.ts +46 -0
- package/src/ts/types/utils/ievents.type.ts +6 -1
- package/src/ts/types/utils/istorage.type.ts +8 -4
- package/src/ts/types/utils/libs.type.ts +2 -2
- package/src/ts/types/utils/selective.type.ts +14 -1
- package/src/ts/utils/callback-scheduler.ts +115 -37
- package/src/ts/utils/ievents.ts +91 -29
- package/src/ts/utils/libs.ts +41 -65
- package/src/ts/utils/selective.ts +412 -79
- package/src/ts/views/group-view.ts +142 -31
- 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
|
-
*
|
|
10
|
-
* for multiple-selection lists.
|
|
9
|
+
* OptionHandle
|
|
11
10
|
*
|
|
12
|
-
*
|
|
13
|
-
*
|
|
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
|
-
*
|
|
19
|
-
* -
|
|
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
|
-
*
|
|
27
|
-
*
|
|
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
|
|
52
|
+
private nodeMounted: MountViewResult | null = null;
|
|
30
53
|
|
|
31
54
|
/**
|
|
32
|
-
* Root
|
|
33
|
-
*
|
|
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
|
|
39
|
-
* (
|
|
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
|
-
*
|
|
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
|
-
*
|
|
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
|
|
94
|
+
* Creates an {@link OptionHandle}.
|
|
55
95
|
*
|
|
56
|
-
* If `options`
|
|
57
|
-
* enters the
|
|
58
|
-
*
|
|
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 -
|
|
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
|
|
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
|
-
*
|
|
71
|
-
* -
|
|
72
|
-
* -
|
|
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
|
-
*
|
|
75
|
-
* via
|
|
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: ["
|
|
129
|
+
tag: { node: "div", classList: ["seui-option-handle", "hide"] },
|
|
83
130
|
child: {
|
|
84
131
|
SelectAll: {
|
|
85
132
|
tag: {
|
|
86
133
|
node: "a",
|
|
87
|
-
classList: "
|
|
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: "
|
|
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
|
-
})
|
|
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
|
-
*
|
|
116
|
-
* based on current configuration flags.
|
|
162
|
+
* Computes whether this control is enabled/available under current configuration.
|
|
117
163
|
*
|
|
118
|
-
*
|
|
119
|
-
* -
|
|
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
|
-
*
|
|
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
|
-
*
|
|
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
|
-
*
|
|
133
|
-
* -
|
|
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
|
-
*
|
|
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
|
-
*
|
|
204
|
+
* Shows the control by removing the `hide` CSS class on the root node.
|
|
151
205
|
*
|
|
152
|
-
*
|
|
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
|
|
214
|
+
* Hides the control by adding the `hide` CSS class on the root node.
|
|
161
215
|
*
|
|
162
|
-
*
|
|
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
|
|
224
|
+
* Registers a callback for the external "Select all" intent.
|
|
171
225
|
*
|
|
172
|
-
*
|
|
173
|
-
*
|
|
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 -
|
|
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
|
|
241
|
+
* Registers a callback for the external "Deselect all" intent.
|
|
185
242
|
*
|
|
186
|
-
*
|
|
187
|
-
*
|
|
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 -
|
|
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
|
-
*
|
|
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
|
-
*
|
|
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
|
-
*
|
|
7
|
+
* PlaceHolder
|
|
8
8
|
*
|
|
9
|
-
*
|
|
10
|
-
*
|
|
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
|
-
*
|
|
13
|
-
*
|
|
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
|
|
21
|
-
*
|
|
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
|
|
27
|
-
*
|
|
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
|
|
57
|
+
* Creates a new {@link PlaceHolder}.
|
|
33
58
|
*
|
|
34
|
-
* If options
|
|
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 -
|
|
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
|
-
*
|
|
71
|
+
* Builds the placeholder DOM node and starts the lifecycle.
|
|
46
72
|
*
|
|
47
|
-
*
|
|
48
|
-
*
|
|
49
|
-
*
|
|
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 -
|
|
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: "
|
|
84
|
+
classList: "seui-placeholder",
|
|
57
85
|
innerHTML: options.placeholder,
|
|
58
|
-
})
|
|
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
|
|
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
|
|
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
|
-
*
|
|
78
|
-
*
|
|
79
|
-
* -
|
|
80
|
-
* -
|
|
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 -
|
|
83
|
-
* @param isSave -
|
|
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
|
-
*
|
|
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
|
-
*
|
|
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
|
|
8
|
+
* Lightweight UI state box that renders contextual "empty" feedback.
|
|
9
9
|
*
|
|
10
|
-
*
|
|
11
|
-
* -
|
|
12
|
-
*
|
|
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
|
-
*
|
|
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
|
|
22
|
-
*
|
|
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
|
|
28
|
-
*
|
|
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
|
|
57
|
+
* Creates a new {@link EmptyState}.
|
|
34
58
|
*
|
|
35
|
-
* If options are provided,
|
|
59
|
+
* If `options` are provided, initialization runs immediately (creates {@link node} and
|
|
60
|
+
* transitions to `INITIALIZED`).
|
|
36
61
|
*
|
|
37
|
-
* @param options - Configuration containing messages
|
|
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
|
|
70
|
+
* Initializes internal resources for this component.
|
|
47
71
|
*
|
|
48
|
-
*
|
|
49
|
-
*
|
|
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: ["
|
|
85
|
+
classList: ["seui-empty-state", "hide"],
|
|
59
86
|
role: "status",
|
|
60
87
|
ariaLive: "polite",
|
|
61
|
-
})
|
|
88
|
+
});
|
|
62
89
|
|
|
63
90
|
this.init();
|
|
64
91
|
}
|
|
65
92
|
|
|
66
93
|
/**
|
|
67
|
-
*
|
|
94
|
+
* Shows the empty state message for the given scenario.
|
|
68
95
|
*
|
|
69
|
-
*
|
|
70
|
-
* - `"
|
|
71
|
-
* - `"notfound"`: no matching search results
|
|
96
|
+
* - `"nodata"`: uses `options.textNoData`
|
|
97
|
+
* - `"notfound"`: uses `options.textNotFound`
|
|
72
98
|
*
|
|
73
|
-
* @
|
|
74
|
-
*
|
|
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
|
|
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
|
-
*
|
|
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
|
-
*
|
|
130
|
+
* Whether the empty state is currently visible.
|
|
101
131
|
*
|
|
102
|
-
* @returns
|
|
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
|
-
*
|
|
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
|
-
*
|
|
112
|
-
*
|
|
147
|
+
* @returns {void}
|
|
148
|
+
* @override
|
|
113
149
|
*/
|
|
114
150
|
public override destroy(): void {
|
|
115
151
|
if (this.is(LifecycleState.DESTROYED)) {
|