import type { SelectProps as ChakraNativeSelectProps } from "@chakra-ui/react";
import type { ReactNode } from "react";
import type { ControlSize } from "../../Theme/tokens/primitives/controlSizes";
/** Select option; icon/description/group/isDisabled are popup-mode extras, all optional and additive. */
export type OptionProp = {
/** Unique value of the option: what `value` / `defaultValue` select by and what native mode emits as the `` value. */
id: string | number;
/** Text shown in the row and the trigger; the default filter matches against it. */
label: string | number;
/** Popup mode only — leading icon/avatar for this option's row (and the trigger, when selected). */
icon?: ReactNode;
/** Popup mode only — secondary line under the label; searched only when `searchFields` includes "description". */
description?: string;
/** Row is dimmed and unselectable. Native mode emits a disabled ` `. */
isDisabled?: boolean;
/** Group heading. Options group in the order they appear; emitted as ` ` in native mode. */
group?: string;
};
type SelectCommonProps = {
/** Choices in display order; options that share a `group` should sit next to each other. */
options: OptionProp[];
/** Renders a themed popup instead of a real `` when false. Defaults to true — today's `Select`, unchanged. */
native?: boolean;
/** DOM id of the control; auto-generated when omitted, so the label and messages always associate. */
id?: string;
/** Native: the `` name. Popup: submits the selection through hidden input(s), one per value when multi. */
name?: string;
/** Label above the field; its `for` points at the control. */
label?: string;
/** Native: the built-in placeholder ``. Popup: trigger text when nothing is selected. Defaults to "Select Option". */
placeholder?: string;
/** Height on the field control scale (xs 32 · sm 38 · md 40 · lg 48px). Defaults to `sm`. */
size?: ControlSize;
/** Applied to the wrapping FormControl. */
width?: string | number;
/** Native mode only — overrides the size-driven height (sets both `h` and `minH`). */
height?: string | number;
/** Marks the field invalid and swaps `helperText` for `errorMessage`. */
error?: boolean;
/** Rendered only while `error` is true. */
errorMessage?: string;
/** Hint under the field; hidden while `error` is true. */
helperText?: string;
/** Disables the field; the popup cannot open. */
isDisabled?: boolean;
/** Shows the value but ignores changes; the popup never opens. */
isReadOnly?: boolean;
/** Label asterisk and `aria-required`; the popup never blocks an empty submit. */
isRequired?: boolean;
/** Shows an info icon beside the label; needs `label` and `informationMessage` too. */
isInformation?: boolean;
/** Tooltip text for the info icon; ignored unless `isInformation` is true. */
informationMessage?: string;
/** Inline style on the wrapping FormControl (label + control + message). */
formControlStyle?: React.CSSProperties;
/** Accessible name when there is no `label`; the popup forwards it to the trigger. */
"aria-label"?: string;
/** Id of an element that names the field; the popup forwards it to the trigger. */
"aria-labelledby"?: string;
/** Popup mode only — forwarded to the trigger, the same node as `role="combobox"`. */
"data-testid"?: string;
/** Native mode only — field style: `outline` (default), `filled`, `flushed` or `unstyled`; inert (dev-warned) when `native` is false. */
variant?: ChakraNativeSelectProps["variant"];
/** Native mode only — forwarded to Chakra's Select; inert (dev-warned) when `native` is false. */
colorScheme?: ChakraNativeSelectProps["colorScheme"];
/** Native mode only — replaces the dropdown arrow; inert (dev-warned) when `native` is false. */
icon?: ChakraNativeSelectProps["icon"];
/** Native mode only — background of the ``; inert (dev-warned) when `native` is false. */
bg?: ChakraNativeSelectProps["bg"];
/** Native mode only — border colour of the ``; inert (dev-warned) when `native` is false. */
borderColor?: ChakraNativeSelectProps["borderColor"];
/** Native mode only — text colour of the ``; inert (dev-warned) when `native` is false. */
color?: ChakraNativeSelectProps["color"];
/** Native mode only — border colour while `error`; inert (dev-warned) when `native` is false. */
errorBorderColor?: ChakraNativeSelectProps["errorBorderColor"];
/** Native mode only — inline style on the `` element itself. */
selectStyle?: React.CSSProperties;
/** Native: real DOM blur. Popup: fires when the popup closes (SelectV2's existing behaviour). */
onBlur?: (event?: React.FocusEvent) => void;
/** Native: real DOM focus. Popup: fires when the popup opens (SelectV2's existing behaviour). */
onFocus?: (event?: React.FocusEvent) => void;
/** Shows a type-ahead filter input at the top of the popup. Defaults to true. Popup mode only. */
isSearchable?: boolean;
/** Controlled filter text — a second axis, independent of `value`. Popup mode only. */
searchValue?: string;
/** Seeds an uncontrolled filter text; ignored once `searchValue` is passed. Popup mode only. */
defaultSearchValue?: string;
/** Fires with the filter text, debounced by `searchDebounceMs`. Popup mode only. */
onSearchChange?: (query: string) => void;
/** Debounce for `onSearchChange`, in ms. Defaults to 0 — every keystroke, matching SelectV2 today. */
searchDebounceMs?: number;
/** Option fields the local filter matches (case-insensitive substring). Defaults to `["label"]`. Popup mode only. */
searchFields?: ("label" | "description")[];
/** Replaces the built-in substring match; receives the trimmed query and wins over `searchFields`. Popup mode only. */
filterOption?: (option: OptionProp, query: string) => boolean;
/** Applies the local label-substring filter. Defaults to false once `onSearchChange` is set (server owns matching), true otherwise. */
filterOptions?: boolean;
/** Shows a clear (x) control once a value is selected. Popup mode only. */
isClearable?: boolean;
/** Fires after a clear action, in addition to `onChange`. Popup mode only. */
onClear?: () => void;
/** Shows a loading row under the options and marks the listbox aria-busy. Popup mode only. */
isLoading?: boolean;
/** Text of the loading row. Defaults to "Loading...". Popup mode only. */
loadingText?: string;
/** Swaps the trailing chevron for a spinner and marks the field aria-busy, even while the popup is closed. Popup mode only. */
isFieldLoading?: boolean;
/** Trailing slot replacing the chevron; with `onClick` it renders a button (so `ariaLabel` is required) that does not toggle the popup. Popup mode only. */
rightIcon?: {
/** Node shown in place of the chevron. */
icon: ReactNode;
} | {
/** Node shown in place of the chevron. */
icon: ReactNode;
/** Makes the icon a button that calls this without toggling the popup. */
onClick: () => void;
/** Accessible name of the icon button. */
ariaLabel: string;
};
/** Empty-list message. Native mode stringifies it (plain `` text); popup mode accepts any node. */
noOptionsMessage?: ReactNode;
/** Fires when the list is scrolled to its end and not loading — append the next page. Popup mode only. */
onLoadMore?: () => void;
/** Set explicitly to false once paging is exhausted, to stop further `onLoadMore` calls. Popup mode only. */
hasMore?: boolean;
/** Full control over an option row; receives isSelected/isActive. Popup mode only. */
renderOption?: (option: OptionProp, state: {
isSelected: boolean;
isActive: boolean;
}) => ReactNode;
/** Non-interactive content between the row and its check, e.g. a badge; receives isSelected/isActive. Popup mode only. */
renderOptionEnd?: (option: OptionProp, state: {
isSelected: boolean;
isActive: boolean;
}) => ReactNode;
/** One pointer-only row action: aria-hidden, tabIndex -1, and a click never selects the row. Popup mode only. */
optionAction?: {
/** Node shown in the action button. */
icon: ReactNode;
/** Hover title of the button; the button itself is hidden from assistive tech, so offer the action another way too. */
label: string;
/** Called with the row's option when the button is clicked. */
onAction: (option: OptionProp) => void;
};
/** Single-select check position: "end" (default) trails the row, "start" sits in the leading column the multi checkbox uses. Popup mode only. */
checkPlacement?: "end" | "start";
/** Custom trigger content once a value is selected. Popup mode only. */
renderValue?: (selected: OptionProp[]) => ReactNode;
/** Shows a "Create …" row when the filter matches no option; fires with the trimmed query. Popup mode only. */
onCreate?: (label: string) => void;
/** Overrides the default `Create "«query»"` row content. Popup mode only. */
createLabel?: (query: string) => ReactNode;
/** When the Create row shows: only with zero matches (default), or whenever no option's label equals the query exactly. Popup mode only. */
createWhen?: "no-options" | "no-exact-match";
/** A persistent last row ("Add new…") shown whatever the filter; `onSelect` gets the trimmed query and the popup closes. Popup mode only. */
footerAction?: {
/** Text of the footer row. */
label: ReactNode;
/** Called with the trimmed filter text when the row is chosen; the popup then closes. */
onSelect: (query: string) => void;
/** Icon before the label; a plus icon when omitted. */
icon?: ReactNode;
};
/** Max popup height before the option list scrolls. Defaults to "16rem". Popup mode only. */
maxMenuHeight?: string;
/** Opens the popup on mount, for inline-edit cells. Popup mode only. */
defaultOpen?: boolean;
/** Windows the list once it holds more options than this. Off by default; works with grouped options too. */
virtualizeAfter?: number;
};
/** Single-select arm of `SelectFieldProps`; `isMulti` false/absent discriminates it from the multi variant. */
export type SelectSingleProps = SelectCommonProps & {
/** False or absent selects this single-select variant. */
isMulti?: false;
/** Controlled selected option id; native mode stays uncontrolled until a defined `value` has been passed (`""` shows the placeholder). */
value?: string | number;
/** Seeds an uncontrolled selection in native and popup mode; ignored once `value` is passed (even `""`). */
defaultValue?: string | number;
/** Canonical change callback. Never fires for the native placeholder; gate a reset behind `isClearable`. */
onChange: (selectedOption: OptionProp | undefined) => void;
};
/** Multi-select arm of `SelectFieldProps`; `isMulti: true` discriminates it and makes `value` an array. */
export type SelectMultiProps = SelectCommonProps & {
/** Multi-select requires the popup — rendered even if `native` is left true, with a dev warning. */
isMulti: true;
/** Controlled selected option ids. */
value?: (string | number)[];
/** Seeds an uncontrolled selection; ignored once `value` is passed. */
defaultValue?: (string | number)[];
/** Called with the full selection after each toggle or chip removal. */
onChange: (selectedOptions: OptionProp[]) => void;
/** Chips shown before the rest collapse into a "+N" chip. Defaults to 6. */
maxVisibleChips?: number;
/** Feeds the trigger's aria-label summary once chips collapse. Defaults to 2. */
maxLabelsBeforeSummary?: number;
/** Truncates each chip label to this many characters, adding an ellipsis. */
chipMaxLength?: number;
/** Set false to hide the chips' remove (x) and disable Backspace-removal. Defaults to true. */
isChipRemovable?: boolean;
/** Adds a Select All / Unselect All row that toggles every enabled option the filter currently shows. */
isSelectAll?: boolean;
};
/** Props for the unified `Select` — a discriminated union on `isMulti` between the single and multi variants. */
export type SelectFieldProps = SelectSingleProps | SelectMultiProps;
/** @deprecated historical export name — `src/index.ts` re-exports this as the public `SelectProps`. */
export type chakraSelectProps = SelectFieldProps;
export {};