@cfxlabsinc/b2b-services
    Preparing search index...
    type InfiniteComboboxProps<T> = {
        buildHref?: (nextIds: string[]) => string;
        extraItem?: (query: string, noMatches: boolean) => ComboboxExtraItem | null;
        facets?: readonly ComboboxFacet[];
        fetchPage: EntitySelectorFetchPage<T>;
        getId: (item: T) => string;
        getLabel: (item: T) => string;
        getSublabel?: (item: T) => string | undefined;
        initialPage?: EntitySelectorPage<T> | null;
        isOptionDisabled?: (item: T) => boolean;
        linkComponent?: LinkComponent;
        multiple?: boolean;
        noun: string;
        onClear?: () => void;
        onToggle?: (item: T) => void;
        queryKey?: ReadonlyArray<unknown>;
        renderEmpty?: (query: string) => ReactNode;
        renderFooter?: (query: string) => ReactNode;
        renderRow?: (item: T) => ReactNode;
        searchPlaceholder: string;
        selectedIds: string[];
        selectedSeed: T[];
        sortOptions?: readonly EntitySelectorSortOption[];
    }

    Type Parameters

    • T
    Index
    buildHref?: (nextIds: string[]) => string

    Link mode: serialize a full next-selection of ids to its URL. The combobox owns the toggle math (add/remove for multiple, replace for single) and calls this with the resulting id set — so hrefs are always computed from the live optimistic selection, never a stale mounted URL. Set with linkComponent to render rows as optimistic soft-nav links; onToggle/onClear are unused.

    extraItem?: (query: string, noMatches: boolean) => ComboboxExtraItem | null

    Query-driven action rendered as the FIRST CommandItem, so it owns cmdk's Enter (cmdk re-highlights the first item on every input change) and joins arrow-key navigation. id must be distinct from every row id.

    facets?: readonly ComboboxFacet[]

    Drilldown facets rendered below the search box. Their selected values are forwarded to fetchPage as facets[key], so the backing service must accept them. Omit for a plain (search-only) selector.

    Fetches one page of options; { ok: false } becomes the error state.

    getId: (item: T) => string
    getLabel: (item: T) => string
    getSublabel?: (item: T) => string | undefined

    Optional monospaced secondary label (e.g. an id) shown on rows that fall back to the default renderRow.

    initialPage?: EntitySelectorPage<T> | null

    Server-rendered first page; see useInfiniteOptions. Default view only.

    isOptionDisabled?: (item: T) => boolean

    Marks a row unselectable while keeping it listed — for an option that exists but can't be used here. Prefer this over filtering: a missing row reads as "no such account", a disabled one as "this one, but not yet".

    linkComponent?: LinkComponent

    Link mode: the host's typed link component (e.g. admin's PageLink).

    multiple?: boolean

    Whether a pick accumulates (multi-select) or replaces the last (single). In link mode the combobox owns that toggle math; in callback mode the caller does, and this drives the row affordance (checkbox vs. check mark) and aria-multiselectable so the control doesn't advertise the wrong contract. Defaults to multi-select.

    noun: string

    Plural noun, e.g. "customers" — used in counts, error, and empty copy.

    onClear?: () => void

    Callback mode: clears the selection. Unused in link mode.

    onToggle?: (item: T) => void

    Callback mode: toggles a row in the selection. Unused in link mode.

    queryKey?: ReadonlyArray<unknown>

    Base react-query key. Defaults to ["infinite-combobox", noun]; pass a distinct key when two comboboxes share a noun but return different rows.

    renderEmpty?: (query: string) => ReactNode

    Replaces the default empty copy when the result set is empty. Fed the live (undebounced) query so the caller can name what it searched for. Use it to explain WHY a constrained list is empty; omit it for the default copy.

    renderFooter?: (query: string) => ReactNode

    Renders below the list, fed the live (undebounced) search text — an escape hatch for NON-interactive hints. Anything actionable belongs in extraItem (a footer sits outside cmdk's listbox, so keys never reach it).

    renderRow?: (item: T) => ReactNode

    The row's inner content (name / id lines), sans checkbox. Omit it to get getLabel + getSublabel as a two-line pair, the same fallback the popover uses.

    searchPlaceholder: string
    selectedIds: string[]

    Live selection for this dimension (drives the checked + pinned state).

    selectedSeed: T[]

    Resolved rows for the committed selection — seeds pinned rendering so an off-page selection is still shown while it's not on a loaded page.

    sortOptions?: readonly EntitySelectorSortOption[]

    Sort choices; the first is the default. Omit for an unsorted source.