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
@@ -10,46 +10,90 @@ import { Libs } from "../utils/libs";
10
10
  import { LifecycleState } from "../types/core/base/lifecycle.type";
11
11
 
12
12
  /**
13
- * Adapter that can render a heterogeneous list composed of groups and options.
13
+ * Mixed (heterogeneous) adapter for rendering and interacting with a list that contains
14
+ * both {@link GroupModel} and {@link OptionModel} items.
14
15
  *
15
- * Responsibilities:
16
- * - Build and maintain a flat list of options for navigation and visibility stats
17
- * - Create proper views based on item type (GroupView / OptionView)
18
- * - Bind view logic (events, image/label rendering, collapsed/expanded state)
19
- * - Track selection for both single and multiple-select modes
20
- * - Emit visibility-change statistics to subscribed listeners
16
+ * ### Responsibility
17
+ * - Flatten hierarchical data (groups options) into `flatOptions` for:
18
+ * - keyboard navigation (highlight + next/prev visible),
19
+ * - visibility aggregation (visibleCount/totalCount),
20
+ * - selection helpers (getSelectedItem(s), checkAll).
21
+ * - Create the correct view implementation per item:
22
+ * - {@link GroupView} for groups,
23
+ * - {@link OptionView} for options (including options inside a group container).
24
+ * - Bind DOM events and model hooks to keep View ↔ Model synchronized:
25
+ * - click → selection changes,
26
+ * - mouseenter → highlight changes,
27
+ * - model visibility change → group visibility recalculation + debounced stats event.
21
28
  *
22
- * Lifecycle / Events:
23
- * - On `init()`: schedule a debounced visibility aggregation dispatcher
24
- * - On item changes: rebuild the flat structure and notify observers
25
- * - Delegates selection updates via the underlying `OptionModel` event hooks
29
+ * ### Lifecycle (Strict FSM, idempotency)
30
+ * - `init()` registers a debounced visibility aggregation job and then calls `mount()`.
31
+ * - View binding in `handleGroupView` / `handleOptionView` is guarded by `model.isInit`
32
+ * to avoid double-wiring listeners (idempotent binding).
33
+ * - `destroy()` clears scheduler jobs and destroys groups (cascades into their options/views),
34
+ * then transitions to `DESTROYED`. Subsequent destroy calls are **no-ops**.
26
35
  *
27
- * @extends Adapter
36
+ * ### Event / Hook flow (external vs internal)
37
+ * - **External selection**: user click triggers `changingProp("select")` then changes `OptionModel.selected`,
38
+ * and eventually emits adapter-level `changeProp("selected")` via `optionModel.onSelected`.
39
+ * - **Internal selection**: `OptionModel.selectedNonTrigger` / `onInternalSelected` updates internal state
40
+ * (e.g., cache `selectedItemSingle`) and emits adapter-level `changeProp("selected_internal")`
41
+ * without implying user intent.
42
+ *
43
+ * ### Visibility / Highlight / Navigation
44
+ * - Visibility is tracked per option model (`OptionModel.visible`), and groups can update their own
45
+ * derived visibility via `GroupModel.updateVisibility()`.
46
+ * - Highlight is tracked by flat index (`currentHighlightIndex`) and by model flag
47
+ * (`OptionModel.highlighted`), enabling view-level styling / a11y hooks.
48
+ *
49
+ * ### DOM side effects / a11y notes
50
+ * - Adds DOM listeners (`click`, `mouseenter`) on first bind only.
51
+ * - Uses `Element.scrollIntoView()` when highlighting with scrolling enabled.
52
+ * - When options are virtualized, falls back to `recyclerView.ensureRendered(i, { scrollIntoView: true })`
53
+ * before attempting to scroll.
54
+ *
55
+ * @extends {Adapter<MixedItem, GroupView | OptionView>}
56
+ * @see {@link GroupModel}
57
+ * @see {@link OptionModel}
58
+ * @see {@link GroupView}
59
+ * @see {@link OptionView}
28
60
  */
29
61
  export class MixedAdapter extends Adapter<MixedItem, GroupView | OptionView> {
30
62
  /** Whether the adapter operates in multi-selection mode. */
31
63
  public isMultiple = false;
32
64
 
33
- /** Registered listeners for aggregated visibility statistics. */
65
+ /**
66
+ * Subscribers for aggregated visibility statistics.
67
+ * Fired via a debounced scheduler to avoid repeated recomputation during batch updates.
68
+ */
34
69
  private visibilityChangedCallbacks: Array<(stats: VisibilityStats) => void> = [];
35
70
 
36
- /** Index (in the flat list) of the currently highlighted option. */
71
+ /**
72
+ * Flat index of the currently highlighted option.
73
+ * `-1` indicates "no highlight".
74
+ */
37
75
  private currentHighlightIndex = -1;
38
76
 
39
- /** Cached pointer to the single selected item in single-select mode. */
77
+ /**
78
+ * Cached pointer to the selected option in single-select mode.
79
+ * Used to efficiently clear previous selection when selecting a new option.
80
+ */
40
81
  private selectedItemSingle: OptionModel | null = null;
41
82
 
42
83
  /** Top-level group models (if any). */
43
84
  public groups: GroupModel[] = [];
44
85
 
45
- /** Flat list of all option models (including those inside groups). */
86
+ /**
87
+ * Flattened list of all option models, including options inside groups.
88
+ * This is the primary index space for navigation/highlight.
89
+ */
46
90
  public flatOptions: OptionModel[] = [];
47
91
 
48
92
  /**
49
93
  * Creates a MixedAdapter with an optional initial list of items.
50
- * Builds an initial flat structure for fast navigation and stats.
94
+ * Immediately computes `groups` and `flatOptions` for navigation/stats.
51
95
  *
52
- * @param items - Initial items (groups and/or options).
96
+ * @param {MixedItem[]} [items=[]] - Initial items (groups and/or options).
53
97
  */
54
98
  public constructor(items: MixedItem[] = []) {
55
99
  super(items);
@@ -57,11 +101,19 @@ export class MixedAdapter extends Adapter<MixedItem, GroupView | OptionView> {
57
101
  }
58
102
 
59
103
  /**
60
- * Initializes internal scheduling for visibility-change notifications,
61
- * then calls the base lifecycle and mounts immediately.
104
+ * Initializes debounced visibility aggregation and transitions lifecycle forward.
105
+ *
106
+ * - Registers `sche_vis_${adapterKey}`:
107
+ * - computes `{ visibleCount, totalCount, hasVisible, isEmpty }` from `flatOptions`,
108
+ * - notifies {@link onVisibilityChanged} subscribers,
109
+ * - triggers a proxy scheduler `sche_vis_proxy_${adapterKey}` for downstream chaining.
110
+ * - Calls base `init()` and mounts immediately.
62
111
  *
63
- * - Debounced `sche_vis_${adapterKey}` computes visibility aggregates
64
- * and invokes all registered `visibilityChangedCallbacks`.
112
+ * Idempotency:
113
+ * - Scheduler key is deterministic per adapter instance (`adapterKey`).
114
+ *
115
+ * @returns {void}
116
+ * @override
65
117
  */
66
118
  public override init() {
67
119
  Libs.callbackScheduler.on(
@@ -90,11 +142,16 @@ export class MixedAdapter extends Adapter<MixedItem, GroupView | OptionView> {
90
142
  }
91
143
 
92
144
  /**
93
- * Builds / rebuilds a flat list of options and captures group references.
145
+ * Rebuilds the derived structures:
146
+ * - `groups`: top-level {@link GroupModel} list
147
+ * - `flatOptions`: all {@link OptionModel} instances in traversal order
94
148
  *
95
149
  * The flat list is used for:
96
- * - navigation across visible options
97
- * - computing visibility statistics quickly
150
+ * - navigation across visible options,
151
+ * - computing visibility statistics,
152
+ * - highlight index mapping.
153
+ *
154
+ * @returns {void}
98
155
  */
99
156
  private buildFlatStructure(): void {
100
157
  this.flatOptions = [];
@@ -111,11 +168,12 @@ export class MixedAdapter extends Adapter<MixedItem, GroupView | OptionView> {
111
168
  }
112
169
 
113
170
  /**
114
- * Factory method returning the appropriate view implementation per item type.
171
+ * Creates the appropriate view instance for a given item.
115
172
  *
116
- * @param parent - The container element where the view will be mounted.
117
- * @param item - The item to render (group or option).
118
- * @returns A new GroupView/OptionView instance based on the item type.
173
+ * @param {HTMLElement} parent - Container element where the view will be mounted.
174
+ * @param {MixedItem} item - The item to render (group or option).
175
+ * @returns {GroupView | OptionView} A view instance matching the item type.
176
+ * @override
119
177
  */
120
178
  override viewHolder(parent: HTMLElement, item: MixedItem): GroupView | OptionView {
121
179
  if (item instanceof GroupModel) return new GroupView(parent);
@@ -123,12 +181,17 @@ export class MixedAdapter extends Adapter<MixedItem, GroupView | OptionView> {
123
181
  }
124
182
 
125
183
  /**
126
- * Binds a data model (group or option) to its view and delegates rendering
127
- * to specialized handlers.
184
+ * Binds a model (group or option) to its view and delegates to specialized handlers.
128
185
  *
129
- * @param item - GroupModel or OptionModel.
130
- * @param viewer - The view instance that will render the model.
131
- * @param position - Position of the item in the top-level list.
186
+ * Notes:
187
+ * - Assigns `item.position` in the top-level `items` list (not the `flatOptions` index).
188
+ * - Performs one-time listener binding guarded by `item.isInit`.
189
+ *
190
+ * @param {MixedItem} item - {@link GroupModel} or {@link OptionModel}.
191
+ * @param {GroupView | OptionView | null} viewer - The view instance that will render the model.
192
+ * @param {number} position - Position in the top-level mixed list.
193
+ * @returns {void}
194
+ * @override
132
195
  */
133
196
  override onViewHolder(item: MixedItem, viewer: GroupView | OptionView | null, position: number): void {
134
197
  item.position = position;
@@ -143,15 +206,24 @@ export class MixedAdapter extends Adapter<MixedItem, GroupView | OptionView> {
143
206
  }
144
207
 
145
208
  /**
146
- * Handles binding/rendering for a group:
147
- * - Sets header text and click-to-toggle behavior
148
- * - Observes collapsed state to hide/show child options
149
- * - Ensures each child option is rendered and bound
150
- * - Syncs collapsed state and visibility
151
- *
152
- * @param groupModel - Group data model.
153
- * @param groupView - Group view instance.
154
- * @param position - Group index.
209
+ * Binds / renders a group header and its option children.
210
+ *
211
+ * Responsibilities:
212
+ * - Set header label and click-to-toggle behavior (one-time).
213
+ * - Observe collapsed state:
214
+ * - toggles child option DOM display,
215
+ * - invokes {@link onCollapsedChange} hook.
216
+ * - Ensure each child option has a view and is bound via {@link handleOptionView}.
217
+ * - Sync collapsed UI and derived visibility for the group view.
218
+ *
219
+ * DOM side effects:
220
+ * - Adds a click listener to the group header (only once).
221
+ * - Updates child option view element `style.display` on collapse changes.
222
+ *
223
+ * @param {GroupModel} groupModel - Group data model.
224
+ * @param {GroupView} groupView - Group view instance.
225
+ * @param {number} position - Group index in the top-level list.
226
+ * @returns {void}
155
227
  */
156
228
  private handleGroupView(groupModel: GroupModel, groupView: GroupView, position: number): void {
157
229
  super.onViewHolder(groupModel, groupView, position);
@@ -196,15 +268,32 @@ export class MixedAdapter extends Adapter<MixedItem, GroupView | OptionView> {
196
268
  }
197
269
 
198
270
  /**
199
- * Handles binding/rendering for an option:
200
- * - Applies adapter-wide and model-specific visual configuration (image, label alignment, etc.)
201
- * - Wires click/hover listeners for selection and highlighting
202
- * - Syncs selection state (single vs multiple)
203
- * - Updates image source/alt and label HTML
204
- *
205
- * @param optionModel - Option data model.
206
- * @param optionViewer - Option view instance.
207
- * @param position - Option index within its group list.
271
+ * Binds / renders an option row and wires selection/highlight/visibility behavior.
272
+ *
273
+ * Responsibilities:
274
+ * - Apply visual configuration from the model options (image sizing/position, label alignment).
275
+ * - Render image (src/alt) and label HTML.
276
+ * - Wire DOM events (one-time):
277
+ * - click selection (single/multiple),
278
+ * - mouseenter highlight.
279
+ * - Wire model hooks (one-time):
280
+ * - `onSelected` → `changeProp("selected")` (external semantics),
281
+ * - `onInternalSelected` → cache single selected + `changeProp("selected_internal")` (internal semantics),
282
+ * - `onVisibilityChanged` → group visibility recompute + debounced visibility stats.
283
+ *
284
+ * Selection semantics:
285
+ * - Multi-select: toggles `selected` for the clicked option.
286
+ * - Single-select: clears previous selected option (if cached) and selects the clicked one.
287
+ * - Both paths run `changingProp("select")` before mutating selection when not skipping events.
288
+ *
289
+ * DOM side effects:
290
+ * - Adds listeners to `OptionView` element only on first bind.
291
+ * - Updates image and label DOM each bind.
292
+ *
293
+ * @param {OptionModel} optionModel - Option data model.
294
+ * @param {OptionView} optionViewer - Option view instance.
295
+ * @param {number} position - Option index within its group list (or rendering context).
296
+ * @returns {void}
208
297
  */
209
298
  private handleOptionView(optionModel: OptionModel, optionViewer: OptionView, position: number): void {
210
299
  optionViewer.isMultiple = this.isMultiple;
@@ -226,13 +315,14 @@ export class MixedAdapter extends Adapter<MixedItem, GroupView | OptionView> {
226
315
  optionModel.view = optionViewer;
227
316
 
228
317
  if (optionModel.hasImage) {
229
- const imageTag = optionViewer.view.tags.OptionImage as HTMLImageElement | null;
318
+ const imageTag = optionViewer.view.tags.OptionImage;
230
319
  if (imageTag) {
231
320
  if (imageTag.src !== optionModel.imageSrc) imageTag.src = optionModel.imageSrc;
232
321
  if (imageTag.alt !== optionModel.text) imageTag.alt = optionModel.text;
233
322
  }
234
323
  }
235
324
 
325
+ // Label uses HTML to support rich content; consumers must ensure the model text is safe.
236
326
  optionViewer.view.tags.LabelContent.innerHTML = optionModel.text;
237
327
 
238
328
  if (!optionModel.isInit) {
@@ -259,21 +349,25 @@ export class MixedAdapter extends Adapter<MixedItem, GroupView | OptionView> {
259
349
  this.setHighlight(this.flatOptions.indexOf(optionModel), false);
260
350
  });
261
351
 
352
+ // External selection notification (user-facing semantics).
262
353
  optionModel.onSelected((_evtToken: IEventCallback, _el: OptionModel, _selected: boolean) => {
263
354
  this.changeProp("selected");
264
355
  });
265
356
 
357
+ // Internal selection notification (non-trigger semantics).
266
358
  optionModel.onInternalSelected((_evtToken: IEventCallback, _el: OptionModel, selected: boolean) => {
267
359
  if (selected) this.selectedItemSingle = optionModel;
268
360
  this.changeProp("selected_internal");
269
361
  });
270
362
 
363
+ // Visibility changes affect group visibility and aggregated visibility stats.
271
364
  optionModel.onVisibilityChanged((_evtToken: IEventCallback, model: OptionModel, _visible: boolean) => {
272
365
  model.group?.updateVisibility();
273
366
  this.notifyVisibilityChanged();
274
367
  });
275
368
  }
276
369
 
370
+ // Ensure single-select cache and suppress re-trigger when model is already selected.
277
371
  if (optionModel.selected) {
278
372
  this.selectedItemSingle = optionModel;
279
373
  optionModel.selectedNonTrigger = true;
@@ -281,10 +375,17 @@ export class MixedAdapter extends Adapter<MixedItem, GroupView | OptionView> {
281
375
  }
282
376
 
283
377
  /**
284
- * Replaces items and rebuilds the internal flat structure.
285
- * Emits the standard pre/post change notifications and updates lifecycle.
378
+ * Replaces items and rebuilds derived structures with full change notifications.
379
+ *
380
+ * Flow:
381
+ * - `changingProp("items", items)` (pre-change pipeline)
382
+ * - assign `this.items`, rebuild `groups`/`flatOptions`
383
+ * - `changeProp("items", items)` (post-change pipeline)
384
+ * - {@link Lifecycle.update}
286
385
  *
287
- * @param items - New mixed item collection (groups/options).
386
+ * @param {MixedItem[]} items - New mixed item collection (groups/options).
387
+ * @returns {Promise<void>}
388
+ * @override
288
389
  */
289
390
  override async setItems(items: MixedItem[]): Promise<void> {
290
391
  await this.changingProp("items", items);
@@ -295,18 +396,23 @@ export class MixedAdapter extends Adapter<MixedItem, GroupView | OptionView> {
295
396
  }
296
397
 
297
398
  /**
298
- * Synchronizes items from an external source by delegating to `setItems()`.
399
+ * Synchronizes items from an external source by delegating to {@link setItems}.
299
400
  *
300
- * @param items - New mixed item collection (groups/options).
401
+ * @param {MixedItem[]} items - New mixed item collection (groups/options).
402
+ * @returns {Promise<void>}
403
+ * @override
301
404
  */
302
405
  override async syncFromSource(items: MixedItem[]): Promise<void> {
303
406
  await this.setItems(items);
304
407
  }
305
408
 
306
409
  /**
307
- * Updates items and rebuilds the flat structure **without** firing change notifications.
410
+ * Updates items and rebuilds derived structures **without** emitting change notifications.
411
+ * Useful for internal reconciliation where observers should not be notified.
308
412
  *
309
- * @param items - New mixed item collection (groups/options).
413
+ * @param {MixedItem[]} items - New mixed item collection (groups/options).
414
+ * @returns {void}
415
+ * @override
310
416
  */
311
417
  override updateData(items: MixedItem[]): void {
312
418
  this.items = items;
@@ -315,10 +421,18 @@ export class MixedAdapter extends Adapter<MixedItem, GroupView | OptionView> {
315
421
  }
316
422
 
317
423
  /**
318
- * Destroys the adapter and cleans up:
319
- * - Clears visibility scheduler
320
- * - Destroys all groups (cascades to their items/views)
321
- * - Resets cached state and arrays
424
+ * Releases adapter resources and clears derived state.
425
+ *
426
+ * Behavior:
427
+ * - Clears visibility scheduler task (`sche_vis_${adapterKey}`).
428
+ * - Destroys all group models (which may cascade to child models/views).
429
+ * - Resets cached selection/highlight and subscriber lists.
430
+ *
431
+ * Idempotent:
432
+ * - Returns early if already in {@link LifecycleState.DESTROYED}.
433
+ *
434
+ * @returns {void}
435
+ * @override
322
436
  */
323
437
  public override destroy(): void {
324
438
  if (this.is(LifecycleState.DESTROYED)) {
@@ -341,27 +455,30 @@ export class MixedAdapter extends Adapter<MixedItem, GroupView | OptionView> {
341
455
  }
342
456
 
343
457
  /**
344
- * Returns all currently selected option items.
458
+ * Returns all currently selected options from the flat list.
345
459
  *
346
- * @returns Array of selected options from the flat list.
460
+ * @returns {OptionModel[]} Selected options.
347
461
  */
348
462
  public getSelectedItems(): OptionModel[] {
349
463
  return this.flatOptions.filter((item) => item.selected);
350
464
  }
351
465
 
352
466
  /**
353
- * Returns the first selected option (if any).
467
+ * Returns the first selected option from the flat list (if any).
468
+ * Primarily useful for single-select mode.
354
469
  *
355
- * @returns The first selected option; `undefined` if none.
470
+ * @returns {OptionModel | undefined} The first selected option, or `undefined` if none.
356
471
  */
357
472
  public getSelectedItem(): OptionModel | undefined {
358
473
  return this.flatOptions.find((item) => item.selected);
359
474
  }
360
475
 
361
476
  /**
362
- * Checks/unchecks all options when in multiple selection mode.
477
+ * Selects or deselects all options when in multiple selection mode.
478
+ * No-op if `isMultiple` is false.
363
479
  *
364
- * @param isChecked - `true` to select all; `false` to deselect all.
480
+ * @param {boolean} isChecked - `true` to select all; `false` to deselect all.
481
+ * @returns {void}
365
482
  */
366
483
  public checkAll(isChecked: boolean): void {
367
484
  if (!this.isMultiple) return;
@@ -371,16 +488,20 @@ export class MixedAdapter extends Adapter<MixedItem, GroupView | OptionView> {
371
488
  }
372
489
 
373
490
  /**
374
- * Subscribes a callback to visibility changes across options.
491
+ * Subscribes to aggregated visibility changes across all options.
492
+ * The callback is invoked from a debounced scheduler.
375
493
  *
376
- * @param callback - Invoked with aggregated visibility stats.
494
+ * @param {(stats: VisibilityStats) => void} callback - Invoked with `{ visibleCount, totalCount, hasVisible, isEmpty }`.
495
+ * @returns {void}
377
496
  */
378
497
  public onVisibilityChanged(callback: (stats: VisibilityStats) => void): void {
379
498
  this.visibilityChangedCallbacks.push(callback);
380
499
  }
381
500
 
382
501
  /**
383
- * Schedules a visibility statistics recomputation and notifies subscribers.
502
+ * Schedules a debounced visibility statistics recomputation and subscriber notification.
503
+ *
504
+ * @returns {void}
384
505
  */
385
506
  private notifyVisibilityChanged(): void {
386
507
  Libs.callbackScheduler.run(`sche_vis_${this.adapterKey}`);
@@ -389,7 +510,7 @@ export class MixedAdapter extends Adapter<MixedItem, GroupView | OptionView> {
389
510
  /**
390
511
  * Computes and returns current visibility statistics for options.
391
512
  *
392
- * @returns Aggregated stats: `{ visibleCount, totalCount, hasVisible, isEmpty }`.
513
+ * @returns {VisibilityStats} Aggregated stats: `{ visibleCount, totalCount, hasVisible, isEmpty }`.
393
514
  */
394
515
  public getVisibilityStats(): VisibilityStats {
395
516
  const visibleCount = this.flatOptions.filter((item) => item.visible).length;
@@ -404,17 +525,23 @@ export class MixedAdapter extends Adapter<MixedItem, GroupView | OptionView> {
404
525
  }
405
526
 
406
527
  /**
407
- * Resets the highlight to the first visible option.
528
+ * Resets highlight navigation to the first visible option (starting from index 0).
529
+ *
530
+ * @returns {void}
408
531
  */
409
532
  public resetHighlight(): void {
410
533
  this.setHighlight(0);
411
534
  }
412
535
 
413
536
  /**
414
- * Moves the highlight among visible options and optionally scrolls into view.
537
+ * Moves highlight among **visible** options and optionally scrolls the new target into view.
538
+ *
539
+ * - Wraps around at both ends (circular navigation).
540
+ * - Uses the current highlight (flat index) as the starting point.
415
541
  *
416
- * @param direction - +1 to move forward; -1 to move backward.
417
- * @param isScrollToView - Whether to scroll the highlighted item into view. Defaults to `true`.
542
+ * @param {number} direction - `+1` to move forward; `-1` to move backward.
543
+ * @param {boolean} [isScrollToView=true] - Whether to scroll the highlighted item into view.
544
+ * @returns {void}
418
545
  */
419
546
  public navigate(direction: number, isScrollToView: boolean = true): void {
420
547
  const visibleOptions = this.flatOptions.filter((opt) => opt.visible);
@@ -436,8 +563,18 @@ export class MixedAdapter extends Adapter<MixedItem, GroupView | OptionView> {
436
563
  }
437
564
 
438
565
  /**
439
- * Triggers a click on the currently highlighted, visible option.
440
- * No-ops if nothing is highlighted or the item is hidden.
566
+ * Programmatically selects (clicks) the currently highlighted option if it is visible.
567
+ *
568
+ * DOM side effects:
569
+ * - Calls `HTMLElement.click()` on the rendered option view element.
570
+ *
571
+ * No-op if:
572
+ * - No highlight is set,
573
+ * - Highlighted item does not exist,
574
+ * - Highlighted item is not visible,
575
+ * - View element is not available (e.g., not rendered).
576
+ *
577
+ * @returns {void}
441
578
  */
442
579
  public selectHighlighted(): void {
443
580
  if (this.currentHighlightIndex > -1 && this.flatOptions[this.currentHighlightIndex]) {
@@ -450,11 +587,20 @@ export class MixedAdapter extends Adapter<MixedItem, GroupView | OptionView> {
450
587
  }
451
588
 
452
589
  /**
453
- * Highlights a target option (by flat index or model reference),
454
- * skipping invisible items and optionally scrolling into view.
590
+ * Highlights a target option (by flat index or model reference), skipping invisible items.
591
+ * Optionally scrolls the highlighted item into view.
592
+ *
593
+ * Behavior:
594
+ * - Clears previous highlight (if any) by toggling `OptionModel.highlighted = false`.
595
+ * - Starting from the resolved index, finds the first visible option and highlights it.
596
+ * - If scrolling is enabled:
597
+ * - scrolls the DOM element when available, otherwise
598
+ * - asks the recycler to render the item and scroll into view (virtualized lists).
599
+ * - Invokes {@link onHighlightChange} hook after applying highlight.
455
600
  *
456
- * @param target - Flat index or OptionModel instance to highlight.
457
- * @param isScrollToView - Whether to scroll the highlighted item into view. Defaults to `true`.
601
+ * @param {number | OptionModel} target - Flat index or option model to highlight.
602
+ * @param {boolean} [isScrollToView=true] - Whether to scroll the highlighted item into view.
603
+ * @returns {void}
458
604
  */
459
605
  public setHighlight(target: number | OptionModel, isScrollToView: boolean = true): void {
460
606
  let index = 0;
@@ -495,20 +641,30 @@ export class MixedAdapter extends Adapter<MixedItem, GroupView | OptionView> {
495
641
  }
496
642
 
497
643
  /**
498
- * Hook invoked whenever the highlighted item changes.
499
- * Override to handle UI side effects (e.g., ARIA announcement, focus sync).
644
+ * Hook called whenever highlight changes.
500
645
  *
501
- * @param index - Flat index of the newly highlighted item.
502
- * @param id - Optional DOM id of the highlighted view element.
646
+ * Intended for UI side effects that do not belong in core adapter logic, e.g.:
647
+ * - roving tabindex / focus synchronization,
648
+ * - ARIA live region announcements,
649
+ * - analytics/navigation instrumentation.
650
+ *
651
+ * @param {number} index - Flat index of the newly highlighted option.
652
+ * @param {string} [id] - Optional DOM id of the highlighted view element (when available).
653
+ * @returns {void}
503
654
  */
504
655
  public onHighlightChange(index: number, id?: string): void { }
505
656
 
506
657
  /**
507
- * Hook invoked whenever a group's collapsed state changes.
508
- * Override to handle side effects (e.g., analytics, layout adjustments).
658
+ * Hook called whenever a group's collapsed state changes.
659
+ *
660
+ * Intended for integration side effects, e.g.:
661
+ * - layout recalculation,
662
+ * - popup resize,
663
+ * - analytics.
509
664
  *
510
- * @param model - The group whose collapsed state changed.
511
- * @param collapsed - New collapsed state.
665
+ * @param {GroupModel} model - The group whose collapsed state changed.
666
+ * @param {boolean} collapsed - New collapsed state.
667
+ * @returns {void}
512
668
  */
513
669
  public onCollapsedChange(model: GroupModel, collapsed: boolean): void { }
514
670
  }