@keenmate/web-multiselect 2.2.0-rc01 → 2.2.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/dist/index.d.ts CHANGED
@@ -22,7 +22,7 @@ import { TABLET_MIN_SHORT_SIDE } from '@keenmate/web-components-core';
22
22
  * Action button configuration for dropdown actions (Select All, Clear All, custom actions)
23
23
  * @template T The type of data items
24
24
  */
25
- declare interface ActionButton<T = any> {
25
+ export declare interface ActionButton<T = any> {
26
26
  /** Action identifier ('select-all', 'clear-all', or 'custom' for custom actions) */
27
27
  action: 'select-all' | 'clear-all' | 'custom';
28
28
  /** Button text label */
@@ -42,18 +42,53 @@ declare interface ActionButton<T = any> {
42
42
  isVisible?: boolean;
43
43
  /** Static disabled state - set to true to disable button */
44
44
  isDisabled?: boolean;
45
- /** Custom click handler (required for 'custom' action) */
46
- onClick?: (multiselect: any) => void | Promise<void>;
47
- /** Dynamic visibility callback - return false to hide button (takes priority over isVisible) */
48
- getIsVisibleCallback?: (multiselect: any) => boolean;
49
- /** Dynamic disabled state callback - return true to disable button (takes priority over isDisabled) */
50
- getIsDisabledCallback?: (multiselect: any) => boolean;
51
- /** Dynamic text callback - return button text (takes priority over text) */
52
- getTextCallback?: (multiselect: any) => string;
53
- /** Dynamic CSS class callback - return class name(s) (takes priority over cssClass) */
54
- getClassCallback?: (multiselect: any) => string | string[];
55
- /** Dynamic tooltip callback - return tooltip text (takes priority over tooltip) */
56
- getTooltipCallback?: (multiselect: any) => string;
45
+ /**
46
+ * Custom click handler (required for 'custom' action). The 1st arg is the live picker
47
+ * instance (as before); the additive 2nd arg is a typed {@link ActionContext} (state +
48
+ * controller). One-argument handlers keep working.
49
+ */
50
+ onClick?: (multiselect: any, context?: ActionContext<T>) => void | Promise<void>;
51
+ /** Dynamic visibility callback - return false to hide button (takes priority over isVisible). Additive 2nd arg: {@link ActionContext}. */
52
+ getIsVisibleCallback?: (multiselect: any, context?: ActionContext<T>) => boolean;
53
+ /** Dynamic disabled state callback - return true to disable button (takes priority over isDisabled). Additive 2nd arg: {@link ActionContext}. */
54
+ getIsDisabledCallback?: (multiselect: any, context?: ActionContext<T>) => boolean;
55
+ /** Dynamic text callback - return button text (takes priority over text). Additive 2nd arg: {@link ActionContext}. */
56
+ getTextCallback?: (multiselect: any, context?: ActionContext<T>) => string;
57
+ /** Dynamic CSS class callback - return class name(s) (takes priority over cssClass). Additive 2nd arg: {@link ActionContext}. */
58
+ getClassCallback?: (multiselect: any, context?: ActionContext<T>) => string | string[];
59
+ /** Dynamic tooltip callback - return tooltip text (takes priority over tooltip). Additive 2nd arg: {@link ActionContext}. */
60
+ getTooltipCallback?: (multiselect: any, context?: ActionContext<T>) => string;
61
+ }
62
+
63
+ /**
64
+ * Context handed (as the additive 2nd argument) to the action-button callbacks —
65
+ * {@link ActionButton.getTextCallback} / `getIsVisibleCallback` / `getIsDisabledCallback` /
66
+ * `getClassCallback` / `getTooltipCallback` — and to the `onClick` event. A typed snapshot of
67
+ * live state plus a {@link MultiSelectController} facade, replacing reliance on the untyped picker
68
+ * instance passed as the first argument. Extends {@link PresentationContext}, so a callback can
69
+ * branch on how the panel is presented (floating vs fullscreen), consistent with the render callbacks.
70
+ */
71
+ export declare interface ActionContext<T = any> extends PresentationContext {
72
+ /** The action button config entry this callback belongs to. */
73
+ button: ActionButton<T>;
74
+ /** Currently selected scalar values. */
75
+ selectedValues: (string | number)[];
76
+ /** Currently selected option objects, in selection order. */
77
+ selectedOptions: T[];
78
+ /** All available options (post assignment). */
79
+ options: ReadonlyArray<T>;
80
+ /** `selectedOptions.length` — convenience. */
81
+ selectedCount: number;
82
+ /** `options.length` — convenience (the Select-All denominator). */
83
+ optionCount: number;
84
+ /** Whether the dropdown is currently open. */
85
+ isOpen: boolean;
86
+ /** The current search term. */
87
+ searchTerm: string;
88
+ /** Imperative facade — drive the picker (selection / dropdown / search / messages). */
89
+ controller: MultiSelectController<T>;
90
+ /** Escape hatch: the host custom element, for anything not on the controller. */
91
+ element: HTMLElement & Record<string, any>;
57
92
  }
58
93
 
59
94
  /** Horizontal arrangement of buttons within an action row. `stretch` = full-width (default). */
@@ -218,8 +253,15 @@ declare interface MultiSelectConfig<T = any> {
218
253
  displayValueMember?: string;
219
254
  /** Callback to extract display value from item */
220
255
  getDisplayValueCallback?: (item: T) => string;
221
- /** Callback to customize badge display text (defaults to display value if not provided) */
222
- getBadgeDisplayCallback?: (item: T) => string;
256
+ /**
257
+ * Callback to customize badge display text (defaults to display value if not provided).
258
+ * The second argument is additive: when the value is computed while rendering a specific
259
+ * badge/popover item it receives that item's {@link BadgeContentRenderContext} (`displayMode`,
260
+ * `isInPopover`, and the shared presentation fields); it is **absent** when the value is needed
261
+ * outside a render (e.g. selected-order sorting, the counter chip's title), so one-argument
262
+ * callbacks keep working. Treat `context` as optional.
263
+ */
264
+ getBadgeDisplayCallback?: (item: T, context?: BadgeContentRenderContext) => string;
223
265
  /**
224
266
  * Order of the CURRENTLY-SELECTED items *where they are displayed* — badges, partial mode
225
267
  * (i.e. which items sit behind the "+N more" badge), and the selected-items popover. This is a
@@ -248,10 +290,22 @@ declare interface MultiSelectConfig<T = any> {
248
290
  fullTitleMember?: string;
249
291
  /** Callback to extract the full title from an item (takes precedence over `fullTitleMember`). */
250
292
  getFullTitleCallback?: (item: T) => string;
251
- /** Callback to add custom CSS classes to badges - return string or array of class names */
252
- getBadgeClassCallback?: (item: T) => string | string[];
293
+ /**
294
+ * Callback to add custom CSS classes to badges - return string or array of class names.
295
+ * Additive 2nd arg: receives the badge's {@link BadgeContentRenderContext} when invoked during
296
+ * a badge render (the same context {@link renderBadgeContentCallback} gets), so classes can react
297
+ * to `displayMode` / `isInPopover` / presentation. Optional — one-argument callbacks keep working.
298
+ */
299
+ getBadgeClassCallback?: (item: T, context?: BadgeContentRenderContext) => string | string[];
253
300
  /** Callback to inject custom CSS into Shadow DOM - return CSS string for styling custom classes */
254
301
  customStylesCallback?: () => string;
302
+ /**
303
+ * Static CSS string injected into the Shadow DOM (attribute alternative to
304
+ * `customStylesCallback`, via the `custom-styles` attribute). The value is a
305
+ * raw stylesheet — selectors and all — dropped verbatim into the same
306
+ * replaceable style slot. `customStylesCallback` wins when both are set.
307
+ */
308
+ customStyles?: string;
255
309
  /** Member property name for search value extraction */
256
310
  searchValueMember?: string;
257
311
  /** Callback to extract search value from item */
@@ -362,8 +416,13 @@ declare interface MultiSelectConfig<T = any> {
362
416
  * presentation fields. The second argument is additive; one-argument callbacks keep working.
363
417
  */
364
418
  renderSelectedItemContentCallback?: (item: T, context: BadgeContentRenderContext) => string | HTMLElement;
365
- /** Callback to add custom CSS classes to selected items in popover - return string or array of class names */
366
- getSelectedItemClassCallback?: (item: T) => string | string[];
419
+ /**
420
+ * Callback to add custom CSS classes to selected items in popover - return string or array of
421
+ * class names. Additive 2nd arg: receives the {@link BadgeContentRenderContext} for the popover
422
+ * item (`isInPopover` is `true`) — the same context {@link renderSelectedItemContentCallback}
423
+ * gets. Optional — one-argument callbacks keep working.
424
+ */
425
+ getSelectedItemClassCallback?: (item: T, context?: BadgeContentRenderContext) => string | string[];
367
426
  /**
368
427
  * Custom renderer for the selected item display in single-select mode — return plain text (it
369
428
  * becomes the input value). Receives a {@link SelectedContentRenderContext} (2nd arg) carrying
@@ -687,10 +746,17 @@ declare interface MultiSelectConfig<T = any> {
687
746
  getCountLabelCallback?: ((selected: number, total: number) => string) | null;
688
747
  /** Enable tooltips on selected item badges (internal: isBadgeTooltipsEnabled) */
689
748
  isBadgeTooltipsEnabled?: boolean;
690
- /** Callback to generate custom tooltip content for a badge */
691
- getBadgeTooltipCallback?: ((item: T) => string | HTMLElement) | null;
692
- /** Callback to generate custom tooltip text for a remove button */
693
- getRemoveButtonTooltipCallback?: ((item: T) => string) | null;
749
+ /**
750
+ * Callback to generate custom tooltip content for a badge. Additive 2nd arg: receives the
751
+ * badge's {@link BadgeContentRenderContext} (`displayMode` / `isInPopover` / presentation), the
752
+ * same context {@link renderBadgeContentCallback} gets. Optional — one-argument callbacks keep working.
753
+ */
754
+ getBadgeTooltipCallback?: ((item: T, context?: BadgeContentRenderContext) => string | HTMLElement) | null;
755
+ /**
756
+ * Callback to generate custom tooltip text for a remove button. Additive 2nd arg: the badge's
757
+ * {@link BadgeContentRenderContext}. Optional — one-argument callbacks keep working.
758
+ */
759
+ getRemoveButtonTooltipCallback?: ((item: T, context?: BadgeContentRenderContext) => string) | null;
694
760
  /** Format string for remove button tooltip text. Use {0} as placeholder for item name. Default: "Remove {0}" */
695
761
  removeButtonTooltipText?: string;
696
762
  /**
@@ -706,8 +772,14 @@ declare interface MultiSelectConfig<T = any> {
706
772
  badgeTooltipOffset?: number;
707
773
  /** Enable tooltips on dropdown options (internal: isOptionTooltipsEnabled) */
708
774
  isOptionTooltipsEnabled?: boolean;
709
- /** Callback to generate custom tooltip content for a dropdown option. Default: display value, plus subtitle on the next line when present. */
710
- getOptionTooltipCallback?: ((item: T) => string | HTMLElement) | null;
775
+ /**
776
+ * Callback to generate custom tooltip content for a dropdown option. Default: display value, plus
777
+ * subtitle on the next line when present. Additive 2nd arg: receives the row's
778
+ * {@link OptionContentRenderContext} (`index`, `isSelected`, `isFocused`, `isMatched`,
779
+ * `isDisabled`, presentation), the same context {@link renderOptionContentCallback} gets, so an
780
+ * option tooltip can match how the row itself was rendered. Optional — one-argument callbacks keep working.
781
+ */
782
+ getOptionTooltipCallback?: ((item: T, context?: OptionContentRenderContext) => string | HTMLElement) | null;
711
783
  /**
712
784
  * Option tooltip placement (Floating UI `Placement`). Default `top-start`
713
785
  * (anchored to the row's start edge, so it doesn't center on a full-width row).
@@ -729,6 +801,46 @@ declare interface MultiSelectConfig<T = any> {
729
801
  hostElement?: HTMLElement;
730
802
  }
731
803
 
804
+ /**
805
+ * Imperative facade for driving a live picker from a callback without reaching into internals.
806
+ * Shared base of the callback controllers: {@link MultiSelectKeyboardController} (handed to
807
+ * `keydownCallback`) extends it with focus-navigation, and {@link ActionContext} exposes it to
808
+ * the action-button callbacks. Every method mirrors a public element method.
809
+ */
810
+ export declare interface MultiSelectController<T = any> {
811
+ /** The selected option objects, in selection order (mirrors `el.getSelected()`). */
812
+ getSelected(): T[];
813
+ /** The selection as returned to forms/consumers — scalar or array per `multiple` (mirrors `el.getValue()`). */
814
+ getValue(): string | number | (string | number)[] | null;
815
+ /** All available options (post assignment, pre-filter). */
816
+ getOptions(): ReadonlyArray<T>;
817
+ /** Replace the selection. Silent by default; pass `{ notify: true }` to emit ONE aggregate `change`. */
818
+ setSelected(values: (string | number)[], opts?: {
819
+ notify?: boolean;
820
+ }): void;
821
+ /** Select every selectable option. */
822
+ selectAll(): void;
823
+ /** Clear the whole selection. */
824
+ clearAll(): void;
825
+ /** Toggle a single option by its value (select ⇄ deselect). */
826
+ toggleValue(value: string | number): void;
827
+ open(): void;
828
+ close(): void;
829
+ toggle(): void;
830
+ /** Set the search box text (runs the search, exactly as if typed). */
831
+ search(term: string): void;
832
+ /** Clear the search box and restore the full list (does not touch the selection). */
833
+ clearSearch(): void;
834
+ /** Scroll a specific option / group / index into view. */
835
+ scrollToValue(value: string | number): void;
836
+ scrollToGroup(group: string): void;
837
+ scrollToIndex(index: number): void;
838
+ /** Surface a transient message ("toast"), visible even in the fullscreen overlay. */
839
+ showMessage(content: string | HTMLElement, opts?: MessageOptions): void;
840
+ /** Dismiss the transient message, if any. */
841
+ hideMessage(): void;
842
+ }
843
+
732
844
  export declare class MultiSelectElement<T = any> extends BlissElement<MultiSelectEvents> {
733
845
  #private;
734
846
  static formAssociated: boolean;
@@ -895,10 +1007,11 @@ declare type MultiSelectEvents = {
895
1007
  };
896
1008
 
897
1009
  /**
898
- * Imperative facade handed to `keydownCallback` so a consumer can drive the picker without
899
- * reaching into internals. Every method mirrors a built-in keyboard action.
1010
+ * Imperative facade handed to `keydownCallback`: the shared {@link MultiSelectController} plus the
1011
+ * focus-navigation methods that only make sense mid-keystroke. Every method mirrors a built-in
1012
+ * keyboard action.
900
1013
  */
901
- declare interface MultiSelectKeyboardController<T = any> {
1014
+ export declare interface MultiSelectKeyboardController<T = any> extends MultiSelectController<T> {
902
1015
  /** Move focus to the next / previous option. */
903
1016
  focusNext(): void;
904
1017
  focusPrevious(): void;
@@ -915,19 +1028,12 @@ declare interface MultiSelectKeyboardController<T = any> {
915
1028
  focusIndex(index: number): void;
916
1029
  /** Toggle the currently focused option (no-op if nothing is focused). */
917
1030
  toggleFocused(): void;
918
- /** Toggle a specific option by its value (select ⇄ deselect). */
919
- toggleValue(value: string | number): void;
920
1031
  /** Select a specific option by its value (no-op if already selected). */
921
1032
  selectValue(value: string | number): void;
922
1033
  /** Deselect a specific option by its value (no-op if not selected). */
923
1034
  deselectValue(value: string | number): void;
924
- /** Open / close the dropdown. */
925
- open(): void;
926
- close(): void;
927
- /** Set the search box text (runs the search). */
1035
+ /** @deprecated Alias of {@link MultiSelectController.search}. */
928
1036
  setSearch(term: string): void;
929
- /** Clear the search box and reset the visible list. */
930
- clearSearch(): void;
931
1037
  }
932
1038
 
933
1039
  /**
@@ -1534,6 +1640,17 @@ export declare class WebMultiSelect<T = any> {
1534
1640
  /** Lazily build (and cache) the imperative facade passed to `keydownCallback`. Bound to the
1535
1641
  * same private actions the built-in key handling uses, so consumer shortcuts behave identically. */
1536
1642
  private getKeyboardController;
1643
+ /**
1644
+ * The host custom element — `this.element` is the internal `.ms` mount (inside the shadow
1645
+ * root), so the host is its root node's `host` when shadowed, else the mount itself. Used as
1646
+ * the {@link ActionContext} escape hatch (and where a wrapper hangs a server bridge).
1647
+ */
1648
+ private hostEl;
1649
+ /**
1650
+ * Build the {@link ActionContext} passed (as the additive 2nd arg) to every action-button
1651
+ * callback and the `onClick` event: a snapshot of live state plus the shared controller.
1652
+ */
1653
+ private buildActionContext;
1537
1654
  /** Clear the search box (both the main input and the fullscreen search) and reset the visible
1538
1655
  * list. Shared by Escape and the keyboard controller. */
1539
1656
  /**
@@ -1707,6 +1824,22 @@ export declare class WebMultiSelect<T = any> {
1707
1824
  * so they don't actually break the sheet.
1708
1825
  */
1709
1826
  private warnFullscreenContainingBlock;
1827
+ /**
1828
+ * Re-anchor an already-open floating dropdown from scratch so a frozen placement
1829
+ * is re-evaluated against the panel's CURRENT height.
1830
+ *
1831
+ * Why it's needed: an async `searchCallback` opens the panel while it's still
1832
+ * empty / showing the loader — short, so it fits below the input and (with the
1833
+ * default `lock-placement`) freezes to `bottom`. When results arrive the panel
1834
+ * grows to full height, but the frozen placement pins it below the input, so it
1835
+ * overflows the viewport bottom instead of flipping above into the free space.
1836
+ * `renderDropdown()` only rewrites the inner HTML; it never re-anchors. Tearing
1837
+ * down and recreating the anchor re-runs core's flip-on-first-compute against the
1838
+ * new height (picking the side that fits), then re-freezes — so `lock-placement`
1839
+ * still holds for the common case (panels that open already-populated, e.g. local
1840
+ * filtering, never hit this path). No-op unless a floating dropdown is open.
1841
+ */
1842
+ private repositionDropdown;
1710
1843
  private positionDropdown;
1711
1844
  /**
1712
1845
  * Switch how the open panels are presented. 'floating' anchors them to the input
@@ -1886,6 +2019,12 @@ export declare class WebMultiSelect<T = any> {
1886
2019
  * `null → ""`. Callers add their own leading space / base class as needed.
1887
2020
  */
1888
2021
  private classSuffix;
2022
+ /**
2023
+ * Build the {@link BadgeContentRenderContext} handed to the sibling `get*` callbacks
2024
+ * (badge display / class / tooltip), so they see the same context the `render*` badge
2025
+ * callbacks get: `displayMode`, `isInPopover`, plus the shared presentation fields.
2026
+ */
2027
+ private badgeRenderContext;
1889
2028
  /**
1890
2029
  * Render a removable badge for a selected option (used by the badges/partial display modes
1891
2030
  * and by the selected-items popover).