export type ComboboxSource = HTMLInputElement | HTMLSelectElement; export type MatchStrategy = "includes" | "startswith" | "fuzzy" | "pattern"; export type ComboboxContext = { combobox: Combobox; source: ComboboxSource; /** * The live search/filter input */ input: HTMLInputElement; }; export type LoadContext = { signal: AbortSignal; cursor: string | null; combobox: Combobox; source: ComboboxSource; input: HTMLInputElement; }; export type LoadResult = { items: import("./helpers.js").ComboboxItem[]; cursor: string | null; } | import("./helpers.js").ComboboxItem[]; export type LoadCallback = (query: string, context: LoadContext) => Promise; export type CreateContext = { signal: AbortSignal; combobox: Combobox; source: ComboboxSource; input: HTMLInputElement; /** * True when running on the native fallback path */ fallback?: boolean; }; export type CreateCallback = (label: string, context: CreateContext) => Promise; export type GuardCallback = (payload: any, context: CreateContext) => Promise | boolean | void; export type TokenizeCallback = (value: string, context: ComboboxContext) => { tokens: string[]; rest?: string; }; export type RenderContext = { combobox: Combobox; query?: string; selected?: boolean; error?: any; }; export type ItemRenderer = (item: import("./helpers.js").ComboboxItem, context: RenderContext) => Node | string | null | undefined; export type TextRenderer = (query: string, context: RenderContext) => Node | string | null | undefined; export type ErrorRenderer = (query: string, context: RenderContext & { error: any; }) => Node | string | null | undefined; export type AttributeSnapshot = { /** * Restore the captured attributes (removes what * was absent, rewrites what had a value) */ restore: () => void; }; export type Messages = import("./messages.js").Messages; export type RenderMap = { option?: ItemRenderer; group?: TextRenderer; item?: ItemRenderer; create?: TextRenderer; noResults?: TextRenderer; loading?: TextRenderer; error?: ErrorRenderer; }; export type GuardMap = { add?: GuardCallback; remove?: GuardCallback; clear?: GuardCallback; }; export type ComboboxOptions = { match?: MatchStrategy | ((item: import("./helpers.js").ComboboxItem, query: string, context: ComboboxContext) => boolean); searchFields?: string | string[]; minChars?: number; allowEmptyOption?: boolean; placeholder?: string; messages?: Messages; load?: LoadCallback; loadOnEmpty?: boolean; shouldLoad?: (query: string, context: ComboboxContext) => boolean; debounce?: number; createFilter?: (value: string, context: ComboboxContext) => boolean; create?: boolean | CreateCallback; maxItems?: number; maxOptions?: number; separators?: string[]; tokenize?: TokenizeCallback; closeOnSelect?: boolean; createOnBlur?: boolean; autoselectFirst?: boolean; tabSelect?: boolean; toggleSelected?: boolean; labelField?: string; valueField?: string; guards?: GuardMap; selectionOrder?: "source" | "selected"; observeSource?: boolean; /** * Picker coordinate * space: "viewport" uses position: fixed (default), "document" uses * position: absolute. Read once per opening; no layout inference. */ coordinateSpace?: "viewport" | "document"; render?: RenderMap; /** * Consumer-authored positioning/control region */ anchor?: HTMLElement; sort?: (a: import("./helpers.js").ComboboxItem, b: import("./helpers.js").ComboboxItem, query: string, context: ComboboxContext) => number; score?: (item: import("./helpers.js").ComboboxItem, query: string, context: ComboboxContext) => number | false | null; filter?: (item: import("./helpers.js").ComboboxItem, query: string, context: ComboboxContext) => boolean; }; export type ResolvedOptions = ComboboxOptions & { match: MatchStrategy | ((item: import("./helpers.js").ComboboxItem, query: string, context: ComboboxContext) => boolean); searchFields: string | string[]; minChars: number; allowEmptyOption: boolean; placeholder: string; messages: Messages; loadOnEmpty: boolean; debounce: number; maxItems: number; maxOptions: number; separators: string[]; createOnBlur: boolean; autoselectFirst: boolean; tabSelect: boolean; toggleSelected: boolean; guards: GuardMap; selectionOrder: "source" | "selected"; observeSource: boolean; coordinateSpace: "viewport" | "document"; render: RenderMap; load: LoadCallback | null; create: boolean | CreateCallback; createFilter: ((value: string, context: ComboboxContext) => boolean) | null; shouldLoad: ((query: string, context: ComboboxContext) => boolean) | null; tokenize: TokenizeCallback | null; sort: ((a: import("./helpers.js").ComboboxItem, b: import("./helpers.js").ComboboxItem, query: string, context: ComboboxContext) => number) | null; score: ((item: import("./helpers.js").ComboboxItem, query: string, context: ComboboxContext) => number | false | null) | null; filter: ((item: import("./helpers.js").ComboboxItem, query: string, context: ComboboxContext) => boolean) | null; anchor: HTMLElement | null; }; export type ViewState = { /** * Wrapper control (select-only; null for * an input whose source input is itself the control) */ control: HTMLElement | null; input: HTMLInputElement; /** * Only meaningful for a select */ chips: HTMLElement | null; /** * Only meaningful for an input */ datalist: HTMLDataListElement | null; inputSnapshot: AttributeSnapshot | null; sourceSnapshot: AttributeSnapshot | null; }; /** * Optional bookkeeping snapshot of one element's attributes so `dispose()` can * restore the authored state exactly. * @typedef {Object} AttributeSnapshot * @property {() => void} restore Restore the captured attributes (removes what * was absent, rewrites what had a value) */ /** * UI status messages (noResults/loading/loadError text, create/position label * producers). See messages.js for the canonical catalog and the locale * registry. * @typedef {import("./messages.js").Messages} Messages */ /** * Optional DOM-representation hooks. `option`/`group`/`item`/`create`/ * `noResults`/`loading`/`error` render the respective rows; a returned DOM * Node is inserted for rich content, anything else (strings) is text. * @typedef {Object} RenderMap * @property {ItemRenderer} [option] * @property {TextRenderer} [group] * @property {ItemRenderer} [item] * @property {TextRenderer} [create] * @property {TextRenderer} [noResults] * @property {TextRenderer} [loading] * @property {ErrorRenderer} [error] */ /** * Async guards keyed by lifecycle phase. `false` is a voluntary refusal that * mutates nothing; a rejected promise is an application error surfaced via * `combobox:guarderror`. * @typedef {Object} GuardMap * @property {GuardCallback} [add] * @property {GuardCallback} [remove] * @property {GuardCallback} [clear] */ /** * Combobox configuration. All properties are optional; the engine deep-merges * with its defaults. Callbacks are the JavaScript-only surface. * @typedef {Object} ComboboxOptions * @property {MatchStrategy | ((item: import("./helpers.js").ComboboxItem, query: string, context: ComboboxContext) => boolean)} [match] * @property {string | string[]} [searchFields] * @property {number} [minChars] * @property {boolean} [allowEmptyOption] * @property {string} [placeholder] * @property {Messages} [messages] * @property {LoadCallback} [load] * @property {boolean} [loadOnEmpty] * @property {(query: string, context: ComboboxContext) => boolean} [shouldLoad] * @property {number} [debounce] * @property {(value: string, context: ComboboxContext) => boolean} [createFilter] * @property {boolean | CreateCallback} [create] * @property {number} [maxItems] * @property {number} [maxOptions] * @property {string[]} [separators] * @property {TokenizeCallback} [tokenize] * @property {boolean} [closeOnSelect] * @property {boolean} [createOnBlur] * @property {boolean} [autoselectFirst] * @property {boolean} [tabSelect] * @property {boolean} [toggleSelected] * @property {string} [labelField] * @property {string} [valueField] * @property {GuardMap} [guards] * @property {"source" | "selected"} [selectionOrder] * @property {boolean} [observeSource] * @property {"viewport" | "document"} [coordinateSpace] Picker coordinate * space: "viewport" uses position: fixed (default), "document" uses * position: absolute. Read once per opening; no layout inference. * @property {RenderMap} [render] * @property {HTMLElement} [anchor] Consumer-authored positioning/control region * @property {(a: import("./helpers.js").ComboboxItem, b: import("./helpers.js").ComboboxItem, query: string, context: ComboboxContext) => number} [sort] * @property {(item: import("./helpers.js").ComboboxItem, query: string, context: ComboboxContext) => number | false | null} [score] * @property {(item: import("./helpers.js").ComboboxItem, query: string, context: ComboboxContext) => boolean} [filter] */ /** * Internal options after the defaults merge. Every defaulted field is present * and readable without optional-chaining; the nullable seams (`load`, `create` * and friends) stay nullable and are always guarded by `typeof === "function"`. * @typedef {ComboboxOptions & { * match: MatchStrategy | ((item: import("./helpers.js").ComboboxItem, query: string, context: ComboboxContext) => boolean), * searchFields: string | string[], * minChars: number, * allowEmptyOption: boolean, * placeholder: string, * messages: Messages, * loadOnEmpty: boolean, * debounce: number, * maxItems: number, * maxOptions: number, * separators: string[], * createOnBlur: boolean, * autoselectFirst: boolean, * tabSelect: boolean, * toggleSelected: boolean, * guards: GuardMap, * selectionOrder: "source" | "selected", * observeSource: boolean, * coordinateSpace: "viewport" | "document", * render: RenderMap, * load: LoadCallback | null, * create: boolean | CreateCallback, * createFilter: ((value: string, context: ComboboxContext) => boolean) | null, * shouldLoad: ((query: string, context: ComboboxContext) => boolean) | null, * tokenize: TokenizeCallback | null, * sort: ((a: import("./helpers.js").ComboboxItem, b: import("./helpers.js").ComboboxItem, query: string, context: ComboboxContext) => number) | null, * score: ((item: import("./helpers.js").ComboboxItem, query: string, context: ComboboxContext) => number | false | null) | null, * filter: ((item: import("./helpers.js").ComboboxItem, query: string, context: ComboboxContext) => boolean) | null, * anchor: HTMLElement | null, * }} ResolvedOptions */ /** * State produced by the two initialisation branches (select vs input+datalist). * Both return every property so the constructor receives a uniform view; the * null-able entries are genuinely branch- or mode-conditional. * @typedef {Object} ViewState * @property {HTMLElement | null} control Wrapper control (select-only; null for * an input whose source input is itself the control) * @property {HTMLInputElement} input * @property {HTMLElement | null} chips Only meaningful for a select * @property {HTMLDataListElement | null} datalist Only meaningful for an input * @property {AttributeSnapshot | null} inputSnapshot * @property {AttributeSnapshot | null} sourceSnapshot */ /** * Native-first combobox / filterable-select skeleton. * * The source element is always the form-value owner: * - : the original input owns arbitrary text. * - : selected