@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/README.md +39 -11
- package/component-variables.manifest.json +1 -0
- package/dist/index.d.ts +340 -21
- package/dist/multiselect.js +1608 -878
- package/dist/multiselect.umd.js +22 -21
- package/dist/style.css +1 -1
- package/docs/examples.md +46 -2
- package/docs/usage.md +32 -4
- package/package.json +2 -1
- package/src/css/controls.css +50 -4
- package/src/css/floating.css +28 -0
- package/src/css/main.css +1 -0
- package/src/css/options.css +52 -2
- package/src/css/tree.css +42 -0
- package/src/css/variables.css +45 -4
package/README.md
CHANGED
|
@@ -21,17 +21,29 @@ 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
|
-
- **search
|
|
29
|
-
- **
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
- **
|
|
34
|
-
|
|
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
|
+
|
|
35
|
+
## What's New in v1.12.0-rc05
|
|
36
|
+
|
|
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.
|
|
38
|
+
|
|
39
|
+
- **Events — fire-and-forget callbacks renamed to `on*` (breaking)** — `selectCallback` / `deselectCallback` / `changeCallback` are now `onSelect` / `onDeselect` / `onChange`, and on `ActionButton` the predicate callbacks become `getIsVisibleCallback` / `getIsDisabledCallback` while `onClick` keeps its name. The rule is simple: a function whose return value the component ignores is an *event* and takes the `on*` shape; functions whose return value is consumed stay `get*Callback` / `before*Callback`. The bubbling `select` / `deselect` / `change` DOM events are unchanged — the `on*` properties are just their JS-property twins. There are no deprecated aliases, so update `el.selectCallback` → `el.onSelect` (and the matching config keys); `addEventListener('select' | 'deselect' | 'change', …)` is unaffected.
|
|
40
|
+
- **Selection — new `beforeSelectCallback` / `beforeDeselectCallback` interceptors** — veto a selection or deselection before it happens by returning `false`; each receives the option being toggled plus the current selection. The veto is silent — a blocked action mutates no state and fires no event, so your handler owns any feedback. The deselect veto is enforced across *every* interactive removal path (dropdown toggle, badge `×`, selected-items popover, and the "remove hidden" badge) by routing them through a single funnel, so no affordance can bypass it; programmatic `setSelected()` and the Select-All / Clear-All buttons deliberately skip it. Typical uses: mutually-exclusive options, or a required item that can't be removed.
|
|
41
|
+
- **Dropdown options — hover tooltips, independently configurable** — option rows can now show a hover tooltip via `enable-option-tooltips`, built on the same Floating-UI `Tooltip` engine as the badge and action tooltips (show/hide delays, plus re-attachment as virtual-scrolled rows recycle into view). Default content is the option's display value with its subtitle on a second line; override per option with `getOptionTooltipCallback`. Unlike the badge tooltips they share an engine with, option tooltips are fully independent: `option-tooltip-placement` defaults to `top-start` so the tooltip anchors to a wide row's start edge instead of centering in the middle of a 1920px-wide row, `option-tooltip-follow-cursor` makes it track the pointer, and a dedicated `.ms__option-tooltip` class plus `--ms-option-tooltip-*` variables (each defaulting to the shared `--ms-tooltip-*` surface) let you restyle them separately. Demonstrated on the new `examples-tooltips.html`.
|
|
42
|
+
- **Action buttons — position, rows, and alignment** — the action-buttons block can sit at the `bottom` of the dropdown as a sticky footer (`actions-position`), buttons can be arranged across multiple stacked rows via a per-button `row` property (row 1 always at the panel's outer edge, higher rows stacking inward toward the options list — so `column-reverse` keeps row 1 at the bottom for the footer case), and `actions-align` controls horizontal arrangement (`stretch` default, plus `left` / `right` / `center` / `space-between`). Internally `.ms__actions` became a vertical stack of `.ms__actions-row` lines. All additive and backward compatible — no `row` means a single row, and `stretch` preserves the previous full-width buttons.
|
|
43
|
+
- **Action buttons — smarter built-in defaults** — the built-in `select-all` and `clear-all` actions now manage their own disabled state: Select All disables once every selectable option is chosen, and Clear All disables while nothing is selected. This applies only when you haven't set an explicit static `isDisabled` or a `getIsDisabledCallback` (precedence: dynamic callback › static `isDisabled` › built-in default), so existing custom action configs are untouched.
|
|
44
|
+
- **Single-select — dropdown no longer reopens with a stale filter** — the single-select input doubles as label display and search box; reopening used to blank the visible text while leaving the list filtered by the previous search, so the box and the list silently disagreed. `open()` now mirrors the kept search term back into the box, keeping the visible input and the filtered results in sync (honoring `should-keep-search-on-close`).
|
|
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.
|
|
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.
|
|
35
47
|
|
|
36
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.
|
|
37
49
|
|
|
@@ -119,6 +131,22 @@ npm run test:e2e # Playwright (browser) — interaction/visual
|
|
|
119
131
|
npm test # both
|
|
120
132
|
```
|
|
121
133
|
|
|
134
|
+
## Code structure
|
|
135
|
+
|
|
136
|
+
Follows the BlissFramework four-layer web-component layout:
|
|
137
|
+
|
|
138
|
+
| Layer | File | Role |
|
|
139
|
+
|-------|------|------|
|
|
140
|
+
| Element | `src/web-component.ts` | `MultiSelectElement` — custom-element I/O wrapper, `ATTRIBUTE_TABLE`-driven |
|
|
141
|
+
| Logic | `src/multiselect.ts` | `WebMultiSelect<T>` — framework-agnostic core |
|
|
142
|
+
| Service | `src/tooltip.ts`, `src/virtual-scroll.ts` | single-purpose helpers (`Tooltip`, `VirtualScroll`) |
|
|
143
|
+
| Side | `src/types.ts`, `src/logger.ts`, `src/vendor/` | types, logging, vendored deps |
|
|
144
|
+
|
|
145
|
+
Two deviations from the canonical shape, both intentional:
|
|
146
|
+
|
|
147
|
+
- **`MultiSelectElement extends BaseElement`, not `HTMLElement` directly.** `BaseElement` is a local `const` resolving to `HTMLElement` in the browser and to a stub class under SSR (`typeof HTMLElement === 'undefined'`), so importing the module in Node doesn't throw. A literal `grep "extends HTMLElement"` structure check will not match here by design.
|
|
148
|
+
- **`src/vite-env.d.ts`** is a standard Vite ambient-types file, not part of the four-layer model.
|
|
149
|
+
|
|
122
150
|
## License
|
|
123
151
|
|
|
124
152
|
MIT — see [LICENSE](./LICENSE).
|
|
@@ -241,6 +241,7 @@
|
|
|
241
241
|
{ "name": "ms-checkbox-checked-bg-hover", "category": "checkbox", "usage": "Checked checkbox hover background" },
|
|
242
242
|
{ "name": "ms-checkbox-checked-border-color-hover", "category": "checkbox", "usage": "Checked checkbox hover border" },
|
|
243
243
|
|
|
244
|
+
{ "name": "ms-state-min-height", "category": "empty", "usage": "Shared min-height for the empty and loading states so the dropdown doesn't resize when swapping between them" },
|
|
244
245
|
{ "name": "ms-empty-padding", "category": "empty", "usage": "Empty state padding" },
|
|
245
246
|
{ "name": "ms-empty-font-size", "category": "empty", "usage": "Empty state font size" },
|
|
246
247
|
{ "name": "ms-empty-color", "category": "empty", "usage": "Empty state text color" },
|