@keenmate/web-multiselect 1.12.0-rc05 → 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/README.md +11 -7
- package/dist/index.d.ts +215 -0
- package/dist/multiselect.js +1505 -966
- package/dist/multiselect.umd.js +16 -16
- package/dist/style.css +1 -1
- package/package.json +2 -1
- package/src/css/main.css +1 -0
- package/src/css/options.css +47 -2
- package/src/css/tree.css +42 -0
- package/src/css/variables.css +28 -3
package/README.md
CHANGED
|
@@ -21,6 +21,17 @@ Reads `--base-*` variables from the page if [`@keenmate/theme-designer`](https:/
|
|
|
21
21
|
- Custom rendering callbacks for options, badges, and group headers.
|
|
22
22
|
- Form integration via standard hidden inputs (FormData-compatible).
|
|
23
23
|
|
|
24
|
+
## What's New in v1.12.0-rc06
|
|
25
|
+
|
|
26
|
+
- **Tree of options — options can render as an always-expanded hierarchy** — give each option a materialized dot-path (`"1"`, `"1.1"`, `"1.1.1"`, …) and set `path-member` (or `el.getPathCallback`) and tree mode auto-enables, deriving parent/level from the path and rendering depth-first with indentation. The hierarchy model is a trimmed lift of `@keenmate/web-treeview`'s `ltree` (`src/tree/`) so ordering matches the treeview. There is no collapse/chevron — reach for `@keenmate/web-treeview` when you need expand/collapse. A separate `renderTreeNode` path carries the same selection/checkbox/icon/subtitle content plus depth indentation (via `--ms-tree-depth` → `src/css/tree.css`, tuned with `--ms-tree-indent`), and search filters the hierarchy to matches plus their ancestors so indentation stays coherent.
|
|
27
|
+
- **Per-node selectability — mark tree nodes non-selectable without greying them out** — `is-selectable-member` (a data flag) or `el.getIsSelectableCallback` (a predicate over the built `LTreeNode`, so it can read the derived `node.hasChildren`) makes the common "leaves only" rule a one-liner. A non-selectable node is distinct from a disabled one: it renders *normally* but drops its checkbox, is skipped by keyboard focus, and can't be toggled by click/Enter or Select All. New `.ms__option--tree-unselectable` theming hook (plus `data-selectable="false"`) ships with only `cursor: default` and no hover highlight, so structural branch rows read as structure while staying visually normal.
|
|
28
|
+
- **External search now composes with tree mode** — previously the async `searchCallback` path set the flat `filteredOptions` but not the parallel `treeNodes`, so an externalized search over a tree rendered blank or mis-indexed. It now rebuilds the hierarchy from the returned matches, keeping their ancestors (`rebuildTreeVisibleFromMatches`) — the async analogue of the built-in matches-plus-ancestors behaviour — and the below-min-length / error fallbacks are tree-aware too. A new `examples-search-index.html` demonstrates delegating the whole search to FlexSearch (fuzzy / accent-insensitive / ranked) over both a flat list and the full ISCO-08 tree, with an exact/prefix code lookup so `2211` resolves to its node and `72` to the whole subtree. FlexSearch stays a dev/consumer dependency — never in the shipped bundle.
|
|
29
|
+
- **Badge full title — render a supplied breadcrumb on badges** — new `full-title-member` (or `el.getFullTitleCallback`) reads a fully-qualified label that ships with the data (e.g. `"Fruit / Pome fruit / Apple"`); it is never computed. Turn on `show-badge-full-title` and badges render that full title instead of the display value, falling back to the display value for options without one, while an explicit `getBadgeDisplayCallback` still wins. Pairs naturally with tree mode to show the full path in the badge.
|
|
30
|
+
- **Adjustable row height and long-title handling** — new CSS variables (no new attributes) control every `.ms__option`, flat and tree: `--ms-option-min-height` sets a consistent minimum row height for non-virtual rows (they still grow if content is taller; virtual rows keep the fixed `--ms-option-height`), and `--ms-option-title-white-space` / `-overflow` / `-text-overflow` flip a title from the default wrap to a one-line ellipsis. `.ms__option-title` is the label hook (the analog of web-treeview's `.wtv__node-label`), and because its flex ancestors already set `min-width: 0`, ellipsis works with no extra CSS — pair it with `enable-option-tooltips` to reveal the full text on hover.
|
|
31
|
+
- **Cascade checkboxes for tree mode, with a value policy** — set `checkbox-mode="cascade"` and checking a node toggles its whole subtree while a partially-selected branch shows a tristate (dash) box. Orthogonally, `cascade-select-policy` decides *which values* the selection emits: `rolled-up` (default) collapses a fully-selected subtree to one "complete node" value and surfaces individual descendants under partially-selected branches (the minimal cover — the bit svelte-treeview lacks); `leaves` emits only checked leaves; `all` emits every checked node like web-treeview. Canonical state is the set of checked leaf atoms and the emitted `selectedValues` is a pure projection, so badges/form/`change` are untouched and every policy round-trips. Non-breaking — `independent` (the previous behaviour) is the default. Logic is isolated in `src/tree/cascade.ts`; tristate renders from a CSS modifier class (virtual-scroll-safe). Demoed as §10 on `examples-tree.html`.
|
|
32
|
+
- **More pronounced option checkboxes** — the unchecked box draws its border from a dedicated mid-grey token (`--ms-checkbox-border-color`, default `#8f8f8f`) instead of the faint generic `--ms-border`, so it reads as a real, clickable control, with a slightly bolder checkmark. New themeable tokens `--ms-checkbox-border-width` (1px), `--ms-checkbox-border-color`, and `--ms-checkbox-checkmark-thickness` drive it, and `--ms-checkbox-border` / `--ms-checkbox-checked-border` recompose from the width token — override any of them to restyle.
|
|
33
|
+
- **Fixes — stable tree rows across search** — two virtual-scroll/tree regressions are fixed: row height no longer jumps as filtering crosses the virtual-scroll threshold (non-virtual rows now pin to `--ms-option-height` when virtual scroll is enabled), and clearing a search with Escape (or reopening after a search) no longer leaves the dropdown blank — both the Escape-clear and `close()` paths route through a tree-aware `resetVisibleToAll()` that rebuilds `treeNodes` from the full tree.
|
|
34
|
+
|
|
24
35
|
## What's New in v1.12.0-rc05
|
|
25
36
|
|
|
26
37
|
> ⚠️ **Breaking change — event-handler rename, no aliases.** The fire-and-forget notification callbacks are renamed to `on*` with **no backward-compatible aliases**: `selectCallback` → `onSelect`, `deselectCallback` → `onDeselect`, `changeCallback` → `onChange`, and on `ActionButton`, `isVisibleCallback` / `isDisabledCallback` → `getIsVisibleCallback` / `getIsDisabledCallback`. If you assign these as element properties or config keys, **rename them or your handlers silently stop firing**. The `select` / `deselect` / `change` **DOM events are unchanged**, so `addEventListener(...)` consumers are unaffected. Full rationale in the first bullet below.
|
|
@@ -34,13 +45,6 @@ Reads `--base-*` variables from the page if [`@keenmate/theme-designer`](https:/
|
|
|
34
45
|
- **Async search — dropdown stops jumping between empty and loading states** — the "No data" empty state and the loading spinner now share a min-height (`--ms-state-min-height`, a new themable variable), so the panel keeps a stable footprint instead of resizing as an async search swaps one block for the other.
|
|
35
46
|
- **Docs — new live demo page for events & interceptors** — `examples-events-callbacks.html` shows the DOM events, the `on*` property twin firing in parallel, and interactive veto demos for the new interceptors.
|
|
36
47
|
|
|
37
|
-
## What's New in v1.12.0-rc04
|
|
38
|
-
|
|
39
|
-
- **Placeholders — context-aware wording for pickers and empty cascades** — The input used to show `"Search..."` unconditionally, even when search was off or there was nothing to pick. Two new attributes fix that. `select-placeholder` (default `"Pick an option..."`) is shown when the input isn't a usable search box — `enable-search="false"`, or `search-input-mode="readonly"`/`"hidden"` — so a non-searchable picker stops mislabeling itself. `no-data-placeholder` is an opt-in string shown when the option list is empty, letting users see there's nothing to choose without opening the dropdown; it targets cascade multiselects whose parent isn't resolved yet, and stays unset by default so async-loaded selects don't flash an empty-state before their data lands. Placeholder resolution is now recomputed on live updates too, so changing these attributes, the search mode, or the option list updates the visible text on the fly. **Behavior change:** search-disabled instances without a custom placeholder now read `"Pick an option..."` — set `select-placeholder="Search..."` to keep the old text.
|
|
40
|
-
- **setAttributes() — batch attribute updates in a single render** — Setting attributes one at a time triggered a full re-render per call. The new `setAttributes(attrs)` method on `<web-multiselect>` applies a whole map of attributes in one in-place update (or a single reinit at most, if any change is structural). Keys are kebab-case attribute names, exactly like `setAttribute`; a value of `null`/`undefined`/`false` removes the attribute and `true` sets it to `""`. It's particularly handy for i18n language switches that swap several placeholder strings at once without flicker.
|
|
41
|
-
- **search-debounce — coalesce async search into one request** — A new `search-debounce` attribute (milliseconds, default `0`) debounces the async `searchCallback` so a burst of keystrokes collapses into a single request instead of one per character. Each keystroke resets the timer, and it applies to the async `searchCallback` path only — local in-memory filtering stays instant. The existing stale-result guard (results applied only if the term still matches) remains as a second line of defense against out-of-order responses. The examples page §8 now ships a "Debounced Search" demo with a live keystrokes-vs-API-calls counter that makes the coalescing obvious.
|
|
42
|
-
- **searchCallback AbortSignal — cancel superseded in-flight requests** — `searchCallback` now receives an `AbortSignal` as its second argument: `(searchTerm, signal) => Promise<options[]>`. When a newer search supersedes an in-flight one — or the term drops below `min-search-length`, or the component is destroyed — the previous request's signal is aborted; forward it into `fetch(url, { signal })` to actually cancel the network call. It's backward compatible: the argument is optional, and existing callbacks that ignore it keep working with their stale results discarded as before. Aborted or superseded responses never overwrite the live filtered options.
|
|
43
|
-
|
|
44
48
|
> ⚠️ **Security notice:** This component intentionally allows raw HTML in rendering callbacks to give developers full control over content display. If you display user-generated content, you must sanitize it yourself. See [docs/examples.md → HTML Injection (XSS) notice](./docs/examples.md#html-injection-xss-notice) for the complete list of affected callbacks.
|
|
45
49
|
|
|
46
50
|
## Demos & docs
|
package/dist/index.d.ts
CHANGED
|
@@ -120,6 +120,31 @@ export declare const interactionLogger: any;
|
|
|
120
120
|
*/
|
|
121
121
|
export declare const LOGGING_CATEGORIES: string[];
|
|
122
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
|
+
|
|
123
148
|
/**
|
|
124
149
|
* Generic configuration options for the MultiSelect component
|
|
125
150
|
* @template T The type of data items
|
|
@@ -137,6 +162,15 @@ declare interface MultiSelectConfig<T = any> {
|
|
|
137
162
|
getDisplayValueCallback?: (item: T) => string;
|
|
138
163
|
/** Callback to customize badge display text (defaults to display value if not provided) */
|
|
139
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;
|
|
140
174
|
/** Callback to add custom CSS classes to badges - return string or array of class names */
|
|
141
175
|
getBadgeClassCallback?: (item: T) => string | string[];
|
|
142
176
|
/** Callback to inject custom CSS into Shadow DOM - return CSS string for styling custom classes */
|
|
@@ -153,6 +187,55 @@ declare interface MultiSelectConfig<T = any> {
|
|
|
153
187
|
subtitleMember?: string;
|
|
154
188
|
/** Callback to extract subtitle from item */
|
|
155
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';
|
|
156
239
|
/** Member property name for group extraction */
|
|
157
240
|
groupMember?: string;
|
|
158
241
|
/** Callback to extract group from item */
|
|
@@ -199,6 +282,12 @@ declare interface MultiSelectConfig<T = any> {
|
|
|
199
282
|
isAddNewAllowed?: boolean;
|
|
200
283
|
/** Show count badge next to toggle icon (internal: isCounterShown) */
|
|
201
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;
|
|
202
291
|
/** Keep initial options visible when searchCallback is active and search term is empty/short (internal: isKeepOptionsOnSearch) */
|
|
203
292
|
isKeepOptionsOnSearch?: boolean;
|
|
204
293
|
/** Keep search text and filtered results when dropdown closes (default: true) */
|
|
@@ -359,11 +448,15 @@ export declare class MultiSelectElement<T = any> extends BaseElement {
|
|
|
359
448
|
private _getIconCallback?;
|
|
360
449
|
private _subtitleMember?;
|
|
361
450
|
private _getSubtitleCallback?;
|
|
451
|
+
private _getFullTitleCallback?;
|
|
362
452
|
private _groupMember?;
|
|
363
453
|
private _getGroupCallback?;
|
|
364
454
|
private _renderGroupLabelContentCallback?;
|
|
365
455
|
private _disabledMember?;
|
|
366
456
|
private _getDisabledCallback?;
|
|
457
|
+
private _getPathCallback?;
|
|
458
|
+
private _isTreeEnabled?;
|
|
459
|
+
private _getIsSelectableCallback?;
|
|
367
460
|
private _getValueFormatCallback?;
|
|
368
461
|
private _getBadgeTooltipCallback?;
|
|
369
462
|
private _getOptionTooltipCallback?;
|
|
@@ -447,10 +540,28 @@ export declare class MultiSelectElement<T = any> extends BaseElement {
|
|
|
447
540
|
get iconMember(): string | null;
|
|
448
541
|
set subtitleMember(value: string | null);
|
|
449
542
|
get subtitleMember(): string | null;
|
|
543
|
+
set fullTitleMember(value: string | null);
|
|
544
|
+
get fullTitleMember(): string | null;
|
|
450
545
|
set groupMember(value: string | null);
|
|
451
546
|
get groupMember(): string | null;
|
|
452
547
|
set disabledMember(value: string | null);
|
|
453
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;
|
|
454
565
|
set getValueCallback(callback: ((item: T) => string | number) | undefined);
|
|
455
566
|
get getValueCallback(): ((item: T) => string | number) | undefined;
|
|
456
567
|
set getDisplayValueCallback(callback: ((item: T) => string) | undefined);
|
|
@@ -467,8 +578,24 @@ export declare class MultiSelectElement<T = any> extends BaseElement {
|
|
|
467
578
|
get getIconCallback(): ((item: T) => string) | undefined;
|
|
468
579
|
set getSubtitleCallback(callback: ((item: T) => string) | undefined);
|
|
469
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;
|
|
470
584
|
set getGroupCallback(callback: ((item: T) => string) | undefined);
|
|
471
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;
|
|
472
599
|
set renderGroupLabelContentCallback(callback: ((groupName: string) => string | HTMLElement) | undefined);
|
|
473
600
|
get renderGroupLabelContentCallback(): ((groupName: string) => string | HTMLElement) | undefined;
|
|
474
601
|
set getDisabledCallback(callback: ((item: T) => boolean) | undefined);
|
|
@@ -591,6 +718,17 @@ export declare interface MultiSelectOptions extends MultiSelectConfig<MultiSelec
|
|
|
591
718
|
onChange?: ((selectedOptions: MultiSelectOption[]) => void) | null;
|
|
592
719
|
}
|
|
593
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
|
+
|
|
594
732
|
/**
|
|
595
733
|
* Context provided to renderOptionContentCallback
|
|
596
734
|
*/
|
|
@@ -649,6 +787,10 @@ export declare class WebMultiSelect<T = any> {
|
|
|
649
787
|
private selectedOptions;
|
|
650
788
|
private allOptions;
|
|
651
789
|
private filteredOptions;
|
|
790
|
+
private tree;
|
|
791
|
+
private treeNodes;
|
|
792
|
+
private cascadeIndex;
|
|
793
|
+
private cascadeCheckedAtoms;
|
|
652
794
|
private hiddenInputs;
|
|
653
795
|
private focusedIndex;
|
|
654
796
|
private matchingIndices;
|
|
@@ -702,14 +844,72 @@ export declare class WebMultiSelect<T = any> {
|
|
|
702
844
|
* text independently. Doesn't fit the extractField shape (no tuple/member layer of its own).
|
|
703
845
|
*/
|
|
704
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;
|
|
705
852
|
private getItemSearchValue;
|
|
706
853
|
private getItemIcon;
|
|
707
854
|
private getItemSubtitle;
|
|
708
855
|
private getItemGroup;
|
|
709
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;
|
|
710
866
|
constructor(element: HTMLElement, options?: Partial<MultiSelectConfig<T>>);
|
|
711
867
|
private init;
|
|
712
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;
|
|
713
913
|
private buildHTML;
|
|
714
914
|
/**
|
|
715
915
|
* Check if virtual scroll should be used
|
|
@@ -738,6 +938,14 @@ export declare class WebMultiSelect<T = any> {
|
|
|
738
938
|
private getBuiltInActionDisabled;
|
|
739
939
|
private renderActionsHTML;
|
|
740
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;
|
|
741
949
|
private highlightMatch;
|
|
742
950
|
private groupOptions;
|
|
743
951
|
/** Whether the input currently functions as a usable search field (drives placeholder wording). */
|
|
@@ -774,6 +982,13 @@ export declare class WebMultiSelect<T = any> {
|
|
|
774
982
|
* Returning -1 from `compute` is a no-op (used for empty list / no match).
|
|
775
983
|
*/
|
|
776
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;
|
|
777
992
|
private focusNext;
|
|
778
993
|
private focusPrevious;
|
|
779
994
|
private focusFirst;
|