import { KeyboardEvent, ReactElement, RefObject } from 'react'; import { CheckState } from '../../../types'; import { SelectFieldGroupByValue, SelectFieldOption } from '../SelectField/types'; import { MultiSelectFieldCacheOptions, MultiSelectFieldEagerLoader, MultiSelectFieldGroupLazyLoader, MultiSelectFieldOffsetLazyLoader, MultiSelectFieldPageLazyLoader, MultiSelectFieldPinnedOptions, MultiSelectFieldSearchProps } from '../MultiSelectField/types'; export type MultiSelectMenuOption = SelectFieldOption; export type MultiSelectMenuGroupByValue = SelectFieldGroupByValue; /** * Configuration options for the MultiSelectMenu 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 MultiSelectMenuCacheOptions = MultiSelectFieldCacheOptions; /** * Imperative handle for the MultiSelectMenu component. * @property clearCache - Clears the options cache. * @property invalidate - Clears the cache and triggers a fresh options load from the data source. */ export type MultiSelectMenuHandle = { clearCache: () => void; invalidate: () => void; }; export type MultiSelectMenuEagerLoader = MultiSelectFieldEagerLoader; export type MultiSelectMenuPageLazyLoader = MultiSelectFieldPageLazyLoader; export type MultiSelectMenuOffsetLazyLoader = MultiSelectFieldOffsetLazyLoader; export type MultiSelectMenuGroupLazyLoader = MultiSelectFieldGroupLazyLoader; export type MultiSelectMenuPinnedOptions = MultiSelectFieldPinnedOptions; export type MultiSelectMenuSearchProps = MultiSelectFieldSearchProps; /** * Props provided to the trigger render function. * @property ref - Ref to attach to the trigger element for positioning * @property onClick - Click handler to toggle the menu * @property onKeyDown - Key handler for keyboard navigation * @property aria-haspopup - ARIA attribute indicating the trigger opens a listbox * @property aria-controls - ARIA attribute linking to the menu element * @property aria-expanded - ARIA attribute indicating whether the menu is open * @property data-state - Data attribute indicating the open/close state */ export type MultiSelectMenuTriggerProps = { ref: RefObject; onClick: () => void; onKeyDown: (e: KeyboardEvent) => void; "aria-haspopup": "listbox"; "aria-controls": string; "aria-expanded": boolean; "data-state": "open" | "close"; }; type MultiSelectMenuCommonProps = { /** * The id of the multi-select menu. */ id?: string; /** * Render function that receives trigger props and returns the element that opens the menu. */ trigger: (props: MultiSelectMenuTriggerProps) => ReactElement; /** * Called when a key is pressed inside the menu (search field or listbox), before the * menu's own key handling. Use `e.preventDefault()` to suppress the default behavior. */ onMenuKeyDown?: (e: KeyboardEvent) => void; /** * Called when the menu closes implicitly — due to Tab navigation, clicking outside, * or other ambient focus loss. Focus is not restored to the trigger in these cases. */ onImplicitClose?: () => void; /** * Called when the menu closes explicitly — the user intentionally dismissed it * via Escape or toggling the trigger. Focus is restored to the trigger. */ onExplicitClose?: () => void; /** * Accessible label for the menu. Used by screen readers but not rendered visually. */ label: string; /** * Placeholder text shown in the search input inside the menu. */ searchPlaceholder?: string; /** * The selected options. Must be controlled state. */ value: MultiSelectMenuOption[]; /** * Callback fired when the selected options change. * @param options - The new array of selected options. */ onSelectedOptionsChange: (options: MultiSelectMenuOption[]) => void; /** * Defines the initial loading behavior of the options. * @default "auto" */ initialLoad?: "auto" | "immediate" | "open"; /** * Options to pin to the top of the list. */ pinned?: MultiSelectMenuPinnedOptions; /** * Configuration for caching loadOptions results. * Caching is enabled by default. Set `{ enabled: false }` to disable. */ cache?: MultiSelectMenuCacheOptions; /** * Controls how the menu is displayed. * @default "auto" * @description "auto" uses a popover on desktop and a dialog on mobile. * @description "popover" always uses a popover. * @description "dialog" always uses a dialog. */ displayMenuAs?: "auto" | "popover" | "dialog"; /** * Whether to virtualize the list using windowed rendering. * @default false */ virtualize?: boolean; /** * Whether to disable the search input inside the menu. * @default false */ disableSearch?: boolean; /** * Configuration for the "Select All" option at the top of the list. * Shown when the search input is empty. */ selectAll?: { label?: string; onClick: () => void; checkState: boolean | CheckState; }; /** * Function that receives the current search value and returns configuration for * the "Select Filtered" option at the top of the list. * Shown when a search term is active (instead of Select All). */ selectFiltered?: (searchValue: string) => { label?: string; onClick: () => void; checkState: boolean | CheckState; }; /** * Width of the popover. Only applies when `displayMenuAs="popover"` (or when the adaptive mode resolves to popover). Has no effect in dialog mode. * Accepts a CSS width value or "reference" to match the trigger element's width. * @default 320 */ popoverWidth?: "reference" | number | string; } & MultiSelectMenuSearchProps; type MultiSelectMenuGroupingProps = { /** * Function to convert a group value to a display label. */ groupToString?: (groupValue: MultiSelectMenuGroupByValue) => string; /** * Custom comparator function to sort groups. */ groupSorter?: (a: MultiSelectMenuGroupByValue, b: MultiSelectMenuGroupByValue) => number; }; type MultiSelectMenuNonGroupingProps = { groupToString?: never; groupSorter?: never; }; export type MultiSelectMenuPropsLazyPage = MultiSelectMenuCommonProps & { lazy: "page"; lazyOptions?: { pageSize?: number; }; loadOptions: MultiSelectMenuPageLazyLoader; } & MultiSelectMenuNonGroupingProps; export type MultiSelectMenuPropsLazyOffset = MultiSelectMenuCommonProps & { lazy: "offset"; lazyOptions?: { limit?: number; }; loadOptions: MultiSelectMenuOffsetLazyLoader; } & MultiSelectMenuNonGroupingProps; export type MultiSelectMenuPropsLazyGroup = MultiSelectMenuCommonProps & { lazy: "group"; lazyOptions?: object; loadOptions: MultiSelectMenuGroupLazyLoader; } & MultiSelectMenuGroupingProps; export type MultiSelectMenuPropsEager = MultiSelectMenuCommonProps & { lazy?: false; loadOptions: MultiSelectMenuEagerLoader; } & MultiSelectMenuGroupingProps; export type MultiSelectMenuPropsLazy = MultiSelectMenuPropsLazyPage | MultiSelectMenuPropsLazyOffset | MultiSelectMenuPropsLazyGroup; export type MultiSelectMenuProps = MultiSelectMenuPropsLazy | MultiSelectMenuPropsEager; export {};