import { type KeyboardEvent, type ReactNode, type Ref } from 'react'; import { type LucideIcon } from 'lucide-react'; /** * z-index of the portaled list. Dialogs/sheets sit at 50; the list must stay * above the modal it was opened from regardless of portal order. */ export declare const RECORD_PICKER_Z_INDEX = 60; export interface UseRecordSearchOptions { /** Characters required before searching (default 2). 0 = search on empty too. */ minChars?: number; /** Debounce in ms (default 250). */ delay?: number; /** When false nothing is fetched and results reset (e.g. list closed). Default true. */ enabled?: boolean; } export interface UseRecordSearchResult { results: T[]; /** A request is in flight. */ loading: boolean; /** The last request failed. */ error: boolean; /** The debounce has not caught up with the typed text yet. */ pending: boolean; } /** Debounced async search with cancellation. `search` must be stable (useCallback). */ export declare function useRecordSearch(query: string, search: (q: string, signal: AbortSignal) => Promise, opts?: UseRecordSearchOptions): UseRecordSearchResult; /** * Stable identity for a caller's search function: always calls the latest one, * so an inline (non-memoized) fetcher doesn't re-trigger the search on every * render. */ export declare function useLatestSearch(fn: (...args: A) => R): (...args: A) => R; export interface RecordPickerItemState { /** Keyboard / pointer highlight. */ active: boolean; /** Part of the current value. */ selected: boolean; disabled: boolean; } export type RecordPickerVariant = 'field' | 'cell'; export type RecordPickerTrigger = 'button' | 'input'; export interface RecordPickerProps { /** Stable id of an item (the stored value). */ getKey: (item: T) => string; /** Display text of an item. */ getLabel: (item: T) => string; /** Caller-owned list (resolver, static options, a search the caller drives). Wins over `search`. */ items?: readonly T[]; /** Async source run here (debounced, cancelled, gated by `minChars`). Must be stable. */ search?: (q: string, signal: AbortSignal) => Promise; /** Characters required before searching / opening the input list. Default 0 (button) / 2 (input). */ minChars?: number; /** Debounce for `search` (ms). Default 250. */ debounceMs?: number; /** Loading flag for caller-owned `items`. */ loading?: boolean; /** Error flag for caller-owned `items`. */ error?: boolean; query?: string; onQueryChange?: (query: string) => void; /** Button: popover open. Input: the user is actively searching. */ open?: boolean; onOpenChange?: (open: boolean) => void; multiple?: boolean; /** Current value: an id (single) or ids (multiple). */ value?: string | number | null | readonly (string | number)[]; /** Resolved item(s) of the current value, for the trigger. */ selected?: T | readonly T[] | null; /** An item was picked (in `multiple`, toggle it). */ onSelect: (item: T) => void; /** Shows a clear (×) when there is a value. */ onClear?: () => void; /** Chip remove in `multiple`. */ onRemove?: (item: T) => void; /** Close after a pick. Default: true for single, false for multiple. */ closeOnSelect?: boolean; /** Full option body override. */ renderItem?: (item: T, state: RecordPickerItemState) => ReactNode; /** Leading visual (avatar / initials / thumbnail / icon). */ renderLead?: (item: T, where: 'option' | 'value') => ReactNode; /** Subtitle under the label (SKU, RFC, phone…). */ getDescription?: (item: T) => ReactNode; /** Right-aligned content (price, stock badge…). */ renderTrailing?: (item: T, where: 'option' | 'value') => ReactNode; /** Trigger content override (button) / selected display (input). */ renderValue?: (selected: T | null) => ReactNode; isItemDisabled?: (item: T) => boolean; /** Check mark column. Default: true in button mode. */ showCheck?: boolean; placeholder?: string; searchPlaceholder?: string; emptyText?: ReactNode; loadingText?: ReactNode; errorText?: ReactNode; /** Shown below `minChars` in button mode. */ minCharsText?: ReactNode; clearLabel?: string; /** Accessible name of the listbox. */ listLabel?: string; /** Human name of the referenced model, for "Crear …" / "Editar …". */ entityLabel?: string; /** "+" (empty) and list footer. Receives the searched text to prefill. */ onCreate?: (query: string) => void; /** Pencil (with a value). */ onEdit?: () => void; createLabel?: string; editLabel?: string; /** When the "Crear …" footer shows: always (default), only with no hits, or never. */ createFooter?: 'always' | 'empty' | 'never'; createFooterLabel?: (query: string) => ReactNode; /** Hide the joined +/pencil segment (keeps the footer). */ hideJoinedAction?: boolean; trigger?: RecordPickerTrigger; variant?: RecordPickerVariant; /** Input mode: the typed text IS the value (never shows a selected display). */ freeText?: boolean; icon?: LucideIcon; id?: string; ariaLabel?: string; disabled?: boolean; /** Locked display of the current value (no list, no actions). */ readOnly?: boolean; invalid?: boolean; /** data-slot of the root (keeps legacy hooks like `dynamic-select`). */ slot?: string; /** data-slot of the input-mode anchor. */ anchorSlot?: string; /** data-slot of the portaled list container. */ contentSlot?: string; className?: string; triggerClassName?: string; /** Extra attributes for the trigger / text box (data-*, …). */ triggerProps?: Record; inputRef?: Ref; /** Floor for the list width (CSS length). Default 14rem. */ minListWidth?: string; /** Ceiling for the list width (CSS length), e.g. under a very wide trigger. Default: none. */ maxListWidth?: string; /** Rendered after the joined group (e.g. a scan button). */ after?: ReactNode; /** Rendered under the control (badges, hints). */ below?: ReactNode; /** Rendered at the top of the list. */ beforeList?: ReactNode; /** Runs first on every key in the text box; `preventDefault()` to take over. */ onInputKeyDown?: (e: KeyboardEvent) => void; /** Keys the picker did not consume (closed list, Enter with no hits…). */ onUnhandledKeyDown?: (e: KeyboardEvent) => void; } export declare function RecordPicker(props: RecordPickerProps): string | number | bigint | boolean | Iterable | Promise> | Iterable | null | undefined> | import("react").JSX.Element | null | undefined; //# sourceMappingURL=record-picker.d.ts.map