@itwin/itwinui-react
Version:
A react component library for iTwinUI
281 lines (280 loc) • 8.43 kB
TypeScript
import * as React from 'react';
import type { CommonProps, PortalProps } from '../../utils/index.js';
import { usePopover } from '../Popover/Popover.js';
/**
* Select component to select value from options.
* Generic type is used for value. It prevents you from mistakenly using other types in `options`, `value` and `onChange`.
* @example
* <caption>Basic select.</caption>
* <Select
* options={[
* { value: '1', label: 'Option 1' },
* { value: '2', label: 'Option 2' },
* { value: '3', label: 'Option 3' },
* ]}
* />
* @example
* <caption>Native select.</caption>
* <Select
* native
* options={[
* { value: '1', label: 'Option 1' },
* { value: '2', label: 'Option 2' },
* { value: '3', label: 'Option 3' },
* ]}
* />
* @example
* <caption>Disabled select with placeholder.</caption>
* <Select
* disabled={true}
* placeholder='Placeholder text'
* options={[{ value: 1, label: 'Option 1' }, { value: 2, label: 'Option 2' }, { value: 3, label: 'Option 3' }]}
* />
* @example
* <caption>Select with selected value and change handler.</caption>
* <Select
* value={selectedValue}
* onChange={(value) => setSelectedValue(value)}
* options={[{ value: 1, label: 'Option 1' }, { value: 2, label: 'Option 2' }, { value: 3, label: 'Option 3' }]}
* />
* @example
* <caption>Select using custom renderers for menu items and selected value.</caption>
* <Select
* options={[
* { value: 'yellow', label: 'Yellow' },
* { value: 'green', label: 'Green' },
* { value: 'red', label: 'Red' },
* ]}
* value={selectedValue}
* placeholder='Placeholder text'
* itemRenderer={(option, itemProps) => (
* <MenuItem
* style={{ color: option.value }}
* isSelected={itemProps.isSelected}
* onClick={() => {
* setSelectedValue(option.value);
* itemProps.close();
* }}
* role='option'
* ref={(el) => itemProps.isSelected && el?.scrollIntoView()}
* >
* {option.label}
* </MenuItem>
* )}
* selectedItemRenderer={(option) => (
* <span style={{ backgroundColor: option.value }}>{option.label}</span>
* )}
* />
*/
export declare const Select: <T>(props: SelectProps<T> & {
ref?: React.ForwardedRef<HTMLElement>;
}) => React.JSX.Element;
export type SelectProps<T> = Omit<React.ComponentPropsWithoutRef<'div'>, 'onChange' | 'placeholder' | 'value' | 'defaultValue'> & (({
/**
* If true, the native `<select>` element will be rendered.
*
* Extra props, such as `name` can be passed to the `<select>` using `triggerProps`.
*
* @default false
*/
native: true;
} & NativeSelectProps) | ({
native?: false;
} & CustomSelectProps<T> & {
/**
* styleType is only supported for `<Select native>`.
*/
styleType?: never;
}));
type NativeSelectProps = SelectCommonProps & {
/**
* Selected option value.
*
* Must be a string, because it is passed as an attribute to the native <select>.
*
* Alternatively, pass `null` to reset the value.
*/
value?: string | null;
/**
* Callback invoked when the selected value changes.
*/
onChange?: (value: string, event: React.ChangeEvent<HTMLSelectElement>) => void;
/**
* Array of options that populates the select menu.
*
* The `value` property of each option must be a string.
*/
options: Array<{
label: string;
value: string;
disabled?: boolean;
}>;
/**
* Default value that is selected on initial render. This is useful when you don't want to
* maintain your own state but still want to control the initial value.
*
* If not passed, the first option (or placeholder) will be automatically selected.
*/
defaultValue?: string;
/**
* Props to pass to the select element.
*/
triggerProps?: Omit<React.ComponentPropsWithRef<'select'>, 'size'>;
required?: boolean;
multiple?: never;
} & NativeSelectStyleTypeProps;
type NativeSelectStyleTypeProps = {
/**
* Style of the select.
* Use 'borderless' to hide outline.
* @default 'default'
*/
styleType?: 'default';
/**
* Placeholder for when no item is selected.
*
* Will be rendered as a disabled option at the top of the list, and automatically
* selected when no `value` or `defaultValue` is provided.
*
* Not allowed when `styleType` is `borderless`.
*/
placeholder?: string;
} | {
styleType: 'borderless';
placeholder?: never;
};
type SelectCommonProps = {
/**
* Disables select.
* @default false
*/
disabled?: boolean;
/**
* Modify size of select.
*/
size?: 'small' | 'large';
/**
* Status of select.
*/
status?: 'positive' | 'warning' | 'negative';
};
export type CustomSelectProps<T> = SelectCommonProps & {
/**
* Placeholder when no item is selected.
*/
placeholder?: React.ReactNode;
/**
* Array of options that populates the select menu.
*/
options: SelectOption<T>[];
/**
* Custom renderer for an item in the dropdown list. `MenuItem` item props are going to be populated if not provided.
*/
itemRenderer?: (option: SelectOption<T>, itemProps: ItemRendererProps) => React.JSX.Element;
/**
* Custom class for menu.
*/
menuClassName?: string;
/**
* Custom style for menu.
*/
menuStyle?: React.CSSProperties;
/**
* Props to customize Popover behavior.
*/
popoverProps?: Pick<Parameters<typeof usePopover>[0], 'visible' | 'onVisibleChange' | 'placement' | 'matchWidth' | 'closeOnOutsideClick'> & {
/**
* Middleware options.
*
* By default, `hide` is enabled. If the floating options get hidden even when they shouldn't (e.g. some custom
* styles interfering with the trigger's hide detection) consider disabling the `hide` middleware.
*
* @see https://floating-ui.com/docs/middleware
*/
middleware?: {
hide?: boolean;
};
} & Pick<PortalProps, 'portal'>;
/**
* Props to pass to the select button (trigger) element.
*/
triggerProps?: React.ComponentPropsWithRef<'div'>;
} & SelectMultipleTypeProps<T> & Omit<React.ComponentPropsWithoutRef<'div'>, 'size' | 'disabled' | 'placeholder' | 'onChange'>;
export type SelectValueChangeEvent = 'added' | 'removed';
export type SelectMultipleTypeProps<T> = {
/**
* Enable multiple selection.
* @default false
*/
multiple?: false;
/**
* Custom renderer for the selected item in select.
* If `multiple` is enabled, it will give array of options to render.
*/
selectedItemRenderer?: (option: SelectOption<T>) => React.JSX.Element;
/**
* Selected option value.
* If `multiple` is enabled, it is an array of values.
*
* Pass `null` to reset the value.
*/
value?: T | null;
/**
* Callback function handling change event on select.
*/
onChange?: (value: T) => void;
} | {
multiple: true;
selectedItemRenderer?: (options: SelectOption<T>[]) => React.JSX.Element;
value?: T[];
onChange?: (value: T, event: SelectValueChangeEvent) => void;
};
export type ItemRendererProps = {
/**
* Close handler that closes the dropdown.
*/
close: () => void;
/**
* Indicates whether an item is selected.
*/
isSelected: boolean;
};
export type SelectOption<T> = {
/**
* Label of the item used in dropdown list and when selected.
*/
label: string;
/**
* Sublabel of the item shown below the label.
*/
sublabel?: React.ReactNode;
/**
* Modify height of the item.
* Use 'large' when any of the select options have `sublabel`.
*
* Defaults to 'large' if `sublabel` provided, otherwise 'default'.
*/
size?: 'default' | 'large';
/**
* Value of the item.
*/
value: T;
/**
* @deprecated Use startIcon
* SVG icon component shown on the left.
*/
icon?: React.JSX.Element;
/**
* SVG icon component shown on the left.
*/
startIcon?: React.JSX.Element;
/**
* Item is disabled.
*/
disabled?: boolean;
/**
* Any other props.
*/
[key: string]: unknown;
} & CommonProps;
export {};