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 can be any snippet content, while the
renderSelectedItemof AutoComplete may only return a string
Demos
How to import
<script lang="ts">
import { AutoComplete } from '@fibrae/semi-ui-svelte';
</script>
Basic usage
Monitor user input through onSearch, pass input suggestions through data, and keep the value with bind:value, which updates when the input box changes or an input item is selected. There is no defaultValue: a one-way value={x} only seeds the input, and bind:value={get, set} makes it fully controlled
当前输入:
Code
<script lang="ts">
import { IconSearch } from '@fibrae/semi-icons-svelte';
import { AutoComplete } from '@fibrae/semi-ui-svelte';
let stringData = $state<string[]>([]);
let value = $state<string | number>('');
const handleStringSearch = (input: string) => {
stringData = input ? ['gmail.com', '163.com', 'qq.com'].map((domain) => `${input}@${domain}`) : [];
};
</script>
<AutoComplete bind:value data={stringData} onSearch={handleStringSearch} placeholder="搜索... " showClear style="width: 200px">
{#snippet prefix()}
<IconSearch />
{/snippet}
</AutoComplete>
<p>当前输入:{value}</p>Custom option rendering
When you need to customize the rendering of candidates, data can be passed in an array of objects (each Object contains two keys, label and value; value is the value selected by the candidate, and label is the text displayed by the candidate, which falls back to value when missing)
The content of a candidate can be customized through the item snippet, which receives { item, index }: item is the data item as given, so keep only data in the items and write the markup in the snippet
Code
<script lang="ts">
import { IconSearch } from '@fibrae/semi-icons-svelte';
import { AutoComplete, Avatar } from '@fibrae/semi-ui-svelte';
interface ListItem {
name: string;
email: string;
abbr: string;
color: 'amber' | 'indigo' | 'blue' | 'cyan';
value?: string;
label?: string;
}
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' },
];
let data = $state<ListItem[]>([]);
const handleStringSearch = (value: string) => {
data = value ? list.map((item) => ({ ...item, value: item.name, label: item.email })) : [];
};
const renderSelectedItem = (option: ListItem) => option.email;
</script>
<AutoComplete aria-label="搜索邮箱" {data} onSearch={handleStringSearch} {renderSelectedItem} showClear style="width: 280px">
{#snippet prefix()}
<IconSearch />
{/snippet}
{#snippet item({ item })}
<Avatar color={item.color} size="small">{item.abbr}</Avatar>
<div style="margin-left: 4px">
<div style="font-size: 14px; margin-left: 4px">{item.name}</div>
<div style="margin-left: 4px">{item.email}</div>
</div>
{/snippet}
</AutoComplete>Remote search
Get user input value from onSearch, update data dynamically, update loading
已选择:
Code
<script lang="ts">
import { debounce } from 'es-toolkit';
import { IconSearch } from '@fibrae/semi-icons-svelte';
import { IconButton, IconForm, IconInput, IconSelect, IconTable } from '@fibrae/semi-icons-svelte/lab';
import { AutoComplete, type AutoCompleteDataItem } from '@fibrae/semi-ui-svelte';
interface ListItem {
value: 'select' | 'input' | 'form' | 'button' | 'table';
label: string;
}
const icons = {
select: IconSelect,
input: IconInput,
form: IconForm,
button: IconButton,
table: IconTable,
};
const initList: ListItem[] = [
{ value: 'select', label: '选择器' },
{ value: 'input', label: '输入框' },
{ value: 'form', label: '表单' },
{ value: 'button', label: '按钮' },
{ value: 'table', label: '表格' },
];
let loading = $state(false);
let list = $state(initList);
let selected = $state('');
const handleSearch = (inputValue: string) => {
loading = true;
const newList = inputValue ? initList.filter((item) => item.value.includes(inputValue)) : initList;
setTimeout(() => {
list = newList;
loading = false;
}, 1000);
};
const search = debounce(handleSearch, 200);
const handleSelect = (value: string | number | AutoCompleteDataItem) => {
selected = String(value);
};
// Unlike Select, this can return only a string, not a node
const renderSelectedItem = (item: ListItem) => item.value;
</script>
<AutoComplete aria-label="搜索" data={list} {loading} onSearch={search} onSelect={handleSelect} {renderSelectedItem} style="width: 250px">
{#snippet prefix()}
<IconSearch />
{/snippet}
{#snippet item({ item })}
{@const Icon = icons[item.value]}
<div style="display: flex; align-items: center">
<div style="font-size: 32px">
<Icon />
</div>
<div style="margin-left: 12px">
<p>{item.value}</p>
<p>{item.label}</p>
</div>
</div>
{/snippet}
</AutoComplete>
<p>已选择:{selected}</p>Size
The size of the input box can be set by setting size, optional small, default (default), large
Code
<script lang="ts">
import { AutoComplete } from '@fibrae/semi-ui-svelte';
const data = [1, 2, 3, 4];
</script>
<div>
<AutoComplete {data} placeholder="small" size="small" style="width: 200px" />
<br />
<br />
<AutoComplete {data} placeholder="default" size="default" style="width: 200px" />
<br />
<br />
<AutoComplete {data} placeholder="large" size="large" style="width: 200px" />
</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
<script lang="ts">
import { AutoComplete } from '@fibrae/semi-ui-svelte';
let data = $state<string[]>([]);
const change = (input: string) => {
data = input ? ['gmail.com', '163.com', 'qq.com'].map((domain) => `${input}@${domain}`) : [];
};
</script>
<div>
<AutoComplete {data} onSearch={change} placeholder="选项菜单在上方显示" position="top" style="width: 200px; margin: 10px" />
<AutoComplete {data} onSearch={change} placeholder="选项菜单在右侧显示" position="rightTop" style="width: 200px; margin: 10px" />
</div>Disabled
Code
<script lang="ts">
import { AutoComplete } from '@fibrae/semi-ui-svelte';
const data = [1, 2, 3, 4];
</script>
<AutoComplete {data} disabled placeholder="禁用下拉菜单" style="width: 200px" />Validate status
Different verification states can be set to show different styles
Code
<script lang="ts">
import { AutoComplete } from '@fibrae/semi-ui-svelte';
let warning = $state('ies');
let error = $state('ies');
let normal = $state('ies');
</script>
<AutoComplete aria-label="Warning status" bind:value={warning} validateStatus="warning" />
<br />
<br />
<AutoComplete aria-label="Error status" bind:value={error} validateStatus="error" />
<br />
<br />
<AutoComplete aria-label="Default status" bind:value={normal} />Custom empty content
Can set up custom display empty content
Code
<script lang="ts">
import { IllustrationNoContent } from '@fibrae/semi-illustrations-svelte';
import { AutoComplete, Empty } from '@fibrae/semi-ui-svelte';
let data = $state<number[]>([]);
let loading = $state(false);
const fetchData = (input: string) => {
loading = true;
setTimeout(() => {
data = input ? Array.from({ length: 5 }, () => Math.random()) : [];
loading = false;
}, 1000);
};
</script>
<AutoComplete aria-label="搜索" {data} {loading} onSearch={fetchData}>
{#snippet emptyContent()}
<Empty style="padding: 12px; width: 300px">
{#snippet image()}
<IllustrationNoContent style="width: 150px; height: 150px" />
{/snippet}
{#snippet description()}暂无内容{/snippet}
</Empty>
{/snippet}
</AutoComplete>API reference
Props
| Property | Description | Type | Default |
|---|---|---|---|
| autoAdjustOverflow | Whether the dropdown flips automatically when it would be clipped. | boolean | - |
| autofocus | Whether to focus the input after mount. | boolean | false |
| clearIcon | Snippet for the custom clear icon, effective when showClear is true. | Snippet | - |
| data | Data source of the candidates: an array of strings, numbers or objects. An object item uses value as the picked value and label as its text (value when label is missing); its disabled, class, style and data-* fields go to the matching row, where class takes a string, array or object and style is a string. Use the item snippet for markup. | 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; AutoComplete has no controlled open state. | boolean | - |
| 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 string of the dropdown list, applied to the role="listbox" element. | string | - |
| emptyContent | Snippet for the content shown in the dropdown when data is empty. | Snippet | - |
| insetLabel | Snippet for the inset label of the input; passing it counts as having content. It is not rendered, and does not name the component, when prefix or trigger is used. | Snippet | - |
| 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 | - |
| item | Snippet for the content of a candidate row, receiving { item, index }: item is the data item as given and index is its position among the rendered items; without it the row shows the label. | TypeSnippet<[AutoCompleteItemSnippetProps<T>]> | - |
| 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 | Called once when the focus leaves the input. | Type(event: FocusEvent) => void | - |
| onChange | Called when the input text changes or a candidate is picked. | Type(value: string | number) => void | - |
| onClear | Called after the clear button is used. | Type() => void | - |
| onDropdownVisibleChange | Called when the dropdown shows or hides, with its visibility. | Type(visible: boolean) => void | - |
| onFocus | Called once when the focus enters the input. | Type(event: FocusEvent) => void | - |
| onSearch | Called as the user types, with the typed text. | Type(inputValue: string) => void | - |
| onSelect | Called when a candidate is picked, with its value, or with the item without internal fields when onSelectWithObject is set. | Type(value: string | number | AutoCompleteDataItem) => void | - |
| onSelectWithObject | A boolean attribute, not a callback: 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 | Snippet for the prefix of the input. | Snippet | - |
| 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: AutoCompleteSelectedItem<T>) => string | - |
| showClear | Whether to show the clear button when there is content. | boolean | false |
| size | Size of the input. | Size | - |
| stopPropagation | Whether click, mousedown and focus in/out events on the dropdown stop propagating. | boolean | string | true |
| suffix | Snippet for the suffix of the input. | Snippet | - |
| trigger | Snippet that replaces the input. Its parameters are value, inputValue, placeholder, disabled, autofocus, size, validateStatus, showClear, insetLabelId, onChange (updates the input text, calls onSearch and onChange and opens the list), onClear, onFocus, onBlur and controlAttrs (the aria-* attributes given to the component, for the focusable element of the custom trigger). With it, id lands on the root element. | TypeSnippet<[AutoCompleteTriggerSnippetProps]> | - |
| validateStatus | Validation status; it only affects the style. | ValidateStatus | "default" |
| value | Current value of the input, supports bind:value. Left out, the component manages it; a one-way value={x} only seeds it, and for a fully controlled input use the function binding bind:value={get, set}. There is no defaultValue. | string | number | - |
Methods
| Method | Description | Type |
|---|---|---|
| blur | Removes focus from the input. | Type() => void |
| focus | Focuses the input. | Type(options?: FocusOptions) => void |
| getNativeElement | Returns the root div that wraps the input or the custom trigger. | Type() => HTMLDivElement | undefined |
id, name, and aria-describedby, aria-errormessage, aria-invalid, aria-label, aria-labelledby, aria-required land on the input (role="combobox"), so a <label for> finds it. class (a string, array or object), style (a string), data-* and other undeclared attributes and event handlers (for example onclick) land on the root div, whose tabindex is fixed to -1; onkeydown runs after the component has handled the key. The lowercase onfocus, onblur, onchange and onselect are not native passthroughs; use the callbacks onFocus, onBlur, onChange and onSelect. With the trigger snippet there is no input: id lands on the root div, name lands nowhere, and the aria-* attributes reach the focusable element of the custom trigger through the controlAttrs parameter.
Object items of data:
| Field | Description | Type |
|---|---|---|
| value | The value after picking | string | number |
| label | The text of the candidate, falling back to value; use the item snippet for markup | string | number |
| disabled | Whether the candidate is disabled | boolean |
| class | Class of the candidate row | string | object | array |
| style | Style of the candidate row | string |
Other fields are kept as they are and show up in item of the item snippet and in the payload of onSelect (when onSelectWithObject is set). Fields starting with data- land on the candidate row. A string or number item reaches renderSelectedItem as { value, label }.
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) | 自动完成菜单选项文本颜色 |