When to use
Used to provide input suggestions to the input box and perform automatic completion operations
The difference with the searchable Select component:
- AutoComplete is essentially an enhanced Input component that provides input suggestions, while Select is a selector
- When you click to expand, Select will clear all the values in the input box, and AutoComplete will keep the last selected value
- Select's selected item rendering (renderSelectedItem) can be more customized and can be any type of ReactNode, while AutoComplete only allows strings
Demos
How to import
import { AutoComplete } from '@fibrous/semi-ui-react';
Basic usage
Monitor user input through onSearch, pass input suggestions through data, and maintain control through onChange. OnChange is triggered when the input box changes/selects an input item
Code
import { useState } from 'react';
import { IconSearch } from '@fibrous/semi-icons-react';
import { AutoComplete } from '@fibrous/semi-ui-react';
export default function AutoCompleteDemo() {
const [stringData, setStringData] = useState<string[]>([]);
const [value, setValue] = useState<string | number>('');
const handleStringSearch = (value: string) => {
let result: string[];
if (value) {
result = ['gmail.com', '163.com', 'qq.com'].map((domain) => `${value}@${domain}`);
} else {
result = [];
}
setStringData(result);
};
const handleChange = (value: string | number) => {
console.log('onChange', value);
setValue(value);
};
return <AutoComplete data={stringData} value={value} showClear prefix={<IconSearch />} placeholder="搜索... " onSearch={handleStringSearch} onChange={handleChange} style={{ width: 200 }} />;
}Custom option rendering
When you need to customize the rendering of candidates, data can be passed in an array of objects (each Object must contain two keys, label and value, value is the value selected by the candidate, and label is the content displayed by the candidate) The rendering of candidates can be customized through renderItem
Code
import { useState } from 'react';
import { IconSearch } from '@fibrous/semi-icons-react';
import { AutoComplete, Avatar } from '@fibrous/semi-ui-react';
const list: ListItem[] = [
{ name: '夏可漫', email: 'xiakeman@example.com', abbr: 'XK', color: 'amber' },
{ name: '申悦', email: 'shenyue@example.com', abbr: 'SY', color: 'indigo' },
{ name: '曲晨一', email: 'quchenyi@example.com', abbr: 'CY', color: 'blue' },
{ name: '文嘉茂', email: 'wenjiamao@example.com', abbr: 'JM', color: 'cyan' },
];
interface ListItem {
name: string;
email: string;
abbr: string;
color: 'amber' | 'indigo' | 'blue' | 'cyan';
value?: string;
label?: string;
}
export default function AutoCompleteDemo() {
const [data, setData] = useState<ListItem[]>([]);
const handleStringSearch = (value: string) => {
let result: ListItem[];
if (value) {
result = list.map((item) => {
return { ...item, value: item.name, label: item.email };
});
} else {
result = [];
}
setData(result);
};
const renderOption = (item: ListItem) => {
return (
<>
<Avatar color={item.color} size="small">
{item.abbr}
</Avatar>
<div style={{ marginLeft: 4 }}>
<div style={{ fontSize: 14, marginLeft: 4 }}>{item.name}</div>
<div style={{ marginLeft: 4 }}>{item.email}</div>
</div>
</>
);
};
return <AutoComplete aria-label="搜索邮箱" data={data} showClear prefix={<IconSearch />} onSearch={handleStringSearch} renderItem={renderOption} renderSelectedItem={(option) => option.email} style={{ width: 280 }} />;
}Remote search
Get user input value from onSearch, update data value dynamically, update loading
Code
import { useMemo, useState } from 'react';
import { debounce } from 'es-toolkit';
import { IconSearch } from '@fibrous/semi-icons-react';
import { IconSelect, IconForm, IconTable, IconInput, IconButton } from '@fibrous/semi-icons-react/lab';
import { AutoComplete, type AutoCompleteDataItem } from '@fibrous/semi-ui-react';
const initList = [
{ value: 'select', label: '选择器', icon: <IconSelect /> },
{ value: 'input', label: '输入框', icon: <IconInput /> },
{ value: 'form', label: '表单', icon: <IconForm /> },
{ value: 'button', label: '按钮', icon: <IconButton /> },
{ value: 'table', label: '表格', icon: <IconTable /> },
];
type ListItem = (typeof initList)[number];
export default function AutoCompleteDemo() {
const [loading, setLoading] = useState(false);
const [list, setList] = useState(initList);
const search = useMemo(
() =>
debounce((inputValue: string) => {
setLoading(true);
let newList = initList;
if (inputValue) {
newList = initList.filter((item) => item.value.includes(inputValue));
}
setTimeout(() => {
setList(newList);
setLoading(false);
}, 1000);
}, 200),
[]
);
const handleSelect = (value: string | number | AutoCompleteDataItem) => {
console.log(value);
};
const renderItem = (item: ListItem) => {
return (
<div style={{ display: 'flex', alignItems: 'center' }}>
<div style={{ fontSize: 32 }}>{item.icon}</div>
<div style={{ marginLeft: 12 }}>
<p>{item.value}</p>
<p>{item.label}</p>
</div>
</div>
);
};
const renderSelectedItem = (item: ListItem) => {
// Unlike Select, this can return only a string, not a ReactNode
return item.value;
};
return <AutoComplete aria-label="搜索" data={list} style={{ width: 250 }} prefix={<IconSearch />} onSearch={search} loading={loading} renderItem={renderItem} renderSelectedItem={renderSelectedItem} onSelect={handleSelect} />;
}Size
The size of the input box can be set by setting size, optional small, default (default), large
Code
import { AutoComplete } from '@fibrous/semi-ui-react';
export default function AutoCompleteDemo() {
return (
<div>
<AutoComplete data={[1, 2, 3, 4]} size="small" placeholder="small" style={{ width: 200 }} />
<br />
<br />
<AutoComplete data={[1, 2, 3, 4]} size="default" placeholder="default" style={{ width: 200 }} />
<br />
<br />
<AutoComplete data={[1, 2, 3, 4]} size="large" placeholder="large" style={{ width: 200 }} />
</div>
);
}The position of the drop-down menu
The position of the drop-down menu can be set by setting position, and the optional values refer to Tooltip position
Code
import { useState } from 'react';
import { AutoComplete } from '@fibrous/semi-ui-react';
export default function AutoCompleteDemo() {
const [data, setData] = useState<string[]>([]);
const change = (input: string) => {
let newData = ['gmail.com', '163.com', 'qq.com'].map((domain) => `${input}@${domain}`);
if (!input) {
newData = [];
}
setData(newData);
};
return (
<div>
<AutoComplete data={data} position="top" onSearch={change} placeholder="选项菜单在上方显示" style={{ width: 200, margin: 10 }} />
<AutoComplete data={data} position="rightTop" onSearch={change} placeholder="选项菜单在右侧显示" style={{ width: 200, margin: 10 }} />
</div>
);
}Disabled
Code
import { AutoComplete } from '@fibrous/semi-ui-react';
export default function AutoCompleteDemo() {
return <AutoComplete data={[1, 2, 3, 4]} placeholder="禁用下拉菜单" disabled style={{ width: 200 }} />;
}Validate status
Different verification states can be set to show different styles
Code
import { AutoComplete } from '@fibrous/semi-ui-react';
export default function AutoCompleteDemo() {
return (
<>
<AutoComplete aria-label="Warning status" defaultValue="ies" validateStatus="warning" />
<br />
<br />
<AutoComplete aria-label="Error status" defaultValue="ies" validateStatus="error" />
<br />
<br />
<AutoComplete aria-label="Default status" defaultValue="ies" />
</>
);
}Custom empty content
Can set up custom display empty content
Code
import { useState } from 'react';
import { IllustrationNoContent } from '@fibrous/semi-illustrations-react';
import { AutoComplete, Empty } from '@fibrous/semi-ui-react';
export default function AutoCompleteDemo() {
let [data, setData] = useState<number[]>([]);
const [loading, setLoading] = useState(false);
const fetchData = (v: string) => {
setLoading(true);
setTimeout(() => {
if (!v) {
setData([]);
setLoading(false);
return;
}
setData(() => {
const res = Array.from(Array(5)).map(() => Math.random());
return res;
});
setLoading(false);
}, 1000);
};
return <AutoComplete aria-label="搜索" loading={loading} data={data} emptyContent={<Empty style={{ padding: 12, width: 300 }} image={<IllustrationNoContent style={{ width: 150, height: 150 }} />} description="暂无内容" />} onSearch={fetchData} />;
}API reference
Props
| Property | Description | Type | Default |
|---|---|---|---|
| autoAdjustOverflow | Whether the dropdown flips automatically when it would be clipped. Unset, it takes the Popover default, true. | boolean | - |
| autoFocus | Whether the input is focused after mount. | boolean | false |
| className | Class name of the root div. | string | - |
| clearIcon | Custom clear icon, effective when showClear is true. | ReactNode | - |
| data | Data source of the candidates: an array of strings, numbers or objects. An object item's value is the picked value and its label is the row content, which stays empty when it is not given. Other fields are kept as they are and reach the renderItem argument. Pass a new array to update the list. | T[] | [] |
| defaultActiveFirstOption | Whether the first candidate is highlighted when the panel opens, so that Enter picks it directly. | boolean | false |
| defaultOpen | Whether the dropdown panel is open on mount. | boolean | - |
| defaultValue | Initial value of the input when uncontrolled, read on mount only. | string | number | - |
| disabled | Whether the component is disabled. | boolean | - |
| dropdownClassName | Class name of the dropdown list, applied to the role="listbox" element. | string | - |
| dropdownMatchSelectWidth | Whether the minimum width of the dropdown equals the width of the input. | boolean | true |
| dropdownStyle | Inline style of the dropdown list, merged with maxHeight and applied to the role="listbox" element. | React.CSSProperties | - |
| emptyContent | Content shown in the dropdown when data is empty. While loading, the spinner is shown instead. | React.ReactNode | null |
| id | The id of the input, for <label for>; with triggerRender it lands on the root div. | string | - |
| insetLabel | Inset label of the input, rendered in the prefix position. It is not rendered, and does not name the component, when prefix or triggerRender is set. | React.ReactNode | - |
| insetLabelId | The id of the insetLabel element, which names the combobox and the listbox. When omitted, the component generates one while an insetLabel is rendered. | string | - |
| loading | Whether the dropdown shows a loading spinner in place of the candidates and the empty content. | boolean | false |
| maxHeight | Maximum height of the dropdown list; a number is in px. | string | number | 300 |
| motion | Whether the dropdown animates when it shows and hides. | boolean | true |
| onBlur | Callback when the focus leaves the input area, with the FocusEvent. Moving the focus inside the area, for example to the clear button, does not fire it. | Type(e: React.FocusEvent) => void | - |
| onChange | Callback when the input text changes or a candidate is picked, with the current value. | Type(value: string | number) => void | - |
| onChangeWithObject | Has no effect; the component does not read it. | boolean | - |
| onClear | Callback after the clear button empties the input. | Type() => void | - |
| onDropdownVisibleChange | Callback when the dropdown shows or hides, with its visibility. | Type(visible: boolean) => void | - |
| onFocus | Callback when the focus enters the input area, with the FocusEvent. Moving the focus inside the area, for example to the clear button, does not fire it. | Type(e: React.FocusEvent) => void | - |
| onKeyDown | Callback on key down, fired on the root div, with a React.KeyboardEvent<HTMLDivElement>. | Type(e: React.KeyboardEvent<HTMLDivElement>) => void | - |
| onSearch | Callback as the user types, with the typed text. | Type(inputValue: string) => void | - |
| onSelect | Callback when a candidate is picked, with its value, or with the item without internal fields when onSelectWithObject is set. | Type(value: string | number | BaseDataItem) => void | - |
| onSelectWithObject | When true, onSelect receives the data item without internal fields instead of its value. | boolean | false |
| placeholder | Placeholder of the input. | string | - |
| position | Position of the dropdown; the values are those of the Tooltip position. Unset, it is bottomLeft, or bottomRight when the ConfigProvider direction is rtl. | Position | - |
| prefix | Prefix content of the input. When it is set, insetLabel is not rendered. | React.ReactNode | - |
| ref | Reference to the root div, which wraps the input or the output of triggerRender. It is the native div element, so its focus() does not focus the input. | TypeReact.Ref<HTMLDivElement> | - |
| renderItem | Renders a candidate row. The argument is the data item itself, and the return value is a ReactNode. Without it the row shows label. | Type(option: T) => React.ReactNode | - |
| renderSelectedItem | The text the input shows for a picked item; it must return a string. A string or number item receives { value, label }, an object item the item itself. | Type(option: T extends string | number ? BaseDataItem : T) => string | - |
| showClear | Whether to show the clear button when there is content. | boolean | false |
| size | Size of the input: small, default or large. | Size | "default" |
| stopPropagation | Whether click, mousedown and focus in/out events on the dropdown stop propagating. | boolean | string | true |
| style | Inline style of the root div. | React.CSSProperties | - |
| suffix | Suffix content of the input. | React.ReactNode | - |
| triggerRender | Custom trigger, which replaces the input. Its parameters include value (the array of selected items), inputValue, placeholder, disabled, autoFocus, size, validateStatus, showClear, prefix, suffix, clearIcon, the aria-label, aria-labelledby, aria-describedby, aria-errormessage, aria-invalid and aria-required attributes (other aria-* attributes are not passed as direct parameters and are available in componentProps), onChange (updates the input text, calls onSearch and onChange and opens the list), onClear, onFocus, onBlur and componentProps. With it, id lands on the root div. | Type(props?: any) => React.ReactNode | - |
| validateStatus | Validation status: default, error or warning. It only affects the style. | ValidateStatus | "default" |
| value | Current value. Passing it makes the component controlled; setting it back to undefined later clears the input and leaves it uncontrolled. | string | number | - |
className, style, data-* and other native attributes and events (for example onClick) land on the root div, whose tabIndex is fixed to -1. id, name, aria-describedby, aria-errormessage, aria-invalid, aria-label, aria-labelledby and aria-required land on the input (role="combobox"), so a <label for> finds it. With triggerRender there is no input: id lands on the root div, name lands nowhere, the aria-* attributes listed above reach the focusable element of the custom trigger through its parameters, and other aria-* attributes are not passed to the trigger as direct parameters.
Accessibility
Keyboard and Focus
- AutoComplete's input box has
role="combobox"and is linked to the options panel througharia-expanded,aria-controls,aria-autocomplete="list"andaria-activedescendant - 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). The listbox uses the same name source. AutoComplete has no default name: with none of these, name the input witharia-labeloraria-labelledby; a<label for>names only the input and leaves the listbox unnamed - AutoComplete's input box can be focused, and once focused, keyboard users can use
Up ArroworDown Arrowto open the options panel; when data is empty the panel shows emptyContent (nothing when it is not provided), and a loading spinner while loading - AutoComplete also supports opening and closing panels via
Enterkey - If the user sets the defaultActiveFirstOption property to true, the first option is highlighted by default when the options panel is opened
- If the drop-down menu is open:
- Use
Escto close the menu - Use
Tabto close the menu; the focus moves on to the next focusable element as usual - Use
Up ArroworDown Arrowto toggle options; the highlighted option (the onearia-activedescendantpoints at) hasaria-selected="true" - The focused option can be selected with the
Enterkey and the panel will be collapsed - After picking an option with the mouse, the focus stays in the input
- Use
- With
showClearand a value, the clear button is the same as Input's: a nativebuttonnamed "Clear input" from the locale; from the input,Tabmoves to it, andEnterorSpaceclears and puts the focus back in the input
Content Guidelines
- Content needs to be presented clearly so that users can clearly perceive the options available
- Limit the number of options displayed at one time
Design Token
| Sass variable | Value | Usage |
|---|---|---|
| $transition_duration-autoComplete_option-bg | var(--semi-transition_duration-none) | 选择器-选项-动画持续时间 |
| $transition_function-autoComplete_option-bg | var(--semi-transition_function-easeIn) | 选择器-选项-过渡曲线 |
| $transition_delay-autoComplete_option-bg | var(--semi-transition_delay-none) | 选择器-选项-延迟时间 |
| $spacing-autoComplete_loading_wrapper-paddingTop | $spacing-tight | 加载搜索结果时的顶部内边距 |
| $spacing-autoComplete_loading_wrapper-paddingBottom | $spacing-tight | 加载搜索结果时的底部内边距 |
| $color-autoComplete_option-icon-default | transparent | 自动完成菜单选项图标颜色 - 默认态 |
| $color-autoComplete_option-bg-hover | var(--semi-color-fill-0) | 自动完成菜单选项背景颜色 - 悬停态 |
| $color-autoComplete_option-bg-active | var(--semi-color-fill-1) | 自动完成菜单选项背景颜色 - 按下态 |
| $color-autoComplete_option_disabled-text | var(--semi-color-disabled-text) | 禁用自动完成菜单选项文字颜色 |
| $width-autoComplete_option_tick | $width-icon-small | 自动完成菜单项选中对勾图标大小 |
| $spacing-autoComplete_option_tick-marginRight | $spacing-tight | 自动完成菜单选中对勾右侧外边距 |
| $spacing-autoComplete_option-paddingLeft | $spacing-base-tight | 自动完成菜单项左侧内边距 |
| $spacing-autoComplete_option-paddingRight | $spacing-base-tight | 自动完成菜单项右侧内边距 |
| $spacing-autoComplete_option-paddingTop | $spacing-tight | 自动完成菜单项顶部内边距 |
| $spacing-autoComplete_option-paddingBottom | $spacing-tight | 自动完成菜单项底部内边距 |
| $spacing-autoComplete_option_first-marginTop | $spacing-extra-tight | 自动完成第一个菜单项顶部外边距 |
| $spacing-autoComplete_option_last-marginBottom | $spacing-extra-tight | 自动完成最后一个菜单项顶部外边距 |
| $radius-autoComplete_option | 0px | 自动完成待选项圆角 |
| $color-autoComplete_option_main-text | var(--semi-color-text-0) | 自动完成菜单选项文本颜色 |