import {useMemo} from 'react'; import {Item, Section} from '@react-stately/collections'; import domId from 'sentry/utils/domId'; import {Control, ControlProps} from './control'; import {List, MultipleListProps, SingleListProps} from './list'; import type { SelectOption, SelectOptionOrSection, SelectOptionOrSectionWithKey, SelectSection, } from './types'; export type {SelectOption, SelectOptionOrSection, SelectSection}; interface BaseSelectProps extends ControlProps { options: SelectOptionOrSection[]; } export interface SingleSelectProps extends BaseSelectProps, Omit< SingleListProps, 'children' | 'items' | 'grid' | 'compositeIndex' | 'label' > {} export interface MultipleSelectProps extends BaseSelectProps, Omit< MultipleListProps, 'children' | 'items' | 'grid' | 'compositeIndex' | 'label' > {} export type SelectProps = | SingleSelectProps | MultipleSelectProps; // A series of TS function overloads to properly parse prop types across 2 dimensions: // option value types (number vs string), and selection mode (singular vs multiple) function CompactSelect(props: SelectProps): JSX.Element; function CompactSelect(props: SelectProps): JSX.Element; function CompactSelect(props: SelectProps): JSX.Element; /** * Flexible select component with a customizable trigger button */ function CompactSelect({ // List props options, value, defaultValue, onChange, multiple, disallowEmptySelection, isOptionDisabled, // Control props grid, disabled, size = 'md', closeOnSelect, triggerProps, ...controlProps }: SelectProps) { const triggerId = useMemo(() => domId('select-trigger-'), []); // Combine list props into an object with two clearly separated types, one where // `multiple` is true and the other where it's not. Necessary to avoid TS errors. const listProps = useMemo(() => { if (multiple) { return {multiple, value, defaultValue, onChange, closeOnSelect, grid}; } return {multiple, value, defaultValue, onChange, closeOnSelect, grid}; }, [multiple, value, defaultValue, onChange, closeOnSelect, grid]); const optionsWithKey = useMemo[]>( () => options.map((item, i) => ({ ...item, key: 'options' in item ? item.key ?? i : item.value, })), [options] ); const controlDisabled = useMemo( () => disabled ?? options?.length === 0, [disabled, options] ); return ( {(item: SelectOptionOrSection) => { if ('options' in item) { return (
{item.options.map(opt => ( {opt.label} ))}
); } return ( {item.label} ); }}
); } export {CompactSelect};