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 (the
#selectedItemslot) can be any node, while therenderSelectedItemof AutoComplete may only return a string
Demos
How to import
<script setup lang="ts">
import { AutoComplete } from '@fibrous/semi-ui-vue';
</script>
Basic usage
Monitor user input through @search, pass input suggestions through data, and maintain control with v-model, which updates when the input box changes/selects an input item
Code
<script setup lang="ts">
import { ref } from 'vue';
import { IconSearch } from '@fibrous/semi-icons-vue';
import { AutoComplete } from '@fibrous/semi-ui-vue';
const stringData = ref<string[]>([]);
const value = ref<string | number>('');
const handleStringSearch = (input: string) => {
stringData.value = input ? ['gmail.com', '163.com', 'qq.com'].map((domain) => `${input}@${domain}`) : [];
};
const handleChange = (input: string | number) => {
console.log('onChange', input);
};
</script>
<template>
<AutoComplete v-model="value" :data="stringData" placeholder="搜索... " showClear style="width: 200px" @change="handleChange" @search="handleStringSearch">
<template #prefix>
<IconSearch />
</template>
</AutoComplete>
</template>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="{ item, index }" slot: item is the data item as given, so keep only data in the items and write the markup in the slot
Code
<script setup lang="ts">
import { ref } from 'vue';
import { IconSearch } from '@fibrous/semi-icons-vue';
import { AutoComplete, Avatar } from '@fibrous/semi-ui-vue';
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' },
];
const data = ref<ListItem[]>([]);
const handleStringSearch = (value: string) => {
data.value = value ? list.map((item) => ({ ...item, value: item.name, label: item.email })) : [];
};
const renderSelectedItem = (option: ListItem) => option.email;
</script>
<template>
<AutoComplete aria-label="搜索邮箱" :data="data" :renderSelectedItem="renderSelectedItem" showClear style="width: 280px" @search="handleStringSearch">
<template #prefix>
<IconSearch />
</template>
<template #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>
</template>
</AutoComplete>
</template>Remote search
Get user input value from @search, update data dynamically, update loading
Code
<script setup lang="ts">
import { ref } from 'vue';
import { debounce } from 'es-toolkit';
import { IconSearch } from '@fibrous/semi-icons-vue';
import { IconButton, IconForm, IconInput, IconSelect, IconTable } from '@fibrous/semi-icons-vue/lab';
import { AutoComplete, type AutoCompleteDataItem } from '@fibrous/semi-ui-vue';
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: '表格' },
];
const loading = ref(false);
const list = ref(initList);
const handleSearch = (inputValue: string) => {
loading.value = true;
const newList = inputValue ? initList.filter((item) => item.value.includes(inputValue)) : initList;
setTimeout(() => {
list.value = newList;
loading.value = false;
}, 1000);
};
const search = debounce(handleSearch, 200);
const handleSelect = (value: string | number | AutoCompleteDataItem) => {
console.log(value);
};
// Unlike Select, this can return only a string, not a node
const renderSelectedItem = (item: ListItem) => item.value;
</script>
<template>
<AutoComplete aria-label="搜索" :data="list" :loading="loading" :renderSelectedItem="renderSelectedItem" style="width: 250px" @search="search" @select="handleSelect">
<template #prefix>
<IconSearch />
</template>
<template #item="{ item }">
<div style="display: flex; align-items: center">
<div style="font-size: 32px">
<component :is="icons[item.value]" />
</div>
<div style="margin-left: 12px">
<p>{{ item.value }}</p>
<p>{{ item.label }}</p>
</div>
</div>
</template>
</AutoComplete>
</template>Size
The size of the input box can be set by setting size, optional small, default (default), large
Code
<script setup lang="ts">
import { AutoComplete } from '@fibrous/semi-ui-vue';
const data = [1, 2, 3, 4];
</script>
<template>
<div>
<AutoComplete :data="data" placeholder="small" size="small" style="width: 200px" />
<br />
<br />
<AutoComplete :data="data" placeholder="default" size="default" style="width: 200px" />
<br />
<br />
<AutoComplete :data="data" placeholder="large" size="large" style="width: 200px" />
</div>
</template>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 setup lang="ts">
import { ref } from 'vue';
import { AutoComplete } from '@fibrous/semi-ui-vue';
const data = ref<string[]>([]);
const change = (input: string) => {
data.value = input ? ['gmail.com', '163.com', 'qq.com'].map((domain) => `${input}@${domain}`) : [];
};
</script>
<template>
<div>
<AutoComplete :data="data" placeholder="选项菜单在上方显示" position="top" style="width: 200px; margin: 10px" @search="change" />
<AutoComplete :data="data" placeholder="选项菜单在右侧显示" position="rightTop" style="width: 200px; margin: 10px" @search="change" />
</div>
</template>Disabled
Code
<script setup lang="ts">
import { AutoComplete } from '@fibrous/semi-ui-vue';
const data = [1, 2, 3, 4];
</script>
<template>
<AutoComplete :data="data" disabled placeholder="禁用下拉菜单" style="width: 200px" />
</template>Validate status
Different verification states can be set to show different styles
Code
<script setup lang="ts">
import { AutoComplete } from '@fibrous/semi-ui-vue';
</script>
<template>
<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" />
</template>Custom empty content
Can set up custom display empty content
Code
<script setup lang="ts">
import { ref } from 'vue';
import { IllustrationNoContent } from '@fibrous/semi-illustrations-vue';
import { AutoComplete, Empty } from '@fibrous/semi-ui-vue';
const data = ref<number[]>([]);
const loading = ref(false);
const fetchData = (input: string) => {
loading.value = true;
setTimeout(() => {
data.value = input ? Array.from({ length: 5 }, () => Math.random()) : [];
loading.value = false;
}, 1000);
};
</script>
<template>
<AutoComplete aria-label="搜索" :data="data" :loading="loading" @search="fetchData">
<template #emptyContent>
<Empty style="padding: 12px; width: 300px">
<template #image>
<IllustrationNoContent style="width: 150px; height: 150px" />
</template>
<template #description>暂无内容</template>
</Empty>
</template>
</AutoComplete>
</template>API reference
Props
| Property | Description | Type | Default |
|---|---|---|---|
| modelValue | Current value of the input, bound with v-model. Passing it makes the component controlled; undefined leaves it uncontrolled. | string | number | undefined |
| modelModifiers | The trim and number v-model modifiers, passed by Vue for v-model.trim and v-model.number. | ModelModifiers | undefined |
| defaultValue | Initial value of the input when uncontrolled. | string | number | semiGlobalDefault("AutoComplete", "defaultValue") |
| 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. Use the item slot for markup. Changing the array in place updates the list as well. | T[] | semiDefault("AutoComplete", "data", []) as T[] |
| autofocus | Whether to focus the input after mount. | boolean | semiDefault("AutoComplete", "autofocus", false) |
| autoAdjustOverflow | Whether the dropdown flips automatically when it would be clipped. | boolean | semiGlobalDefault("AutoComplete", "autoAdjustOverflow") |
| defaultActiveFirstOption | Whether the first candidate is highlighted when the panel opens, so that Enter picks it directly. | boolean | semiDefault("AutoComplete", "defaultActiveFirstOption", false) |
| defaultOpen | Whether the dropdown panel is open on mount. | boolean | semiGlobalDefault("AutoComplete", "defaultOpen") |
| disabled | Whether the component is disabled. | boolean | semiGlobalDefault("AutoComplete", "disabled") |
| dropdownClassName | Class name of the dropdown list, applied to the role="listbox" element. | string | semiGlobalDefault("AutoComplete", "dropdownClassName") |
| dropdownMatchSelectWidth | Whether the minimum width of the dropdown equals the width of the input. | boolean | semiDefault("AutoComplete", "dropdownMatchSelectWidth", true) |
| dropdownStyle | Inline style of the dropdown list, applied to the role="listbox" element. | StyleValue | semiGlobalDefault("AutoComplete", "dropdownStyle") |
| 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 | undefined |
| loading | Whether the dropdown shows a loading spinner in place of the candidates and the empty content. | boolean | semiDefault("AutoComplete", "loading", false) |
| maxHeight | Maximum height of the dropdown list; a number is in px. | string | number | semiDefault("AutoComplete", "maxHeight", 300) |
| motion | Whether the dropdown animates when it shows and hides. | boolean | semiDefault("AutoComplete", "motion", true) |
| selectWithObject | A boolean attribute, not an event: when true, the select event receives the data item without internal fields instead of its value. | boolean | semiDefault("AutoComplete", "selectWithObject", false) |
| placeholder | Placeholder of the input. | string | semiGlobalDefault("AutoComplete", "placeholder") |
| 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. | Type"top" | "bottom" | "left" | "right" | "topLeft" | "topRight" | "leftTop" | "leftBottom" | "rightTop" | "rightBottom" | "bottomLeft" | "bottomRight" | "leftTopOver" | "rightTopOver" | "leftBottomOver" | "rightBottomOver" | semiGlobalDefault("AutoComplete", "position") |
| 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. It stays a function prop because the foundation calls it outside rendering. | Type((option: AutoCompleteSelectedItem<T>) => string) | undefined |
| showClear | Whether to show the clear button when there is content. | boolean | semiDefault("AutoComplete", "showClear", false) |
| size | Size of the input. | Size | semiDefault("AutoComplete", "size", "default") |
| stopPropagation | Whether click, mousedown and focus in/out events on the dropdown stop propagating. | string | boolean | semiDefault("AutoComplete", "stopPropagation", true) |
| validateStatus | Validation status; it only affects the style. | "default" | "error" | "warning" | "success" | semiDefault("AutoComplete", "validateStatus", "default") |
Events
| Event | Description | Callback parameters |
|---|---|---|
| update:modelValue | Emitted when the input text changes or a candidate is picked, for v-model to write back. It fires before change. | [value: string | number] |
| change | Emitted when the input text changes or a candidate is picked. It is a component event and hides the native change event of the input. | [value: string | number] |
| search | Emitted as the user types, with the typed text. | [inputValue: string] |
| select | Emitted when a candidate is picked, with its value, or with the item without internal fields when selectWithObject is set. | [value: string | number | AutoCompleteDataItem] |
| clear | Emitted after the clear button is used. | [] |
| focus | Emitted once when the focus enters the component. It is a component event and hides the native focus event. | [event: FocusEvent] |
| blur | Emitted once when the focus leaves the component. It is a component event and hides the native blur event. | [event: FocusEvent] |
| dropdownVisibleChange | Emitted when the dropdown shows or hides, with its visibility. It also fires with false when the component is deactivated inside KeepAlive. | [visible: boolean] |
| keydown | Emitted after the component has handled the key, with the KeyboardEvent. It hides the native keydown event. | [event: KeyboardEvent] |
Slots
| Slot | Description | Scope parameters |
|---|---|---|
| prefix | Prefix content of the input. | any |
| suffix | Suffix content of the input. | any |
| insetLabel | Inset label of the input. It is not rendered, and does not name the component, when prefix or trigger is used. | any |
| clearIcon | Custom clear icon, effective when showClear is true. | any |
| emptyContent | Content shown in the dropdown when data is empty. | any |
| item | Content of a candidate row. item is the data item as given and index is its position among the rendered items; without it the row shows the label. | TypeAutoCompleteItemSlotProps<T> |
| trigger | Replaces the input. Its parameters are value, inputValue, placeholder, disabled, autofocus, size, validateStatus, showClear, insetLabelId, onChange (updates the input text, emits search and change and opens the list), onClear, onFocus, onBlur, controlAttrs (the aria-* attributes given to the component, for the focusable element of the custom trigger) and componentProps. With it, id lands on the root element. | TypeAutoCompleteTriggerSlotProps<T> |
Exposes
| Name | Description | Type |
|---|---|---|
| None |
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, style, data-* and other undeclared attributes and listeners (for example @click) land on the root div, whose tabindex is fixed to -1. With #trigger 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 slot 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 slot 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 | StyleValue |
Other fields are kept as they are and show up in item of the #item slot and in the payload of @select (when selectWithObject is set). Fields starting with data- land on the candidate row. A string or number item reaches renderSelectedItem as { value, label }.
AutoComplete exposes the following members through a template ref, typed as AutoCompleteHandle:
| Member | Description | Type |
|---|---|---|
| nativeElement | Read-only. The root div that wraps the input or the custom trigger, null before mount | HTMLDivElement | null |
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) | 自动完成菜单选项文本颜色 |