@keenmate/web-multiselect 1.12.0-rc04 → 1.12.0-rc06

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/dist/index.d.ts CHANGED
@@ -11,6 +11,13 @@ declare interface ActionButton<T = any> {
11
11
  text: string;
12
12
  /** Optional CSS class(es) to add to the button */
13
13
  cssClass?: string;
14
+ /**
15
+ * 1-based row this button belongs to (default `1`). Buttons sharing a `row` render on the same
16
+ * horizontal line; different values stack into multiple rows. Row 1 sits at the panel's outer edge
17
+ * and higher rows stack inward toward the options list — so with `actions-position="top"` row 1 is
18
+ * the topmost line, and with `actions-position="bottom"` row 1 is the bottommost line.
19
+ */
20
+ row?: number;
14
21
  /** Optional tooltip text */
15
22
  tooltip?: string;
16
23
  /** Static visibility - set to false to hide button */
@@ -20,9 +27,9 @@ declare interface ActionButton<T = any> {
20
27
  /** Custom click handler (required for 'custom' action) */
21
28
  onClick?: (multiselect: any) => void | Promise<void>;
22
29
  /** Dynamic visibility callback - return false to hide button (takes priority over isVisible) */
23
- isVisibleCallback?: (multiselect: any) => boolean;
30
+ getIsVisibleCallback?: (multiselect: any) => boolean;
24
31
  /** Dynamic disabled state callback - return true to disable button (takes priority over isDisabled) */
25
- isDisabledCallback?: (multiselect: any) => boolean;
32
+ getIsDisabledCallback?: (multiselect: any) => boolean;
26
33
  /** Dynamic text callback - return button text (takes priority over text) */
27
34
  getTextCallback?: (multiselect: any) => string;
28
35
  /** Dynamic CSS class callback - return class name(s) (takes priority over cssClass) */
@@ -31,6 +38,9 @@ declare interface ActionButton<T = any> {
31
38
  getTooltipCallback?: (multiselect: any) => string;
32
39
  }
33
40
 
41
+ /** Horizontal arrangement of buttons within an action row. `stretch` = full-width (default). */
42
+ declare type ActionsAlign = 'stretch' | 'left' | 'right' | 'center' | 'space-between';
43
+
34
44
  /**
35
45
  * Layout mode for action buttons container
36
46
  * - 'nowrap': Buttons stay in single row (default)
@@ -38,6 +48,9 @@ declare interface ActionButton<T = any> {
38
48
  */
39
49
  declare type ActionsLayout = 'nowrap' | 'wrap';
40
50
 
51
+ /** Where the action-buttons block sits in the dropdown panel. */
52
+ declare type ActionsPosition = 'top' | 'bottom';
53
+
41
54
  /**
42
55
  * Context provided to renderBadgeContentCallback
43
56
  */
@@ -107,6 +120,31 @@ export declare const interactionLogger: any;
107
120
  */
108
121
  export declare const LOGGING_CATEGORIES: string[];
109
122
 
123
+ declare interface LTreeNode<T> {
124
+ treeId: string;
125
+ id: NodeId;
126
+ /** Materialized dot-path, e.g. "1.2.3". */
127
+ path: string;
128
+ /** This node's own segment relative to its parent. */
129
+ pathSegment: string;
130
+ parentPath: string | null | undefined;
131
+ /** Depth in the tree (1-based; root children are level 1). */
132
+ level: number | null | undefined;
133
+ /** Children keyed by prefixed segment (see `segmentPrefix` in ltree.ts). */
134
+ children: Record<string, LTreeNode<T>>;
135
+ hasChildren: boolean;
136
+ /**
137
+ * Whether this node may be selected. Defaults to `true`; set `false` (via
138
+ * `isSelectableMember`/`getIsSelectableCallback`) to make a node non-interactive
139
+ * — it renders normally (no grey/disabled styling) but has no checkbox, is
140
+ * skipped by keyboard focus, and cannot be toggled or selected by Select-All.
141
+ * This is distinct from `disabled` (which greys the row out).
142
+ */
143
+ isSelectable: boolean;
144
+ /** The original option object this node was built from. */
145
+ data: T | null | undefined;
146
+ }
147
+
110
148
  /**
111
149
  * Generic configuration options for the MultiSelect component
112
150
  * @template T The type of data items
@@ -124,6 +162,15 @@ declare interface MultiSelectConfig<T = any> {
124
162
  getDisplayValueCallback?: (item: T) => string;
125
163
  /** Callback to customize badge display text (defaults to display value if not provided) */
126
164
  getBadgeDisplayCallback?: (item: T) => string;
165
+ /**
166
+ * Member property name for a "full title" — a fully-qualified label that ships with the
167
+ * data (e.g. a breadcrumb like "Fruit / Pome fruit / Apple"). It is never computed by the
168
+ * component. When `isBadgeFullTitleShown` is on, badges display this instead of the display
169
+ * value (falling back to the display value when an option has none).
170
+ */
171
+ fullTitleMember?: string;
172
+ /** Callback to extract the full title from an item (takes precedence over `fullTitleMember`). */
173
+ getFullTitleCallback?: (item: T) => string;
127
174
  /** Callback to add custom CSS classes to badges - return string or array of class names */
128
175
  getBadgeClassCallback?: (item: T) => string | string[];
129
176
  /** Callback to inject custom CSS into Shadow DOM - return CSS string for styling custom classes */
@@ -140,6 +187,55 @@ declare interface MultiSelectConfig<T = any> {
140
187
  subtitleMember?: string;
141
188
  /** Callback to extract subtitle from item */
142
189
  getSubtitleCallback?: (item: T) => string;
190
+ /**
191
+ * Enable tree mode: options are rendered as a hierarchy, indented by depth.
192
+ * Auto-enabled when a path source (`pathMember`/`getPathCallback`) is set;
193
+ * pass `false` to force it off. The tree is always fully expanded — there is
194
+ * no collapse (use @keenmate/web-treeview if you need expand/collapse).
195
+ */
196
+ isTreeEnabled?: boolean;
197
+ /** Member property name holding each option's materialized dot-path (e.g. "1.2.3"). */
198
+ pathMember?: string;
199
+ /** Callback returning an option's materialized dot-path (takes precedence over pathMember). */
200
+ getPathCallback?: (item: T) => string;
201
+ /** Member holding an option's parent path (otherwise derived from its path). */
202
+ parentPathMember?: string;
203
+ /** Member holding an option's depth/level (otherwise derived from its path). */
204
+ levelMember?: string;
205
+ /** Member holding a precomputed hasChildren flag (otherwise derived from the tree). */
206
+ hasChildrenMember?: string;
207
+ /** Path separator for tree paths. Default: "." */
208
+ treePathSeparator?: string;
209
+ /**
210
+ * Member holding a per-option `isSelectable` flag for tree mode. A node with a
211
+ * falsy value renders normally (NOT greyed like `disabled`) but has no checkbox,
212
+ * is skipped by keyboard focus, and cannot be toggled or picked by Select-All.
213
+ * Options default to selectable. Tree mode only.
214
+ */
215
+ isSelectableMember?: string;
216
+ /**
217
+ * Callback deciding whether a tree node is selectable (takes precedence over
218
+ * `isSelectableMember`). Receives the built tree node, so `node.hasChildren` /
219
+ * `node.level` are available — e.g. `(node) => !node.hasChildren` for a
220
+ * leaves-only tree. Tree mode only.
221
+ */
222
+ getIsSelectableCallback?: (node: LTreeNode<T>) => boolean;
223
+ /**
224
+ * Tree checkbox interaction. `independent` (default) toggles only the clicked
225
+ * node. `cascade` checks a node's whole subtree and shows a tristate
226
+ * (checked / indeterminate / unchecked) box on branches. Tree + multiple only.
227
+ */
228
+ checkboxMode?: 'independent' | 'cascade';
229
+ /**
230
+ * In `cascade` mode, which values a selection emits (badges / form / change):
231
+ * - `rolled-up` (default) — minimal cover: a fully-selected subtree collapses
232
+ * to its root ("complete node"); partially-selected branches emit their
233
+ * individually-checked descendants. Rolls to the nearest selectable
234
+ * descendant when the complete node itself is non-selectable.
235
+ * - `leaves` — only the checked leaf-level nodes.
236
+ * - `all` — every fully-checked node (branches and leaves), like web-treeview.
237
+ */
238
+ cascadeSelectPolicy?: 'rolled-up' | 'leaves' | 'all';
143
239
  /** Member property name for group extraction */
144
240
  groupMember?: string;
145
241
  /** Callback to extract group from item */
@@ -186,6 +282,12 @@ declare interface MultiSelectConfig<T = any> {
186
282
  isAddNewAllowed?: boolean;
187
283
  /** Show count badge next to toggle icon (internal: isCounterShown) */
188
284
  isCounterShown?: boolean;
285
+ /**
286
+ * Make badges display each option's `fullTitleMember` / `getFullTitleCallback` value
287
+ * instead of its display value. Falls back to the display value for options without a
288
+ * full title. An explicit `getBadgeDisplayCallback` still takes precedence. Off by default.
289
+ */
290
+ isBadgeFullTitleShown?: boolean;
189
291
  /** Keep initial options visible when searchCallback is active and search term is empty/short (internal: isKeepOptionsOnSearch) */
190
292
  isKeepOptionsOnSearch?: boolean;
191
293
  /** Keep search text and filtered results when dropdown closes (default: true) */
@@ -232,6 +334,10 @@ declare interface MultiSelectConfig<T = any> {
232
334
  searchMode?: SearchMode;
233
335
  /** Layout mode for action buttons: 'nowrap' (default) or 'wrap' for multi-row */
234
336
  actionsLayout?: ActionsLayout;
337
+ /** Where the action-buttons block sits in the dropdown: 'top' (default) or 'bottom' (sticky footer). */
338
+ actionsPosition?: ActionsPosition;
339
+ /** Horizontal arrangement of buttons within a row: 'stretch' (default, full-width), 'left', 'right', 'center', or 'space-between'. */
340
+ actionsAlign?: ActionsAlign;
235
341
  /** Auto-switch from badges to count when threshold is exceeded */
236
342
  badgesThreshold?: number | null;
237
343
  /** Maximum number of badges to show in partial mode (used with thresholdMode='partial') */
@@ -255,6 +361,23 @@ declare interface MultiSelectConfig<T = any> {
255
361
  virtualScrollBuffer?: number;
256
362
  /** Pre-process search term before calling searchCallback. Return null to prevent search. Use for accent removal, validation, etc. */
257
363
  beforeSearchCallback?: ((searchTerm: string) => string | null) | null;
364
+ /**
365
+ * Interceptor: runs before an option is selected via user interaction.
366
+ * Receives the option about to be added and the current selection (before the change).
367
+ * Return `false` to block the selection; return `true`/`undefined` to allow.
368
+ * Silent — a blocked action fires no event. Bypassed by programmatic `setSelected`
369
+ * and the Select-All action button.
370
+ */
371
+ beforeSelectCallback?: ((option: T, selectedOptions: T[]) => boolean | void) | null;
372
+ /**
373
+ * Interceptor: runs before an option is deselected via user interaction — the dropdown
374
+ * option toggle, a badge's remove (×) button, the selected-items popover's remove button,
375
+ * and the "remove hidden" badge (checked per item). Receives the option about to be
376
+ * removed and the current selection (before the change). Return `false` to block the
377
+ * deselection; return `true`/`undefined` to allow. Silent — a blocked action fires no
378
+ * event. Bypassed by programmatic `setSelected` and the Clear-All action button.
379
+ */
380
+ beforeDeselectCallback?: ((option: T, selectedOptions: T[]) => boolean | void) | null;
258
381
  /**
259
382
  * Async function to load data: `(searchTerm, signal) => Promise<options[]>`.
260
383
  * The optional second argument is an `AbortSignal` that fires when a newer search
@@ -264,12 +387,12 @@ declare interface MultiSelectConfig<T = any> {
264
387
  searchCallback?: ((searchTerm: string, signal?: AbortSignal) => Promise<T[]>) | null;
265
388
  /** Callback to add a new option when isAddNewAllowed is true */
266
389
  addNewCallback?: ((value: string) => T | Promise<T>) | null;
267
- /** Callback when an option is selected */
268
- selectCallback?: ((option: T) => void) | null;
269
- /** Callback when an option is deselected */
270
- deselectCallback?: ((option: T) => void) | null;
271
- /** Callback when selection changes */
272
- changeCallback?: ((selectedOptions: T[]) => void) | null;
390
+ /** Event handler: an option was selected (fire-and-forget; return value ignored). Mirrors the bubbling `select` CustomEvent on the element. */
391
+ onSelect?: ((option: T) => void) | null;
392
+ /** Event handler: an option was deselected (fire-and-forget). Mirrors the bubbling `deselect` CustomEvent on the element. */
393
+ onDeselect?: ((option: T) => void) | null;
394
+ /** Event handler: the selection set changed (fire-and-forget). Mirrors the bubbling `change` CustomEvent on the element. */
395
+ onChange?: ((selectedOptions: T[]) => void) | null;
273
396
  /** Callback to format count badge text (for i18n/pluralization). When moreCount is provided, it's for the "+X more" badge in partial mode. */
274
397
  getCounterCallback?: ((count: number, moreCount?: number) => string) | null;
275
398
  /** Enable tooltips on selected item badges (internal: isBadgeTooltipsEnabled) */
@@ -286,6 +409,18 @@ declare interface MultiSelectConfig<T = any> {
286
409
  badgeTooltipDelay?: number;
287
410
  /** Offset distance for tooltip in pixels */
288
411
  badgeTooltipOffset?: number;
412
+ /** Enable tooltips on dropdown options (internal: isOptionTooltipsEnabled) */
413
+ isOptionTooltipsEnabled?: boolean;
414
+ /** Callback to generate custom tooltip content for a dropdown option. Default: display value, plus subtitle on the next line when present. */
415
+ getOptionTooltipCallback?: ((item: T) => string | HTMLElement) | null;
416
+ /** Option tooltip placement. Default `'top-start'` (anchored to the row's start edge, so it doesn't center on a full-width row). Use `'left'`/`'right'` (start/end side) for a narrow multiselect. */
417
+ optionTooltipPlacement?: Placement;
418
+ /** Delay before showing an option tooltip (ms). Falls back to `badgeTooltipDelay`, then `100`. */
419
+ optionTooltipDelay?: number;
420
+ /** Offset distance for an option tooltip (px). Falls back to `badgeTooltipOffset`, then `8`. */
421
+ optionTooltipOffset?: number;
422
+ /** Anchor the option tooltip to the mouse pointer and follow it across the row (best for full-width rows). Default `false`. */
423
+ isOptionTooltipFollowCursor?: boolean;
289
424
  /** Container element for dropdown/hint/popover (for Shadow DOM support) */
290
425
  container?: HTMLElement | null;
291
426
  /** Host element for appending hidden inputs (for form integration with shadow DOM) */
@@ -313,13 +448,18 @@ export declare class MultiSelectElement<T = any> extends BaseElement {
313
448
  private _getIconCallback?;
314
449
  private _subtitleMember?;
315
450
  private _getSubtitleCallback?;
451
+ private _getFullTitleCallback?;
316
452
  private _groupMember?;
317
453
  private _getGroupCallback?;
318
454
  private _renderGroupLabelContentCallback?;
319
455
  private _disabledMember?;
320
456
  private _getDisabledCallback?;
457
+ private _getPathCallback?;
458
+ private _isTreeEnabled?;
459
+ private _getIsSelectableCallback?;
321
460
  private _getValueFormatCallback?;
322
461
  private _getBadgeTooltipCallback?;
462
+ private _getOptionTooltipCallback?;
323
463
  private _getRemoveButtonTooltipCallback?;
324
464
  private _renderOptionContentCallback?;
325
465
  private _renderBadgeContentCallback?;
@@ -332,11 +472,13 @@ export declare class MultiSelectElement<T = any> extends BaseElement {
332
472
  private _batchPartial;
333
473
  private _batchNeedsReinit;
334
474
  private _beforeSearchCallback?;
475
+ private _beforeSelectCallback?;
476
+ private _beforeDeselectCallback?;
335
477
  private _searchCallback?;
336
478
  private _addNewCallback?;
337
- private _selectCallback?;
338
- private _deselectCallback?;
339
- private _changeCallback?;
479
+ private _onSelect?;
480
+ private _onDeselect?;
481
+ private _onChange?;
340
482
  static get observedAttributes(): string[];
341
483
  constructor();
342
484
  /**
@@ -398,10 +540,28 @@ export declare class MultiSelectElement<T = any> extends BaseElement {
398
540
  get iconMember(): string | null;
399
541
  set subtitleMember(value: string | null);
400
542
  get subtitleMember(): string | null;
543
+ set fullTitleMember(value: string | null);
544
+ get fullTitleMember(): string | null;
401
545
  set groupMember(value: string | null);
402
546
  get groupMember(): string | null;
403
547
  set disabledMember(value: string | null);
404
548
  get disabledMember(): string | null;
549
+ set pathMember(value: string | null);
550
+ get pathMember(): string | null;
551
+ set parentPathMember(value: string | null);
552
+ get parentPathMember(): string | null;
553
+ set levelMember(value: string | null);
554
+ get levelMember(): string | null;
555
+ set hasChildrenMember(value: string | null);
556
+ get hasChildrenMember(): string | null;
557
+ set isSelectableMember(value: string | null);
558
+ get isSelectableMember(): string | null;
559
+ set treePathSeparator(value: string | null);
560
+ get treePathSeparator(): string | null;
561
+ set checkboxMode(value: 'independent' | 'cascade' | null);
562
+ get checkboxMode(): 'independent' | 'cascade' | null;
563
+ set cascadeSelectPolicy(value: 'rolled-up' | 'leaves' | 'all' | null);
564
+ get cascadeSelectPolicy(): 'rolled-up' | 'leaves' | 'all' | null;
405
565
  set getValueCallback(callback: ((item: T) => string | number) | undefined);
406
566
  get getValueCallback(): ((item: T) => string | number) | undefined;
407
567
  set getDisplayValueCallback(callback: ((item: T) => string) | undefined);
@@ -418,8 +578,24 @@ export declare class MultiSelectElement<T = any> extends BaseElement {
418
578
  get getIconCallback(): ((item: T) => string) | undefined;
419
579
  set getSubtitleCallback(callback: ((item: T) => string) | undefined);
420
580
  get getSubtitleCallback(): ((item: T) => string) | undefined;
581
+ /** Callback returning an option's full title (used by badges when show-badge-full-title is on). */
582
+ set getFullTitleCallback(callback: ((item: T) => string) | undefined);
583
+ get getFullTitleCallback(): ((item: T) => string) | undefined;
421
584
  set getGroupCallback(callback: ((item: T) => string) | undefined);
422
585
  get getGroupCallback(): ((item: T) => string) | undefined;
586
+ /** Callback returning an option's materialized dot-path (enables tree mode). */
587
+ set getPathCallback(callback: ((item: T) => string) | undefined);
588
+ get getPathCallback(): ((item: T) => string) | undefined;
589
+ /** Force tree mode on/off. When unset, tree mode auto-enables if a path source is present. */
590
+ set isTreeEnabled(value: boolean | undefined);
591
+ get isTreeEnabled(): boolean | undefined;
592
+ /**
593
+ * Callback deciding whether a tree node is selectable (takes precedence over
594
+ * `is-selectable-member`). Receives the built node — e.g.
595
+ * `el.getIsSelectableCallback = (node) => !node.hasChildren` for leaves only.
596
+ */
597
+ set getIsSelectableCallback(callback: ((node: LTreeNode<T>) => boolean) | undefined);
598
+ get getIsSelectableCallback(): ((node: LTreeNode<T>) => boolean) | undefined;
423
599
  set renderGroupLabelContentCallback(callback: ((groupName: string) => string | HTMLElement) | undefined);
424
600
  get renderGroupLabelContentCallback(): ((groupName: string) => string | HTMLElement) | undefined;
425
601
  set getDisabledCallback(callback: ((item: T) => boolean) | undefined);
@@ -448,6 +624,18 @@ export declare class MultiSelectElement<T = any> extends BaseElement {
448
624
  get checkboxAlign(): string | null;
449
625
  set enableBadgeTooltips(value: boolean);
450
626
  get enableBadgeTooltips(): boolean;
627
+ set enableOptionTooltips(value: boolean);
628
+ get enableOptionTooltips(): boolean;
629
+ set getOptionTooltipCallback(callback: ((item: T) => string | HTMLElement) | undefined);
630
+ get getOptionTooltipCallback(): ((item: T) => string | HTMLElement) | undefined;
631
+ set optionTooltipPlacement(value: string | null);
632
+ get optionTooltipPlacement(): string | null;
633
+ set optionTooltipFollowCursor(value: boolean);
634
+ get optionTooltipFollowCursor(): boolean;
635
+ set actionsPosition(value: string | null);
636
+ get actionsPosition(): string | null;
637
+ set actionsAlign(value: string | null);
638
+ get actionsAlign(): string | null;
451
639
  set badgeTooltipPlacement(value: string | null);
452
640
  get badgeTooltipPlacement(): string | null;
453
641
  set getBadgeTooltipCallback(callback: ((item: T) => string | HTMLElement) | undefined);
@@ -460,16 +648,20 @@ export declare class MultiSelectElement<T = any> extends BaseElement {
460
648
  get getCounterCallback(): ((count: number, moreCount?: number) => string) | undefined;
461
649
  get beforeSearchCallback(): ((searchTerm: string) => string | null) | undefined;
462
650
  set beforeSearchCallback(callback: ((searchTerm: string) => string | null) | undefined);
651
+ get beforeSelectCallback(): ((option: T, selectedOptions: T[]) => boolean | void) | undefined;
652
+ set beforeSelectCallback(callback: ((option: T, selectedOptions: T[]) => boolean | void) | undefined);
653
+ get beforeDeselectCallback(): ((option: T, selectedOptions: T[]) => boolean | void) | undefined;
654
+ set beforeDeselectCallback(callback: ((option: T, selectedOptions: T[]) => boolean | void) | undefined);
463
655
  get searchCallback(): ((searchTerm: string, signal?: AbortSignal) => Promise<T[]>) | undefined;
464
656
  set searchCallback(callback: ((searchTerm: string, signal?: AbortSignal) => Promise<T[]>) | undefined);
465
657
  get addNewCallback(): ((value: string) => T | Promise<T>) | undefined;
466
658
  set addNewCallback(callback: ((value: string) => T | Promise<T>) | undefined);
467
- get selectCallback(): ((option: T) => void) | undefined;
468
- set selectCallback(callback: ((option: T) => void) | undefined);
469
- get deselectCallback(): ((option: T) => void) | undefined;
470
- set deselectCallback(callback: ((option: T) => void) | undefined);
471
- get changeCallback(): ((selectedOptions: T[]) => void) | undefined;
472
- set changeCallback(callback: ((selectedOptions: T[]) => void) | undefined);
659
+ get onSelect(): ((option: T) => void) | undefined;
660
+ set onSelect(callback: ((option: T) => void) | undefined);
661
+ get onDeselect(): ((option: T) => void) | undefined;
662
+ set onDeselect(callback: ((option: T) => void) | undefined);
663
+ get onChange(): ((selectedOptions: T[]) => void) | undefined;
664
+ set onChange(callback: ((selectedOptions: T[]) => void) | undefined);
473
665
  get actionButtons(): any[] | undefined;
474
666
  set actionButtons(value: any[] | undefined);
475
667
  get selectedValue(): string | number | (string | number)[] | null;
@@ -521,11 +713,22 @@ export declare interface MultiSelectOptions extends MultiSelectConfig<MultiSelec
521
713
  options?: MultiSelectOption[];
522
714
  searchCallback?: ((searchTerm: string, signal?: AbortSignal) => Promise<MultiSelectOption[]>) | null;
523
715
  addNewCallback?: ((value: string) => MultiSelectOption | Promise<MultiSelectOption>) | null;
524
- selectCallback?: ((option: MultiSelectOption) => void) | null;
525
- deselectCallback?: ((option: MultiSelectOption) => void) | null;
526
- changeCallback?: ((selectedOptions: MultiSelectOption[]) => void) | null;
716
+ onSelect?: ((option: MultiSelectOption) => void) | null;
717
+ onDeselect?: ((option: MultiSelectOption) => void) | null;
718
+ onChange?: ((selectedOptions: MultiSelectOption[]) => void) | null;
527
719
  }
528
720
 
721
+ /**
722
+ * LTreeNode — a single node in the option tree.
723
+ *
724
+ * Trimmed lift of web-treeview's `ltree/ltree-node.ts`. The multiselect renders
725
+ * every node expanded (no collapse), so this keeps only the structural fields
726
+ * needed to order nodes and indent them: path/parent/level/children/hasChildren
727
+ * plus the original option `data`. Expand state, drag-drop, checkbox, selection
728
+ * and drop-position fields are all thrown out.
729
+ */
730
+ declare type NodeId = string | number;
731
+
529
732
  /**
530
733
  * Context provided to renderOptionContentCallback
531
734
  */
@@ -584,6 +787,10 @@ export declare class WebMultiSelect<T = any> {
584
787
  private selectedOptions;
585
788
  private allOptions;
586
789
  private filteredOptions;
790
+ private tree;
791
+ private treeNodes;
792
+ private cascadeIndex;
793
+ private cascadeCheckedAtoms;
587
794
  private hiddenInputs;
588
795
  private focusedIndex;
589
796
  private matchingIndices;
@@ -637,14 +844,72 @@ export declare class WebMultiSelect<T = any> {
637
844
  * text independently. Doesn't fit the extractField shape (no tuple/member layer of its own).
638
845
  */
639
846
  private getItemBadgeDisplayValue;
847
+ /**
848
+ * Full title — a fully-qualified label supplied with the data (never computed here). Used by
849
+ * badges when `isBadgeFullTitleShown` is on. Returns undefined when the option has none.
850
+ */
851
+ private getItemFullTitle;
640
852
  private getItemSearchValue;
641
853
  private getItemIcon;
642
854
  private getItemSubtitle;
643
855
  private getItemGroup;
644
856
  private getItemDisabled;
857
+ /**
858
+ * Tree mode: whether the visible node at `index` may be selected. Non-selectable
859
+ * nodes (see `isSelectableMember`/`getIsSelectableCallback`) still render — just
860
+ * without a checkbox — but are skipped by focus and cannot be toggled. Always
861
+ * true outside tree mode. `treeNodes` is index-aligned with `filteredOptions`.
862
+ */
863
+ private isIndexSelectable;
864
+ /** Whether an option may be selected. Always true outside tree mode. */
865
+ private isOptionSelectable;
645
866
  constructor(element: HTMLElement, options?: Partial<MultiSelectConfig<T>>);
646
867
  private init;
647
868
  private parseOptions;
869
+ /** Whether options should be rendered as an (always-expanded) tree. */
870
+ private isTreeMode;
871
+ /** (Re)build the ltree from `allOptions` and derive the visible flat list. */
872
+ private buildTree;
873
+ /**
874
+ * Whether cascade checkbox mode is active: a multi-select tree with
875
+ * `checkbox-mode="cascade"`. Checking a node then toggles its whole subtree
876
+ * and branches show a tristate box.
877
+ */
878
+ private isCascadeMode;
879
+ private cascadePolicy;
880
+ /** Refresh the derived checked-atom set from the emitted `selectedValues`. */
881
+ private refreshCascadeAtoms;
882
+ /**
883
+ * Toggle a tree node in cascade mode: flip its whole subtree, re-project the
884
+ * checked atoms to emitted values under the active policy, and commit the diff
885
+ * so badges / form / change events reflect the policy (rolled-up branches, etc.).
886
+ */
887
+ private toggleTreeCascade;
888
+ /**
889
+ * Derive `treeNodes` + `filteredOptions` from the full tree, applying the
890
+ * current search term. Matching nodes keep all their ancestors visible so
891
+ * indentation stays coherent (the tree is always fully expanded).
892
+ */
893
+ private rebuildTreeVisible;
894
+ /**
895
+ * Reset the visible list to "everything". **Tree-aware**: in tree mode it
896
+ * rebuilds `treeNodes` (kept index-aligned with `filteredOptions`) from the
897
+ * full tree, so the two never drift. A raw `filteredOptions = [...allOptions]`
898
+ * would leave `treeNodes` stale after clearing a search — the virtual list
899
+ * then reserves height for every option but renders blank rows because
900
+ * `treeNodes[index]` is undefined. Always use this to clear the visible list.
901
+ */
902
+ private resetVisibleToAll;
903
+ /**
904
+ * Tree mode: derive the visible list from an **external** set of matched
905
+ * options — e.g. the results returned by `searchCallback` — keeping each
906
+ * match's ancestors so indentation stays coherent. This is the async-search
907
+ * analogue of `rebuildTreeVisible`: the matching is done by the caller (their
908
+ * own index/engine) instead of a local substring test, but ancestor
909
+ * preservation and `treeNodes`/`filteredOptions` index-alignment still happen
910
+ * here. Pass all options to show the whole tree.
911
+ */
912
+ private rebuildTreeVisibleFromMatches;
648
913
  private buildHTML;
649
914
  /**
650
915
  * Check if virtual scroll should be used
@@ -663,8 +928,24 @@ export declare class WebMultiSelect<T = any> {
663
928
  * Render the Select All / Clear All / custom action buttons row.
664
929
  * Returns the empty string if multiple-select is off or no buttons are configured.
665
930
  */
931
+ /**
932
+ * Default enabled/disabled state for the built-in actions, applied only when the consumer hasn't
933
+ * set an explicit `isDisabled` / `getIsDisabledCallback`:
934
+ * - `select-all` is disabled when it would add nothing (every selectable, non-disabled filtered
935
+ * option is already selected — this also covers an empty list).
936
+ * - `clear-all` is disabled when nothing is selected.
937
+ */
938
+ private getBuiltInActionDisabled;
666
939
  private renderActionsHTML;
667
940
  private renderOption;
941
+ /**
942
+ * Render a single tree-mode row. Separate from `renderOption`: a tree row is
943
+ * indented by its depth (via the `--ms-tree-depth` custom property) and
944
+ * tagged branch/leaf, but otherwise carries the same selection/checkbox/
945
+ * icon/subtitle content. The tree is always fully expanded, so there is no
946
+ * chevron/toggle — every node is just a normal, selectable option.
947
+ */
948
+ private renderTreeNode;
668
949
  private highlightMatch;
669
950
  private groupOptions;
670
951
  /** Whether the input currently functions as a usable search field (drives placeholder wording). */
@@ -701,6 +982,13 @@ export declare class WebMultiSelect<T = any> {
701
982
  * Returning -1 from `compute` is a no-op (used for empty list / no match).
702
983
  */
703
984
  private focusBy;
985
+ /**
986
+ * Given a target index and a preferred direction, return the nearest index
987
+ * whose node is selectable (skipping non-selectable tree nodes). Falls back to
988
+ * the opposite direction, then to -1 if nothing is selectable. No-op outside
989
+ * tree mode.
990
+ */
991
+ private resolveSelectableIndex;
704
992
  private focusNext;
705
993
  private focusPrevious;
706
994
  private focusFirst;
@@ -711,6 +999,23 @@ export declare class WebMultiSelect<T = any> {
711
999
  private focusPreviousMatch;
712
1000
  private scrollToFocused;
713
1001
  private toggleOption;
1002
+ /**
1003
+ * The single funnel for an interactive (user-initiated) selection. Consults
1004
+ * `beforeSelectCallback` and only mutates state if allowed, so the veto can
1005
+ * never be bypassed by a new UI entry point. Programmatic `setSelected` and
1006
+ * the Select-All button deliberately do not route through here.
1007
+ * Returns true if the option was selected, false if the veto blocked it.
1008
+ */
1009
+ private interactiveSelect;
1010
+ /**
1011
+ * The single funnel for an interactive (user-initiated) deselection. Every
1012
+ * removal affordance — dropdown toggle, badge × button, selected-items
1013
+ * popover × button, and the "remove hidden" badge — routes through here so
1014
+ * the `beforeDeselectCallback` veto applies uniformly. Programmatic
1015
+ * `setSelected` and the Clear-All button deliberately bypass it.
1016
+ * Returns true if the option was deselected, false if the veto blocked it.
1017
+ */
1018
+ private interactiveDeselect;
714
1019
  private handleAddNew;
715
1020
  private selectOption;
716
1021
  private deselectOption;
@@ -719,7 +1024,7 @@ export declare class WebMultiSelect<T = any> {
719
1024
  /**
720
1025
  * Re-render and fire callbacks after a selection state change.
721
1026
  * `added` / `removed` drive per-item select/deselect callbacks.
722
- * `changeCallback` fires once if anything actually changed.
1027
+ * `onChange` fires once if anything actually changed.
723
1028
  */
724
1029
  private commit;
725
1030
  private open;
@@ -803,6 +1108,20 @@ export declare class WebMultiSelect<T = any> {
803
1108
  /** Build the remove-button tooltip text (callback > format string with {0} > "Remove {name}"). */
804
1109
  private buildRemoveButtonTooltipText;
805
1110
  private attachBadgeTooltips;
1111
+ /** Build the option tooltip content (callback overrides; default = displayValue + optional subtitle on next line). */
1112
+ private buildOptionTooltipContent;
1113
+ /**
1114
+ * Attach hover tooltips to the currently rendered dropdown options. Prunes existing option
1115
+ * tooltips first, so it's safe to call on every render and on every virtual-scroll range change
1116
+ * (where option DOM is recycled). Each option resolves its source object via `data-index` into
1117
+ * `filteredOptions`, the same global index `renderOption` was given.
1118
+ */
1119
+ private attachOptionTooltips;
1120
+ /**
1121
+ * Destroy only the option tooltips (prefixed `option-`). Called before re-rendering or
1122
+ * recycling the options list so per-option tooltip state doesn't leak.
1123
+ */
1124
+ private destroyAllOptionTooltips;
806
1125
  private attachActionButtonTooltips;
807
1126
  /**
808
1127
  * Destroy only the action-button tooltips. Called from `renderDropdown`/`renderDropdownVirtual`