import type { CSSProperties, ReactElement, ReactNode, Ref } from "react"; /** * Imperative handle exposed via `ref`. `focus()` moves focus to the field's * trigger — enough to support focus-on-error in a form. */ export interface SelectRef { readonly focus: () => void; } export interface SelectProps { /** * Floating label shown inside the field. It sits as the placeholder when no * value is selected and rises to a mini-label once a value is chosen. * Recommended for accessibility. Hidden at the `small` size. */ readonly label?: string; /** * Helper text rendered beneath the field. */ readonly description?: ReactNode; /** * Error message. Shows the message beneath the field and applies invalid * styling. Replaces the description while present. */ readonly error?: string; /** * Applies invalid styling without rendering a message. Use when the message * is rendered elsewhere (e.g. by React Hook Form). */ readonly invalid?: boolean; /** * The controlled selected value. `null` (or `undefined`) when empty. */ readonly value?: string | null; /** * Called with the newly selected value. Named to match Base UI. Note: React * Hook Form's `field.onChange` will not be wired by a `{...field}` spread — * pass `onValueChange={field.onChange}` explicitly. */ readonly onValueChange?: (value: string) => void; /** * Renders the selected value shown in the closed trigger. When omitted, the * value is shown capitalized (e.g. `"active"` → `"Active"`). */ readonly renderValue?: (value: string) => ReactNode; /** * Called when the field's trigger loses focus. Useful for touched state and * blur-mode validation. */ readonly onBlur?: () => void; /** * Called when the field's trigger receives focus. */ readonly onFocus?: () => void; /** * Name used for the form field (FormData key, autofill, test selectors). */ readonly name?: string; /** * Id applied to the field's trigger. Auto-generated when omitted. */ readonly id?: string; /** * Imperative handle exposing `focus()`. */ readonly ref?: Ref; /** * Disables the whole field. */ readonly disabled?: boolean; /** * Field size. */ readonly size?: "small" | "large"; /** * Renders the field inline and suppresses the description / error. */ readonly inline?: boolean; /** * `Select.Item`, `Select.Group`, `Select.GroupLabel`, and `Select.Separator` * that make up the dropdown list. */ readonly children?: ReactNode; } export interface SelectItemProps { /** * The value submitted when this option is selected. */ readonly value: string; /** * The display label for the option. */ readonly children: ReactNode; readonly className?: string; readonly style?: CSSProperties; } export interface SelectGroupProps { /** * A `Select.GroupLabel` and the `Select.Item`s that belong to the group. */ readonly children?: ReactNode; readonly className?: string; readonly style?: CSSProperties; } export interface SelectGroupLabelProps { /** * The section heading shown above the group's options. */ readonly children?: ReactNode; readonly className?: string; readonly style?: CSSProperties; } export interface SelectSeparatorProps { readonly className?: string; readonly style?: CSSProperties; } /** * Internal. The platform-specific implementations that the `Select.*` * subcomponents dispatch to (desktop dropdown vs. mobile bottom sheet). One * authored child tree renders on both platforms; each node resolves to the * active shell's part — mirroring Menu's shell pattern. */ export interface SelectShell { readonly Item: (props: SelectItemProps) => ReactElement; readonly Group: (props: SelectGroupProps) => ReactElement; readonly GroupLabel: (props: SelectGroupLabelProps) => ReactElement; readonly Separator: (props: SelectSeparatorProps) => ReactElement; } /** * Internal. Shared props both shell roots accept; each root reads only the * fields its presentation needs and ignores the rest. */ export interface SelectShellRootProps { readonly value?: string | null; readonly onValueChange?: (value: string) => void; readonly onBlur?: () => void; readonly onFocus?: () => void; readonly disabled?: boolean; readonly invalid?: boolean; readonly triggerId: string; readonly triggerClassName: string; readonly ariaLabel?: string; /** Renders the selected value in the (closed) trigger on both platforms. */ readonly renderValue: (value: string) => ReactNode; readonly children: ReactNode; }