Demos
How to import
import { Select } from '@fibrous/semi-ui-react';
const Option = Select.Option;
Basic Usage
Each Option tag must declare the value attribute, and the Option children content will be rendered to the drop-down list
Code
import { Select } from '@fibrous/semi-ui-react';
export default function SelectDemo() {
return (
<>
<Select aria-label="业务线" defaultValue="douyin" style={{ width: 120 }}>
<Select.Option value="douyin">抖音</Select.Option>
<Select.Option value="ulikecam">轻颜相机</Select.Option>
<Select.Option value="jianying" disabled>
剪映
</Select.Option>
<Select.Option value="xigua">西瓜视频</Select.Option>
</Select>
<br />
<br />
<Select aria-label="业务线" defaultValue="douyin" disabled style={{ width: 120 }}>
<Select.Option value="douyin">抖音</Select.Option>
<Select.Option value="ulikecam">轻颜相机</Select.Option>
</Select>
<br />
<br />
<Select aria-label="业务线" placeholder="请选择业务线" style={{ width: 120 }}>
<Select.Option value="douyin">抖音</Select.Option>
<Select.Option value="ulikecam">轻颜相机</Select.Option>
<Select.Option value="jianying" disabled>
剪映
</Select.Option>
<Select.Option value="xigua">西瓜视频</Select.Option>
</Select>
</>
);
}Pass Option as an array
You can pass an array of objects directly through optionList. Each object must contain the value / label attribute.
Code
import { Select } from '@fibrous/semi-ui-react';
export default function SelectDemo() {
const list = [
{ value: 'douyin', label: '抖音', otherKey: 0 },
{ value: 'ulikecam', label: '轻颜相机', disabled: true, otherKey: 1 },
{ value: 'jianying', label: '剪映', otherKey: 2 },
{ value: 'toutiao', label: '今日头条', otherKey: 3 },
];
return <Select aria-label="业务线" placeholder="请选择业务线" style={{ width: 180 }} optionList={list} />;
}Multi-choice
Since v2.28, the selector will have its own maxHeight 270, and the content can be viewed by scrolling vertically after it exceeds.
Configuration multiple properties that can support multi-selection
Configuration maxTagCount. You can limit the number of options displayed, and the excess will be displayed in the form of + N
Configure ellipsisTrigger (>= v2.28.0) to do adaptive processing on the overflow part of the tag. When the width is insufficient, the last tag content will be truncated. After enabling this function, there will be a certain performance loss, and it is not recommended to use it in large form scenarios
Configure expandRestTagsOnClick (>= v2.28.0) to display all remaining tags by clicking when maxTagCount is set
Use showRestTagsPopover (>= v2.22.0) to set whether hover +N displays Popover after exceeding maxTagCount, the default is false. Also, popovers can be configured in the restTagsPopoverProps property
Configuration max Properties can limit the maximum number of options and cannot be selected beyond the maximum limit, while triggeringOn Exceedcallback
Code
import { Select, Toast } from '@fibrous/semi-ui-react';
export default function SelectDemo() {
return (
<>
<Select aria-label="Select 示例" multiple style={{ width: '320px' }} defaultValue={['douyin', 'ulikecam']}>
<Select.Option value="douyin">抖音</Select.Option>
<Select.Option value="ulikecam">轻颜相机</Select.Option>
<Select.Option value="jianying">剪映</Select.Option>
<Select.Option value="xigua">西瓜视频</Select.Option>
</Select>
<br />
<br />
<Select aria-label="Select 示例" multiple maxTagCount={2} showRestTagsPopover restTagsPopoverProps={{ position: 'top' }} style={{ width: '320px' }} defaultValue={['douyin', 'ulikecam', 'jianying']}>
<Select.Option value="douyin">抖音</Select.Option>
<Select.Option value="ulikecam">轻颜相机</Select.Option>
<Select.Option value="jianying">剪映</Select.Option>
<Select.Option value="xigua">西瓜视频</Select.Option>
</Select>
<br />
<br />
<Select aria-label="Select 示例" multiple style={{ width: '320px' }} defaultValue={['douyin']} max={2} onExceed={() => Toast.warning('最多只允许选择两项')}>
<Select.Option value="douyin">抖音</Select.Option>
<Select.Option value="ulikecam">轻颜相机</Select.Option>
<Select.Option value="jianying">剪映</Select.Option>
<Select.Option value="xigua">西瓜视频</Select.Option>
</Select>
<br />
<br />
<Select aria-label="Select 示例" multiple maxTagCount={2} showRestTagsPopover restTagsPopoverProps={{ position: 'top' }} style={{ width: '220px' }} defaultValue={['xigua', 'ulikecam', 'jianying', 'douyin']} ellipsisTrigger expandRestTagsOnClick>
<Select.Option value="douyin">抖音</Select.Option>
<Select.Option value="ulikecam">轻颜相机</Select.Option>
<Select.Option value="jianying">剪映</Select.Option>
<Select.Option value="xigua">西瓜视频</Select.Option>
</Select>
</>
);
}With Group
Grouping Option with OptGroup(Only supports the declaration of children through jsx, and does not support pass in through optionList)
Code
import { Select } from '@fibrous/semi-ui-react';
export default function SelectDemo() {
return (
<Select aria-label="地区" placeholder="" style={{ width: 180 }} filter>
<Select.OptGroup label="Asia">
<Select.Option value="a-1">China</Select.Option>
<Select.Option value="a-2">Korea</Select.Option>
</Select.OptGroup>
<Select.OptGroup label="Europe">
<Select.Option value="b-1">Germany</Select.Option>
<Select.Option value="b-2">France</Select.Option>
</Select.OptGroup>
<Select.OptGroup label="South America">
<Select.Option value="c-1">Peru</Select.Option>
</Select.OptGroup>
</Select>
);
}Code
import { Select } from '@fibrous/semi-ui-react';
export default function SelectDemo() {
const data = [
{
label: 'Asia',
children: [
{ value: 'a-1', label: 'China' },
{ value: 'a-2', label: 'Korea' },
],
},
{
label: 'Europe',
children: [
{ value: 'b-1', label: 'Germany' },
{ value: 'b-2', label: 'France' },
],
},
{
label: 'South America',
children: [{ value: 'c-1', label: 'Peru' }],
},
];
return (
<Select aria-label="地区" placeholder="" style={{ width: 180 }} filter>
{data.map((group, index) => (
<Select.OptGroup label={group.label} key={`${index}-${group.label}`}>
{group.children.map((option, index2) => (
<Select.Option value={option.value} key={`${index2}-${group.label}`}>
{option.label}
</Select.Option>
))}
</Select.OptGroup>
))}
</Select>
);
}Different sizes
Size: small / default / large
Code
import { Select } from '@fibrous/semi-ui-react';
export default function SelectDemo() {
return (
<>
<Select aria-label="small" placeholder="请选择业务线" style={{ width: '180px' }} size="small">
<Select.Option value="ulikecam">轻颜相机</Select.Option>
</Select>
<br />
<br />
<Select aria-label="default" placeholder="请选择业务线" style={{ width: '180px' }}>
<Select.Option value="ulikecam">轻颜相机</Select.Option>
</Select>
<br />
<br />
<Select aria-label="large" placeholder="请选择业务线" style={{ width: '180px' }} size="large">
<Select.Option value="ulikecam">轻颜相机</Select.Option>
</Select>
</>
);
}Different validate status
validateStatus: default / warning / error
Code
import { Select } from '@fibrous/semi-ui-react';
export default function SelectDemo() {
return (
<>
<Select aria-label="default" style={{ width: '180px' }}>
<Select.Option value="ulikecam">轻颜相机</Select.Option>
</Select>
<br />
<br />
<Select aria-label="warning" style={{ width: '180px' }} validateStatus="warning">
<Select.Option value="ulikecam">轻颜相机</Select.Option>
</Select>
<br />
<br />
<Select aria-label="error" style={{ width: '180px' }} validateStatus="error">
<Select.Option value="ulikecam">轻颜相机</Select.Option>
</Select>
</>
);
}Configure Prefix, Suffix, Clear Button
- You can pass the selection box prefix through
prefix, the selection box suffix throughsuffix, for text or React Node
The left and right padding is automatically brought when the content passed in by prefix and reactix is text or Icon. If it is a custom ReactNode, the left and right padding is 0.
- Whether to show the clear button is displayed by
showClear - Whether to show the right drop-down arrow is displayed by
showArrow
Code
import { IconVigoLogo, IconGift } from '@fibrous/semi-icons-react';
import { Select } from '@fibrous/semi-ui-react';
export default function SelectDemo() {
return (
<>
<Select aria-label="Select 示例" style={{ width: '320px' }} defaultValue="ulikecam" prefix={<IconVigoLogo />} showClear>
<Select.Option value="douyin">抖音</Select.Option>
<Select.Option value="ulikecam">轻颜相机</Select.Option>
<Select.Option value="jianying">剪映</Select.Option>
<Select.Option value="xigua">西瓜视频</Select.Option>
</Select>
<br />
<br />
<Select aria-label="Select 示例" style={{ width: '320px' }} defaultValue="ulikecam" prefix={<IconVigoLogo />} suffix={<IconGift />} showArrow={false}>
<Select.Option value="douyin">抖音</Select.Option>
<Select.Option value="ulikecam">轻颜相机</Select.Option>
<Select.Option value="jianying">剪映</Select.Option>
<Select.Option value="xigua">西瓜视频</Select.Option>
</Select>
</>
);
}Additional items
We have reserved two slots at the bottom of the pop-up layer, which you can use when you need to add a custom node to the pop-up layer.
UseinnerTopSlot or outerTopSlot to pass the custom node, which will be rendered at the top of the pop-up layer. UseinnerBottomSlot or outerBottomSlot instead at the bottom.
innerTopSlotandinnerBottomSlotwill be rendered inside the Option ListouterTopSlotandouterBottomSlotwill be rendered to level with the option List
outerBottomSlot:
innerBottomSlot:
Code
import { Select } from '@fibrous/semi-ui-react';
export default function SelectDemo() {
let innerSlotStyle = {
backgroundColor: 'var(--color-white)',
height: '36px',
display: 'flex',
alignItems: 'center',
cursor: 'pointer',
paddingLeft: 32,
borderTop: '1px solid var(--semi-color-border)',
borderRadius: '0 0 6px 6px',
color: 'var(--semi-color-link)',
};
let innerSlotNode = <div style={innerSlotStyle}>点击加载更多</div>;
let outSlotStyle = {
backgroundColor: 'var(--semi-color-fill-0)',
height: '36px',
display: 'flex',
paddingLeft: 32,
color: 'var(--semi-color-link)',
alignItems: 'center',
cursor: 'pointer',
borderTop: '1px solid var(--semi-color-border)',
borderRadius: '0 0 6px 6px',
};
let outSlotNode = (
<div style={outSlotStyle}>
<span style={{ color: 'var(--semi-color-link)' }}>未找到应用?</span>
</div>
);
return (
<div>
<p>outerBottomSlot:</p>
<Select aria-label="outerBottomSlot" style={{ width: 300 }} dropdownStyle={{ width: 180 }} maxHeight={150} outerBottomSlot={outSlotNode} placeholder="自定义外侧底部slot,始终显示" defaultOpen autoAdjustOverflow={false} position="bottom">
<Select.Option value="douyin">抖音</Select.Option>
<Select.Option value="ulikecam">轻颜相机</Select.Option>
<Select.Option value="jianying">剪映</Select.Option>
<Select.Option value="duoshan">多闪</Select.Option>
<Select.Option value="xigua">西瓜视频</Select.Option>
</Select>
<p style={{ marginTop: 200 }}>innerBottomSlot:</p>
<Select aria-label="innerBottomSlot" style={{ width: 300 }} dropdownStyle={{ width: 180 }} maxHeight={150} innerBottomSlot={innerSlotNode} placeholder="自定义内侧底部slot,滚动至底部显示">
<Select.Option value="douyin">抖音</Select.Option>
<Select.Option value="ulikecam">轻颜相机</Select.Option>
<Select.Option value="jianying">剪映</Select.Option>
<Select.Option value="duoshan">多闪</Select.Option>
<Select.Option value="xigua">西瓜视频</Select.Option>
</Select>
</div>
);
}Using outerTopSlot to insert content
Code
import { useState } from 'react';
import { RadioGroup, Select } from '@fibrous/semi-ui-react';
interface Item {
value: string;
label: string;
}
export default function SelectDemo() {
const list: Record<string, Item[]> = {
component: [
{ value: 'select', label: '选择器' },
{ value: 'tabs', label: '标签' },
{ value: 'avatar', label: '头像' },
{ value: 'button', label: '按钮' },
],
design: [
{ value: 'color', label: '颜色' },
{ value: 'dark', label: '暗色模式' },
{ value: 'icon', label: '图标' },
{ value: 'font', label: '字体' },
],
feedback: [
{ value: 'faq', label: '常见问题' },
{ value: 'join', label: '加入用户群' },
{ value: 'hornbill', label: '犀鸟反馈问题' },
],
};
const [key, setKey] = useState('component');
const [value, setValue] = useState<Item | undefined>({ value: 'faq', label: '常见问题' });
const categoryWrapper = {
padding: '8px 12px',
borderBottom: '0.5px solid var(--semi-color-border)',
};
const categoryOptions = [
{ value: 'component', label: '组件' },
{ value: 'design', label: '设计' },
{ value: 'feedback', label: '反馈' },
];
// The radio group switches with arrow keys, so categories need no tabpanel association
const outerTopSlotNode = (
<div style={categoryWrapper}>
<RadioGroup type="button" buttonSize="small" aria-label="选项分类" options={categoryOptions} value={key} onChange={(e) => setKey(e.target.value)} />
</div>
);
return <Select aria-label="选项" defaultOpen autoAdjustOverflow={false} value={value} onChangeWithObject onChange={(obj) => setValue(obj)} style={{ width: 200 }} outerTopSlot={outerTopSlotNode} optionList={list[key]} />;
}Controlled component
When value is passed, Select is a controlled component, and the value selected is entirely determined by value.
Code
import { useState } from 'react';
import { Select } from '@fibrous/semi-ui-react';
export default function SelectDemo() {
let [value, setValue] = useState<string | undefined>('xigua');
return (
<>
<Select aria-label="业务线" value={value} style={{ width: '300px' }} onChange={setValue} placeholder="受控的Select">
<Select.Option value="douyin">抖音</Select.Option>
<Select.Option value="ulikecam">轻颜相机</Select.Option>
<Select.Option value="jianying">剪映</Select.Option>
<Select.Option value="xigua">西瓜视频</Select.Option>
</Select>
</>
);
}Dynamic Modification Options
If you need to update Options dynamically, you should use controlled value
Code
import { useState } from 'react';
import { Select, Button } from '@fibrous/semi-ui-react';
export default function SelectDemo() {
let [options, setOptions] = useState([1, 2, 3, 4]);
function add() {
let length = Math.ceil(Math.random() * 10);
let newOptions = Array.from({ length }, (_v, i) => i + 1);
setOptions(newOptions);
}
return (
<>
<Select aria-label="数字" style={{ width: '180px' }} placeholder="请选择" value={4}>
{options.map((option) => (
<Select.Option value={option} key={option}>
{option}
</Select.Option>
))}
</Select>
<br />
<br />
<Button onClick={add}>changeOptions Dynamic</Button>
</>
);
}Linkage Select
Use a controlled value to link different Selects. If it is a complex linkage with a hierarchical relationship, it is recommended to use Cascader components directly
Code
import { useState } from 'react';
import { Select } from '@fibrous/semi-ui-react';
export default function SelectDemo() {
const provinces = ['四川', '广东'];
const maps: Record<string, string[]> = {
四川: ['成都', '都江堰'],
广东: ['广州', '深圳', '东莞'],
};
const [province, setProvince] = useState(provinces[0]);
const citys = maps[province];
const [city, setCity] = useState<string | undefined>(citys[0]);
const provinceChange = (newProvince?: string) => {
if (!newProvince) {
return;
}
setProvince(newProvince);
setCity(maps[newProvince][0]);
};
const cityChange = (newCity?: string) => setCity(newCity);
return (
<>
<Select aria-label="省份" style={{ width: '150px', margin: '10px' }} onChange={provinceChange} value={province}>
{provinces.map((pro) => (
<Select.Option value={pro} key={pro}>
{pro}
</Select.Option>
))}
</Select>
<Select aria-label="城市" style={{ width: '150px', margin: '10px' }} value={city} onChange={cityChange}>
{citys.map((c) => (
<Select.Option value={c} key={c}>
{c}
</Select.Option>
))}
</Select>
</>
);
}Search
You can turn on the search capability by setting filter to true.
The default search strategy will include comparison of the input value with the label value of option
By default, the search keywords will be cleared automatically after multiple selection is selected. If you want to keep it, you can turn off the default behavior by setting autoClearSearchValue to false (provided after v2.3)
Code
import { Select } from '@fibrous/semi-ui-react';
export default function SelectDemo() {
return (
<>
<Select aria-label="单选" filter style={{ width: 180 }} placeholder="带搜索功能的单选">
<Select.Option value="douyin">抖音</Select.Option>
<Select.Option value="ulikecam">轻颜相机</Select.Option>
<Select.Option value="jianying">剪映</Select.Option>
<Select.Option value="xigua">西瓜视频</Select.Option>
</Select>
<br />
<br />
<Select aria-label="多选" filter multiple style={{ width: 300 }} placeholder="带搜索功能的多选" autoClearSearchValue={false}>
<Select.Option value="semi-0">Semi-0</Select.Option>
<Select.Option value="semi-1">Semi-1</Select.Option>
<Select.Option value="semi-2">Semi-2</Select.Option>
<Select.Option value="semi-3">Semi-3</Select.Option>
<Select.Option value="semi-4">Semi-4</Select.Option>
</Select>
</>
);
}Search position
The default search input is displayed on the Select Trigger. You can specify different positions through searchPosition, and you can choose dropdown or trigger. Available after v2.61.0
If you want to customize the placeholder of the Input search box in the dropdown, you can control it through searchPlaceholder
If the searchPosition is trigger, when showClear=true, clicking the clear button of the input will clear the selected items and the search text in the input at the same time
If the searchPosition is dropdown, when showClear=true, clicking the clear button of the trigger will clear the selected items, clicking the clear button in the dropdown input will clear search text
Code
import { Select } from '@fibrous/semi-ui-react';
export default function SelectDemo() {
return (
<>
<Select aria-label="单选" filter searchPosition="dropdown" style={{ width: 200 }} defaultValue="ulikecam" placeholder="我的搜索框在下拉菜单中" searchPlaceholder="带搜索功能的单选">
<Select.Option value="douyin">抖音</Select.Option>
<Select.Option value="ulikecam">轻颜相机</Select.Option>
<Select.Option value="jianying">剪映</Select.Option>
<Select.Option value="xigua">西瓜视频</Select.Option>
</Select>
<br />
<br />
<Select aria-label="多选" filter searchPosition="dropdown" multiple style={{ width: 300 }} defaultValue={['semi-1']} placeholder="我的搜索框在下拉菜单中" searchPlaceholder="带搜索功能的多选" autoClearSearchValue={false}>
<Select.Option value="semi-0">Semi-0</Select.Option>
<Select.Option value="semi-1">Semi-1</Select.Option>
<Select.Option value="semi-2">Semi-2</Select.Option>
<Select.Option value="semi-3">Semi-3</Select.Option>
<Select.Option value="semi-4">Semi-4</Select.Option>
</Select>
</>
);
}Remote search
A multi-select example with remote search, request debounce, loading status.
- Use
filterturn on the search capability. - Use
remoteto disabled local filter - Dynamic Update
optionListafteronSearchcallback - Update
loadingwhen fetching data / finish
- Use controlled value attribute
Code
import { useMemo, useState } from 'react';
import { debounce } from 'es-toolkit';
import { Select } from '@fibrous/semi-ui-react';
interface Item {
value: string;
label: string;
type: number;
}
const mockSearch = (inputValue: string) => {
const length = Math.ceil(Math.random() * 100);
return Array.from({ length }, (_v, i) => {
return { value: inputValue + i, label: `相近业务 ${inputValue}${i}`, type: i + 1 };
});
};
export default function SelectDemo() {
const [loading, setLoading] = useState(false);
const optionList: Item[] = [
{ value: 'douyin', label: '抖音', type: 1 },
{ value: 'xingtu', label: '醒图', type: 2 },
{ value: 'jianying', label: '剪映', type: 3 },
{ value: 'toutiao', label: '今日头条', type: 4 },
];
const [list, setList] = useState(optionList);
const [value, setValue] = useState<Item[]>([]);
const handleMultipleChange = (newValue: Item[]) => {
setValue(newValue);
};
const handleSearch = useMemo(
() =>
debounce((inputValue: string) => {
setLoading(true);
if (inputValue) {
const result = mockSearch(inputValue);
setTimeout(() => {
setLoading(false);
setList(result);
}, 1000);
} else {
setLoading(false);
}
}, 1000),
[]
);
return <Select aria-label="远程搜索" style={{ width: 300 }} filter remote onChangeWithObject multiple value={value} onSearch={handleSearch} optionList={list} loading={loading} onChange={handleMultipleChange} emptyContent={null} />;
}Custom search strategy
By default, the user's search input will be compared with the option's label value as a string include.
You can set filter as a custom function to customize your filter strategy.
Code
import { Select, type SelectOptionProps } from '@fibrous/semi-ui-react';
export default function SelectDemo() {
function searchLabel(sugInput: string, option: SelectOptionProps) {
let label = String(option.label).toUpperCase();
let sug = sugInput.toUpperCase();
return label.includes(sug);
}
return (
<Select aria-label="业务线" filter={searchLabel} style={{ width: '180px' }} placeholder="try douyin">
<Select.Option value="douyin">douyin</Select.Option>
<Select.Option value="ulikecam">HOTSOON</Select.Option>
<Select.Option value="jianying">PIPIXIA</Select.Option>
<Select.Option value="xigua">XIGUA</Select.Option>
</Select>
);
}Custom selection rendering
By default, the content of option.label or option.children will be backfilled into the selection box when the option is selected.
But you can customize the rendering of the selection box through the renderSelectedItem function
- Select:
renderSelectedItem(optionNode: object) => content: ReactNode - Multiple Select:
renderSelectedItem(optionNode: object, { index: number, onClose: function }) => { isRenderInTag: boolean, content: ReactNode }- When
isRenderInTagis true, content will automatically wrapped inTagrendering (with background color and close button) - When
isRenderInTagis false, it renders the returned content directly
- When
shenyue@example.com

Code
import { Select, Avatar, Tag, type SelectRenderMultipleSelectedItemFn as RenderMultipleSelectedItemFn, type SelectRenderSingleSelectedItemFn as RenderSingleSelectedItemFn } from '@fibrous/semi-ui-react';
interface ListItem {
name: string;
email: string;
avatar: string;
}
export default function SelectDemo() {
const list: ListItem[] = [
{
name: '夏可漫',
email: 'xiakeman@example.com',
avatar: '/assets/placeholder/avatar-a.svg',
},
{
name: '申悦',
email: 'shenyue@example.com',
avatar: '/assets/root-web-sites/bag.jpeg',
},
{
name: '曲晨一',
email: 'quchenyi@example.com',
avatar: '/assets/placeholder/avatar-c.svg',
},
{
name: '文嘉茂',
email: 'wenjiamao@example.com',
avatar: '/assets/root-web-sites/6fbafc2d-e3e6-4cff-a1e2-17709c680624.png',
},
];
const renderSelectedItem: RenderSingleSelectedItemFn = (optionNode) => (
<div key={optionNode.email} style={{ display: 'flex', alignItems: 'center' }}>
<Avatar src={optionNode.avatar} alt="" size="small" />
<span style={{ marginLeft: 8 }}>{optionNode.email}</span>
</div>
);
const renderMultipleWithCustomTag: RenderMultipleSelectedItemFn = (optionNode, { onClose }) => {
const content = (
<Tag avatarSrc={optionNode.avatar} avatarShape="circle" closable onClose={onClose} size="large" key={optionNode.name}>
{optionNode.name}
</Tag>
);
return {
isRenderInTag: false,
content,
};
};
const renderMultipleWithCustomTag2: RenderMultipleSelectedItemFn = (optionNode, { onClose }) => {
const content = (
<Tag avatarSrc={optionNode.avatar} avatarShape="square" closable onClose={onClose} size="large" key={optionNode.name}>
{optionNode.name}
</Tag>
);
return {
isRenderInTag: false,
content,
};
};
const renderCustomOption = (item: ListItem) => {
const optionStyle = {
display: 'flex',
paddingLeft: 24,
paddingTop: 10,
paddingBottom: 10,
};
return (
<Select.Option value={item.name} style={optionStyle} showTick {...item} key={item.email}>
<Avatar size="small" src={item.avatar} alt="" />
<div style={{ marginLeft: 8 }}>
<div style={{ fontSize: 14 }}>{item.name}</div>
<div style={{ color: 'var(--color-text-2)', fontSize: 12, lineHeight: '16px', fontWeight: 'normal' }}>{item.email}</div>
</div>
</Select.Option>
);
};
return (
<>
<Select placeholder="请选择" style={{ width: 280, height: 40 }} onChange={(v) => console.log(v)} defaultValue="申悦" renderSelectedItem={renderSelectedItem}>
{list.map(renderCustomOption)}
</Select>
<Select placeholder="请选择" maxTagCount={2} style={{ width: 280, marginTop: 20 }} onChange={(v) => console.log(v)} defaultValue={['申悦', '曲晨一']} multiple renderSelectedItem={renderMultipleWithCustomTag}>
{list.map(renderCustomOption)}
</Select>
<Select placeholder="请选择" maxTagCount={2} style={{ width: 280, marginTop: 20 }} onChange={(v) => console.log(v)} defaultValue={['申悦', '曲晨一']} multiple renderSelectedItem={renderMultipleWithCustomTag2}>
{list.map(renderCustomOption)}
</Select>
</>
);
}Custom pop-up layer style
You can control the style of the pop-up layer through dropdownClassName, dropdownStyle
For example, when you customize the width of the pop-up layer, you can pass the width through drowndownStyle
Code
import { Select } from '@fibrous/semi-ui-react';
export default function SelectDemo() {
return (
<Select aria-label="业务线" placeholder="自定义弹出层样式的" style={{ width: 180 }} dropdownStyle={{ width: 250 }} dropdownClassName="test">
<Select.Option value="douyin">抖音</Select.Option>
<Select.Option value="ulikecam">轻颜相机</Select.Option>
<Select.Option value="jianying">剪映</Select.Option>
<Select.Option value="xigua">西瓜视频</Select.Option>
</Select>
);
}Get all attribute of selected option
By default, through onChange uou can only get value attribute of selected option.
If you need to take other attributes of the selected option, you can use onChangeWithObject Properties
At this time, the argument of onChange will be object, containing various attributes of selected option, eg: onChange({ value, label, ...rest })
onChange回调:
onChange回调:
Code
import { useState } from 'react';
import { Select, TextArea } from '@fibrous/semi-ui-react';
interface Item {
value: string;
label: string;
type: number;
}
export default function SelectDemo() {
const list: Item[] = [
{ value: 'douyin', label: '抖音', type: 1 },
{ value: 'ulikecam', label: '轻颜相机', type: 2 },
{ value: 'jianying', label: '剪映', type: 3 },
{ value: 'toutiao', label: '今日头条', type: 4 },
];
const [cbValue, setCbValue] = useState<Item>();
const [multipleCbValue, setMultipleCbValue] = useState<Item[]>();
const onChange = (value?: Item) => {
setCbValue(value);
console.log(value);
};
const onMultipleChange = (value: Item[]) => {
setMultipleCbValue(value);
console.log(value);
};
return (
<div>
<div>
<Select aria-label="单选" style={{ width: 150 }} onChangeWithObject optionList={list} placeholder="单选" defaultValue={list[0]} onChange={onChange} />
<h4 id="select-on-change-with-object-single">onChange回调:</h4>
<TextArea aria-labelledby="select-on-change-with-object-single" style={{ width: 320, marginBottom: 48 }} autosize value={JSON.stringify(cbValue)} rows={2} />
</div>
<div>
<Select aria-label="多选" style={{ width: 320 }} onChangeWithObject multiple optionList={list} onChange={onMultipleChange} placeholder="多选" />
<h4 id="select-on-change-with-object-multiple">onChange回调:</h4>
<TextArea aria-labelledby="select-on-change-with-object-multiple" style={{ width: 320 }} autosize value={JSON.stringify(multipleCbValue)} />
</div>
</div>
);
}Create entries
You can create and select entries that do not exist in the options by setting allowCreate=true
You can customize the content display when creating the label through renderCreateItem (by returning ReactNode, note that you need to customize the style)
In addition, can be used with the defaultActiveFirstOption property to automatically select the first item. When you enter directly and press Enter, you can immediately create an Option
Code
import { Select } from '@fibrous/semi-ui-react';
export default function SelectDemo() {
const optionList = [
{ value: 'douyin', label: '抖音' },
{ value: 'ulikecam', label: '轻颜相机' },
{ value: 'jianying', label: '剪映' },
{ value: 'toutiao', label: '今日头条' },
];
return (
<>
<Select aria-label="业务线" style={{ width: 400 }} optionList={optionList} allowCreate multiple filter onChange={(v) => console.log(v)} defaultActiveFirstOption />
<br />
<br />
<Select
aria-label="业务线"
style={{ width: 400 }}
optionList={optionList}
allowCreate
multiple
filter
placeholder="With renderCreateItem"
renderCreateItem={(input, _isFocus, style) => <div style={{ padding: 10, ...style }}>Create Item:{input}</div>}
onChange={(v) => console.log(v)}
defaultActiveFirstOption
/>
</>
);
}Virtualize
Turn on list virtualization when passing in virtualize to optimize performance when there are a large number of Option nodes virtualize is an object containing the following values:
- height: Option list height value, default 270 (before v2.20.8 was 300)
- width: Option list width value, default 100%
- itemSize: The height of each line of Option, must be passed
Code
import { Select } from '@fibrous/semi-ui-react';
export default function SelectDemo() {
const newOptions = Array.from({ length: 3000 }, (_v, i) => ({ label: `option-${i}`, value: i }));
let virtualize = {
height: 270,
width: '100%',
itemSize: 36, // px
};
return <Select aria-label="virtualize" placeholder="拥有3k个Option的Select" style={{ width: 260 }} filter virtualize={virtualize} optionList={newOptions} />;
}Custom Trigger
If the default layout style of the selection box does not meet your needs, you can use triggerRender to customize the display of the selection box
If you want to keep the search and filter ability without rendering the Input structure yourself, you can also set searchPosition='dropdown' to put the default search box in the dropdown list
The parameters of triggerRender are as follows
interface TriggerRenderProps {
value: array<object> // All currently selected options
inputValue: string; // The input value of the current input box
onSearch: (inputValue: string) => void; // The function used to update the value of the input box. You should call this function when the value of the Input component you customize in triggerRender is updated to synchronize the state to the Select internal. props.filter needs to be true, support after v2.32
onClear: () => void; // Function to clear the value
onRemove: (option: object) => void; // support after v2.32
disabled: boolean; // Whether to disable Select
placeholder: ReactNode; // Select placeholder
componentProps: //All props passed to Select by users
}
不同背景色的触发器
使用 circle Tag 作为触发器
Code
import { useState } from 'react';
import { IconAppCenter, IconChevronDown } from '@fibrous/semi-icons-react';
import { Select, Tag, type SelectTriggerRenderProps } from '@fibrous/semi-ui-react';
export default function SelectDemo() {
const [valList, setValList] = useState(['douyin', 'ulikecam']);
const list = [
{ value: 'douyin', label: '抖音' },
{ value: 'ulikecam', label: '轻颜相机' },
{ value: 'jianying', label: '剪映' },
{ value: 'toutiao', label: '今日头条' },
];
const triggerRender = ({ value }: SelectTriggerRenderProps) => {
return (
<div
style={{
minWidth: '112px',
backgroundColor: 'var(--semi-color-primary-light-default)',
height: 32,
display: 'flex',
alignItems: 'center',
paddingLeft: 12,
borderRadius: 3,
color: 'var(--semi-color-primary)',
}}
>
<div
style={{
fontWeight: 600,
flexShrink: 0,
fontSize: 14,
}}
>
业务线
</div>
<div
style={{
margin: 4,
whiteSpace: 'nowrap',
textOverflow: 'ellipsis',
flexGrow: 1,
overflow: 'hidden',
}}
>
{value.map((item) => item.label).join(' , ')}
</div>
<IconAppCenter style={{ marginRight: 8, flexShrink: 0 }} />
</div>
);
};
const triggerRender2 = ({ value }: SelectTriggerRenderProps) => {
return (
<div
style={{
margin: 4,
whiteSpace: 'nowrap',
textOverflow: 'ellipsis',
flexGrow: 1,
overflow: 'hidden',
display: 'flex',
alignItems: 'center',
}}
>
<Tag size="large" color="cyan" shape="circle" suffixIcon={<IconChevronDown />}>
{value.map((item) => item.label).join(' / ')}
</Tag>
</div>
);
};
return (
<div>
<h4>不同背景色的触发器</h4>
<Select aria-label="不同背景色的触发器" value={valList} triggerRender={triggerRender} optionList={list} onChange={(value) => setValList(value)} multiple filter searchPosition="dropdown" style={{ width: 240 }} />
<br />
<br />
<h4>使用 circle Tag 作为触发器</h4>
<Select aria-label="circle Tag 触发器" value={valList} onChange={(value) => setValList(value)} triggerRender={triggerRender2} optionList={list} filter multiple searchPosition="dropdown" style={{ width: 240, marginTop: 20, outline: 0 }} />
</div>
);
}The following is a more complex example: Reusing the drag-and-sort capability of TagInput and adding sorting to Select through a custom trigger.
可对已选项拖拽重新排序的 Select
抖音
轻颜相机
Code
import { useState } from 'react';
import { Select, TagInput, type SelectTriggerRenderProps } from '@fibrous/semi-ui-react';
export default function SelectDemo() {
const [valList, setValList] = useState(['douyin', 'ulikecam']);
const [inputVal, setInputVal] = useState('');
const list = [
{ value: 'douyin', label: '抖音' },
{ value: 'ulikecam', label: '轻颜相机' },
{ value: 'jianying', label: '剪映' },
{ value: 'toutiao', label: '今日头条' },
];
const handleSort = (currentLabels: string[]) => {
const newValue = currentLabels.flatMap((item) => list.find((i) => i.label === item)?.value ?? []);
setValList(newValue);
};
const triggerRender = ({ value, onSearch }: SelectTriggerRenderProps) => {
return (
<div role="presentation" onKeyDown={(e) => e.stopPropagation()}>
<TagInput
draggable
allowDuplicates={false}
value={value.map((item) => String(item.label))}
inputValue={inputVal}
onInputChange={(word) => {
onSearch(word);
setInputVal(word);
}}
onChange={handleSort}
showClear
/>
</div>
);
};
return (
<>
<h4>可对已选项拖拽重新排序的 Select </h4>
<Select aria-label="可拖拽排序的 Select" value={valList} triggerRender={triggerRender} optionList={list} onChange={(value) => setValList(value)} multiple filter style={{ width: 240 }} />
</>
);
}Custom Option Render
- Simple customization: Pass the label property of Option or children into ReactNode, you can control the rendering of the candidates, and the content will automatically bring styles such as padding, background color, etc.
- Complete customization: By passing in
renderOptionItem, you can completely take over the rendering of the candidates in the list, and get the relevant state values from the callback input parameters. Achieve a higher degree of freedom of structural rendering
Notice:- The style passed in by props needs to be consumed on wrapper dom, otherwise it will not be able to be used normally in virtualization scenarios
- The styles of selected, focused, disabled, etc. state need to be added by yourself, and you can get the relative boolean value from props
onMouseEnter、classNameneeds to be bound on the wrapper dom, otherwise the display will be problematic when the upper and lower keyboards are operated- If your custom item is Select.Option, you need to pass renderProps.onClick transparently to the onSelect prop of Option
- Put
idon the wrapper dom withrole="option",aria-selectedandaria-disabled: the Select pointsaria-activedescendantat that id, so screen readers announce the option reached with the arrow keys; Select also uses this id to scroll the option into view when the dropdown opens or the arrow keys move
Code
import { useState } from 'react';
import clsx from 'clsx';
import { Select, Checkbox, Highlight, type SelectOptionRenderProps } from '@fibrous/semi-ui-react';
export default function SelectDemo() {
const [inputValue, setInputValue] = useState('');
const renderOptionItem = ({ id, disabled, selected, label, focused, className, style, onMouseEnter, onClick }: SelectOptionRenderProps) => {
const optionCls = clsx(className, {
['custom-option-render']: true,
['custom-option-render-focused']: focused,
['custom-option-render-disabled']: disabled,
['custom-option-render-selected']: selected,
});
const searchWords = [inputValue];
// Put style on the outer DOM or the virtual list breaks; add selected/focused/disabled styles from props;
// bind onMouseEnter and className on the outer DOM or arrow keys display wrongly; put id there so screen
// readers announce the option the arrow keys reach
return (
<div id={id} role="option" aria-selected={selected} aria-disabled={disabled} style={style} className={optionCls} onClick={onClick} onMouseEnter={onMouseEnter}>
<Checkbox checked={selected} />
<div className="option-right">
<Highlight sourceString={label as string} searchWords={searchWords} />
</div>
</div>
);
};
const optionList = [
{ value: 'douyin', label: '抖音', otherKey: 0 },
{ value: 'ulikecam', label: '轻颜相机', disabled: true, otherKey: 1 },
{ value: 'jianying', label: '剪映', otherKey: 2 },
{ value: 'toutiao', label: '今日头条', otherKey: 3 },
];
return (
<>
<Select aria-label="单选" filter placeholder="单选" onSearch={(v) => setInputValue(v)} dropdownClassName="components-select-demo-renderOptionItem" optionList={optionList} style={{ width: 180 }} renderOptionItem={renderOptionItem} />
<br />
<br />
<Select aria-label="多选" filter placeholder="多选" multiple onSearch={(v) => setInputValue(v)} dropdownClassName="components-select-demo-renderOptionItem" optionList={optionList} style={{ width: 320 }} renderOptionItem={renderOptionItem} />
</>
);
}.components-select-demo-renderOptionItem {
.custom-option-render {
display: flex;
font-size: 14px;
line-height: 20px;
word-break: break-all;
padding-left: 12px;
padding-right: 12px;
padding-top: 8px;
padding-bottom: 8px;
color: var(--semi-color-text-0);
position: relative;
display: flex;
align-items: center;
cursor: pointer;
box-sizing: border-box;
.option-right {
margin-left: 8px;
display: inline-flex;
align-items: center;
}
&:active {
background-color: var(--semi-color-fill-1);
}
&-focused {
background-color: var(--semi-color-fill-0);
}
&-selected {
//font-weight: 700;
}
&-disabled {
color: var(--semi-color-disabled-text);
cursor: not-allowed;
}
&:first-of-type {
margin-top: 4px;
}
&:last-of-type {
margin-bottom: 4px;
}
}
}
API reference
Select Props
Props
| Property | Description | Type | Default |
|---|---|---|---|
| allowCreate | Whether items missing from the list can be created, used together with filter. onCreate fires on creation, and renderCreateItem customizes the created item. | boolean | false |
| aria-label | Accessible name of the combobox. It takes precedence over insetLabel, and aria-labelledby takes precedence over it. | React.AriaAttributes['aria-label'] | - |
| arrowIcon | Custom dropdown arrow icon on the right. With showClear on and a value selected, hovering shows the clear icon first. | React.ReactNode | Type<IconChevronDown /> |
| autoAdjustOverflow | Whether the dropdown flips its direction automatically when it is covered. | boolean | true |
| autoClearSearchValue | With both multiple and filter on, whether the search keyword is cleared after an option is selected. | boolean | true |
| autoFocus | Whether the Select is focused on the initial render. | boolean | - |
| borderless | Whether to use the borderless style. | boolean | false |
| children | Options. Direct children must be Select.Option or Select.OptGroup, and fragments are expanded. When optionList is passed, it is used and children are ignored. | React.ReactNode | - |
| className | Class name, applied to the trigger root element. | string | - |
| clearIcon | Custom icon of the clear button, effective when showClear is true. | React.ReactNode | - |
| clickToHide | When the dropdown is open, whether clicking the Select again closes the dropdown. | boolean | - |
| defaultActiveFirstOption | Whether the first enabled option is highlighted when nothing is selected, so Enter selects it directly. When a value is selected, opening the dropdown highlights the first selected option. | boolean | true |
| defaultOpen | Whether the dropdown is open by default. | boolean | false |
| defaultValue | Initially selected value, for uncontrolled use. An array in multiple mode. | TypeSelectValue<T> | - |
| disabled | Whether the Select is disabled. | boolean | false |
| dropdownClassName | Class name of the dropdown. | string | - |
| dropdownMatchSelectWidth | Whether the minimum width of the dropdown matches the Select. The width itself is not fixed; use dropdownStyle to set a fixed width. | boolean | true |
| dropdownStyle | Style of the dropdown, for example width to set its width. | React.CSSProperties | - |
| ellipsisTrigger | With maxTagCount set in multiple mode, whether overflowing tags adapt to the width, truncating the last tag when space runs short. It costs some performance, so it is not recommended for large forms. | boolean | false |
| emptyContent | Content shown when there are no results. When unset, the locale's empty text is shown; null shows nothing in the dropdown. | React.ReactNode | - |
| expandRestTagsOnClick | With maxTagCount set in multiple mode, whether all remaining tags are shown while the dropdown is open. | boolean | false |
| filter | Whether the Select is searchable. true filters by whether the option label contains the input. A function receives the input value and the option, and returns true to keep it. | Typeboolean | ((inpueValue: string, option: OptionProps) => boolean) | false |
| id | Id of the trigger. Option ids are derived from it. Generated by the component when unset. | string | - |
| innerBottomSlot | Custom content rendered at the bottom inside the option list. It scrolls with the list and shows when the list is scrolled to the bottom. | React.ReactNode | - |
| innerTopSlot | Custom content rendered at the top inside the option list. It scrolls with the list and shows when the list is scrolled to the top. | React.ReactNode | - |
| inputProps | Extra props of the search input when filter is true; see the Input component for the available props. Do not pass value, ref, onChange or onFocus, which would override the Select's handlers and break its behavior. | TypeOmit<InputProps, keyof { value?: InputProps['value']; onFocus?: InputProps['onFocus']; onChange?: InputProps['onChange']; }> | - |
| insetLabel | Inset label inside the trigger, also the name source of the combobox and the listbox. It is not rendered and is not used as the name when prefix is set. | React.ReactNode | - |
| insetLabelId | Id of the insetLabel element, which the aria-labelledby of the combobox points to. Generated by the component when unset. | string | - |
| loading | Whether the dropdown shows a loading spinner. Options are hidden while loading. | boolean | - |
| max | Maximum number of selectable options, effective in multiple mode only. Options beyond it cannot be selected, and onExceed fires. | number | - |
| maxHeight | Maximum height of the option list in the dropdown. With virtualization and virtualize.height above the default 270px, set it to the same value to avoid a double scrollbar. | string | number | 270 |
| maxTagCount | In multiple mode, selected items beyond this count are shown as +N. | number | - |
| motion | Whether the dropdown uses motion. | boolean | true |
| multiple | Whether multiple selection is enabled. | Multiple | false |
| onBlur | Callback when the Select loses focus. When a click outside closes the open dropdown, the argument is that mousedown's native event. | Type(e: React.FocusEvent<HTMLElement> | globalThis.MouseEvent) => void | - |
| onChange | Callback when the value changes. The argument is T | undefined in single mode and T[] in multiple mode, or the option object when onChangeWithObject is true. | Type(value: SelectChangeValue<T, Multiple>) => void | - |
| onChangeWithObject | Whether the other properties of the option are passed to onChange. When true, onChange receives an object such as { value, label, ...rest }, and value and defaultValue should also be objects with value and label keys. | boolean | false |
| onClear | Callback when the clear button is clicked. | Type() => void | - |
| onCreate | Callback when an option is created with allowCreate on. The argument is the created option. | Type(option: OptionProps) => void | - |
| onDeselect | Callback when an option is deselected, in multiple mode only. The first argument is the original option value, and the second is the option object. | Type(value: OptionProps['value'], option: Record<string, any>) => void | - |
| onDropdownVisibleChange | Callback when the dropdown opens or closes, receiving whether it is visible. | Type(visible: boolean) => void | - |
| onExceed | Callback when the selection would exceed max, in multiple mode only. The argument is the option that was rejected. | Type(option: OptionProps) => void | - |
| onFocus | Callback when the Select gets focus. | Type(e: React.FocusEvent<HTMLElement>) => void | - |
| onListScroll | Callback when the option list scrolls. | Type(e: React.UIEvent<HTMLDivElement>) => void | - |
| onMouseEnter | Callback when the mouse enters the Select. | Type(e: React.MouseEvent) => any | - |
| onMouseLeave | Callback when the mouse leaves the Select. | Type(e: React.MouseEvent) => any | - |
| onSearch | Callback when the search input changes. The first argument is the input value, and the second is the event that caused the change. It is undefined when triggered through the ref search method or triggerRender. | Type(value: string, event?: InputChangeEvent | React.KeyboardEvent | React.MouseEvent | globalThis.MouseEvent) => void | - |
| onSelect | Callback when an option is selected. The first argument is always the original option value, even with onChangeWithObject, and the second is the option object. | Type(value: OptionProps['value'], option: Record<string, any>) => void | - |
| optionList | Options as an array. Each item needs value and label; its other properties are passed back with the option. | OptionProps[] | - |
| outerBottomSlot | Custom content rendered at the bottom of the dropdown, beside the option list. It stays visible whether or not the list scrolls. | React.ReactNode | - |
| outerTopSlot | Custom content rendered at the top of the dropdown, beside the option list. It stays visible whether or not the list scrolls. | React.ReactNode | - |
| placeholder | Text shown when no option is selected. | React.ReactNode | "" |
| position | Position of the dropdown, with the same values as the Tooltip position. When unset, it is bottomLeft in left-to-right text and bottomRight in right-to-left text. | Position | - |
| prefix | Prefix of the trigger, as text or a ReactNode. Text and icons get automatic side spacing, while custom nodes get none. | React.ReactNode | - |
| preventScroll | Whether focusing through the focus method prevents the browser from scrolling the document to show the focused element. | boolean | - |
| ref | Points to the SelectHandle, whose methods are listed in Methods below. | TypeReact.Ref<SelectHandle> | - |
| remote | Whether search is remote. When true, input changes skip local filtering, so the results must be supplied by updating optionList. | boolean | false |
| renderCreateItem | Custom content of the create item when allowCreate is true, called with (inputValue, isFocus, style). When unset, it shows the locale's create text followed by the input. With virtualization, apply style to the custom node. | Type(inputValue: OptionProps['value'], focus: boolean, style?: React.CSSProperties) => React.ReactNode | - |
| renderOptionItem | Takes over the rendering of each candidate option. The arguments include id, value, label, selected, focused, disabled, inputValue, className, style, onClick and onMouseEnter. Apply style, className, onClick and onMouseEnter to the wrapper element, and write id on it together with role="option", aria-selected and aria-disabled. | Type(props: optionRenderProps) => React.ReactNode | - |
| renderSelectedItem | Custom rendering of the selected item in the trigger. In single mode it receives the option object and returns a ReactNode. In multiple mode it receives the option object and { index, disabled, onClose }, and returns { isRenderInTag, content }; when isRenderInTag is true, content is wrapped in a tag with a background and a close button. | TypeByMultiple<Multiple, RenderSingleSelectedItemFn, RenderMultipleSelectedItemFn> | - |
| restTagsPopoverProps | Popover props for the overflowing tags when showRestTagsPopover is on, such as position and trigger. See the Popover component. | PopoverProps | {} |
| searchPlaceholder | Placeholder of the search input when it is in the dropdown, that is, when searchPosition is dropdown. | string | - |
| searchPosition | Position of the search input when filter is on. trigger places it inside the Select, and dropdown places it at the top of the dropdown. Combined with triggerRender, it allows more flexible interaction. | 'trigger' | 'dropdown' | "trigger" |
| showArrow | Whether the dropdown arrow is shown. | boolean | true |
| showClear | Whether the clear button is shown. | boolean | false |
| showRestTagsPopover | When tags overflow maxTagCount, whether hovering over +N shows the remaining content in a Popover. | boolean | false |
| size | Size, one of small, default and large. The default size applies when unset. | SelectSize | - |
| spacing | Distance in px between the dropdown and the Select. An { x, y } object sets the horizontal and vertical distances separately. It is 8 when unset. | number | { x: number; y: number } | - |
| stopPropagation | Whether click events on the dropdown stop propagating. | boolean | true |
| style | Style, applied to the trigger root element. | React.CSSProperties | - |
| suffix | Suffix of the trigger, as text or a ReactNode. The rules are the same as for prefix. | React.ReactNode | - |
| triggerRender | Custom trigger of the Select. Its arguments include value, inputValue, onSearch, onRemove, onClear, disabled, placeholder and componentProps. To keep search without rendering an input, set searchPosition to dropdown as well. | Type(props: TriggerRenderProps) => React.ReactNode | - |
| validateStatus | Validation status, one of warning, error and default. It only changes styles such as the background color. default applies when unset. | ValidateStatus | - |
| value | Selected value. Passing it makes the Select controlled, used together with onChange. An array in multiple mode. | TypeSelectValue<T> | - |
| virtualize | Enables list virtualization for large option lists. It takes height (default 270), width (default 100%) and itemSize (the height of each option row, required). | virtualListProps | - |
Methods
| Method | Description | Type |
|---|---|---|
| clearInput | Clears the value of the search input. | Type() => void |
| close | Closes the dropdown. | Type() => void |
| deselectAll | Clears all selected options. | Type() => void |
| focus | Focuses the trigger, or the search input when the Select is searchable. The dropdown opens only when the Select is searchable. | Type() => void |
| open | Opens the dropdown. | Type() => void |
| rePosition | Recomputes the dropdown position from the trigger's current position at once. The dropdown already follows scrolling, window resizing, trigger moves and size changes of the trigger or list, so this is rarely needed; following a trigger move lands a frame late, so call this to align within the same frame. | Type() => void |
| search | Searches as if the value had been typed, and sets it into the search input. Without an event, the second argument of onSearch is undefined. | Type(value: string, event?: any) => void |
| selectAll | Selects every visible, enabled option. Effective in multiple mode only. | Type() => void |
Other native attributes (such as data-*) land on the trigger root element, and style is not passed to the dropdown (use dropdownStyle). aria-label, aria-labelledby, aria-describedby, aria-errormessage, aria-invalid and aria-required land on the combobox; when the search input mounts in a searchable Select, it becomes the combobox and these attributes move to it.
Option Props
An option is identified by its value, which should not repeat within one Select; labels may repeat
Props
| Property | Description | Type | Default |
|---|---|---|---|
| children | Content of the option. It is shown when label is unset. | React.ReactNode | - |
| className | Class name of the option. | string | - |
| current | Internal prop set by AutoComplete to mark the option as the current one (aria-selected). Select users do not need to set it. | boolean | - |
| disabled | Whether the option is disabled. | boolean | - |
| label | Displayed text, a string, a number or a ReactNode. Rendering prefers label, then falls back to children and then value. | string | number | React.ReactNode | - |
| showTick | Whether a check icon is shown when the option is selected. Options rendered by Select show it by default. | boolean | - |
| style | Style of the option row. | React.CSSProperties | - |
| value | Value of the option. It should be unique within a Select, while label may repeat. | string | number | - |
className, style and data-* land on the option row; other undeclared attributes never reach the DOM and travel with the option record to onSelect, onDeselect, renderSelectedItem and renderOptionItem.
OptGroup Props
Props
| Property | Description | Type | Default |
|---|---|---|---|
| children | Options in the group, as Select.Option elements. | React.ReactNode | - |
| className | Class name of the group title. | string | - |
| label | Title of the group. Options in the group sit in a group named by this title. | React.ReactNode | - |
| style | Style of the group title. | React.CSSProperties | - |
className, style and other native attributes (such as id and data-*) land on the group title element.
Methods
Some internal methods provided by Select can be accessed through ref:
The methods are listed in the Methods table after Select Props.
Accessibility
ARIA
- Following the ARIA 1.2 combobox pattern, exactly one element is the combobox at any time; the popup layer is a listbox and each option an option:
- Without search, or with the search input in the dropdown (
searchPosition="dropdown"), the Select trigger is a select-only combobox and the selected content is its value - With the search input in the trigger, the input itself is the combobox, with
aria-autocomplete="list", and the trigger has no role and no Tab stop. In single mode the input appears only while searching, so the trigger is the combobox otherwise; the input is empty until the user types, so the selected text is linked to it as its description witharia-describedby. In multiple mode the input is always there - A search input in the dropdown stays a text box with
aria-controls,aria-autocompleteandaria-activedescendant, named like the combobox
- Without search, or with the search input in the dropdown (
- The combobox takes its name from, in order:
aria-labelledby,aria-label, the rendered insetLabel (the combobox'saria-labelledbypoints at it, with an id the component generates wheninsetLabelIdis not given; with a prefix the insetLabel is not rendered and does not name it), and otherwise "Select" from the locale. Pass a name that matches the form label. The listbox and a search input in the dropdown use the same name source, never the combobox itself. The selected content is the value of the combobox and is not part of the name - The combobox has aria-haspopup, aria-expanded, and aria-controls properties, indicating its relationship with the popup layer
- When multiple selections are made, listbox aria-multiselectable is true, indicating that multiple selections are currently available
- aria-selected is true when Option is selected; aria-disabled is true when Option is disabled
- The options of an OptGroup with a label sit in a
role="group"named by the group title (aria-labelledby), so screen readers announce the group of an option; a virtualized list shows no group titles and has no groups - While the dropdown is open, aria-activedescendant points at the option focused from the keyboard, which ensures that it is recognized when the narration is spoken; with a fully custom option rendering, put the
idfrom the parameters on the option element (for more information, please refer to Managing Focus in Composites Using aria-activedescendant)
Keyboard and Focus
Select without Filter, or with the search box in the dropdown (searchPosition="dropdown"):
- After Select is focused, keyboard users can open the dropdown menu with
Space,Up Arrow,Down Arrow(includingAlt+Down Arrow) orEnter. With a selected value the focus goes to the first selected option, so pressing Enter right away keeps the value; without one the current highlight stays, starting on the first enabled option (defaultActiveFirstOptiondefaults to true) HomeandEndopen the dropdown menu and move the focus to the first and the last enabled option- Typing a character opens the dropdown menu and moves the focus to the next option that starts with it; characters typed in quick succession match as a whole, and typing the same character again cycles through the options that start with it
- When the dropdown menu is open:
- Use
Esckey orTabkey to close the menu - Use
Up ArroworDown Arrowto toggle options,HomeandEndto move to either end, and a typed character to move to an option that starts with it EnterorSpaceselects the focused option: a single Select selects it and collapses the panel, a multiple Select toggles it and stays open. While characters are being typed in quick succession,Spacejoins the search string instead of selecting. With the focus in the search box inside the dropdown,Home,End,Spaceand characters edit the search text without moving the option focus
- Use
- In a multiple Select with the focus on a selected tag, keys act on the tag only:
BackspaceandDeleteremove it, andEnterandSpaceneither open the dropdown menu nor change the value - When the focus is on the dropdown menu and the user uses an
innerBottomSlotorouterBottomSlotattribute with a custom slot with an interactive element:- You can use the
Tabkey to switch to these interactive elements - When the focus is on the first interactive element of the custom slot, use
Shift+Tabto return the focus to the Select box
- You can use the
Select with Filter function:
- When Select is focused, keyboard users can open dropdown menus with
Up ArroworDown ArroworEnterkeys. At this point, the focus is still on the Select box, the user can enter content, and can also use theup arrowordown arrowto switch options - When the dropdown menu is open:
Up ArrowandDown Arrowswitch options;Enterselects the focused option, collapsing the panel in a single Select and toggling it while staying open in a multiple Select;EscorTabcloses the menu.Home,End,Spaceand characters belong to the input in the Select box and edit the search text without moving the option focus - When the focus is on the Select box, and the user uses an
innerBottomSlotorouterBottomSlotproperty with a custom slot with an interactive element:- You can use the
Tabkey to switch to these interactive elements - When the focus is on the first interactive element of the custom slot, use
Shift+Tabto return the focus to the Select box
- You can use the
Clear button:
- With
showClear, a selected value or search text, and nodisabled, the trigger renders a clear button. It is a nativebuttonnamed "Clear selection" from the locale, shown on hover, while the dropdown is open, or while focus is inside the Select - From the Select box,
Tabmoves to the clear button;EnterorSpaceclears, as a mouse click does, and the focus goes back to the Select box
Content Guidelines
- Selector trigger
- Describe in 1-3 words the input that the user needs to make
- Use statement writing conventions (first letter uppercase, rest lowercase)
- Avoid punctuation and prepositions ("the", "an", "a")
- Labels need to be independent statements. Don't let the label be the first half of the statement and the option the second half of the statement.
- Use descriptive sentences, not indicative ones. Help text is available under the select box if the option needs more explanation.
- Selector options
- If there is no default option, use "Select" as placeholder copy
- Options should be in alphabetical order or other logical order to make it easier for users to find options
- Use statement writing conventions (first letter uppercase, rest lowercase), avoid commas and semicolons at the end of sentences
- Clearly articulate the purpose of the choice indicated by the option
Design Tokens
| Sass variable | Value | Usage |
|---|---|---|
| $transition_duration-select-bg | var(--semi-transition_duration-none) | 选择器-背景色-动画持续时间 |
| $transition_function-select-bg | var(--semi-transition_function-easeIn) | 选择器-背景色-过渡曲线 |
| $transition_delay-select-bg | var(--semi-transition_delay-none) | 选择器-背景色-延迟时间 |
| $transition_duration-select-border | var(--semi-transition_duration-none) | 选择器-边框-动画持续时间 |
| $transition_function-select-border | var(--semi-transition_function-easeIn) | 选择器-边框-过渡曲线 |
| $transition_delay-select-border | var(--semi-transition_delay-none) | 选择器-边框-延迟时间 |
| $transition_duration-select_option-bg | var(--semi-transition_duration-none) | 选择器-选项-动画持续时间 |
| $transition_function-select_option-bg | var(--semi-transition_function-easeIn) | 选择器-选项-过渡曲线 |
| $transition_delay-select_option-bg | var(--semi-transition_delay-none) | 选择器-选项-延迟时间 |
| $transform_scale-select | var(--semi-transform_scale-none) | 选择框-变大 |
| $transform_rotate-select-arrow | var(--semi-transform_rotate-none) | 选择框-箭头-旋转 |
| $color-select-bg-default | var(--semi-color-fill-0) | 选择器输入框背景色 - 默认态 |
| $color-select-bg-hover | var(--semi-color-fill-1) | 选择器输入框背景色 - 悬停态 |
| $color-select-bg-active | var(--semi-color-fill-2) | 选择器输入框背景色 - 按下态 |
| $color-select-bg-focus | var(--semi-color-fill-0) | 选择器输入框背景色 - 聚焦态 |
| $color-select-border-default | transparent | 选择器输入框描边颜色 |
| $color-select-border-hover | $color-select-border-default | 选择器输入框描边颜色 - 悬浮 |
| $color-select-border-active | var(--semi-color-focus-border) | 选择器输入框描边颜色 - 按下态 |
| $color-select-border-focus | $color-select-border-active | 选择器输入框描边颜色 - 选中态 |
| $color-select_dropdown_input-border | $color-select-border-default | 下拉搜索框底部描边颜色 |
| $color-select_warning-bg-default | var(--semi-color-warning-light-default) | 警示选择器输入框背景色 - 默认态 |
| $color-select_warning-border-default | var(--semi-color-warning-light-default) | 警示选择器输入框描边颜色 - 默认态 |
| $color-select_warning-bg-hover | var(--semi-color-warning-light-hover) | 警示选择器输入框背景色 - 悬停态 |
| $color-select_warning-border-hover | var(--semi-color-warning-light-hover) | 警示选择器输入框描边颜色 - 悬停态 |
| $color-select_warning-bg-focus | var(--semi-color-warning-light-default) | 警示选择器输入框背景色 - 选中态 |
| $color-select_warning-border-focus | var(--semi-color-warning) | 警示选择器输入框描边颜色 - 选中态 |
| $color-select_warning-bg-active | var(--semi-color-warning-light-active) | 警示选择器输入框背景色 - 按下态 |
| $color-select_warning-border-active | var(--semi-color-warning-light-active) | 警示选择器输入框描边颜色 - 按下态 |
| $color-select_danger-bg-default | var(--semi-color-danger-light-default) | 报错选择器输入框背景色 - 默认态 |
| $color-select_danger-border-default | var(--semi-color-danger-light-default) | 报错选择器输入框描边颜色 - 默认态 |
| $color-select_danger-bg-hover | var(--semi-color-danger-light-hover) | 报错选择器输入框背景色 - 悬停态 |
| $color-select_danger-border-hover | var(--semi-color-danger-light-hover) | 报错选择器输入框描边颜色 - 悬停态 |
| $color-select_danger-bg-focus | var(--semi-color-danger-light-default) | 报错选择器输入框背景色 - 选中态 |
| $color-select_danger-border-focus | var(--semi-color-danger) | 报错选择器输入框描边颜色 - 选中态 |
| $color-select_danger-bg-active | var(--semi-color-danger-light-active) | 报错选择器输入框背景色 - 按下态 |
| $color-select_danger-border-active | var(--semi-color-danger-light-active) | 报错选择器输入框描边颜色 - 按下态 |
| $color-select_default-border-focus | var(--semi-color-focus-border) | 默认选择器输入框描边颜色 - 选中态 |
| $color-select_main-text-default | var(--semi-color-text-0) | 选择器输入框回填内容文本颜色 |
| $color-select-icon-default | var(--semi-color-text-2) | 选择器输入框图标颜色 |
| $color-select_clearBtn-text-default | var(--semi-color-text-2) | 选择器输入框清空按钮颜色 - 默认态 |
| $color-select_clearBtn-text-hover | var(--semi-color-primary) | 选择器输入框清空按钮颜色 - 悬停态 |
| $color-select_input_disabled-bg | var(--semi-color-disabled-fill) | 禁用选择器输入框背景色 |
| $color-select_input_disabled-border-focus | transparent | 禁用选择器输入框描边颜色 - 聚焦态 |
| $color-select_input_disabled-text | var(--semi-color-disabled-text) | 禁用选择器输入框回填内容文字颜色 |
| $color-select_input_disabled-bg-hover | var(--semi-color-disabled-fill) | 禁用选择器输入框背景色 - 悬停态 |
| $color-select_input_disabled-bg-focus | var(--semi-color-disabled-fill) | 禁用选择器输入框背景色 - 聚焦态 |
| $color-select_input_placeholder-text | var(--semi-color-text-2) | 选择器输入框占位文本文字颜色 |
| $color-select_option_main-text | var(--semi-color-text-0) | 选择器下拉菜单选项文本颜色 |
| $color-select_option_keyword-text | var(--semi-color-primary) | 选择器下拉菜单选项匹配搜索结果文本颜色 |
| $color-select_option-icon-default | transparent | 选择器下拉菜单选项图标颜色 - 默认态 |
| $color-select_option-bg-hover | var(--semi-color-fill-0) | 选择器下拉菜单选项背景颜色 - 悬停态 |
| $color-select_option-bg-active | var(--semi-color-fill-1) | 选择器下拉菜单选项背景颜色 - 按下态 |
| $color-select_option_disabled-text | var(--semi-color-disabled-text) | 禁用选择器下拉菜单选项文字颜色 |
| $color-select_option-icon-active | var(--semi-color-text-2) | 选择器下拉菜单选项图标颜色 - 选中态 |
| $color-select_option-border-default | var(--semi-color-border) | 选择器下拉菜单分组标题分割线颜色 |
| $color-select-option-bg-selected | transparent | 选择器下拉菜单选项背景颜色 - 选中态 |
| $color-select_create_tips-text | var(--semi-color-text-2) | 选择器下拉菜单新建选项提示文本颜色 |
| $color-select_group-text | var(--semi-color-text-2) | 选择器下拉菜单分组标题文本颜色 |
| $color-select_prefix_suffix_text-default | var(--semi-color-text-2) | 选择器输入框前后缀文本颜色 |
| $height-select_large | $height-control-large | 选择器输入框高度 - 大尺寸 |
| $height-select_small | $height-control-small | 选择器输入框高度 - 小尺寸 |
| $height-select_default | $height-control-default | 选择器输入框高度 - 默认尺寸 |
| $width-select_option_tick | $width-icon-small | 选择器下拉菜单项选中对勾图标大小 |
| $width-select-border | 1px | 选择器输入框描边宽度 |
| $width-select_arrow | 32px | 选择器输入框下拉箭头宽度 |
| $width-select_arrow_empty | 12px | 选择器输入框下拉箭头为空时(有suffix icon)宽度 |
| $width-select_clear-icon | 32px | 选择器输入框清空按钮宽度 |
| $width-select_group_top-border | $border-thickness-control | 选择器下拉菜单分组标题描边宽度 |
| $height-select_multiple_input_small | 20px | 小尺寸多项选择器输入框Input框高度 |
| $height-select_multiple_input_default | 24px | 默认多项选择器输入框Input框高度 |
| $height-select_multiple_input_large | 24px | 大尺寸多项选择器输入框Input框高度 |
| $width-select-border-hover | $width-select-border | 选择器输入框描边宽度 - 悬浮 |
| $width-select-border-focus | $width-select-border-hover | 选择器输入框描边宽度 - 按下 |
| $width-select-border-active | $width-select-border-focus | 选择器输入框描边宽度 - 选中 |
| $spacing-select_option_tick-marginRight | $spacing-tight | 选择器下拉菜单选中对勾右侧外边距 |
| $spacing-select_prefix_suffix_text-marginX | $spacing-base-tight | 选择器输入框前后缀文本水平内边距 |
| $spacing-select_prefix_suffix_text-marginY | 0px | 选择器输入框前后缀文本垂直内边距 |
| $spacing-select_prefix_suffix_icon-marginX | $spacing-tight | 选择器输入框前后缀图标水平内边距 |
| $spacing-select_prefix_suffix_icon-marginY | 0px | 选择器输入框前后缀图标垂直内边距 |
| $spacing-select_create_tips-marginRight | $spacing-extra-tight | 创建新选项右侧外边距 |
| $spacing-select_group-marginTop | $spacing-extra-tight | 选择器下拉菜单分组标题顶部外边距 |
| $spacing-select_group-paddingTop | $spacing-base-tight | 选择器下拉菜单分组标题顶部内边距 |
| $spacing-select_group-paddingBottom | $spacing-extra-tight | 选择器下拉菜单分组标题底部内边距 |
| $spacing-select_group-paddingLeft | $spacing-base-tight + $width-select_option_tick + $spacing-select_option_tick-marginRight | 选择器下拉菜单分组标题左侧内边距 |
| $spacing-select_group-paddingRight | $spacing-base | 选择器下拉菜单分组标题右侧内边距 |
| $spacing-select_loading_wrapper-paddingLeft | $spacing-base | 选择器加载左侧内边距 |
| $spacing-select_loading_wrapper-paddingRight | $spacing-base | 选择器加载右侧内边距 |
| $spacing-select_loading_wrapper-paddingTop | $spacing-tight | 选择器加载顶部内边距 |
| $spacing-select_loading_wrapper-paddingBottom | $spacing-tight | 选择器加载底部内边距 |
| $spacing-select_multiple_content_wrapper_empty-marginLeft | $spacing-tight | 多项选择器空回填内容为空时左侧内边距 |
| $spacing-select_multiple_selection-marginLeft | $spacing-extra-tight | 多项选择器回填内容左侧内边距 |
| $spacing-select_option-paddingLeft | $spacing-base-tight | 选择器下拉菜单项左侧内边距 |
| $spacing-select_option-paddingRight | $spacing-base-tight | 选择器下拉菜单项右侧内边距 |
| $spacing-select_option-paddingTop | $spacing-tight | 选择器下拉菜单项顶部内边距 |
| $spacing-select_option-paddingBottom | $spacing-tight | 选择器下拉菜单项底部内边距 |
| $spacing-select_tag-marginTop | $spacing-super-tight - 1px | 多项选择器标签顶部外边距 |
| $spacing-select_tag-marginRight | $spacing-extra-tight | 多项选择器标签右侧外边距 |
| $spacing-select_tag-marginBottom | $spacing-super-tight - 1px | 多项选择器标签底部外边距 |
| $spacing-select_selection-marginLeft | $spacing-base-tight | 选择器内容区左侧外边距 |
| $spacing-select_option_list-paddingTop | $spacing-extra-tight | 选择器内容区顶部内边距 |
| $spacing-select_option_list-paddingRight | 0px | 选择器内容区右侧内边距 |
| $spacing-select_option_list-paddingBottom | $spacing-extra-tight | 选择器内容区底部内边距 |
| $spacing-select_option_list-paddingLeft | 0px | 选择器内容区左侧内边距 |
| $spacing-select_dropdown_input_paddingTop | 8px | 下拉搜索框顶部内边距 |
| $spacing-select_dropdown_input_paddingBottom | 8px | 下拉搜索框底部内边距 |
| $spacing-select_dropdown_input_paddingRight | 12px | 下拉搜索框右侧内边距 |
| $spacing-select_dropdown_input_paddingLeft | 12px | 下拉搜索框左侧内边距 |
| $radius-select | var(--semi-border-radius-small) | 选择器输入框圆角 |
| $radius-select_option | 0px | 选择器待选项圆角 |
| $font-select-fontWeight | $font-weight-regular | 选择器文本字重 |
| $font-select_keyword-fontWeight | 600 | 选择器搜索结果命关键词中文本字重 |
| $font-select_prefix_suffix-fontWeight | $font-weight-bold | 选择器输入框前后缀文本字重 |
| $opacity-select_selection_text_inactive | 0.4 |
TypeScript Generic Support
Select takes two generic parameters: T, the type of an option's value (the option object when onChangeWithObject is set), and Multiple, the type of the multiple prop. onChange receives T | undefined in single mode and T[] in multiple mode; when multiple is a boolean variable it receives T | T[] | undefined. onSelect and onDeselect receive the option's raw value (string | number), whatever T is. Both parameters are usually inferred from value and multiple; when you write T explicitly in multiple mode, write Multiple too.
import { Select } from '@fibrous/semi-ui-react';
// T is inferred from value: string; value is string | undefined
<Select
value={selectedValue}
onChange={(value) => setSelectedValue(value)}
>
<Select.Option value="option1">Option 1</Select.Option>
<Select.Option value="option2">Option 2</Select.Option>
</Select>
// In multiple mode, value is string[]
<Select
multiple
value={selectedValues}
onChange={(value) => setSelectedValues(value)}
>
<Select.Option value="option1">Option 1</Select.Option>
<Select.Option value="option2">Option 2</Select.Option>
</Select>
// Explicit generic parameters in multiple mode
<Select<string, true> multiple onChange={(value) => setSelectedValues(value)}>
<Select.Option value="option1">Option 1</Select.Option>
<Select.Option value="option2">Option 2</Select.Option>
</Select>
FAQ
-
Searchable Select, using remote data to dynamically update the
optionList, why sometimes there is no data before the asynchronous request is completed?
Please check whetherremoteis turned on. If remote is not turned on, the input value of the input box will be compared with the current optionList by default. If there is no match, no data will be displayed.
You can turn off matching filtering on the current local data by turning on remote. -
Will Select automatically limit the width of the drop-down menu?
MinWidth will be given, but width will not be written dead. If necessary, you can add it yourself through dropdownStyle. -
Why is the blur event not fired after a radio selection option?
Before V2.17.0, after Select radio is selected, the blur event of Select will be triggered. After V2.17.0, Select has added A11y support, which will not trigger Select's blur event. In single-selection selection, the Select floating layer is closed, and the focus is still on the trigger (at this time, the Select floating layer can be opened again by pressing the Enter key) No matter single selection or multiple selection, press Esc, only the Select floating layer is closed, and the trigger keeps the focus (the Select floating layer can be opened again by pressing the Enter key at this time)