import React from "react"; import { XmMultiSelect as XmMultiSelectElement, CustomEvent, } from "../lit/components/multi-select/index.js"; /** * A generic type for strongly typing custom events with their targets * @template T - The type of the event target (extends EventTarget) * @template D - The type of the detail payload for the custom event */ type TypedEvent = E & { target: T; }; /** `XmMultiSelect` component event */ export type XmMultiSelectElementEvent = TypedEvent< XmMultiSelectElement, E >; export type { XmMultiSelectElement, CustomEvent }; export interface XmMultiSelectProps extends Pick< React.AllHTMLAttributes, | "children" | "dir" | "hidden" | "id" | "lang" | "slot" | "style" | "title" | "translate" | "onClick" | "onFocus" | "onBlur" > { /** Render the in-panel search filter. */ searchable?: boolean; /** Dock the panel in place of the combobox trigger — an always-open filter rail (ADR 0031). Search, bulk actions, recents, sections, virtualization and the selection contract are unchanged; only the trigger and the overlay drop away, and the listbox becomes the focus target. */ inline?: boolean; /** Render the "Select all" / "Clear" bulk-action row. "Select all" operates on the search-filtered set and respects `max`. */ bulkActions?: boolean; /** Server mode (ADR 0023): the backend owns filtering — client-side label matching is off, the search emits `xm-multi-select-search`, and scrolling near the end emits `xm-multi-select-load-more`. */ server?: boolean; /** Signal a consumer fetch in flight (server-mode search, or any options refresh): rows already present dim to ghost ink, skeleton rows fade in under them, and "No matches" is suppressed. Distinct from the inherited field-level `loading`, which disables the whole control. */ optionsLoading?: boolean; /** undefined */ required?: boolean; /** undefined */ disabled?: boolean; /** undefined */ readOnly?: boolean; /** undefined */ loading?: boolean; /** INITIAL checked for toggle subclasses; mapped from attribute `checked`. */ initialChecked?: boolean; /** Shown on the trigger when nothing is selected. */ placeholder?: XmMultiSelectElement["placeholder"]; /** Selection cap (0 = uncapped). At the cap, unselected options disable; selected stay removable. */ max?: XmMultiSelectElement["max"]; /** Debounce for `xm-multi-select-search` in milliseconds. */ searchDebounce?: XmMultiSelectElement["searchDebounce"]; /** Server mode: total backend match count for the current query. Drives the "Showing N of M matches" note and load-more paging (0 = unknown/complete). */ totalItems?: XmMultiSelectElement["totalItems"]; /** undefined */ label?: XmMultiSelectElement["label"]; /** undefined */ helper?: XmMultiSelectElement["helper"]; /** Severity copy. Non-empty ⇒ the field is in error (icon + copy, never color). */ error?: XmMultiSelectElement["error"]; /** undefined */ size?: XmMultiSelectElement["size"]; /** Form-control name — mirrors native . */ name?: XmMultiSelectElement["name"]; /** INITIAL value (uncontrolled-first, AD-6): the `value` attribute seeds the live state once, then never overrides user input. Mapped from attribute `value` so authors write ``. */ initialValue?: XmMultiSelectElement["initialValue"]; /** A space-separated list of the classes of the element. Classes allows CSS and JavaScript to select and access specific elements via the class selectors or functions like the method `Document.getElementsByClassName()`. */ className?: string; /** Contains a space-separated list of the part names of the element that should be exposed on the host element. */ exportparts?: string; /** Used for labels to link them with their inputs (using input id). */ htmlFor?: string; /** Used to help React identify which items have changed, are added, or are removed within a list. */ key?: number | string; /** Contains a space-separated list of the part names of the element. Part names allows CSS to select and style specific elements in a shadow tree via the ::part pseudo-element. */ part?: string; /** A mutable ref object whose `.current` property is initialized to the passed argument (`initialValue`). The returned object will persist for the full lifetime of the component. */ ref?: React.Ref; /** Allows developers to make HTML elements focusable, allow or prevent them from being sequentially focusable (usually with the `Tab` key, hence the name) and determine their relative ordering for sequential focus navigation. */ tabIndex?: number; /** The option model — `{ label, value, disabled? }` shared with xm-select / radio-group. */ options?: XmMultiSelectElement["options"]; /** Option values pinned under a "Recent" header above the full list; suppressed while a search query is active. */ recent?: XmMultiSelectElement["recent"]; /** Escape hatch for custom option-row content (mirrors data-table's `renderCell`, ADR 0008/0024): given the option and its render state, returns the row body after the presentational checkbox. The `li`, checkbox, a11y wiring, and toggle stay component-owned. With a virtualized list (>200 rows) the returned content must keep a uniform single-row height. */ renderOption?: XmMultiSelectElement["renderOption"]; /** Read: the selected primitives in option order (values missing from `options` persist after, in selection order). Write (controlled, ADR 0026): replaces the selection wholesale, syncs the form value, and re-renders — without emitting `change` (no controlled echo) — so a consumer can drive the selection from its own state instead of key-remounting. */ selectedValues?: XmMultiSelectElement["selectedValues"]; /** Fired on every toggle; `detail.value` is the full selection as an Array. */ onChange?: (event: XmMultiSelectElementEvent) => void; /** Server mode: debounced search intent (`detail.query`); also fired on open with the current query. The consumer fetches and sets `.options` with the matching set. */ onXmMultiSelectSearch?: (event: XmMultiSelectElementEvent) => void; /** Server mode: fired once per page when the listbox scrolls near its end and `options.length < totalItems` (`detail = { query, offset }`). The consumer appends the next page to `.options`. */ onXmMultiSelectLoadMore?: (event: XmMultiSelectElementEvent) => void; /** Fired on each edit with the current value in `detail` (inherited by every field). */ onInput?: (event: XmMultiSelectElementEvent) => void; } /** * * * ## Attributes & Properties * * Component attributes and properties that can be applied to the element or by using JavaScript. * * - `placeholder`: Shown on the trigger when nothing is selected. * - `max`: Selection cap (0 = uncapped). At the cap, unselected options disable; selected stay removable. * - `searchable`: Render the in-panel search filter. * - `inline`: Dock the panel in place of the combobox trigger — an always-open filter * rail (ADR 0031). Search, bulk actions, recents, sections, virtualization * and the selection contract are unchanged; only the trigger and the * overlay drop away, and the listbox becomes the focus target. * - `bulk-actions`/`bulkActions`: Render the "Select all" / "Clear" bulk-action row. "Select all" operates on the search-filtered set and respects `max`. * - `server`: Server mode (ADR 0023): the backend owns filtering — client-side label matching is off, the search emits `xm-multi-select-search`, and scrolling near the end emits `xm-multi-select-load-more`. * - `options-loading`/`optionsLoading`: Signal a consumer fetch in flight (server-mode search, or any options refresh): rows already present dim to ghost ink, skeleton rows fade in under them, and "No matches" is suppressed. Distinct from the inherited field-level `loading`, which disables the whole control. * - `search-debounce`/`searchDebounce`: Debounce for `xm-multi-select-search` in milliseconds. * - `total-items`/`totalItems`: Server mode: total backend match count for the current query. Drives the "Showing N of M matches" note and load-more paging (0 = unknown/complete). * - `label`: undefined * - `helper`: undefined * - `error`: Severity copy. Non-empty ⇒ the field is in error (icon + copy, never color). * - `size`: undefined * - `required`: undefined * - `disabled`: undefined * - `readonly`/`readOnly`: undefined * - `loading`: undefined * - `name`: Form-control name — mirrors native . * - `value`/`initialValue`: INITIAL value (uncontrolled-first, AD-6): the `value` attribute seeds the * live state once, then never overrides user input. Mapped from attribute * `value` so authors write ``. * - `checked`/`initialChecked`: INITIAL checked for toggle subclasses; mapped from attribute `checked`. * - `options`: The option model — `{ label, value, disabled? }` shared with xm-select / radio-group. (property only) * - `recent`: Option values pinned under a "Recent" header above the full list; suppressed while a search query is active. (property only) * - `renderOption`: Escape hatch for custom option-row content (mirrors data-table's `renderCell`, ADR 0008/0024): * given the option and its render state, returns the row body after the presentational * checkbox. The `li`, checkbox, a11y wiring, and toggle stay component-owned. With a * virtualized list (>200 rows) the returned content must keep a uniform single-row height. (property only) * - `selectedValues`: Read: the selected primitives in option order (values missing from * `options` persist after, in selection order). Write (controlled, ADR * 0026): replaces the selection wholesale, syncs the form value, and * re-renders — without emitting `change` (no controlled echo) — so a * consumer can drive the selection from its own state instead of * key-remounting. (property only) * - `readonly`: undefined (property only) * - `value`: Programmatic reset support — setting `value` after first paint updates live state. (property only) * - `checked`: Public read contract for toggle subclasses (AD-6a). (property only) * * ## Events * * Events that will be emitted by the component. * * - `change`: Fired on every toggle; `detail.value` is the full selection as an Array. * - `xm-multi-select-search`: Server mode: debounced search intent (`detail.query`); also fired on open with the current query. The consumer fetches and sets `.options` with the matching set. * - `xm-multi-select-load-more`: Server mode: fired once per page when the listbox scrolls near its end and `options.length < totalItems` (`detail = { query, offset }`). The consumer appends the next page to `.options`. * - `input`: Fired on each edit with the current value in `detail` (inherited by every field). * * ## Methods * * Methods that can be called to access component functionality. * * - `setFormDisabled(disabled: boolean) => void`: Down-propagation hook: xm-form (Story 2.10) sets this; it OR's with the * field's own disabled and can never re-enable a self-disabled field. */ export const XmMultiSelect: React.ForwardRefExoticComponent;