import type * as React from 'react'; import type { PrimitivePropsWithRef } from '../utils/primitive.cjs'; /** One option for the {@link ListboxProps.options} recipe prop — a flat entry rendered as a `ListboxItem`. */ export interface ListboxOption { /** Unique identifier committed to the listbox's value when this option is chosen. */ value: string; /** * Text shown for the option and matched by typeahead. * @default undefined — falls back to `value`. */ label?: string | undefined; /** * Excludes this option from selection and from keyboard/typeahead navigation. * @default false */ disabled?: boolean | undefined; } /** A labeled group of {@link ListboxOption}s for {@link ListboxProps.options} — grouping is data, never a wrapper component. */ export interface ListboxOptionGroup { /** Heading rendered as a `ListboxLabel` above this group's options. */ label: string; /** The options belonging to this group. */ options: ListboxOption[]; } /** One entry of {@link ListboxProps.options}: a flat option, or a labeled group of them. */ export type ListboxOptionOrGroup = ListboxOption | ListboxOptionGroup; /** Event detail on selection change, naming what drove it and the previous selection. */ export interface ListboxValueChangeDetail { /** The whole selection after the change, not the single option that was toggled — see `selectedValue`. */ value: string[]; /** The selection held before this change, empty when there was none. */ previousValue: string[]; /** What drove the change. */ reason: 'keyboard' | 'pointer' | 'programmatic'; /** The first selected value, or `null` when nothing is selected. */ selectedValue: string | null; } /** @internal Shared state distributed from {@link Listbox} to its parts via context. */ export interface ListboxContextValue { /** Whether more than one option can be selected. */ multiple: boolean; /** Every selected value, normalised to an array whether or not `multiple`. */ selectedValues: string[]; /** Whether one option value is currently selected. */ isSelected: (value: string) => boolean; /** Commits an option, replacing the selection or toggling it when `multiple`. */ selectItem: (value: string, reason: ListboxValueChangeDetail['reason']) => void; /** Whether the whole list is disabled, which disables every option in it. */ disabled: boolean; /** The option value holding the list's single tab stop. */ activeValue: string | null; setActiveValue: (value: string | null) => void; /** Whether moving the pointer over an option moves roving focus onto it. */ focusOnHover: boolean; } /** Props for the {@link Listbox} root. */ export interface ListboxProps extends Omit, 'onChange' | 'defaultValue'> { /** * Selected value for controlled usage — `string` or `string[]` — paired with `onValueChange`. * @default undefined — uncontrolled, tracks selected value internally. */ value?: string | string[] | undefined; /** * Initially selected option's value, for uncontrolled usage — the same bare * `string` or `string[]` shapes `value` accepts, under either cardinality. * @default undefined — no option selected until the user picks one. */ defaultValue?: string | string[] | undefined; /** * Fires on selection change with the whole selection, `selectedValue`, `previousValue`, and `reason`. * @default undefined — no-op if omitted. */ onValueChange?: ((detail: ListboxValueChangeDetail) => void) | undefined; /** * Allow multiple selection: `value` becomes `string[]`, list gets `aria-multiselectable="true"`, * choosing toggles instead of replacing. * @default false */ multiple?: boolean | undefined; /** * Disables all options: `aria-disabled`/`data-disabled` on list and items. * Individual items can disable themselves too. * @default false */ disabled?: boolean | undefined; /** * Control+A selects or clears all enabled options when `multiple`; inert in single-select. * @default true */ allowSelectAll?: boolean | undefined; /** * Layout axis — vertical (ArrowUp/Down) or horizontal (ArrowLeft/Right) — affecting selection-extending keys only. * @default 'vertical' */ orientation?: 'horizontal' | 'vertical' | undefined; /** * Moves roving focus on pointer hover without selecting, never onto a disabled item. * @default false */ focusOnHover?: boolean | undefined; /** * Prevents deselecting the last item and refuses the Control+A clear, when `multiple`. * @default false */ disallowEmptySelection?: boolean | undefined; /** * Auto-renders options as `ListboxItem`s, ignored when children declare their own. * @default undefined — render items yourself via children. */ options?: ListboxOptionOrGroup[] | undefined; /** * Customizes derived option rendering, receiving the option and its selected state. * @default undefined — renders `option.label ?? option.value`. */ renderOption?: ((option: ListboxOption, state: { selected: boolean; }) => React.ReactNode) | undefined; /** * Spacing rung the skin packs the list by, mirrored to `data-density`. * @default undefined — the skin's own default spacing. */ density?: 'compact' | (string & {}) | undefined; /** * Corner rung the skin rounds the list by, mirrored to `data-shape`. * @default undefined — the skin's own default corner. */ shape?: 'square' | 'rounded' | (string & {}) | undefined; /** * Size rung the skin scales the list by, mirrored to `data-size`. * @default undefined — the skin's own default size. */ size?: 'sm' | 'lg' | (string & {}) | undefined; } /** @internal Shared state distributed from {@link ListboxItem} to its indicator via context. */ export interface ListboxItemContextValue { value: string; selected: boolean; disabled: boolean; } /** Props for a single {@link ListboxItem} option. */ export interface ListboxItemProps extends PrimitivePropsWithRef<'div'> { /** Unique identifier committed to the listbox's value when this option is chosen. */ value: string; /** * Excludes this option from selection and from keyboard/typeahead navigation. * @default false */ disabled?: boolean | undefined; /** * Text for typeahead matching, published as `data-text-value` when the option is more than its label. * @default undefined — falls back to rendered text content. */ textValue?: string | undefined; /** * This option's 1-based position and the full set's size for a virtualized or paginated * list, published as `aria-posinset`/`aria-setsize` — set both or neither. * @default undefined — the option set is exactly what is rendered, and the * browser's own count is correct. */ posInSet?: number | undefined; /** * Size of the full option set this option belongs to. See {@link ListboxItemProps.posInSet}. * @default undefined — the option set is exactly what is rendered, and the browser's own count is correct. */ setSize?: number | undefined; } /** Props for {@link ListboxItemIndicator}, shown inside a {@link ListboxItem} while that option is selected. */ export interface ListboxItemIndicatorProps extends PrimitivePropsWithRef<'span'> { /** * Keep this indicator mounted even while its option isn't selected, instead * of rendering nothing. * @default false */ forceMount?: boolean | undefined; } /** Props for {@link ListboxGroup}, a labelled group of related `ListboxItem`s. */ export interface ListboxGroupProps extends PrimitivePropsWithRef<'div'> { } /** Props for {@link ListboxLabel}, the heading naming its {@link ListboxGroup}. */ export interface ListboxLabelProps extends PrimitivePropsWithRef<'div'> { }