@keenmate/web-multiselect 1.9.0 → 1.10.0

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
@@ -7,6 +7,28 @@ A lightweight, accessible multiselect web component with typeahead search, RTL l
7
7
 
8
8
  > **⚠️ 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 [HTML Injection (XSS) Notice](#html-injection-xss-notice) for the complete list of affected callbacks.
9
9
 
10
+ ## What's New in v1.10.0
11
+
12
+ - **`data-options` attribute on `<web-multiselect>`** — set options declaratively from HTML, no JS bootstrap required (works alongside `initial-values` for pure-HTML / server-rendered / SharePoint workbench scenarios).
13
+ - **`form.reset()` now clears the selection** — the element is now form-associated (`static formAssociated = true` + `ElementInternals` + `formResetCallback()`).
14
+ - **Dropdown / hint / selected popover no longer clipped inside scrollable ancestors** — Floating UI now uses `strategy: 'fixed'` for all three panels, so they escape `overflow: hidden|auto|scroll` containers (e.g. SharePoint Framework workbenches).
15
+ - **`initial-values` now works when options arrive after init** — values are reconciled on every `options` mutation, not just at construction.
16
+ - **Remove / close (×) buttons render as SVG masks** — pixel-centered regardless of font; color still flows through the existing `--ms-*-color` variables via `currentColor`; three new `--ms-*-icon-size` variables for theming.
17
+ - **Keyboard navigation now keeps working after a mouse click on an option** — previously, clicking an option moved focus from the search input to the option's checkbox (knocking the `keydown` listener offline) *and* left `focusedIndex` at its pre-click value, so subsequent ArrowDown / ArrowUp / Enter went nowhere visible. Click now anchors `focusedIndex` to the clicked option and refocuses the search input, so arrow keys continue from where you clicked and Enter toggles the option under the cursor.
18
+ - **Count-clear / popover-close hover backdrop now matches the rest of the component** — was a circle (`border-radius: 50%`), now a small rounded rectangle (`--ms-border-radius-sm`) consistent with every other interactive element. Themes that prefer the circle can set `--ms-count-clear-border-radius` and `--ms-selected-popover-close-border-radius` back to `50%`.
19
+ - **Keyboard `Enter` respects disabled options** — previously only the click handler did.
20
+ - **End-to-end test suite** — 114 Playwright specs across 19 fixture pages (`npm run test:e2e`).
21
+ - **`THEMING.md`** — new reference cataloguing every theme-able component state and the CSS variables that drive it.
22
+
23
+ ## What's New in v1.9.0
24
+
25
+ - **Live attribute / callback updates no longer rebuild the DOM** — `updateOptions(partial)` merges in place; selection state, scroll position, focus, and tooltips are preserved across attribute changes.
26
+ - **9 previously-dead per-component CSS override hooks are now wired** — `--ms-hint-border-color`, `--ms-dropdown-border-color`, `--ms-actions-border-color`, `--ms-group-border-color`, `--ms-badge-counter-border-color`, `--ms-selected-popover-border-color`, `--ms-selected-popover-header-border-color`, `--ms-option-outline-color-focused`, `--ms-option-border-matched-color`.
27
+ - **`selectAll` / `clearAll` now fire per-item `selectCallback` / `deselectCallback`** — consumers wiring per-item analytics or side effects no longer silently miss bulk operations.
28
+ - **New `Tooltip` class** consolidating three previous tooltip implementations; fixes a handle leak and a popover-vs-main-container collision.
29
+ - **`--base-primary-bg` theming variable** — `--ms-primary-bg` reads it first, then `--base-main-bg`, then a hardcoded default.
30
+ - Plus many fixes across custom action buttons, grouped-option focus, badge cursors, focus rings, and logging.
31
+
10
32
  ## Features
11
33
 
12
34
  - 📝 **Declarative HTML** - Use standard `<option>` and `<optgroup>` elements - no JavaScript required for simple cases!
@@ -1791,7 +1813,9 @@ For the complete list of all available CSS variables, see:
1791
1813
  | `--ms-badge-font-size` | `0.75rem` | Badge font size |
1792
1814
  | `--ms-badge-border-radius` | `0.375rem` | Badge border radius |
1793
1815
  | `--ms-badge-remove-bg` | `var(--ms-accent-color)` | Remove button background |
1794
- | `--ms-badge-remove-color` | `var(--ms-text-color-on-accent)` | Remove button color |
1816
+ | `--ms-badge-remove-color` | `var(--ms-text-color-on-accent)` | Remove button (X) color — applied to the SVG via `currentColor` |
1817
+ | `--ms-badge-remove-icon-size` | `calc(1.0 * var(--ms-rem))` | Size of the X glyph inside the remove button |
1818
+ | `--ms-icon-remove` | (inline SVG `url(...)`) | The X mask SVG; override to swap the glyph shape (alpha-only — color comes from `--ms-badge-remove-color`) |
1795
1819
  | `--ms-badge-counter-text-bg` | `var(--ms-primary-bg)` | BadgeCounter text background ("+X more") |
1796
1820
  | `--ms-badge-counter-text-color` | `var(--ms-text-color-3)` | BadgeCounter text color |
1797
1821
  | `--ms-badge-counter-remove-bg` | `var(--ms-text-color-3)` | BadgeCounter remove button background |
@@ -270,10 +270,11 @@
270
270
  { "name": "ms-badge-remove-bg", "category": "badge", "usage": "Badge remove button background" },
271
271
  { "name": "ms-badge-remove-color", "category": "badge", "usage": "Badge remove button color" },
272
272
  { "name": "ms-badge-remove-border", "category": "badge", "usage": "Badge remove button border" },
273
- { "name": "ms-badge-remove-font-size", "category": "badge", "usage": "Badge remove button font size" },
273
+ { "name": "ms-badge-remove-font-size", "category": "badge", "usage": "Badge remove button font size (unused since 1.10.0 — kept for backward-compat)" },
274
+ { "name": "ms-badge-remove-icon-size", "category": "badge", "usage": "Badge remove X icon size (mask SVG)" },
274
275
  { "name": "ms-badge-remove-bg-hover", "category": "badge", "usage": "Badge remove button hover background" },
275
276
  { "name": "ms-badge-remove-box-shadow-focus", "category": "badge", "usage": "Badge remove button focus shadow" },
276
- { "name": "ms-icon-remove", "category": "badge", "usage": "Remove icon character" },
277
+ { "name": "ms-icon-remove", "category": "badge", "usage": "Remove icon as CSS url() to a mask-friendly SVG; color comes from currentColor" },
277
278
  { "name": "ms-badge-counter-bg", "category": "badge", "usage": "Counter badge background" },
278
279
  { "name": "ms-badge-counter-border", "category": "badge", "usage": "Counter badge border" },
279
280
  { "name": "ms-badge-counter-border-color", "category": "badge", "usage": "Counter badge border color" },
@@ -306,11 +307,12 @@
306
307
  { "name": "ms-count-clear-size", "category": "count", "usage": "Count clear button size" },
307
308
  { "name": "ms-count-clear-bg", "category": "count", "usage": "Count clear button background" },
308
309
  { "name": "ms-count-clear-color", "category": "count", "usage": "Count clear button color" },
309
- { "name": "ms-count-clear-font-size", "category": "count", "usage": "Count clear button font size" },
310
+ { "name": "ms-count-clear-font-size", "category": "count", "usage": "Count clear button font size (unused since 1.10.0 — kept for backward-compat)" },
311
+ { "name": "ms-count-clear-icon-size", "category": "count", "usage": "Count clear X icon size (mask SVG)" },
310
312
  { "name": "ms-count-clear-border-radius", "category": "count", "usage": "Count clear button border radius" },
311
313
  { "name": "ms-count-clear-bg-hover", "category": "count", "usage": "Count clear button hover background" },
312
314
  { "name": "ms-count-clear-color-hover", "category": "count", "usage": "Count clear button hover color" },
313
- { "name": "ms-icon-clear", "category": "count", "usage": "Clear icon character" },
315
+ { "name": "ms-icon-clear", "category": "count", "usage": "Clear icon as CSS url() to a mask-friendly SVG; defaults to var(--ms-icon-remove)" },
314
316
 
315
317
  { "name": "ms-tooltip-bg", "category": "tooltip", "usage": "Tooltip background" },
316
318
  { "name": "ms-tooltip-text-color", "category": "tooltip", "usage": "Tooltip text color" },
@@ -340,7 +342,8 @@
340
342
  { "name": "ms-popover-close-size", "category": "popover", "usage": "Popover close button size" },
341
343
  { "name": "ms-selected-popover-close-bg", "category": "popover", "usage": "Popover close button background" },
342
344
  { "name": "ms-selected-popover-close-color", "category": "popover", "usage": "Popover close button color" },
343
- { "name": "ms-selected-popover-close-font-size", "category": "popover", "usage": "Popover close button font size" },
345
+ { "name": "ms-selected-popover-close-font-size", "category": "popover", "usage": "Popover close button font size (unused since 1.10.0 — kept for backward-compat)" },
346
+ { "name": "ms-selected-popover-close-icon-size", "category": "popover", "usage": "Popover close X icon size (mask SVG)" },
344
347
  { "name": "ms-selected-popover-close-border-radius", "category": "popover", "usage": "Popover close button border radius" },
345
348
  { "name": "ms-selected-popover-close-bg-hover", "category": "popover", "usage": "Popover close button hover background" },
346
349
  { "name": "ms-selected-popover-close-color-hover", "category": "popover", "usage": "Popover close button hover color" },
package/dist/index.d.ts CHANGED
@@ -194,7 +194,7 @@ declare interface MultiSelectConfig<T = any> {
194
194
  isVirtualScrollEnabled?: boolean;
195
195
  /** Vertical alignment of checkboxes relative to option content */
196
196
  checkboxAlign?: 'top' | 'center' | 'bottom';
197
- /** Hint text shown above the input when focused */
197
+ /** Hint text shown above the input while the dropdown is open. */
198
198
  searchHint?: string;
199
199
  /** Placeholder text for the search input */
200
200
  searchPlaceholder?: string;
@@ -269,9 +269,11 @@ declare interface MultiSelectConfig<T = any> {
269
269
  }
270
270
 
271
271
  export declare class MultiSelectElement<T = any> extends BaseElement {
272
+ static formAssociated: boolean;
272
273
  private picker?;
273
274
  private containerElement?;
274
275
  private shadow;
276
+ private internals?;
275
277
  private _options?;
276
278
  private _valueMember?;
277
279
  private _getValueCallback?;
@@ -309,6 +311,14 @@ export declare class MultiSelectElement<T = any> extends BaseElement {
309
311
  private _changeCallback?;
310
312
  static get observedAttributes(): string[];
311
313
  constructor();
314
+ /**
315
+ * Called by the browser when the surrounding <form> is reset. Clears the
316
+ * picker's selection so the multiselect actually participates in the
317
+ * standard reset lifecycle. (Before form-association, reset was a no-op
318
+ * because the hidden inputs were re-stamped from internal state on every
319
+ * render.)
320
+ */
321
+ formResetCallback(): void;
312
322
  connectedCallback(): void;
313
323
  disconnectedCallback(): void;
314
324
  attributeChangedCallback(name: string, oldValue: string | null, newValue: string | null): void;
@@ -638,7 +648,7 @@ export declare class WebMultiSelect<T = any> {
638
648
  private selectOption;
639
649
  private deselectOption;
640
650
  private selectAll;
641
- private clearAll;
651
+ clearAll(): void;
642
652
  /**
643
653
  * Re-render and fire callbacks after a selection state change.
644
654
  * `added` / `removed` drive per-item select/deselect callbacks.
@@ -658,6 +668,15 @@ export declare class WebMultiSelect<T = any> {
658
668
  private positionDropdown;
659
669
  private positionHint;
660
670
  private parseInitialSelection;
671
+ /**
672
+ * Resolve any `selectedValues` entries that don't yet have a matching
673
+ * `selectedOptions` object by looking them up in the current `allOptions`.
674
+ * Idempotent; safe to call after init *and* after `options` is replaced
675
+ * (e.g., async fetch, `searchCallback` result, or late `element.options =`
676
+ * assignment). Without this, `initial-values` declared before options
677
+ * arrive ends up with phantom values that `getValue()` can never report.
678
+ */
679
+ private reconcileSelectedOptions;
661
680
  private toggleSelectedPopover;
662
681
  private showPopover;
663
682
  private hideSelectedPopover;