@keenmate/web-multiselect 2.0.0-rc12 → 2.0.1

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 CHANGED
@@ -1,186 +1,201 @@
1
- # @keenmate/web-multiselect
2
-
3
- [![License: MIT](https://img.shields.io/badge/License-MIT-blue.svg)](LICENSE)
4
- [![npm version](https://img.shields.io/npm/v/@keenmate/web-multiselect.svg)](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.
1
+ # @keenmate/web-multiselect
2
+
3
+ [![License: MIT](https://img.shields.io/badge/License-MIT-blue.svg)](LICENSE)
4
+ [![npm version](https://img.shields.io/npm/v/@keenmate/web-multiselect.svg)](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.1
25
+
26
+ - **Single-select — re-clicking the selected option no longer clears the field** — In a `multiple="false"` picker, clicking or pressing Enter on the option that's already selected used to toggle it off and empty the control. Because a single-select row shows no checkbox, there was no cue you were un-picking, so a confirming click read as an accidental wipe. It's now a no-op that simply closes the dropdown, keeping the value selected; clearing stays the dedicated ✕ button's job (`show-clear`). Multi-select toggle-off and single-select replacement are untouched.
27
+ - **Checkbox tick now sizes from the shared `--base-icon-check-size` token** — The selected-row checkmark was masked at `contain` (≈100% of the box), which only looked right when the glyph carried its own viewBox padding; a theme swapping `--base-icon-check` for an edge-to-edge glyph (e.g. a star) rendered it oversized, and it didn't match pure-admin's `.pa-checkbox`. It now reads `var(--base-icon-check-size, 68%)` — the exact knob pure-admin uses — so the mark renders identically in both and a theme can rescale it once for every component. The indeterminate dash inherits the same mask box. The default Lucide check shrinks slightly (≈100% → 68%) to match; that's intended.
28
+ - **Build-time variable-manifest validator** — A new `scripts/check-variable-manifest.mjs` step (wired into `npm run build` as `check:vars`) diffs the `--ms-*` declared and `--base-*` consumed across `src/css/**` against `component-variables.manifest.json` and fails the build on dead or missing entries. This closes the silent-drift gap behind the generated IDE autocomplete and theme-designer surfaces, which are all derived from that manifest.
29
+ - **Manifest drift cleanup** — Removed 24 inert entries the manifest still advertised (the commented-out `--ms-input-size-*` size-preset surface and five positioning vars dropped in the rc11 field-shell rework) and added the missing `--base-icon-plus` / `--base-icon-add` glyphs, so autocomplete no longer offers non-functional knobs and now covers the "add new" prompt icons.
30
+
31
+ ## What's New in v2.0.0
32
+
33
+ - **"Add new" creation mode — the picker doubles as a creation tool.** Turn on `allow-add-new`
34
+ and a search with no matches shows a clickable "Add new …" prompt (label via `add-new-text` /
35
+ `getAddNewTextCallback`). Choosing it (click, Enter, or arrow-to-focus) fires a new bubbling
36
+ `add` event; `addNewCallback` creates the option and is **async and cancelable** (return
37
+ `null`/`undefined` to abort after a confirm dialog or server round-trip) and may return a
38
+ **rich option** that renders like any other. While it runs the prompt shows a spinner +
39
+ `add-new-pending-text`. Creation also works with no callback — handle it entirely off the `add`
40
+ event. See `examples-events-callbacks.html` §EV4b/EV4c.
41
+
42
+ - **Scroll-to imperative API — jump to any option or group.** New `scrollToIndex()` /
43
+ `scrollToValue()` / `scrollToGroup()` on the element and picker bring a row into view — pair with
44
+ `open()` for an "open + jump" gesture. Mode-aware (virtual scroll, tree, mobile fullscreen sheet),
45
+ they align to the top by default (`{ block: 'center' }` to center) and return `false` when the
46
+ target isn't in the current filtered list. A companion public `clearSearch()` reveals a
47
+ filtered-out option so you can then scroll to it, plus a `searchText` getter and a `search(term)`
48
+ method to read and programmatically drive the query. See §BU06b, §VS03, §TR09b.
49
+
50
+ - **Checkbox check/dash + filter funnel now flow from the shared `--base-icon-*` contract.** The
51
+ checkmark and indeterminate dash are now `currentColor` mask glyphs (not CSS-border shapes) reading
52
+ `--base-icon-check` / `--base-icon-indeterminate`, and the search-mode funnel reads
53
+ `--base-icon-filter` — so one base override reskins them across every Keenmate component. Inline
54
+ Lucide fallbacks keep the default look unchanged.
55
+
56
+ - **`--ms-rem` now bridges to the shared `--base-rem` knob.** The global sizing unit resolves
57
+ `var(--base-rem, 10px)`, so a theme that sets `--base-rem` rescales the whole component from one
58
+ variable (the `10px` fallback and per-instance overrides still work with no base layer loaded).
59
+
60
+ - **Fixed: the count-chip clear ✕ and popover close ✕ vanished on hover.** Both derived their hover
61
+ background *and* glyph colour from the same accent, so the ✕ melted into its own hover state
62
+ (fully invisible on near-white accents like Minimal dark). They now fill with a solid accent
63
+ background and flip the glyph to the on-accent colour, matching the badge remove button.
64
+
65
+ ## Demos & docs
66
+
67
+ - 🚀 [Live demo](https://web-multiselect.keenmate.dev)
68
+ - 📘 [Usage / API reference](./docs/usage.md) — attributes, properties, methods, events.
69
+ - 🎨 [Theming](./docs/theming.md) — `--ms-*` variables, dark mode, cascade layers, Theme Designer integration.
70
+ - 📚 [Examples / cookbook](./docs/examples.md) — rich content, async search, virtual scroll, custom rendering, forms.
71
+ - ♿ [Accessibility](./docs/accessibility.md) — keyboard model, ARIA labels, focus behavior.
72
+
73
+ ## Install
74
+
75
+ ```bash
76
+ npm install @keenmate/web-multiselect
77
+ ```
78
+
79
+ ## Quick start
80
+
81
+ **Declarative — no JavaScript required:**
82
+
83
+ ```html
84
+ <script type="module">
85
+ import '@keenmate/web-multiselect';
86
+ </script>
87
+
88
+ <web-multiselect placeholder="Pick a country">
89
+ <option value="cz">Czech Republic</option>
90
+ <option value="sk">Slovakia</option>
91
+ <option value="at">Austria</option>
92
+ </web-multiselect>
93
+ ```
94
+
95
+ **Programmatic — dynamic data + events:**
96
+
97
+ ```html
98
+ <web-multiselect id="picker" search-placeholder="Search…"></web-multiselect>
99
+
100
+ <script type="module">
101
+ import '@keenmate/web-multiselect';
102
+
103
+ const picker = document.getElementById('picker');
104
+ picker.options = [
105
+ { value: 'js', label: 'JavaScript', icon: '🟨' },
106
+ { value: 'ts', label: 'TypeScript', icon: '🔷' },
107
+ { value: 'py', label: 'Python', icon: '🐍' }
108
+ ];
109
+
110
+ picker.addEventListener('change', (e) => {
111
+ console.log('Selected:', e.detail.selectedValues);
112
+ });
113
+ </script>
114
+ ```
115
+
116
+ 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).
117
+
118
+ ## Editor IntelliSense
119
+
120
+ The package ships editor metadata so you get autocomplete and hover docs for the
121
+ element's attributes, events, and all `--ms-*` CSS custom properties. All of it is
122
+ generated from the component's source on every build, so it never drifts.
123
+
124
+ - **JetBrains** (WebStorm / IntelliJ) — works automatically. The IDE discovers
125
+ `web-types.json` via the `web-types` field in `package.json`; no setup needed.
126
+ - **VS Code** — the data files ship but VS Code doesn't auto-discover them from a
127
+ dependency, so point your workspace at them once in `.vscode/settings.json`:
128
+
129
+ ```json
130
+ {
131
+ "html.customData": [
132
+ "./node_modules/@keenmate/web-multiselect/vscode.html-custom-data.json"
133
+ ],
134
+ "css.customData": [
135
+ "./node_modules/@keenmate/web-multiselect/vscode.css-custom-data.json"
136
+ ]
137
+ }
138
+ ```
139
+
140
+ `html.customData` powers tag/attribute completion on `<web-multiselect>`;
141
+ `css.customData` powers completion for the `--ms-*` theming variables. Reload
142
+ the window after adding them.
143
+
144
+ ## Browser support
145
+
146
+ Modern evergreen browsers — anything with native `customElements`, Shadow DOM, and CSS `@layer` support:
147
+
148
+ - Chrome / Edge 99+
149
+ - Firefox 97+
150
+ - Safari 15.4+
151
+
152
+ No polyfills are shipped. SSR-safe: the module imports without crashing in Node, but renders only after hydration in the browser.
153
+
154
+ ## Development
155
+
156
+ ```bash
157
+ # Install dependencies
158
+ npm install
159
+
160
+ # Start dev server (HMR)
161
+ npm run dev
162
+
163
+ # Build for production
164
+ npm run build
165
+
166
+ # Create package tarball
167
+ npm run package
168
+
169
+ # Run tests
170
+ npm run test:unit # Vitest (happy-dom) — fast logic checks
171
+ npm run test:e2e # Playwright (browser) — interaction/visual
172
+ npm test # both
173
+ ```
174
+
175
+ ## Code structure
176
+
177
+ Follows the BlissFramework four-layer web-component layout:
178
+
179
+ | Layer | File | Role |
180
+ |-------|------|------|
181
+ | Element | `src/web-component.ts` | `MultiSelectElement` — custom-element I/O wrapper, `ATTRIBUTE_TABLE`-driven |
182
+ | Logic | `src/multiselect.ts` | `WebMultiSelect<T>` — framework-agnostic core |
183
+ | Service | `src/tooltip.ts`, `src/virtual-scroll.ts` | single-purpose helpers (`Tooltip`, `VirtualScroll`) |
184
+ | Side | `src/types.ts`, `src/logger.ts`, `src/vendor/` | types, logging, vendored deps |
185
+
186
+ Two deviations from the canonical shape, both intentional:
187
+
188
+ - **`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.
189
+ - **`src/vite-env.d.ts`** is a standard Vite ambient-types file, not part of the four-layer model.
190
+
191
+ ## License
192
+
193
+ MIT — see [LICENSE](./LICENSE).
194
+
195
+ ## Built with BlissFramework
196
+
197
+ Follows the [BlissFramework component guidelines](https://blissframework.dev/) for structure, theming, color-scheme, and accessibility. Per-check verifications run via `/validate-web-component`.
198
+
199
+ ## Credits
200
+
201
+ Created by [Keenmate](https://github.com/keenmate) as part of the Pure Admin design system.
@@ -49,11 +49,29 @@
49
49
  { "name": "base-border-radius-sm", "required": false, "usage": "Small radius (badges, buttons)" },
50
50
  { "name": "base-border-radius-md", "required": false, "usage": "Medium radius (input)" },
51
51
  { "name": "base-border-radius-lg", "required": false, "usage": "Large radius (dropdown)" },
52
- { "name": "base-input-size-xs-height", "required": false, "usage": "Extra small input height (multiplier)" },
53
- { "name": "base-input-size-sm-height", "required": false, "usage": "Small input height (multiplier)" },
54
- { "name": "base-input-size-md-height", "required": false, "usage": "Medium input height (multiplier)" },
55
- { "name": "base-input-size-lg-height", "required": false, "usage": "Large input height (multiplier)" },
56
- { "name": "base-input-size-xl-height", "required": false, "usage": "Extra large input height (multiplier)" }
52
+ { "name": "base-input-size-md-height", "required": false, "usage": "Medium input height multiplier (consumed by --ms-input-height)" },
53
+ { "name": "base-rem", "required": false, "usage": "Global sizing unit — drives --ms-rem (falls back to 10px); scales the whole component" },
54
+ { "name": "base-ease-standard", "required": false, "usage": "Standard easing curve (drives --ms-easing-snappy)" },
55
+ { "name": "base-checkbox-border-color", "required": false, "usage": "Option checkbox border color" },
56
+ { "name": "base-input-clear-color", "required": false, "usage": "Inline field-clear (✕) icon color" },
57
+ { "name": "base-input-clear-bg-hover", "required": false, "usage": "Inline field-clear (✕) hover background" },
58
+ { "name": "base-icon-chevron", "required": false, "usage": "Shared chevron glyph (toggle indicator + fullscreen pager); inline Lucide SVG fallback" },
59
+ { "name": "base-icon-clear", "required": false, "usage": "Inline field-clear (✕) glyph; falls back to --ms-icon-remove" },
60
+ { "name": "base-icon-remove", "required": false, "usage": "Badge-remove glyph; falls back to base-icon-close then inline Lucide SVG" },
61
+ { "name": "base-icon-close", "required": false, "usage": "Generic close glyph (fallback for badge-remove)" },
62
+ { "name": "base-icon-search", "required": false, "usage": "Search glyph; inline Lucide SVG fallback" },
63
+ { "name": "base-icon-filter", "required": false, "usage": "Funnel/filter glyph (fullscreen search-mode toggle); chained by --ms-icon-filter; inline Lucide SVG fallback" },
64
+ { "name": "base-icon-plus", "required": false, "usage": "Plus glyph for the \"add new\" empty-state prompt; chained by --ms-icon-add-new; falls back to base-icon-add then inline Lucide SVG" },
65
+ { "name": "base-icon-add", "required": false, "usage": "Alternate add glyph (fallback for base-icon-plus in the \"add new\" prompt)" },
66
+ { "name": "base-icon-check", "required": false, "usage": "Checkbox/option checkmark glyph; chained by --ms-icon-check; inline Lucide SVG fallback" },
67
+ { "name": "base-icon-check-size", "required": false, "usage": "mask-size for the checkbox glyph inside its box (shared with pure-admin's .pa-checkbox); default 68%. Also sizes the indeterminate dash (shares the mask box)" },
68
+ { "name": "base-icon-indeterminate", "required": false, "usage": "Tri-state (partial) checkbox dash glyph; chained by --ms-icon-indeterminate; inline Lucide SVG fallback" },
69
+ { "name": "base-danger-bg", "required": false, "usage": "Error message background" },
70
+ { "name": "base-danger-color", "required": false, "usage": "Error message text color" },
71
+ { "name": "base-warning-bg", "required": false, "usage": "Warning message background" },
72
+ { "name": "base-warning-color", "required": false, "usage": "Warning message text color" },
73
+ { "name": "base-success-bg", "required": false, "usage": "Success message background" },
74
+ { "name": "base-success-color", "required": false, "usage": "Success message text color" }
57
75
  ],
58
76
  "componentVariables": [
59
77
  { "name": "ms-rem", "category": "sizing", "usage": "Base sizing unit for proportional scaling (default: 10px)" },
@@ -85,41 +103,17 @@
85
103
  { "name": "ms-input-border-focus", "category": "input", "usage": "Input border when focused" },
86
104
  { "name": "ms-input-placeholder-color", "category": "input", "usage": "Placeholder text color" },
87
105
  { "name": "ms-input-bg-disabled", "category": "input", "usage": "Disabled input background" },
88
- { "name": "ms-input-padding", "category": "input", "usage": "Input padding" },
89
- { "name": "ms-input-padding-right", "category": "input", "usage": "Input right padding (for toggle)" },
90
106
  { "name": "ms-input-height", "category": "input", "usage": "Input height" },
91
107
  { "name": "ms-input-font-size", "category": "input", "usage": "Input font size" },
92
108
  { "name": "ms-input-border-width", "category": "input", "usage": "Input border width" },
93
109
  { "name": "ms-input-border-radius", "category": "input", "usage": "Input border radius" },
94
110
  { "name": "ms-input-text", "category": "input", "usage": "Input text color" },
95
111
 
96
- { "name": "ms-input-size-xs-font", "category": "input-size", "usage": "XS size font" },
97
- { "name": "ms-input-size-xs-padding-v", "category": "input-size", "usage": "XS vertical padding" },
98
- { "name": "ms-input-size-xs-padding-h", "category": "input-size", "usage": "XS horizontal padding" },
99
- { "name": "ms-input-size-xs-height", "category": "input-size", "usage": "XS input height" },
100
- { "name": "ms-input-size-sm-font", "category": "input-size", "usage": "SM size font" },
101
- { "name": "ms-input-size-sm-padding-v", "category": "input-size", "usage": "SM vertical padding" },
102
- { "name": "ms-input-size-sm-padding-h", "category": "input-size", "usage": "SM horizontal padding" },
103
- { "name": "ms-input-size-sm-height", "category": "input-size", "usage": "SM input height" },
104
- { "name": "ms-input-size-md-font", "category": "input-size", "usage": "MD size font" },
105
- { "name": "ms-input-size-md-padding-v", "category": "input-size", "usage": "MD vertical padding" },
106
- { "name": "ms-input-size-md-padding-h", "category": "input-size", "usage": "MD horizontal padding" },
107
- { "name": "ms-input-size-md-height", "category": "input-size", "usage": "MD input height" },
108
- { "name": "ms-input-size-lg-font", "category": "input-size", "usage": "LG size font" },
109
- { "name": "ms-input-size-lg-padding-v", "category": "input-size", "usage": "LG vertical padding" },
110
- { "name": "ms-input-size-lg-padding-h", "category": "input-size", "usage": "LG horizontal padding" },
111
- { "name": "ms-input-size-lg-height", "category": "input-size", "usage": "LG input height" },
112
- { "name": "ms-input-size-xl-font", "category": "input-size", "usage": "XL size font" },
113
- { "name": "ms-input-size-xl-padding-v", "category": "input-size", "usage": "XL vertical padding" },
114
- { "name": "ms-input-size-xl-padding-h", "category": "input-size", "usage": "XL horizontal padding" },
115
- { "name": "ms-input-size-xl-height", "category": "input-size", "usage": "XL input height" },
116
112
 
117
- { "name": "ms-toggle-right", "category": "toggle", "usage": "Toggle icon right position" },
118
113
  { "name": "ms-toggle-color", "category": "toggle", "usage": "Toggle icon color" },
119
114
  { "name": "ms-toggle-icon-color", "category": "toggle", "usage": "Toggle icon color (semantic)" },
120
115
  { "name": "ms-toggle-icon-color-open", "category": "toggle", "usage": "Toggle icon color when open" },
121
116
 
122
- { "name": "ms-counter-offset", "category": "counter", "usage": "Counter badge offset" },
123
117
  { "name": "ms-counter-padding", "category": "counter", "usage": "Counter badge padding" },
124
118
  { "name": "ms-counter-bg", "category": "counter", "usage": "Counter badge background" },
125
119
  { "name": "ms-counter-color", "category": "counter", "usage": "Counter badge text color" },
@@ -280,6 +274,9 @@
280
274
  { "name": "ms-badge-remove-bg-hover", "category": "badge", "usage": "Badge remove button hover background" },
281
275
  { "name": "ms-badge-remove-box-shadow-focus", "category": "badge", "usage": "Badge remove button focus shadow" },
282
276
  { "name": "ms-icon-remove", "category": "badge", "usage": "Remove icon as CSS url() to a mask-friendly SVG; color comes from currentColor" },
277
+ { "name": "ms-icon-filter", "category": "icon", "usage": "Funnel glyph (fullscreen search-mode toggle); chains to --base-icon-filter" },
278
+ { "name": "ms-icon-check", "category": "checkbox", "usage": "Checkmark glyph (mask) for a checked checkbox/option; chains to --base-icon-check" },
279
+ { "name": "ms-icon-indeterminate", "category": "checkbox", "usage": "Dash glyph (mask) for a tri-state/partial checkbox; chains to --base-icon-indeterminate" },
283
280
  { "name": "ms-badge-counter-bg", "category": "badge", "usage": "Counter badge background" },
284
281
  { "name": "ms-badge-counter-border", "category": "badge", "usage": "Counter badge border" },
285
282
  { "name": "ms-badge-counter-border-color", "category": "badge", "usage": "Counter badge border color" },
@@ -404,7 +401,6 @@
404
401
  { "name": "ms-debug-stats-border-radius", "category": "debug", "usage": "Debug stats nested panel corner radius" },
405
402
  { "name": "ms-debug-bullet-color", "category": "debug", "usage": "Debug stat bullet marker color" },
406
403
 
407
- { "name": "ms-transform-center-y", "category": "transform", "usage": "Center Y transform" },
408
404
  { "name": "ms-transform-rotate-180", "category": "transform", "usage": "180 degree rotation" },
409
405
  { "name": "ms-transform-scale-hover", "category": "transform", "usage": "Hover scale transform" },
410
406
  { "name": "ms-transform-scale-active", "category": "transform", "usage": "Active scale transform" },