@keenmate/web-multiselect 2.0.0-rc10 → 2.0.0-rc12
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 +186 -186
- package/custom-elements.json +214 -75
- package/dist/index.d.ts +48 -3
- package/dist/multiselect.js +1342 -1222
- package/dist/multiselect.umd.js +13 -13
- package/dist/style.css +1 -1
- package/docs/usage.md +6 -0
- package/package.json +104 -104
- package/src/css/controls.css +95 -25
- package/src/css/floating.css +15 -4
- package/src/css/states.css +6 -5
- package/src/css/variables.css +59 -22
- package/vscode.html-custom-data.json +11 -1
- package/web-types.json +27 -2
package/README.md
CHANGED
|
@@ -1,186 +1,186 @@
|
|
|
1
|
-
# @keenmate/web-multiselect
|
|
2
|
-
|
|
3
|
-
[](LICENSE)
|
|
4
|
-
[](https://www.npmjs.com/package/@keenmate/web-multiselect)
|
|
5
|
-
|
|
6
|
-
> A lightweight, themeable multi-select web component with typeahead search, RTL support, rich content, and full keyboard navigation.
|
|
7
|
-
|
|
8
|
-
## What is it
|
|
9
|
-
|
|
10
|
-
`@keenmate/web-multiselect` is a custom element (`<web-multiselect>`) that turns a list of options into a searchable, themeable multi-select dropdown. Framework-agnostic — works in React, Vue, Svelte, Blazor, plain HTML.
|
|
11
|
-
|
|
12
|
-
Reads `--base-*` variables from the page if [`@keenmate/theme-designer`](https://theme-designer.keenmate.dev) is present, falls back to sensible OS-aware defaults otherwise, and ships with first-class dark-mode and per-instance theming.
|
|
13
|
-
|
|
14
|
-
**Headline features:**
|
|
15
|
-
|
|
16
|
-
- Declarative `<option>` / `<optgroup>` markup — no JavaScript required for simple cases.
|
|
17
|
-
- Virtual scrolling for 10,000+ option datasets (25× faster opening, 99.8% memory reduction).
|
|
18
|
-
- Filter or navigate search modes; async / hybrid search.
|
|
19
|
-
- Five badge display modes (pills, count, compact, partial, none) with positioning on any side.
|
|
20
|
-
- Full keyboard navigation, RTL language support, badge tooltips.
|
|
21
|
-
- Custom rendering callbacks for options, badges, and group headers.
|
|
22
|
-
- Form integration via standard hidden inputs (FormData-compatible).
|
|
23
|
-
|
|
24
|
-
## What's New in v2.0.0-
|
|
25
|
-
|
|
26
|
-
- **`
|
|
27
|
-
|
|
28
|
-
-
|
|
29
|
-
|
|
30
|
-
-
|
|
31
|
-
|
|
32
|
-
-
|
|
33
|
-
|
|
34
|
-
- **
|
|
35
|
-
|
|
36
|
-
- **Dropdown
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
- **
|
|
43
|
-
|
|
44
|
-
- **
|
|
45
|
-
|
|
46
|
-
- **
|
|
47
|
-
|
|
48
|
-
- **
|
|
49
|
-
|
|
50
|
-
## Demos & docs
|
|
51
|
-
|
|
52
|
-
- 🚀 [Live demo](https://web-multiselect.keenmate.dev)
|
|
53
|
-
- 📘 [Usage / API reference](./docs/usage.md) — attributes, properties, methods, events.
|
|
54
|
-
- 🎨 [Theming](./docs/theming.md) — `--ms-*` variables, dark mode, cascade layers, Theme Designer integration.
|
|
55
|
-
- 📚 [Examples / cookbook](./docs/examples.md) — rich content, async search, virtual scroll, custom rendering, forms.
|
|
56
|
-
- ♿ [Accessibility](./docs/accessibility.md) — keyboard model, ARIA labels, focus behavior.
|
|
57
|
-
|
|
58
|
-
## Install
|
|
59
|
-
|
|
60
|
-
```bash
|
|
61
|
-
npm install @keenmate/web-multiselect
|
|
62
|
-
```
|
|
63
|
-
|
|
64
|
-
## Quick start
|
|
65
|
-
|
|
66
|
-
**Declarative — no JavaScript required:**
|
|
67
|
-
|
|
68
|
-
```html
|
|
69
|
-
<script type="module">
|
|
70
|
-
import '@keenmate/web-multiselect';
|
|
71
|
-
</script>
|
|
72
|
-
|
|
73
|
-
<web-multiselect placeholder="Pick a country">
|
|
74
|
-
<option value="cz">Czech Republic</option>
|
|
75
|
-
<option value="sk">Slovakia</option>
|
|
76
|
-
<option value="at">Austria</option>
|
|
77
|
-
</web-multiselect>
|
|
78
|
-
```
|
|
79
|
-
|
|
80
|
-
**Programmatic — dynamic data + events:**
|
|
81
|
-
|
|
82
|
-
```html
|
|
83
|
-
<web-multiselect id="picker" search-placeholder="Search…"></web-multiselect>
|
|
84
|
-
|
|
85
|
-
<script type="module">
|
|
86
|
-
import '@keenmate/web-multiselect';
|
|
87
|
-
|
|
88
|
-
const picker = document.getElementById('picker');
|
|
89
|
-
picker.options = [
|
|
90
|
-
{ value: 'js', label: 'JavaScript', icon: '🟨' },
|
|
91
|
-
{ value: 'ts', label: 'TypeScript', icon: '🔷' },
|
|
92
|
-
{ value: 'py', label: 'Python', icon: '🐍' }
|
|
93
|
-
];
|
|
94
|
-
|
|
95
|
-
picker.addEventListener('change', (e) => {
|
|
96
|
-
console.log('Selected:', e.detail.selectedValues);
|
|
97
|
-
});
|
|
98
|
-
</script>
|
|
99
|
-
```
|
|
100
|
-
|
|
101
|
-
See [docs/usage.md](./docs/usage.md) for the full API and [docs/examples.md](./docs/examples.md) for advanced patterns (async data, virtual scrolling, custom rendering, form integration).
|
|
102
|
-
|
|
103
|
-
## Editor IntelliSense
|
|
104
|
-
|
|
105
|
-
The package ships editor metadata so you get autocomplete and hover docs for the
|
|
106
|
-
element's attributes, events, and all `--ms-*` CSS custom properties. All of it is
|
|
107
|
-
generated from the component's source on every build, so it never drifts.
|
|
108
|
-
|
|
109
|
-
- **JetBrains** (WebStorm / IntelliJ) — works automatically. The IDE discovers
|
|
110
|
-
`web-types.json` via the `web-types` field in `package.json`; no setup needed.
|
|
111
|
-
- **VS Code** — the data files ship but VS Code doesn't auto-discover them from a
|
|
112
|
-
dependency, so point your workspace at them once in `.vscode/settings.json`:
|
|
113
|
-
|
|
114
|
-
```json
|
|
115
|
-
{
|
|
116
|
-
"html.customData": [
|
|
117
|
-
"./node_modules/@keenmate/web-multiselect/vscode.html-custom-data.json"
|
|
118
|
-
],
|
|
119
|
-
"css.customData": [
|
|
120
|
-
"./node_modules/@keenmate/web-multiselect/vscode.css-custom-data.json"
|
|
121
|
-
]
|
|
122
|
-
}
|
|
123
|
-
```
|
|
124
|
-
|
|
125
|
-
`html.customData` powers tag/attribute completion on `<web-multiselect>`;
|
|
126
|
-
`css.customData` powers completion for the `--ms-*` theming variables. Reload
|
|
127
|
-
the window after adding them.
|
|
128
|
-
|
|
129
|
-
## Browser support
|
|
130
|
-
|
|
131
|
-
Modern evergreen browsers — anything with native `customElements`, Shadow DOM, and CSS `@layer` support:
|
|
132
|
-
|
|
133
|
-
- Chrome / Edge 99+
|
|
134
|
-
- Firefox 97+
|
|
135
|
-
- Safari 15.4+
|
|
136
|
-
|
|
137
|
-
No polyfills are shipped. SSR-safe: the module imports without crashing in Node, but renders only after hydration in the browser.
|
|
138
|
-
|
|
139
|
-
## Development
|
|
140
|
-
|
|
141
|
-
```bash
|
|
142
|
-
# Install dependencies
|
|
143
|
-
npm install
|
|
144
|
-
|
|
145
|
-
# Start dev server (HMR)
|
|
146
|
-
npm run dev
|
|
147
|
-
|
|
148
|
-
# Build for production
|
|
149
|
-
npm run build
|
|
150
|
-
|
|
151
|
-
# Create package tarball
|
|
152
|
-
npm run package
|
|
153
|
-
|
|
154
|
-
# Run tests
|
|
155
|
-
npm run test:unit # Vitest (happy-dom) — fast logic checks
|
|
156
|
-
npm run test:e2e # Playwright (browser) — interaction/visual
|
|
157
|
-
npm test # both
|
|
158
|
-
```
|
|
159
|
-
|
|
160
|
-
## Code structure
|
|
161
|
-
|
|
162
|
-
Follows the BlissFramework four-layer web-component layout:
|
|
163
|
-
|
|
164
|
-
| Layer | File | Role |
|
|
165
|
-
|-------|------|------|
|
|
166
|
-
| Element | `src/web-component.ts` | `MultiSelectElement` — custom-element I/O wrapper, `ATTRIBUTE_TABLE`-driven |
|
|
167
|
-
| Logic | `src/multiselect.ts` | `WebMultiSelect<T>` — framework-agnostic core |
|
|
168
|
-
| Service | `src/tooltip.ts`, `src/virtual-scroll.ts` | single-purpose helpers (`Tooltip`, `VirtualScroll`) |
|
|
169
|
-
| Side | `src/types.ts`, `src/logger.ts`, `src/vendor/` | types, logging, vendored deps |
|
|
170
|
-
|
|
171
|
-
Two deviations from the canonical shape, both intentional:
|
|
172
|
-
|
|
173
|
-
- **`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.
|
|
174
|
-
- **`src/vite-env.d.ts`** is a standard Vite ambient-types file, not part of the four-layer model.
|
|
175
|
-
|
|
176
|
-
## License
|
|
177
|
-
|
|
178
|
-
MIT — see [LICENSE](./LICENSE).
|
|
179
|
-
|
|
180
|
-
## Built with BlissFramework
|
|
181
|
-
|
|
182
|
-
Follows the [BlissFramework component guidelines](https://blissframework.dev/) for structure, theming, color-scheme, and accessibility. Per-check verifications run via `/validate-web-component`.
|
|
183
|
-
|
|
184
|
-
## Credits
|
|
185
|
-
|
|
186
|
-
Created by [Keenmate](https://github.com/keenmate) as part of the Pure Admin design system.
|
|
1
|
+
# @keenmate/web-multiselect
|
|
2
|
+
|
|
3
|
+
[](LICENSE)
|
|
4
|
+
[](https://www.npmjs.com/package/@keenmate/web-multiselect)
|
|
5
|
+
|
|
6
|
+
> A lightweight, themeable multi-select web component with typeahead search, RTL support, rich content, and full keyboard navigation.
|
|
7
|
+
|
|
8
|
+
## What is it
|
|
9
|
+
|
|
10
|
+
`@keenmate/web-multiselect` is a custom element (`<web-multiselect>`) that turns a list of options into a searchable, themeable multi-select dropdown. Framework-agnostic — works in React, Vue, Svelte, Blazor, plain HTML.
|
|
11
|
+
|
|
12
|
+
Reads `--base-*` variables from the page if [`@keenmate/theme-designer`](https://theme-designer.keenmate.dev) is present, falls back to sensible OS-aware defaults otherwise, and ships with first-class dark-mode and per-instance theming.
|
|
13
|
+
|
|
14
|
+
**Headline features:**
|
|
15
|
+
|
|
16
|
+
- Declarative `<option>` / `<optgroup>` markup — no JavaScript required for simple cases.
|
|
17
|
+
- Virtual scrolling for 10,000+ option datasets (25× faster opening, 99.8% memory reduction).
|
|
18
|
+
- Filter or navigate search modes; async / hybrid search.
|
|
19
|
+
- Five badge display modes (pills, count, compact, partial, none) with positioning on any side.
|
|
20
|
+
- Full keyboard navigation, RTL language support, badge tooltips.
|
|
21
|
+
- Custom rendering callbacks for options, badges, and group headers.
|
|
22
|
+
- Form integration via standard hidden inputs (FormData-compatible).
|
|
23
|
+
|
|
24
|
+
## What's New in v2.0.0-rc12
|
|
25
|
+
|
|
26
|
+
- **`overlay-group` — one overlay open at a time, across components.** The dropdown now joins a cross-component single-active-overlay group (core `registerOverlay`): opening it dismisses every other participating overlay — other multiselects, datepickers, or any external popover that fires the `km-overlay-activated` document event — and it closes itself when another overlay in its group opens. The new `overlay-group` attribute/property scopes this to a named group (same group = coordinate, different groups = independent, unset = the default ungrouped pool). Outside-click dismissal is unchanged and always on; this only governs the open-broadcast. Fixes the old behavior where two multiselects could sit open simultaneously.
|
|
27
|
+
|
|
28
|
+
- **Icon glyphs now flow from the shared `--base-icon-*` contract.** The four `--ms-icon-*` glyphs with a shared counterpart (chevron, field-clear, badge-remove, search) are wired through the `@keenmate/base-css-variables` layer, so a single `--base-icon-*` override re-skins that affordance across every KeenMate component at once — matching how the ~95 other `--ms-*` tokens already fall back to `--base-*`. The toggle/pager chevron now flows from `--base-icon-chevron` (a Lucide angle that keeps the previous optical size); field-clear, badge-remove, and search follow their base equivalents. Each keeps its inline Lucide SVG as the standalone fallback, so default appearance is unchanged when no base layer is loaded.
|
|
29
|
+
|
|
30
|
+
- **`--ms-toggle-rotate-closed` / `--ms-toggle-rotate-open` — themeable chevron rotation.** The toggle rotates the *directional* base chevron into place (defaults `90deg` closed → down, `-90deg` open → up). A theme that supplies a **pre-oriented** glyph (one that already points down) can now opt out: set both to `0deg` for a static icon, or `0deg` / `180deg` for a down-glyph that flips up on open — without touching the base contract. The new Material Design card in `examples-theming.html` demonstrates the pairing.
|
|
31
|
+
|
|
32
|
+
- **`--ms-fullscreen-nav-btn-icon` — the pager glyph is now independent.** The fullscreen match-navigator's prev/next buttons previously masked the shared `--ms-icon-chevron`, so a theme that repointed the base chevron at a pre-oriented toggle glyph would leak it into the pager (which rotates its source ±90° and expects a right-pointing chevron). The pager now reads its own token, defaulting to `--ms-icon-chevron` — nothing changes by default, but a theme can diverge the pager glyph on its own.
|
|
33
|
+
|
|
34
|
+
- **More tokens flow from `--base-*` for dark-mode fidelity.** A follow-up audit wired five more `--ms-*` tokens that hardcoded a value where a dedicated `--base-*` counterpart exists: disabled-input background and dropdown box-shadow are now `light-dark()`-aware (they no longer stay light on dark themes), the field-clear color/hover knobs gain dedicated base hooks, and the snappy easing matches the base standard curve. All keep their prior value as the standalone fallback; transition *durations* and the z-index stack are deliberately left local.
|
|
35
|
+
|
|
36
|
+
- **Dropdown / selected-items popover no longer render 2px wider than the field.** Both panels size from `--ms-input-current-width` (the field wrapper's border-box `offsetWidth`) but were themselves `content-box`, so each added its own 1px border on top and overhung the input it anchors to. Both now use `box-sizing: border-box`, so their outer width matches the field exactly.
|
|
37
|
+
|
|
38
|
+
## What's New in v2.0.0-rc11
|
|
39
|
+
|
|
40
|
+
- **Imperative open/close API — drive the dropdown from code.** The `<web-multiselect>` element and the underlying `WebMultiSelect` now expose `open()`, `close()`, `toggle()`, and a read/write `isOpen` property, mirroring the calendar API in web-daterangepicker. Each element method flushes pending property writes first (the same contract as `getSelected()`/`setSelected()`), so `el.options = data; el.open()` works with no `await` in between. Calling `open()` from your own button's click handler now opens *and stays open* — previously the same click bubbled to the outside-click listener and re-closed it. See the new `examples-data-api.html` §API07 demo.
|
|
41
|
+
|
|
42
|
+
- **Inline clear (✕) button — wipe the whole selection from inside the input.** A new opt-in `show-clear` attribute renders a small ✕ at the input's trailing edge that appears only while something is selected. Clicking it clears the selection and any search text, fires a single `change`, refocuses the input, and closes the selected-items popover if it was open — without popping the dropdown open. It's drawn as a themeable CSS mask icon (`--ms-input-clear-*`, whose corner radius follows `--ms-border-radius`). See the new `examples-basic.html` §BU01b demo.
|
|
43
|
+
|
|
44
|
+
- **Input decorations rebuilt as a flex "field shell" — no more overlap or text bleed.** `.ms__input-wrapper` is now the bordered field (border, background, radius, focus ring via `:focus-within`), with the `<input>`, the `[N]` counter, the ✕ clear, and the chevron as real flex children in a spaced row. Previously each was absolutely pinned by a hard-coded inset, so `show-counter` + `show-clear` collided and long text could slide under the icons. Now they space themselves via `--ms-input-gap`, long text clips cleanly inside the input's own box, and RTL mirroring falls out of the flex direction for free. Several obsolete positioning vars were removed (`--ms-input-padding`, `--ms-input-padding-right`, `--ms-toggle-right`, `--ms-counter-offset`, `--ms-input-clear-inset`, `--ms-input-clear-gutter`, `--ms-transform-center-y`).
|
|
45
|
+
|
|
46
|
+
- **Selected-items popover now lines up with the field.** `--ms-selected-popover-width` used to default to a fixed 32rem independent of the control, which looked detached under a wide field; it now defaults to `var(--ms-input-current-width)`, so the popover and the dropdown both track the field width and align under it. Set `selected-popover-width` (or the CSS var) to a fixed length to restore the old constant-width behavior.
|
|
47
|
+
|
|
48
|
+
- **Toggle chevron is now a crisp icon, not a text character.** The dropdown indicator renders the shared `--ms-icon-chevron` glyph through a CSS mask — consistent with the ✕, count-clear, and badge-remove icons — instead of the Unicode `▼`, so it no longer depends on font rendering and themes uniformly via `--ms-toggle-icon-color` / `--ms-toggle-icon-size`. It still points down when closed and rotates up when open.
|
|
49
|
+
|
|
50
|
+
## Demos & docs
|
|
51
|
+
|
|
52
|
+
- 🚀 [Live demo](https://web-multiselect.keenmate.dev)
|
|
53
|
+
- 📘 [Usage / API reference](./docs/usage.md) — attributes, properties, methods, events.
|
|
54
|
+
- 🎨 [Theming](./docs/theming.md) — `--ms-*` variables, dark mode, cascade layers, Theme Designer integration.
|
|
55
|
+
- 📚 [Examples / cookbook](./docs/examples.md) — rich content, async search, virtual scroll, custom rendering, forms.
|
|
56
|
+
- ♿ [Accessibility](./docs/accessibility.md) — keyboard model, ARIA labels, focus behavior.
|
|
57
|
+
|
|
58
|
+
## Install
|
|
59
|
+
|
|
60
|
+
```bash
|
|
61
|
+
npm install @keenmate/web-multiselect
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
## Quick start
|
|
65
|
+
|
|
66
|
+
**Declarative — no JavaScript required:**
|
|
67
|
+
|
|
68
|
+
```html
|
|
69
|
+
<script type="module">
|
|
70
|
+
import '@keenmate/web-multiselect';
|
|
71
|
+
</script>
|
|
72
|
+
|
|
73
|
+
<web-multiselect placeholder="Pick a country">
|
|
74
|
+
<option value="cz">Czech Republic</option>
|
|
75
|
+
<option value="sk">Slovakia</option>
|
|
76
|
+
<option value="at">Austria</option>
|
|
77
|
+
</web-multiselect>
|
|
78
|
+
```
|
|
79
|
+
|
|
80
|
+
**Programmatic — dynamic data + events:**
|
|
81
|
+
|
|
82
|
+
```html
|
|
83
|
+
<web-multiselect id="picker" search-placeholder="Search…"></web-multiselect>
|
|
84
|
+
|
|
85
|
+
<script type="module">
|
|
86
|
+
import '@keenmate/web-multiselect';
|
|
87
|
+
|
|
88
|
+
const picker = document.getElementById('picker');
|
|
89
|
+
picker.options = [
|
|
90
|
+
{ value: 'js', label: 'JavaScript', icon: '🟨' },
|
|
91
|
+
{ value: 'ts', label: 'TypeScript', icon: '🔷' },
|
|
92
|
+
{ value: 'py', label: 'Python', icon: '🐍' }
|
|
93
|
+
];
|
|
94
|
+
|
|
95
|
+
picker.addEventListener('change', (e) => {
|
|
96
|
+
console.log('Selected:', e.detail.selectedValues);
|
|
97
|
+
});
|
|
98
|
+
</script>
|
|
99
|
+
```
|
|
100
|
+
|
|
101
|
+
See [docs/usage.md](./docs/usage.md) for the full API and [docs/examples.md](./docs/examples.md) for advanced patterns (async data, virtual scrolling, custom rendering, form integration).
|
|
102
|
+
|
|
103
|
+
## Editor IntelliSense
|
|
104
|
+
|
|
105
|
+
The package ships editor metadata so you get autocomplete and hover docs for the
|
|
106
|
+
element's attributes, events, and all `--ms-*` CSS custom properties. All of it is
|
|
107
|
+
generated from the component's source on every build, so it never drifts.
|
|
108
|
+
|
|
109
|
+
- **JetBrains** (WebStorm / IntelliJ) — works automatically. The IDE discovers
|
|
110
|
+
`web-types.json` via the `web-types` field in `package.json`; no setup needed.
|
|
111
|
+
- **VS Code** — the data files ship but VS Code doesn't auto-discover them from a
|
|
112
|
+
dependency, so point your workspace at them once in `.vscode/settings.json`:
|
|
113
|
+
|
|
114
|
+
```json
|
|
115
|
+
{
|
|
116
|
+
"html.customData": [
|
|
117
|
+
"./node_modules/@keenmate/web-multiselect/vscode.html-custom-data.json"
|
|
118
|
+
],
|
|
119
|
+
"css.customData": [
|
|
120
|
+
"./node_modules/@keenmate/web-multiselect/vscode.css-custom-data.json"
|
|
121
|
+
]
|
|
122
|
+
}
|
|
123
|
+
```
|
|
124
|
+
|
|
125
|
+
`html.customData` powers tag/attribute completion on `<web-multiselect>`;
|
|
126
|
+
`css.customData` powers completion for the `--ms-*` theming variables. Reload
|
|
127
|
+
the window after adding them.
|
|
128
|
+
|
|
129
|
+
## Browser support
|
|
130
|
+
|
|
131
|
+
Modern evergreen browsers — anything with native `customElements`, Shadow DOM, and CSS `@layer` support:
|
|
132
|
+
|
|
133
|
+
- Chrome / Edge 99+
|
|
134
|
+
- Firefox 97+
|
|
135
|
+
- Safari 15.4+
|
|
136
|
+
|
|
137
|
+
No polyfills are shipped. SSR-safe: the module imports without crashing in Node, but renders only after hydration in the browser.
|
|
138
|
+
|
|
139
|
+
## Development
|
|
140
|
+
|
|
141
|
+
```bash
|
|
142
|
+
# Install dependencies
|
|
143
|
+
npm install
|
|
144
|
+
|
|
145
|
+
# Start dev server (HMR)
|
|
146
|
+
npm run dev
|
|
147
|
+
|
|
148
|
+
# Build for production
|
|
149
|
+
npm run build
|
|
150
|
+
|
|
151
|
+
# Create package tarball
|
|
152
|
+
npm run package
|
|
153
|
+
|
|
154
|
+
# Run tests
|
|
155
|
+
npm run test:unit # Vitest (happy-dom) — fast logic checks
|
|
156
|
+
npm run test:e2e # Playwright (browser) — interaction/visual
|
|
157
|
+
npm test # both
|
|
158
|
+
```
|
|
159
|
+
|
|
160
|
+
## Code structure
|
|
161
|
+
|
|
162
|
+
Follows the BlissFramework four-layer web-component layout:
|
|
163
|
+
|
|
164
|
+
| Layer | File | Role |
|
|
165
|
+
|-------|------|------|
|
|
166
|
+
| Element | `src/web-component.ts` | `MultiSelectElement` — custom-element I/O wrapper, `ATTRIBUTE_TABLE`-driven |
|
|
167
|
+
| Logic | `src/multiselect.ts` | `WebMultiSelect<T>` — framework-agnostic core |
|
|
168
|
+
| Service | `src/tooltip.ts`, `src/virtual-scroll.ts` | single-purpose helpers (`Tooltip`, `VirtualScroll`) |
|
|
169
|
+
| Side | `src/types.ts`, `src/logger.ts`, `src/vendor/` | types, logging, vendored deps |
|
|
170
|
+
|
|
171
|
+
Two deviations from the canonical shape, both intentional:
|
|
172
|
+
|
|
173
|
+
- **`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.
|
|
174
|
+
- **`src/vite-env.d.ts`** is a standard Vite ambient-types file, not part of the four-layer model.
|
|
175
|
+
|
|
176
|
+
## License
|
|
177
|
+
|
|
178
|
+
MIT — see [LICENSE](./LICENSE).
|
|
179
|
+
|
|
180
|
+
## Built with BlissFramework
|
|
181
|
+
|
|
182
|
+
Follows the [BlissFramework component guidelines](https://blissframework.dev/) for structure, theming, color-scheme, and accessibility. Per-check verifications run via `/validate-web-component`.
|
|
183
|
+
|
|
184
|
+
## Credits
|
|
185
|
+
|
|
186
|
+
Created by [Keenmate](https://github.com/keenmate) as part of the Pure Admin design system.
|