输入类

自动完成

输入框自动填充。

使用场景

用于对输入框提供输入建议,进行自动补全的操作

与可搜索的 Select 组件的区别:

  • AutoComplete 本质上是一个增强型的提供了输入建议的 Input 组件,而 Select 是一个选择器
  • 点击展开时,Select 会将输入框的值全部清空,而 AutoComplete 会保留上次选中的值
  • Select 的已选项渲染(renderSelectedItem)可定制化程度更高,可以为任意类型的 ReactNode,而 AutoComplete 只允许为字符串

代码演示

如何引入

import { AutoComplete } from '@fibrous/semi-ui-react';

基本用法

通过 onSearch 监听用户输入,将输入建议通过更新 props.data 传入。通过 onChange 保持受控,当输入框变化/选中输入项时会触发 onChange

代码
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 }} />;
}

自定义候选项渲染

需要自定义候选项渲染时,data 可以传入一个对象数组(每个 Object 必须含有 label、value 两个 key,value 为候选项选中的值,label 为候选项展示的内容)
通过 renderItem 可以自定义候选项的渲染

代码
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 }} />;
}

远程搜索

从 onSearch 中获取用户输入值,动态更新 data 值

尺寸

通过设置 size 可设置输入框尺寸,可选small,default(默认),large





代码
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>
    );
}

下拉菜单的位置

通过设置 position 可设置下拉菜单位置,可选值参考 Tooltip position

代码
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>
    );
}

禁用

代码
import { AutoComplete } from '@fibrous/semi-ui-react';

export default function AutoCompleteDemo() {
    return <AutoComplete data={[1, 2, 3, 4]} placeholder="禁用下拉菜单" disabled style={{ width: 200 }} />;
}

校验状态

可设置不同校验状态,展示不同样式





代码
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" />
        </>
    );
}

自定义空内容

可设置自定义展示空内容

代码
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 参考

Props

属性说明类型默认值
autoAdjustOverflow下拉面板被遮挡时是否自动调整方向。未设置时取 Popover 的默认值 true。boolean-
autoFocus挂载后是否自动聚焦输入框。booleanfalse
className根 div 的类名。string-
clearIcon自定义清除图标,showClear 为 true 时有效。ReactNode-
data候选项的数据源,可以是字符串、数字或对象的数组。对象项的 value 是选中值,label 是候选行的内容,不传时候选行为空。其余字段原样保留,出现在 renderItem 的参数里。需替换为新数组才会更新列表。T[][]
defaultActiveFirstOption面板打开后是否默认高亮第一个候选项,此时按 Enter 可直接选中。booleanfalse
defaultOpen挂载时是否默认展开下拉面板。boolean-
defaultValue非受控时输入框的初始值,只在挂载时读取。string | number-
disabled是否禁用。boolean-
dropdownClassName下拉列表的类名,落在 role="listbox" 元素上。string-
dropdownMatchSelectWidth下拉面板的最小宽度是否等于输入框宽度。booleantrue
dropdownStyle下拉列表的内联样式,与 maxHeight 合并后落在 role="listbox" 元素上。React.CSSProperties-
emptyContentdata 为空时在下拉面板中显示的内容;加载中时代替它显示加载动画。React.ReactNodenull
id输入框的 id,供 <label for> 关联;使用 triggerRender 时落在根 div 上。string-
insetLabel输入框内嵌标签,渲染在前缀位置;设置 prefix 或 triggerRender 时不渲染,也不作为可访问名称。React.ReactNode-
insetLabelIdinsetLabel 元素的 id,combobox 与 listbox 的名称引用它;不传时组件在渲染 insetLabel 时生成。string-
loading下拉列表是否显示加载动画;为 true 时代替候选项和空内容。booleanfalse
maxHeight下拉列表的最大高度,数字按 px 处理。string | number300
motion下拉面板显示、隐藏时是否有动画。booleantrue
onBlur焦点离开输入框所在区域时的回调,参数为 FocusEvent;焦点在区域内移动(例如移到清除按钮)时不触发。Type(e: React.FocusEvent) => void-
onChange输入框内容变化或选中候选项时的回调,参数为当前值。Type(value: string | number) => void-
onChangeWithObject不生效:组件不读取该属性。boolean-
onClear用清除按钮清空内容后的回调。Type() => void-
onDropdownVisibleChange下拉面板显示或隐藏时的回调,参数为是否可见。Type(visible: boolean) => void-
onFocus焦点进入输入框所在区域时的回调,参数为 FocusEvent;焦点在区域内移动(例如移到清除按钮)时不触发。Type(e: React.FocusEvent) => void-
onKeyDown键盘按下时的回调,在根 div 上触发,参数为 React.KeyboardEvent<HTMLDivElement>。Type(e: React.KeyboardEvent<HTMLDivElement>) => void-
onSearch用户输入时的回调,参数为输入的文本。Type(inputValue: string) => void-
onSelect候选项被选中时的回调,参数为选中值;设置 onSelectWithObject 时为去掉内部字段的数据项对象。Type(value: string | number | BaseDataItem) => void-
onSelectWithObject为 true 时 onSelect 收到去掉内部字段的数据项对象,而不是选中值。booleanfalse
placeholder输入框的提示文案。string-
position下拉面板的位置,可选值同 Tooltip 的 position。未设置时为 bottomLeft,ConfigProvider 的 direction 为 rtl 时为 bottomRight。Position-
prefix输入框的前缀内容;设置后 insetLabel 不渲染。React.ReactNode-
ref根 div 的引用,包裹输入框或 triggerRender 的输出。得到的是原生 div 元素,其 focus() 不会聚焦输入框。TypeReact.Ref<HTMLDivElement>-
renderItem自定义候选项的渲染,参数为数据项本身,返回 ReactNode;未设置时显示 label。Type(option: T) => React.ReactNode-
renderSelectedItem选中候选项后输入框显示的文本,必须返回字符串;字符串或数字项收到 { value, label },对象项收到数据项本身。Type(option: T extends string | number ? BaseDataItem : T) => string-
showClear有内容时是否显示清除按钮。booleanfalse
size输入框尺寸,可选 small、default 或 large。Size"default"
stopPropagation是否阻止下拉面板上的 click、mousedown 与焦点进出事件冒泡。boolean | stringtrue
style根 div 的内联样式。React.CSSProperties-
suffix输入框的后缀内容。React.ReactNode-
triggerRender自定义触发器,替换输入框。参数含 value(已选项数组)、inputValue、placeholder、disabled、autoFocus、size、validateStatus、showClear、prefix、suffix、clearIcon、aria-label、aria-labelledby、aria-describedby、aria-errormessage、aria-invalid、aria-required(其他 aria-* 不作为直接参数传入,可从 componentProps 取得)、onChange(更新输入文本,触发 onSearch 与 onChange 并展开列表)、onClear、onFocus、onBlur 与 componentProps。使用时 id 落在根 div 上。Type(props?: any) => React.ReactNode-
validateStatus校验状态,可选 default、error 或 warning,只影响展示样式。ValidateStatus"default"
value当前值。传入后为受控;之后改回 undefined 时输入框清空,组件转为非受控。string | number-

className、style、data-* 与其他原生属性和事件(例如 onClick)落在根 div 上,根 div 的 tabIndex 固定为 -1。id、name、aria-describedby、aria-errormessage、aria-invalid、aria-label、aria-labelledby 与 aria-required 落在输入框(role="combobox")上,因此 <label for> 能关联到输入框。使用 triggerRender 时没有输入框:id 落在根 div 上,name 不落到任何元素,上面列出的 aria-* 通过参数交给自定义触发器的可聚焦元素,其他 aria-* 不作为直接参数传到触发器上。

Accessibility

键盘和焦点

  • AutoComplete 的 input 框带有 role="combobox",并通过 aria-expanded、aria-controls、aria-autocomplete="list" 和 aria-activedescendant 与选项面板关联
  • combobox 的名称按优先级取:aria-labelledby、aria-label、实际渲染出来的 insetLabel(combobox 的 aria-labelledby 指向它,未传 insetLabelId 时组件生成 id;有 prefix 时 insetLabel 不渲染,也不作名称)。listbox 使用同一个名称来源。AutoComplete 没有默认名称:以上都没有时,请用 aria-label 或 aria-labelledby 为输入框命名;<label for> 只命名输入框,listbox 仍没有名称
  • AutoComplete 的 input 框可被聚焦,聚焦后,键盘用户可以通过 上箭头 或 下箭头 打开选项面板;data 为空时面板里显示 emptyContent(没有提供时为空),加载中显示加载动画
  • AutoComplete 也支持通过 Enter 键打开和收起面板
  • 若用户将 defaultActiveFirstOption 属性设置为 true 时,选项面板打开后默认高亮第一个选项
  • 若下拉菜单打开时:
    • 使用 Esc 可以关闭菜单
    • 使用 Tab 可以关闭菜单,焦点照常移到下一个可聚焦元素
    • 使用 上箭头 或 下箭头 可以切换选项,被高亮的选项(aria-activedescendant 指向的项)aria-selected 为 true
    • 被聚焦的选项可以通过 Enter 键选中,并收起面板
    • 用鼠标选中选项后,焦点留在输入框中
  • showClear 开启且有内容时,清除按钮与 Input 相同:原生 button,名称为语言包中的「清空输入」,从输入框按 Tab 移到它,Enter 或空格键清空并把焦点放回输入框

文案规范

  • 需要清晰地展示内容,让用户显而易见地感知到可用的各个选项
  • 限制一次性展示的选项数量

设计变量

下表是组件内部的 Sass 变量,不能覆写。定制样式请覆写 --semi-* CSS 变量(见 Design Token 页),或写不分层的 CSS 规则。
Sass 变量值用法
$transition_duration-autoComplete_option-bgvar(--semi-transition_duration-none)选择器-选项-动画持续时间
$transition_function-autoComplete_option-bgvar(--semi-transition_function-easeIn)选择器-选项-过渡曲线
$transition_delay-autoComplete_option-bgvar(--semi-transition_delay-none)选择器-选项-延迟时间
$spacing-autoComplete_loading_wrapper-paddingTop$spacing-tight加载搜索结果时的顶部内边距
$spacing-autoComplete_loading_wrapper-paddingBottom$spacing-tight加载搜索结果时的底部内边距
$color-autoComplete_option-icon-defaulttransparent自动完成菜单选项图标颜色 - 默认态
$color-autoComplete_option-bg-hovervar(--semi-color-fill-0)自动完成菜单选项背景颜色 - 悬停态
$color-autoComplete_option-bg-activevar(--semi-color-fill-1)自动完成菜单选项背景颜色 - 按下态
$color-autoComplete_option_disabled-textvar(--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_option0px自动完成待选项圆角
$color-autoComplete_option_main-textvar(--semi-color-text-0)自动完成菜单选项文本颜色