/** * createPopoverSelect - the shared HEADLESS engine behind the kit's dropdown * selects (SvMultiSelect, SvTreeSelect, SvGridSelect and any future one). It * owns the parts every anchored-panel select repeats: open/close state, the * `anchoredRect` positioning (reposition on scroll/resize), outside/Escape * dismissal via the shared layer stack, a roving `active` index over the visible * items, the WAI-ARIA combobox wiring (`aria-expanded` / `aria-controls` / * `aria-activedescendant` + stable option ids), and focus management (focus * moves into the panel on open, back to the trigger on close). * * The component owns its own `bind:this` refs and item rendering; it passes the * refs in as getters and spreads the returned prop-getters onto its trigger, * panel and items. Runes-based, like `createListbox`. */ import { type AnchoredRect } from './popover'; export type PopoverSelectConfig = { /** Number of currently-visible/navigable items (post-filter). */ itemCount: () => number; /** Whether item `i` is disabled (skipped by roving, not selectable). */ disabled?: (index: number) => boolean; /** Read-only: value shown, trigger focusable, but the panel will not open. */ readonly?: () => boolean; /** Commit the item at `index` (Enter or a click the component forwards). */ onSelect: (index: number) => void; /** Notified whenever the open state changes. */ onOpenChange?: (open: boolean) => void; /** The trigger element (component owns `bind:this`, passes a getter). */ getTrigger: () => HTMLElement | null; /** The panel element. */ getPanel: () => HTMLElement | null; /** Element focused when the panel opens (e.g. a search input). Defaults to the * panel itself. Its id-holder also carries `aria-activedescendant`. */ getInitialFocus?: () => HTMLElement | null; /** Estimated panel height, for the flip-up decision. Default 280. */ estimatedHeight?: () => number; /** Minimum panel width. Default 200. */ minWidth?: () => number; /** Close after a selection (single-select). Default true. */ closeOnSelect?: () => boolean; /** Active index to start on when opening (e.g. the selected row). Default 0. */ initialActive?: () => number; /** Handle a key the core doesn't (e.g. tree ArrowLeft/Right). Return true if * handled to stop the core's default handling. */ onExtraKey?: (event: KeyboardEvent, active: number) => boolean; idPrefix?: string; }; export declare function createPopoverSelect(config: PopoverSelectConfig): { open: boolean; active: number; readonly rect: AnchoredRect; /** Stable id of the panel element (for aria-controls / labelling). */ panelId: string; /** Stable id of the option at `i` (for aria-activedescendant + item id). */ optionId: (i: number) => string; activeDescendant: () => string | undefined; openPanel: () => void; closePanel: () => void; toggle: () => void; move: (delta: number) => void; first: () => void; last: () => void; selectActive: () => void; updatePos: () => void; onPanelKeydown: (e: KeyboardEvent) => void; /** True when item `i` is the roving-active one. */ isActive: (i: number) => boolean; /** Spread onto the trigger button. Pass the ARIA popup type the panel * exposes (mirrors `focusOwnerProps(role)`); defaults to 'listbox' for * the classic select panels. */ triggerProps: (haspopup?: "listbox" | "tree" | "grid") => { 'aria-haspopup': "grid" | "listbox" | "tree"; 'aria-expanded': boolean; 'aria-controls': string | undefined; 'aria-readonly': true | undefined; onkeydown: (e: KeyboardEvent) => void; }; /** Attributes for the element that OWNS focus + activedescendant (the panel, * or a search input). Pass the ARIA role the panel should expose. */ focusOwnerProps: (role?: "listbox" | "tree" | "grid") => { role: "grid" | "listbox" | "tree"; 'aria-activedescendant': string | undefined; onkeydown: (e: KeyboardEvent) => void; }; /** Spread onto the item element at `i`. */ itemProps: (i: number) => { id: string; 'data-active': string | undefined; onpointermove: () => void; }; }; export type PopoverSelect = ReturnType;