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.
* -