@keenmate/web-multiselect 1.12.0-rc05 → 1.12.0-rc07
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 +16 -19
- package/dist/index.d.ts +237 -2
- package/dist/multiselect.js +1509 -944
- 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 +51 -2
- package/src/css/tree.css +42 -0
- package/src/css/variables.css +28 -3
package/README.md
CHANGED
|
@@ -21,25 +21,22 @@ 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-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
- **
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
- **
|
|
33
|
-
- **
|
|
34
|
-
- **
|
|
35
|
-
- **
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
- **
|
|
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.
|
|
24
|
+
## What's New in v1.12.0-rc07
|
|
25
|
+
|
|
26
|
+
- **Selection — `setSelected(values, { notify: true })` announces programmatic changes** — `setSelected()` stays silent by default (restoring saved state, cascade/dependent resets, and server-authoritative corrections must not re-fire `change`, or they trip "the user changed it" handlers and can bounce in a feedback loop), but the new `{ notify: true }` option fires a **single aggregate `change`** — no per-item `select`/`deselect` flood — for when a programmatic change is a deliberate user gesture, e.g. a custom action button that sets the selection and should reach the same listeners a manual pick does. Threaded through both the internal picker and the web component's `setSelected`.
|
|
27
|
+
- **Cascade mode — Select All now honours the value policy** — the built-in `select-all` action added every selectable node's value directly, bypassing the cascade projection, so with `checkbox-mode="cascade"` + `cascade-select-policy="rolled-up"` it emitted every node instead of the rolled-up roots (unlike a click, which rolls up). `selectAll()` is now cascade-aware — it fills the checked-atom set from the visible nodes and projects through the active policy via a shared `commitCascadeAtoms` helper — so Select All emits the same shape a click does. Shown in the new "Action Buttons" section of `examples-tree.html`.
|
|
28
|
+
- **Options no longer text-select on click-drag** — clicking a row, or dragging across the dropdown, used to highlight the labels like selectable text, which reads as broken for a pure selection gesture (most visible on the denser tree rows). Options now set `user-select: none` (with the `-webkit-` prefix) on `.ms__option`. Purely presentational — no API or behaviour change.
|
|
29
|
+
|
|
30
|
+
## What's New in v1.12.0-rc06
|
|
31
|
+
|
|
32
|
+
- **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.
|
|
33
|
+
- **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.
|
|
34
|
+
- **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.
|
|
35
|
+
- **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.
|
|
36
|
+
- **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.
|
|
37
|
+
- **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`.
|
|
38
|
+
- **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.
|
|
39
|
+
- **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.
|
|
43
40
|
|
|
44
41
|
> ⚠️ **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
42
|
|
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);
|
|
@@ -540,7 +667,9 @@ export declare class MultiSelectElement<T = any> extends BaseElement {
|
|
|
540
667
|
get selectedValue(): string | number | (string | number)[] | null;
|
|
541
668
|
get selectedItem(): T | null;
|
|
542
669
|
getSelected(): T[];
|
|
543
|
-
setSelected(values: (string | number)[]
|
|
670
|
+
setSelected(values: (string | number)[], opts?: {
|
|
671
|
+
notify?: boolean;
|
|
672
|
+
}): void;
|
|
544
673
|
getValue(): string | number | (string | number)[] | null;
|
|
545
674
|
destroy(): void;
|
|
546
675
|
}
|
|
@@ -591,6 +720,17 @@ export declare interface MultiSelectOptions extends MultiSelectConfig<MultiSelec
|
|
|
591
720
|
onChange?: ((selectedOptions: MultiSelectOption[]) => void) | null;
|
|
592
721
|
}
|
|
593
722
|
|
|
723
|
+
/**
|
|
724
|
+
* LTreeNode — a single node in the option tree.
|
|
725
|
+
*
|
|
726
|
+
* Trimmed lift of web-treeview's `ltree/ltree-node.ts`. The multiselect renders
|
|
727
|
+
* every node expanded (no collapse), so this keeps only the structural fields
|
|
728
|
+
* needed to order nodes and indent them: path/parent/level/children/hasChildren
|
|
729
|
+
* plus the original option `data`. Expand state, drag-drop, checkbox, selection
|
|
730
|
+
* and drop-position fields are all thrown out.
|
|
731
|
+
*/
|
|
732
|
+
declare type NodeId = string | number;
|
|
733
|
+
|
|
594
734
|
/**
|
|
595
735
|
* Context provided to renderOptionContentCallback
|
|
596
736
|
*/
|
|
@@ -649,6 +789,10 @@ export declare class WebMultiSelect<T = any> {
|
|
|
649
789
|
private selectedOptions;
|
|
650
790
|
private allOptions;
|
|
651
791
|
private filteredOptions;
|
|
792
|
+
private tree;
|
|
793
|
+
private treeNodes;
|
|
794
|
+
private cascadeIndex;
|
|
795
|
+
private cascadeCheckedAtoms;
|
|
652
796
|
private hiddenInputs;
|
|
653
797
|
private focusedIndex;
|
|
654
798
|
private matchingIndices;
|
|
@@ -702,14 +846,79 @@ export declare class WebMultiSelect<T = any> {
|
|
|
702
846
|
* text independently. Doesn't fit the extractField shape (no tuple/member layer of its own).
|
|
703
847
|
*/
|
|
704
848
|
private getItemBadgeDisplayValue;
|
|
849
|
+
/**
|
|
850
|
+
* Full title — a fully-qualified label supplied with the data (never computed here). Used by
|
|
851
|
+
* badges when `isBadgeFullTitleShown` is on. Returns undefined when the option has none.
|
|
852
|
+
*/
|
|
853
|
+
private getItemFullTitle;
|
|
705
854
|
private getItemSearchValue;
|
|
706
855
|
private getItemIcon;
|
|
707
856
|
private getItemSubtitle;
|
|
708
857
|
private getItemGroup;
|
|
709
858
|
private getItemDisabled;
|
|
859
|
+
/**
|
|
860
|
+
* Tree mode: whether the visible node at `index` may be selected. Non-selectable
|
|
861
|
+
* nodes (see `isSelectableMember`/`getIsSelectableCallback`) still render — just
|
|
862
|
+
* without a checkbox — but are skipped by focus and cannot be toggled. Always
|
|
863
|
+
* true outside tree mode. `treeNodes` is index-aligned with `filteredOptions`.
|
|
864
|
+
*/
|
|
865
|
+
private isIndexSelectable;
|
|
866
|
+
/** Whether an option may be selected. Always true outside tree mode. */
|
|
867
|
+
private isOptionSelectable;
|
|
710
868
|
constructor(element: HTMLElement, options?: Partial<MultiSelectConfig<T>>);
|
|
711
869
|
private init;
|
|
712
870
|
private parseOptions;
|
|
871
|
+
/** Whether options should be rendered as an (always-expanded) tree. */
|
|
872
|
+
private isTreeMode;
|
|
873
|
+
/** (Re)build the ltree from `allOptions` and derive the visible flat list. */
|
|
874
|
+
private buildTree;
|
|
875
|
+
/**
|
|
876
|
+
* Whether cascade checkbox mode is active: a multi-select tree with
|
|
877
|
+
* `checkbox-mode="cascade"`. Checking a node then toggles its whole subtree
|
|
878
|
+
* and branches show a tristate box.
|
|
879
|
+
*/
|
|
880
|
+
private isCascadeMode;
|
|
881
|
+
private cascadePolicy;
|
|
882
|
+
/** Refresh the derived checked-atom set from the emitted `selectedValues`. */
|
|
883
|
+
private refreshCascadeAtoms;
|
|
884
|
+
/**
|
|
885
|
+
* Toggle a tree node in cascade mode: flip its whole subtree, re-project the
|
|
886
|
+
* checked atoms to emitted values under the active policy, and commit the diff
|
|
887
|
+
* so badges / form / change events reflect the policy (rolled-up branches, etc.).
|
|
888
|
+
*/
|
|
889
|
+
private toggleTreeCascade;
|
|
890
|
+
/**
|
|
891
|
+
* Given a checked-atom set, project it to emitted values under the active
|
|
892
|
+
* policy, diff it against the current selection, and commit. Shared by every
|
|
893
|
+
* cascade entry point (node toggle, Select All) so they all emit the same
|
|
894
|
+
* policy-projected shape (e.g. a full subtree rolls up to one value).
|
|
895
|
+
*/
|
|
896
|
+
private commitCascadeAtoms;
|
|
897
|
+
/**
|
|
898
|
+
* Derive `treeNodes` + `filteredOptions` from the full tree, applying the
|
|
899
|
+
* current search term. Matching nodes keep all their ancestors visible so
|
|
900
|
+
* indentation stays coherent (the tree is always fully expanded).
|
|
901
|
+
*/
|
|
902
|
+
private rebuildTreeVisible;
|
|
903
|
+
/**
|
|
904
|
+
* Reset the visible list to "everything". **Tree-aware**: in tree mode it
|
|
905
|
+
* rebuilds `treeNodes` (kept index-aligned with `filteredOptions`) from the
|
|
906
|
+
* full tree, so the two never drift. A raw `filteredOptions = [...allOptions]`
|
|
907
|
+
* would leave `treeNodes` stale after clearing a search — the virtual list
|
|
908
|
+
* then reserves height for every option but renders blank rows because
|
|
909
|
+
* `treeNodes[index]` is undefined. Always use this to clear the visible list.
|
|
910
|
+
*/
|
|
911
|
+
private resetVisibleToAll;
|
|
912
|
+
/**
|
|
913
|
+
* Tree mode: derive the visible list from an **external** set of matched
|
|
914
|
+
* options — e.g. the results returned by `searchCallback` — keeping each
|
|
915
|
+
* match's ancestors so indentation stays coherent. This is the async-search
|
|
916
|
+
* analogue of `rebuildTreeVisible`: the matching is done by the caller (their
|
|
917
|
+
* own index/engine) instead of a local substring test, but ancestor
|
|
918
|
+
* preservation and `treeNodes`/`filteredOptions` index-alignment still happen
|
|
919
|
+
* here. Pass all options to show the whole tree.
|
|
920
|
+
*/
|
|
921
|
+
private rebuildTreeVisibleFromMatches;
|
|
713
922
|
private buildHTML;
|
|
714
923
|
/**
|
|
715
924
|
* Check if virtual scroll should be used
|
|
@@ -738,6 +947,14 @@ export declare class WebMultiSelect<T = any> {
|
|
|
738
947
|
private getBuiltInActionDisabled;
|
|
739
948
|
private renderActionsHTML;
|
|
740
949
|
private renderOption;
|
|
950
|
+
/**
|
|
951
|
+
* Render a single tree-mode row. Separate from `renderOption`: a tree row is
|
|
952
|
+
* indented by its depth (via the `--ms-tree-depth` custom property) and
|
|
953
|
+
* tagged branch/leaf, but otherwise carries the same selection/checkbox/
|
|
954
|
+
* icon/subtitle content. The tree is always fully expanded, so there is no
|
|
955
|
+
* chevron/toggle — every node is just a normal, selectable option.
|
|
956
|
+
*/
|
|
957
|
+
private renderTreeNode;
|
|
741
958
|
private highlightMatch;
|
|
742
959
|
private groupOptions;
|
|
743
960
|
/** Whether the input currently functions as a usable search field (drives placeholder wording). */
|
|
@@ -774,6 +991,13 @@ export declare class WebMultiSelect<T = any> {
|
|
|
774
991
|
* Returning -1 from `compute` is a no-op (used for empty list / no match).
|
|
775
992
|
*/
|
|
776
993
|
private focusBy;
|
|
994
|
+
/**
|
|
995
|
+
* Given a target index and a preferred direction, return the nearest index
|
|
996
|
+
* whose node is selectable (skipping non-selectable tree nodes). Falls back to
|
|
997
|
+
* the opposite direction, then to -1 if nothing is selectable. No-op outside
|
|
998
|
+
* tree mode.
|
|
999
|
+
*/
|
|
1000
|
+
private resolveSelectableIndex;
|
|
777
1001
|
private focusNext;
|
|
778
1002
|
private focusPrevious;
|
|
779
1003
|
private focusFirst;
|
|
@@ -866,7 +1090,18 @@ export declare class WebMultiSelect<T = any> {
|
|
|
866
1090
|
private updateHiddenInput;
|
|
867
1091
|
private getFormValue;
|
|
868
1092
|
getSelected(): T[];
|
|
869
|
-
|
|
1093
|
+
/**
|
|
1094
|
+
* Set the selection programmatically. **Silent by default** — it does not fire
|
|
1095
|
+
* `select`/`deselect`/`change` (so restoring saved state, cascade resets, or a
|
|
1096
|
+
* server-authoritative correction can't loop back or trip "user changed it"
|
|
1097
|
+
* handlers). Pass `{ notify: true }` to announce the result as a **single
|
|
1098
|
+
* aggregate `change`** — for a deliberate user gesture (e.g. an action button)
|
|
1099
|
+
* that should reach the same listeners a manual pick does, without the per-item
|
|
1100
|
+
* `select`/`deselect` flood a bulk change would otherwise cause.
|
|
1101
|
+
*/
|
|
1102
|
+
setSelected(values: (string | number)[], opts?: {
|
|
1103
|
+
notify?: boolean;
|
|
1104
|
+
}): void;
|
|
870
1105
|
/**
|
|
871
1106
|
* Merge a partial config update into the live picker without tearing down the DOM.
|
|
872
1107
|
*
|