import type { SelectItem, SelectOption, SelectRow } from './types'; export type SelectCoreConfig = { picker: 'single' | 'multi'; /** * Fetcher invoked on open (empty query) and on every debounced keystroke. Sync wrappers * pass a `(q) => Promise.resolve(buildResults(options, q))` shim so the core stays * single-pathed — there is no separate "sync mode". */ loadOptions: (query: string) => Promise[]>; /** Debounce window for `loadOptions`. Sync wrappers pass `0`. @default 400 */ getDebounceMs?: () => number; /** Equality comparator for `T`. */ getCompare: () => (a: T, b: T) => boolean; /** When false, typing never enters filtering mode — the popover is a click/keyboard-only picker. @default true */ getSearchable?: () => boolean; /** Optional `createOptions.canCreate` validator — presence enables creatable mode. Called with the trimmed query. */ getCanCreate: () => ((q: string) => boolean) | undefined; /** When true, no `kind:'create'` row is emitted (the host renders its own create UI) — keyboard navigation must not reach a row the listbox doesn't show. @default false */ getHideCreateRow?: () => boolean; /** Group-header behavior. `toggle-all` only makes sense for multi. */ getGroupHeader: () => 'static' | 'value' | 'toggle-all'; /** * Multi-only — controls cascade between group.value and children. * - `'children-only'` (default): picking a child adds only the child; group.value is independent. * - `'children-include-parent'`: picking a child also adds the group.value (and unpicking the last child removes it). */ getSelectionMode?: () => 'children-only' | 'children-include-parent'; /** Per-cmp predicate: is this option currently part of the selection? */ isPicked: (option: SelectOption) => boolean; /** When true, already-selected options are filtered out of the listbox (tag-input style). @default false */ getHideSelected?: () => boolean; /** Currently selected options. Used for highlight init + picked-out-of-page surfacing. */ getSelectedOptions: () => SelectOption[]; /** false when the host is disabled/readonly/inert. */ getInteractive: () => boolean; /** Input ref getter (the cmp owns `bind:this`). */ getInputEl: () => HTMLInputElement | undefined; /** Variant-specific value-flow callbacks. */ onPickOption: (option: SelectOption, parentGroup?: { label: string; value?: T; options: SelectOption[]; }) => void; onPickGroupValue: (group: { label: string; value: T; }) => void; onPickGroupAll: (group: { label: string; value?: T; options: SelectOption[]; }) => void; onClear: () => void; /** * Consumer's create handler. Receives the current query — trimmed, since it becomes the new * item's label — and (optionally) a `parentValue` picked via the create-with-parent section. * May return a Promise — when it does, the spinner appears after the {@link SPINNER_DELAY_MS} gate. */ onCreate: (payload: { query: string; parentValue?: T; }) => void | Promise; }; export type SelectCore = { readonly isOpen: boolean; readonly query: string; /** `query` trimmed — what `canCreate` is asked about, what the create row shows, and what `onCreate` receives. Cmps rendering the typed text as a to-be-created label use this, not `query`. */ readonly createQuery: string; readonly highlight: number; readonly isFiltering: boolean; readonly rows: SelectRow[]; /** Current option list (synced from `loadOptions`). Used by the cmp to render the create-with-parent section when groups are present. */ readonly items: SelectItem[]; /** Mirrors the internal `showCreateRow` predicate (canCreate enabled + non-empty query + predicate true + no exact-label match). Cmps gate the create-with-parent section on this. */ readonly canShowCreate: boolean; /** True only after a load/create has been pending for at least {@link SPINNER_DELAY_MS}. Render-side: show the spinner now. */ readonly loading: boolean; /** Mirrors `loading` — listbox should dim and refuse pick events to avoid races against the in-flight handler. */ readonly interactionLocked: boolean; handleFocus: () => void; handleClick: (e: MouseEvent) => void; handleMousedown: (e: MouseEvent) => void; handleInput: (e: Event) => void; handleKeydown: (e: KeyboardEvent) => void; openPopover: () => void; closePopover: () => void; pickRow: (row: SelectRow) => void; setHighlight: (i: number) => void; /** Re-runs `loadOptions` with the current query while the popover is open. No-op when closed. Sync wrappers call this on `options` prop change. */ refresh: () => void; /** Trigger consumer's `onCreate({ query, parentValue })`. Used by the create-with-parent section when its confirm button is clicked. */ confirmCreate: (parentValue?: T) => void; }; export declare function createSelectCore(config: SelectCoreConfig): SelectCore;