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
@@ -1,41 +1,106 @@
1
-
2
1
  import { View } from "../core/base/view";
3
2
  import { Libs } from "../utils/libs";
4
3
  import type { GroupViewTags, GroupViewResult } from "../types/views/view.group.type";
5
4
 
6
5
  /**
7
- * View implementation responsible for rendering and managing
8
- * a grouped collection of selectable items.
6
+ * GroupView
7
+ *
8
+ * View implementation for rendering grouped collections of selectable items.
9
+ *
10
+ * ### Responsibility
11
+ * - Renders a semantic group structure: header (label) + items container.
12
+ * - Manages group-level visibility based on child item state.
13
+ * - Supports collapse/expand interactions with accessibility annotations.
14
+ * - Provides typed access to DOM structure via {@link view}.
15
+ *
16
+ * ### Structure
17
+ * ```
18
+ * GroupView (root)
19
+ * ├─ GroupHeader (label, role="presentation")
20
+ * └─ GroupItems (container, role="group")
21
+ * ```
22
+ *
23
+ * ### Lifecycle (View-based FSM)
24
+ * - **Construction**: Accepts parent container, transitions `NEW → INITIALIZED`.
25
+ * - **{@link mount}**: Creates DOM structure, appends to parent, transitions `INITIALIZED → MOUNTED`.
26
+ * - **{@link update}**: Refreshes group header label, transitions `MOUNTED → UPDATED → MOUNTED`.
27
+ * - **{@link destroy}**: Removes DOM nodes, transitions to `DESTROYED`.
28
+ *
29
+ * ### Visibility semantics
30
+ * - {@link updateVisibility} hides the entire group when all child items are hidden.
31
+ * - Checks for `"hide"` class on children (does not inspect `display` or `visibility` styles).
9
32
  *
10
- * The group consists of:
11
- * - A header element (label)
12
- * - A container holding child item views
33
+ * ### Accessibility
34
+ * - Root container: `role="group"`, `aria-labelledby` points to header.
35
+ * - Header: `role="presentation"`, unique ID for labeling.
36
+ * - Items container: `role="group"` (nested group).
37
+ * - Collapse state: `aria-expanded` attribute on header (managed by {@link setCollapsed}).
38
+ *
39
+ * ### DOM side effects
40
+ * - {@link mount} creates and appends DOM structure.
41
+ * - {@link updateLabel} mutates header `textContent`.
42
+ * - {@link setCollapsed} toggles CSS classes and ARIA attributes.
43
+ * - {@link updateVisibility} toggles `"hide"` class on root.
44
+ *
45
+ * ### No-op / Idempotency
46
+ * - {@link updateLabel}, {@link updateVisibility}, {@link setCollapsed} are no-ops if not mounted (early return guards).
47
+ * - Safe to call multiple times without side effects beyond DOM state updates.
13
48
  *
14
49
  * @extends View<GroupViewTags>
50
+ * @template GroupViewTags - Type descriptor for the group's DOM structure.
51
+ * @see {@link GroupViewResult}
52
+ * @see {@link View}
15
53
  */
16
54
  export class GroupView extends View<GroupViewTags> {
17
55
 
18
56
  /**
19
57
  * Strongly-typed reference to the mounted group view structure.
20
- * Will be null until the view has been mounted.
58
+ *
59
+ * Structure:
60
+ * - **view**: Root container element.
61
+ * - **tags**: Named references to header and items container.
62
+ *
63
+ * Lifecycle:
64
+ * - `null` until {@link mount} completes.
65
+ * - Cleared during {@link destroy}.
66
+ *
67
+ * @public
21
68
  */
22
69
  public view: GroupViewResult | null = null;
23
70
 
24
71
  /**
25
72
  * Mounts the group view into the DOM.
26
73
  *
27
- * Creates the group container, header, and items wrapper,
28
- * applies required ARIA attributes, and appends the root
29
- * element to the parent container.
74
+ * Creation flow:
75
+ * 1. Generates unique group ID (7-character random string).
76
+ * 2. Creates DOM structure via {@link Libs.mountNode}:
77
+ * - Root: `<div role="group" aria-labelledby="seui-{id}-header">`
78
+ * - Header: `<div role="presentation" id="seui-{id}-header">`
79
+ * - Items: `<div role="group">` (nested group for child items)
80
+ * 3. Appends root to {@link parent} container.
81
+ * 4. Transitions `INITIALIZED → MOUNTED` via `super.mount()`.
82
+ *
83
+ * Accessibility setup:
84
+ * - Root `aria-labelledby` associates group with header text.
85
+ * - Header `role="presentation"` hides it from navigation (purely visual label).
86
+ * - Items container `role="group"` creates semantic boundary for children.
87
+ *
88
+ * Postcondition:
89
+ * - {@link view} is populated with typed DOM references.
90
+ *
91
+ * @public
92
+ * @returns {void}
93
+ * @override
94
+ * @throws {Error} If {@link parent} is null (should never occur due to base `View` constructor).
30
95
  */
31
96
  public override mount(): void {
32
97
  const group_id = Libs.randomString(7);
33
98
 
34
- this.view = Libs.mountView<GroupViewTags>({
99
+ this.view = Libs.mountNode<GroupViewResult>({
35
100
  GroupView: {
36
101
  tag: {
37
102
  node: "div",
38
- classList: ["selective-ui-group"],
103
+ classList: ["seui-group"],
39
104
  role: "group",
40
105
  ariaLabelledby: `seui-${group_id}-header`,
41
106
  id: `seui-${group_id}-group`,
@@ -44,7 +109,7 @@ export class GroupView extends View<GroupViewTags> {
44
109
  GroupHeader: {
45
110
  tag: {
46
111
  node: "div",
47
- classList: ["selective-ui-group-header"],
112
+ classList: ["seui-group-header"],
48
113
  role: "presentation",
49
114
  id: `seui-${group_id}-header`,
50
115
  },
@@ -52,13 +117,13 @@ export class GroupView extends View<GroupViewTags> {
52
117
  GroupItems: {
53
118
  tag: {
54
119
  node: "div",
55
- classList: ["selective-ui-group-items"],
120
+ classList: ["seui-group-items"],
56
121
  role: "group",
57
122
  },
58
123
  },
59
124
  },
60
125
  },
61
- }) as GroupViewResult;
126
+ });
62
127
 
63
128
  // Parent is guaranteed to exist by the base View constructor.
64
129
  this.parent!.appendChild(this.view.view);
@@ -67,10 +132,19 @@ export class GroupView extends View<GroupViewTags> {
67
132
  }
68
133
 
69
134
  /**
70
- * Called when the view needs to be updated.
135
+ * Updates the group view in response to state changes.
136
+ *
137
+ * Behavior:
138
+ * - Refreshes the group header label via {@link updateLabel}.
139
+ * - Transitions `MOUNTED → UPDATED → MOUNTED` via `super.update()`.
71
140
  *
72
- * Currently performs a lightweight refresh by updating
73
- * the group header label.
141
+ * Notes:
142
+ * - Currently performs only label refresh; extend for additional update logic.
143
+ * - Does **not** update visibility or collapse state automatically.
144
+ *
145
+ * @public
146
+ * @returns {void}
147
+ * @override
74
148
  */
75
149
  public override update(): void {
76
150
  this.updateLabel();
@@ -80,8 +154,18 @@ export class GroupView extends View<GroupViewTags> {
80
154
  /**
81
155
  * Updates the text content of the group header.
82
156
  *
83
- * @param label - The new label to display.
84
- * If null, the existing label is preserved.
157
+ * Behavior:
158
+ * - No-op if not mounted ({@link view} is `null`).
159
+ * - If `label` is `null`, preserves existing header text.
160
+ * - Otherwise, replaces header `textContent` with new label.
161
+ *
162
+ * Notes:
163
+ * - Does **not** escape HTML (uses `textContent`, not `innerHTML`).
164
+ * - Safe to call multiple times with same value (idempotent).
165
+ *
166
+ * @public
167
+ * @param {string | null} [label=null] - New label to display; `null` preserves current label.
168
+ * @returns {void}
85
169
  */
86
170
  public updateLabel(label: string | null = null): void {
87
171
  if (!this.view) return;
@@ -93,11 +177,15 @@ export class GroupView extends View<GroupViewTags> {
93
177
  }
94
178
 
95
179
  /**
96
- * Returns the container element that holds all item/option views
97
- * belonging to this group.
180
+ * Returns the container element for child item views.
98
181
  *
99
- * @returns The HTMLDivElement used as the items container.
100
- * @throws {Error} If the view has not been mounted yet.
182
+ * Usage:
183
+ * - Caller appends `OptionView` or other child views to this container.
184
+ * - Container provides semantic grouping (`role="group"`).
185
+ *
186
+ * @public
187
+ * @returns {HTMLDivElement} The items container element.
188
+ * @throws {Error} If the view has not been mounted yet ({@link view} is `null`).
101
189
  */
102
190
  public getItemsContainer(): HTMLDivElement {
103
191
  if (!this.view) {
@@ -107,10 +195,22 @@ export class GroupView extends View<GroupViewTags> {
107
195
  }
108
196
 
109
197
  /**
110
- * Updates the visibility of the group based on its children.
198
+ * Updates the group's visibility based on child item state.
199
+ *
200
+ * Visibility rules:
201
+ * - Iterates through direct children of the items container.
202
+ * - Counts children **without** the `"hide"` CSS class.
203
+ * - Toggles `"hide"` class on root container:
204
+ * - **Added** if all children are hidden (zero visible).
205
+ * - **Removed** if any child is visible.
206
+ *
207
+ * Notes:
208
+ * - No-op if not mounted ({@link view} is `null`).
209
+ * - Only checks for `"hide"` class; does **not** inspect `display` or `visibility` styles.
210
+ * - Safe to call repeatedly (idempotent based on current child state).
111
211
  *
112
- * If all child items are hidden, the entire group
113
- * will be hidden as well.
212
+ * @public
213
+ * @returns {void}
114
214
  */
115
215
  public updateVisibility(): void {
116
216
  if (!this.view) return;
@@ -126,11 +226,22 @@ export class GroupView extends View<GroupViewTags> {
126
226
  /**
127
227
  * Sets the collapsed/expanded state of the group.
128
228
  *
129
- * This updates both:
130
- * - Visual state (CSS classes)
131
- * - Accessibility state (ARIA attributes)
229
+ * State updates:
230
+ * - **CSS**: Toggles `"collapsed"` class on root container.
231
+ * - **ARIA**: Sets `aria-expanded` attribute on header (`"true"` or `"false"`).
232
+ *
233
+ * Visual effects:
234
+ * - CSS class typically controls item container visibility (via stylesheet).
235
+ * - ARIA attribute communicates state to assistive technologies.
236
+ *
237
+ * Notes:
238
+ * - No-op if not mounted ({@link view} is `null`).
239
+ * - Does **not** animate or transition; relies on CSS for presentation.
240
+ * - Safe to call with same value repeatedly (idempotent).
132
241
  *
133
- * @param collapsed - True to collapse the group, false to expand it.
242
+ * @public
243
+ * @param {boolean} collapsed - `true` to collapse the group; `false` to expand.
244
+ * @returns {void}
134
245
  */
135
246
  public setCollapsed(collapsed: boolean): void {
136
247
  if (!this.view) return;