import { AriaAttributes, CSSProperties, ReactElement } from 'react'; import { AiMarkWithTooltipOrPopoverProps, LayoutUtilProps, Size } from '../../../types'; import { AvatarProps, ChipProps, IconProps } from '../../..'; /** * Configuration options for the SelectField cache. * @property enabled - Whether caching is enabled. Defaults to true. * @property maxSize - Maximum number of cached entries before clearing the cache. Defaults to 15. */ export type SelectFieldCacheOptions = { enabled?: boolean; maxSize?: number; }; /** * Imperative handle for the SelectField component. * @property clearCache - Clears the options cache. * @property invalidate - Clears the cache and triggers a fresh options load from the data source. */ export type SelectFieldHandle = { clearCache: () => void; invalidate: () => void; }; export type SelectFieldGroupByValue = string | number; export type SelectFieldOption = { id: string | number; label: string; searchText?: string; group?: SelectFieldGroupByValue; disabled?: boolean; extra?: Record; content?: { title?: string; description?: string; chips?: Pick[]; avatar?: Pick; icon?: Pick & { /** Accessible label for the icon. When omitted, the icon is treated as decorative. */ label?: string; }; }; }; export type SelectFieldGroupedOption = SelectFieldOption & { group: SelectFieldGroupByValue; }; export type SelectFieldUngroupedOption = Omit & { group?: never; }; export type SelectFieldOptionsResult = SelectFieldOption[] | Promise; export type SelectFieldEagerLoader = (searchValue: string) => SelectFieldOptionsResult; export type SelectFieldOffsetLazyResult = { options: SelectFieldUngroupedOption[]; hasMore?: boolean; }; export type SelectFieldOffsetLazyLoader = (searchValue: string, offset: number, limit: number) => SelectFieldOffsetLazyResult | Promise; export type SelectFieldPageLazyResult = { options: SelectFieldUngroupedOption[]; hasMore?: boolean; }; export type SelectFieldPageLazyLoader = (searchValue: string, pageNumber: number, pageSize: number) => SelectFieldPageLazyResult | Promise; export type SelectFieldGroupLazyResult = { options: SelectFieldGroupedOption[]; hasMore?: boolean; }; export type SelectFieldGroupLazyLoader = (searchValue: string, previousGroupKey: SelectFieldGroupByValue | null) => SelectFieldGroupLazyResult | Promise; export type SelectFieldSearchProps = { /** * The current search value. * Not a preferred usage. You likely don't need to control the searchValue yourself. */ searchValue?: string; /** * Callback when the search value changes. * Not a preferred usage. Lean on the searchValue in the loadOptions function instead. */ onSearchChange?: (searchValue: string) => void; /** * The number of milliseconds to debounce the search input. */ debounceMs?: number; }; type SelectFieldAddNewOptionProps = { /** * The configuration for the add new option. */ addNewOption?: { /** * The label to display for the add new option. */ label: string; /** * The content to display in the add new option dialog. */ renderDialogContent: (searchValue: string) => React.ReactNode; }; }; export type SelectFieldPinnedOptionsSection = { options: SelectFieldOption[] | ((searchValue: string) => SelectFieldOption[] | Promise); label: string; /** * Whether to re-call the loader when search value changes. * Only applies when `options` is a function. Defaults to true. * When false, the loader is called once and the result is reused for all search values. * * You may wish to use this, for example, if you are loading a set of "favorites" options that should not be re-loaded when the search value changes. */ searchReactive?: boolean; /** * Maximum number of search results to cache per section. * Only applies when `options` is a function and `searchReactive` is true. * Oldest entries are evicted when the limit is reached. Defaults to 15. */ cacheSize?: number; }; /** * There are two ways to configure pinned options. * 1. A labeled pinned options object (e.g. "Favorites", "Recent", "AI Suggestions", etc.) * 2. An array of labeled pinned options objects (e.g. [{"label": "Favorites", "options": [...]}]) */ export type SelectFieldPinnedOptions = SelectFieldPinnedOptionsSection | SelectFieldPinnedOptionsSection[]; type SelectFieldCommonSelectFieldProps = { /** * The id of the select field. */ id?: string; /** * The label of the select field. */ label: string; /** * The placeholder of the select field. */ placeholder?: string; /** * The size of the select field. */ size?: Extract; /** * The value of the select field. Must be controlled state. */ value: SelectFieldOption | null; /** * The callback to call when an item is clicked. * @param option - The option that was selected. */ onSelectedOptionChange: (option: SelectFieldOption | null) => void; /** * Defines the initial loading behavior of the options. Controls when loadOptions is called. * @default "auto" * @description "auto" default behavior, currently equivalent to "immediate". * @description "immediate" will load the initial options when the component is mounted. * @description "open" will load the initial options if/when the user opens the dropdown. */ initialLoad?: "auto" | "immediate" | "open"; /** * The options to pin to the top of the list. */ pinned?: SelectFieldPinnedOptions; /** * Whether to disable the clear button. * @default false */ disableClearButton?: boolean; /** * Configuration for caching loadOptions results. * Caching is enabled by default. Set `{ enabled: false }` to disable. */ cache?: SelectFieldCacheOptions; /** * The way to display the menu. * @default "auto" * @description "auto" will display the menu as a popover on mobile and a dialog on desktop. * @description "popover" will always display the menu as a popover. * @description "dialog" will always display the menu as a dialog. */ displayMenuAs?: "auto" | "popover" | "dialog"; /** * Error state for the field. Pass `true` to indicate error styling without a message. * Pass a string, string[], or ReactElement (deprecated) for error messages. */ error?: boolean | string | ReactElement | string[]; /** * Visually hides the label while keeping it accessible to screen readers. * @default false */ hideLabel?: boolean; /** * Hint text displayed below the input field. */ hint?: ReactElement | string; /** * AI mark configuration to display next to the label. * Can be a boolean to show a simple AI mark, or an object with tooltip/popover configuration. */ labelAiMark?: AiMarkWithTooltipOrPopoverProps["aiMark"]; /** * Description text displayed below the input field. */ description?: ReactElement | string; /** * @deprecated No longer used. Error messages always use `aria-live="assertive"`. */ errorAriaLive?: AriaAttributes["aria-live"]; /** * Warning message(s) to display. Supports a single string or an array of strings. */ warning?: string | string[]; /** * Whether the field is required. Shows a red asterisk (*) next to the label. */ required?: boolean; /** * Whether the field is disabled. * When disabled, the input is still focusable but menu items cannot be selected. */ disabled?: boolean; /** * Whether the field is read-only. * When read-only, the input is still focusable but menu items cannot be selected. */ readOnly?: boolean; /** * Content to display before the input field. */ prefix?: string | ReactElement; /** * Content to display after the input field. */ suffix?: string | ReactElement; /** * Custom CSS class name for the wrapper element. */ className?: string; /** * Custom inline styles for the wrapper element. */ style?: CSSProperties; /** * Whether to virtualize the dropdown list using windowed rendering. * Enable this for large option sets to improve performance by only rendering visible items. * @default false */ virtualize?: boolean; /** * Whether to disable the search input. * When true, the input is replaced with a non-editable select trigger and the ARIA pattern * changes from combobox to listbox. * @default false */ disableSearch?: boolean; } & SelectFieldSearchProps & SelectFieldAddNewOptionProps & LayoutUtilProps; type SelectFieldGroupingProps = { /** * Function to convert a group value to a display label. * Only used when options have a `group` property. * @param groupValue - The group value from the option's `group` property * @returns The formatted group label */ groupToString?: (groupValue: SelectFieldGroupByValue) => string; /** * Function to compare two group values for sorting. * When provided, options are sorted by group using this comparator, * then by match-sort order within each group. * @param a - First group value to compare * @param b - Second group value to compare * @returns Negative if a < b, positive if a > b, zero if equal */ groupSorter?: (a: SelectFieldGroupByValue, b: SelectFieldGroupByValue) => number; }; type SelectFieldNonGroupingProps = { /** * Incompatible with non-group lazy loading. */ groupToString?: never; /** * Incompatible with non-group lazy loading. */ groupSorter?: never; }; export type SelectFieldPropsLazyPage = SelectFieldCommonSelectFieldProps & { /** * Lazy loading mode using page-based pagination. * Options will be loaded on demand when the user scrolls to the bottom of the list. * This mode only supports flat options (i.e. cannot be used with grouped options). */ lazy: "page"; lazyOptions?: { pageSize?: number; }; /** * Function to load the options. */ loadOptions: SelectFieldPageLazyLoader; } & SelectFieldNonGroupingProps; export type SelectFieldPropsLazyOffset = SelectFieldCommonSelectFieldProps & { /** * Lazy loading mode using offset-based pagination. * Options will be loaded on demand when the user scrolls to the bottom of the list. * This mode only supports flat options (i.e. cannot be used with grouped options). */ lazy: "offset"; lazyOptions?: { limit?: number; }; /** * Function to load the options. */ loadOptions: SelectFieldOffsetLazyLoader; } & SelectFieldNonGroupingProps; export type SelectFieldPropsLazyGroup = SelectFieldCommonSelectFieldProps & { /** * Lazy loading mode using incremental group loading. * Groups will be loaded on demand when the user scrolls to the bottom of the list. * This mode supports grouped options. */ lazy: "group"; lazyOptions?: object; /** * Function to load the options. */ loadOptions: SelectFieldGroupLazyLoader; } & SelectFieldGroupingProps; export type SelectFieldPropsEager = SelectFieldCommonSelectFieldProps & { /** * Whether the options are lazy loaded. If true, the options will be loaded on demand when the user scrolls to the bottom of the list. * @default false */ lazy?: false; /** * Function to load the options. */ loadOptions: SelectFieldEagerLoader; } & SelectFieldGroupingProps; export type SelectFieldPropsLazy = SelectFieldPropsLazyPage | SelectFieldPropsLazyOffset | SelectFieldPropsLazyGroup; export type SelectFieldProps = SelectFieldPropsLazy | SelectFieldPropsEager; export {};