输入类

标签输入框

标签输入框能够将输入的内容生成标签。

代码演示

如何引入

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

基本演示

敲击回车键后,输入内容将成为标签。标签内容如果为空串或者纯空格时,则会被过滤。

抖音

火山

西瓜视频

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

export default function TagInputDemo() {
    return <TagInput defaultValue={['抖音', '火山', '西瓜视频']} placeholder="请输入..." onChange={(v) => console.log(v)} />;
}

批量添加

可以使用 separator 设置分隔符,来实现批量输入,它的默认值为英文逗号。支持多个分隔符以 string[] 格式传入。



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

export default function TagInputDemo() {
    return (
        <>
            <TagInput separator="-" placeholder="使用 - 进行批量输入" onChange={(v) => console.log(v)} />
            <br />
            <br />
            <TagInput separator={['-', '/', '|', '++']} placeholder="支持多个分隔符进行批量输入" onChange={(v) => console.log(v)} />
        </>
    );
}

批量删除

可使用 showClear 设置是否支持一键删除所有标签和输入框内容。

抖音

火山

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

export default function TagInputDemo() {
    return <TagInput showClear defaultValue={['抖音', '火山']} placeholder="请输入..." onChange={(v) => console.log(v)} />;
}

禁用

抖音

火山

西瓜视频

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

export default function TagInputDemo() {
    return <TagInput disabled showClear defaultValue={['抖音', '火山', '西瓜视频']} placeholder="请输入..." />;
}

尺寸大小

通过 size 控制标签输入框的大小尺寸,可选: small 、 default 、 large。





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

export default function TagInputDemo() {
    return (
        <>
            <TagInput size="small" placeholder="small" />
            <br />
            <br />
            <TagInput placeholder="default" />
            <br />
            <br />
            <TagInput size="large" placeholder="large" />
        </>
    );
}

不同校验状态样式

可以使用 validateStatus 设置不同校验状态的样式,它仅影响背景颜色等样式表现,可选值: default 、 warning 、 error。





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

export default function TagInputDemo() {
    return (
        <>
            <TagInput placeholder="default" />
            <br />
            <br />
            <TagInput placeholder="warning" validateStatus="warning" />
            <br />
            <br />
            <TagInput placeholder="error" validateStatus="error" />
        </>
    );
}

前缀 / 后缀

可以通过 prefix 传入输入框前缀,通过 suffix 传入输入框后缀,可以为文本或者 ReactNode。
当 prefix、suffix 传入的内容为 string 或者 Icon 时,会自动带上左右间隔;若为自定义 ReactNode,则左右间隔为 0,如需可以在你传入的 ReactNode中自行设置。



Prefix




Suffix
代码
import { IconVigoLogo, IconGift } from '@fibrous/semi-icons-react';
import { TagInput } from '@fibrous/semi-ui-react';

export default function TagInputDemo() {
    return (
        <>
            <TagInput prefix={<IconVigoLogo />} />
            <br />
            <br />
            <TagInput prefix="Prefix" />
            <br />
            <br />
            <TagInput suffix={<IconGift />} />
            <br />
            <br />
            <TagInput suffix="Suffix" />
        </>
    );
}

失焦后自动创建标签

可使用 addOnBlur,设置是否在 blur 事件触发时,将当前 input 的值自动创建成 tag。

抖音

火山

西瓜视频

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

export default function TagInputDemo() {
    return <TagInput defaultValue={['抖音', '火山', '西瓜视频']} addOnBlur placeholder="请输入..." onChange={(v) => console.log(v)} />;
}

过滤重复标签

可使用 allowDuplicates,设置是否允许创建相同 tag,默认为 true。

抖音

火山

西瓜视频

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

export default function TagInputDemo() {
    return <TagInput defaultValue={['抖音', '火山', '西瓜视频']} allowDuplicates={false} placeholder="请输入..." onChange={(v) => console.log(v)} />;
}

输入限制

可使用 max 限制标签数量,添加后超出的部分不会成为标签,并且触发 onExceed() 回调。

可使用 maxLength 限制单个标签的最大长度,超出后将不允许再输入,并且触发 onInputExceed() 回调。

代码
import { TagInput, Toast } from '@fibrous/semi-ui-react';

export default function TagInputDemo() {
    return (
        <>
            <TagInput
                max={3}
                placeholder="最多输入3条标签.."
                onChange={(v) => console.log(v)}
                onExceed={(v) => {
                    Toast.warning('超过 max');
                    console.log(v);
                }}
            />
            <TagInput
                maxLength={5}
                placeholder="单个标签长度不超过5..."
                style={{ marginTop: 12 }}
                onChange={(v) => console.log(v)}
                onInputExceed={(v) => {
                    Toast.warning('超过 maxLength');
                    console.log(v);
                }}
            />
        </>
    );
}

限制标签展示数量

利用 maxTagCount 可以限制展示的标签数量,超出部分将以 +N 的方式展示。使用 showRestTagsPopover 可以设置在超出 maxTagCount 后,hover +N 是否显示 Popover,并且可以在 restTagsPopoverProps 属性中配置 Popover。

抖音

火山

+1
代码
import { TagInput } from '@fibrous/semi-ui-react';

export default function TagInputDemo() {
    return <TagInput maxTagCount={2} showRestTagsPopover restTagsPopoverProps={{ position: 'top' }} defaultValue={['抖音', '火山', '西瓜视频']} onChange={(v) => console.log(v)} />;
}

标签受控

可使用 value 设置标签内容,并配合 onChange 实现标签内容受控。

抖音

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

export default function TagInputDemo() {
    const [value, setValue] = useState(['抖音']);
    const handleChange = (v: string[]) => setValue(v);
    return <TagInput value={value} onChange={handleChange} />;
}

输入受控

可使用 inputValue 设置输入框内容,并配合 onInputChange 实现输入内容受控。

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

export default function TagInputDemo() {
    const [value, setValue] = useState('abc');
    const handleInputChange = (v: string) => setValue(v);
    return <TagInput inputValue={value} onInputChange={handleInputChange} />;
}

回调

抖音

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

export default function TagInputDemo() {
    return (
        <TagInput
            defaultValue={['抖音']}
            placeholder="请输入..."
            showClear
            onFocus={() => {
                console.log(`onFocus`);
            }}
            onBlur={() => {
                console.log(`onBlur`);
            }}
            onChange={(tag) => {
                console.log(`onChange,当前标签数组:${tag}`);
            }}
            onAdd={(tag) => {
                console.log(`onAdd,新增:${tag}`);
            }}
            onRemove={(v, i) => {
                console.log(`onRemove,移除:${v}, 序号:${i}`);
            }}
            onInputChange={(input) => {
                console.log(`onInputChange,当前输入内容:${input}`);
            }}
        />
    );
}

焦点管理

可以使用 blur() 和 focus() 方法对焦点进行管理。

抖音

火山

代码
import { useRef } from 'react';
import { TagInput, Button, type TagInputHandle } from '@fibrous/semi-ui-react';

export default function TagInputDemo() {
    const ref = useRef<TagInputHandle>(null);
    const handleTagInputFocus = () => {
        ref.current?.focus();
    };

    return (
        <>
            <TagInput defaultValue={['抖音', '火山']} ref={ref} />
            <Button style={{ marginTop: 10 }} onClick={handleTagInputFocus}>
                点击按钮聚焦
            </Button>
        </>
    );
}

自定义标签渲染

可以使用 renderTagItem 自定义标签渲染。 renderTagItem(value: string, index: number, onClose: function ) => React.ReactNode 组件会为每项加上 key。

avatar夏可漫@semi.com
代码
import { useMemo, useState } from 'react';
import { IconClose } from '@fibrous/semi-icons-react';
import { TagInput, Avatar, Button } from '@fibrous/semi-ui-react';

export default function TagInputDemo() {
    const [value, setValue] = useState(['夏可漫']);
    const list = useMemo(
        () => [
            {
                name: '夏可漫',
                avatar: '/assets/root-web-sites/avatarDemo.jpeg',
            },
            {
                name: '申悦',
                avatar: '/assets/root-web-sites/bf8647bffab13c38772c9ff94bf91a9d.jpg',
            },
            {
                name: '曲晨一',
                avatar: '/assets/root-web-sites/bf8647bffab13c38772c9ff94bf91a9d.jpg',
            },
            {
                name: '文嘉茂',
                avatar: '/assets/root-web-sites/avatarDemo.jpeg',
            },
        ],
        []
    );
    const mapList = useMemo(() => new Map(list.map((item) => [item.name, item])), [list]);

    const renderTagItem = (value: string, index: number, onClose: () => void) => {
        const data = mapList.get(value);
        return (
            <div
                key={index}
                style={{
                    backgroundColor: 'var(--semi-color-info-light-default)',
                    padding: '4px 8px',
                    borderRadius: 12,
                    display: 'flex',
                    alignItems: 'center',
                    fontSize: 14,
                    marginRight: 10,
                }}
            >
                <Avatar alt="avatar" src={data ? data.avatar : '/assets/placeholder/avatar-a.svg'} size="extra-small" />
                <span style={{ marginLeft: 8 }}>{`${value}@semi.com`}</span>
                <Button theme="borderless" type="tertiary" size="small" icon={<IconClose size="small" />} aria-label={`移除 ${value}`} onClick={onClose} />
            </div>
        );
    };

    return <TagInput value={value} onChange={setValue} renderTagItem={renderTagItem} />;
}

拖拽排序

将 draggable设为 true,开启拖拽排序功能。拖拽功能开启后,聚焦 TagInput(点击或 Tab)后 Tag 可拖拽,焦点离开 TagInput 后 Tag 不可拖拽。拖拽句柄位于标签旁边,可用键盘操作,见下文「键盘和焦点」。值相同的标签也能分别拖拽排序。

抖音

火山

西瓜视频

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

export default function TagInputDemo() {
    return <TagInput draggable allowDuplicates={false} defaultValue={['抖音', '火山', '西瓜视频']} placeholder="请输入..." onChange={(v) => console.log(v)} />;
}

API 参考

Props

属性说明类型默认值
addOnBlur焦点离开整个组件时,是否将输入框内容自动创建为标签。booleanfalse
allowDuplicates是否允许添加相同的标签。booleantrue
aria-label输入框的可访问名称,落在输入框上,根节点不带。string-
autoFocus挂载时是否自动聚焦输入框并激活组件,禁用时不生效。booleanfalse
className根元素的类名。string-
clearIcon替换清除按钮中的图标,未设置时使用默认图标。React.ReactNode-
defaultValue非受控时的初始标签。string[]-
disabled是否禁用;禁用时输入框为原生 disabled,标签为静态文本。boolean-
draggable是否开启拖拽排序。booleanfalse
expandRestTagsOnClick焦点进入组件(点击、Tab 或 focus())后是否展开被 maxTagCount 折叠的标签;为 false 时始终保持折叠。booleantrue
inputValue当前输入框内容,配合 onInputChange 实现受控。string-
insetLabel内嵌标签,显示在输入框前;同时提供 prefix 时不显示它。显示时,没有 aria-label、aria-labelledby 的输入框以它命名。React.ReactNode-
insetLabelId内嵌标签容器的 id,供 aria-labelledby 引用;显示内嵌标签且未设置时,组件自动生成。string-
max允许的标签最大数量,超出的部分不会成为标签,并触发 onExceed。number-
maxLength单个标签的最大长度,超出后不再接受输入,并触发 onInputExceed。number-
maxTagCount标签的最大展示数量,超出部分以 +N 形式展示;组件激活后展开,除非 expandRestTagsOnClick 为 false。number-
onAdd添加标签时触发,参数为实际新增的标签,超出 max 的部分不计入。Type(addedValue: string[]) => void-
onBlur焦点离开整个组件时触发一次;焦点在组件内部移动不触发。Type(e: React.FocusEvent<HTMLDivElement>) => void-
onChange标签变化时触发,参数为完整的标签数组。Type(value: string[]) => void-
onExceed添加后超过 max 时触发,参数为截断前的完整列表。Type(value: string[]) => void-
onFocus焦点从组件外进入时触发一次;焦点在输入框、标签、清除按钮之间移动不触发。Type(e: React.FocusEvent<HTMLDivElement>) => void-
onInputChange输入框内容变化时触发,参数为新的输入文字与触发它的事件。事件可能是输入事件、KeyboardEvent(回车添加)、FocusEvent(addOnBlur)、MouseEvent(清除)或 CompositionEvent(输入法截断),读取专有字段前先按 event.type 收窄。Type(value: string, e: React.SyntheticEvent) => void-
onInputExceed输入内容超过 maxLength 时触发,参数为被拒绝的输入内容。Type(value: string) => void-
onKeyDown输入框按键时在组件处理之后触发;在其中调用 preventDefault 不会阻止回车添加标签。Type(e: React.KeyboardEvent<HTMLInputElement>) => void-
onRemove移除标签时触发,参数为被移除的标签及其序号。Type(removedValue: string, idx: number) => void-
placeholder没有标签时输入框的占位文字。string-
prefix输入框前缀,文字或图标会自动带上左右间隔;自定义节点的左右间隔为 0,可在节点中自行设置。React.ReactNode-
preventScroll组件内 focus() 调用是否阻止浏览器滚动到新聚焦的元素。boolean-
ref句柄,暴露 focus() 与 blur() 方法,不是根元素。TypeReact.Ref<TagInputHandle>-
renderTagItem自定义标签渲染,返回的节点替换默认标签,组件会为每项加上 key。第三个参数 onClose 关闭该标签,可直接作为按钮的 onClick。Type(value: string, index: number, onClose: () => void) => React.ReactNode-
restTagsPopoverProps+N 浮层的 Popover 属性,如 position、trigger,默认分别为 top 与 hover,具体见 Popover·API 参考;不要传 content,它会替换剩余标签的列表。RestTagsPopoverProps-
separator批量输入时的分隔符,可传多个;传 null 不拆分。string | string[] | null","
showClear是否显示一键清除所有标签和输入内容的按钮。booleanfalse
showContentTooltip标签文字被截断时,hover 标签是否通过浮层显示全部内容;传对象时用 type 指定浮层类型(tooltip 或 popover),用 opts 透传给浮层的属性。boolean | ShowTooltiptrue
showRestTagsPopover超过 maxTagCount 后,hover +N 时是否通过 Popover 显示剩余标签。booleantrue
size尺寸,可选 small、default、large。Size"default"
split自定义分隔符处理函数,参数为原始字符串与 separator,返回拆分后的字符串数组。Type(originString: string, separators: string | string[] | null) => string[]-
style根元素的样式。React.CSSProperties-
suffix输入框后缀,文字或图标会自动带上左右间隔;自定义节点的左右间隔为 0,可在节点中自行设置。React.ReactNode-
validateStatus校验状态,仅影响样式;为 error 时输入框带 aria-invalid。ValidateStatus"default"
value当前标签,配合 onChange 实现受控;undefined 时按非受控处理,null 表示受控且没有标签。string[] | null-

Methods

方法说明类型
blur移出焦点:使组件内持有焦点的元素失去焦点,并退出激活状态。Type() => void
focus聚焦输入框并激活组件;禁用时不激活。Type() => void

原生属性中,id、name、role、tabIndex 与描述输入框的 aria-*(aria-label、aria-labelledby、aria-describedby、aria-invalid、aria-required、aria-controls、aria-expanded、aria-haspopup、aria-autocomplete、aria-activedescendant、aria-errormessage)落在内部输入框上;其余原生属性(如 data-*、title、onClick、onMouseDown)落在根元素上,根元素上的 onClick 与组件内部的激活处理都会执行。

Accessibility

ARIA

  • TagInput 支持传入 aria-label 来表示该 TagInput 作用,它落在输入框上,根节点不带;
  • 根节点的角色是 group,把输入框、标签和清除按钮作为一个整体;禁用时根节点带 aria-disabled="true",未禁用时不带;
  • 禁用时输入框是原生 disabled,标签是静态文本,不带 aria-label;validateStatus 为 error 时输入框带 aria-invalid,显式传入的 aria-invalid 优先;
  • showClear 开启、有标签或输入内容且未禁用时,渲染清除按钮。它是原生 button,名称为语言包中的「清空标签输入」,在 hover 或焦点位于 TagInput 内时显示。从输入框按 Tab 移到清除按钮,Enter 或 Space 清空,效果与鼠标点击相同,清空后焦点回到输入框;也可在空输入框中按 Backspace 删除最后一个标签。

键盘和焦点

  • 输入框中按 Enter 把输入内容添加为标签,空输入时不做任何事。Enter 一律阻止默认行为,TagInput 位于表单内时不会提交表单。
  • 输入框为空时按 Backspace 删除最后一个标签。
  • 可关闭的标签是 role="button" 的 Tab 停靠点,位于输入框之前;聚焦标签后按 Backspace 或 Delete 关闭它,标签被移除后焦点回到输入框。禁用时标签是静态文本,不可聚焦。
  • 开启 showClear 且有内容时,清除按钮在输入框之后:Tab 到达,Enter 或 Space 清空,焦点回到输入框。
  • 激活状态跟随焦点:焦点从组件外进入(点击、Tab 或 focus())即激活,焦点离开整个组件即退出。焦点在输入框、标签与清除按钮之间移动不算离开组件,不触发 focus、blur,也不会触发 addOnBlur。激活时,被 maxTagCount 折叠的标签会展开(expandRestTagsOnClick 为 false 时不展开)。
  • 开启 draggable 并激活后,每个标签前出现拖拽句柄,句柄是独立的 Tab 停靠点,位于它所属的标签之前。聚焦句柄后按 Space 拿起,方向键移动,Space 放下,读屏会播报拿起、移动与放下。

设计变量

下表是组件内部的 Sass 变量,不能覆写。定制样式请覆写 --semi-* CSS 变量(见 Design Token 页),或写不分层的 CSS 规则。
Sass 变量值用法
$transition_duration-tagInput-bgvar(--semi-transition_duration-none)标签输入框-背景色-动画持续时间
$transition_function-tagInput-bgvar(--semi-transition_function-easeIn)标签输入框-背景色-过渡曲线
$transition_delay-tagInput-bgvar(--semi-transition_delay-none)标签输入框-背景色-延迟时间
$transition_duration-tagInput-bordervar(--semi-transition_duration-none)标签输入框-边框-动画持续时间
$transition_function-tagInput-bordervar(--semi-transition_function-easeIn)标签输入框-边框-过渡曲线
$transition_delay-tagInput-bordervar(--semi-transition_delay-none)标签输入框-边框-延迟时间
$color-tagInput-icon-defaultvar(--semi-color-text-2)标签输入框图标颜色 - 默认
$color-tagInput-icon-hovervar(--semi-color-primary-hover)标签输入框图标颜色 - 悬浮
$color-tagInput_disabled-text-defaultvar(--semi-color-disabled-text)标签输入框禁用文字颜色
$color-tagInput_disabled-bgvar(--semi-color-disabled-fill)标签输入框禁用背景色
$color-tagInput_default-bg-focusvar(--semi-color-fill-0)标签输入框背景颜色 - 选中态
$color-tagInput-border-defaulttransparent标签输入框描边颜色 - 默认
$color-tagInput-border-hovertransparent标签输入框描边颜色 - 悬浮
$color-tagInput-border-focusvar(--semi-color-focus-border)标签输入框描边颜色 - 选中态
$color-tagInput_prefix-defaultvar(--semi-color-text-2)标签输入框 prefix 颜色
$color-tagInput_suffix-defaultvar(--semi-color-text-2)标签输入框 suffix 颜色
$color-tagInput_default-bg-defaultvar(--semi-color-fill-0)标签输入框背景颜色 - 默认
$color-tagInput_default-bg-hovervar(--semi-color-fill-1)标签输入框背景颜色 - 悬浮
$color-tagInput_warning-bg-defaultvar(--semi-color-warning-light-default)警告标签输入框背景颜色 - 默认
$color-tagInput_warning-border-defaultvar(--semi-color-warning-light-default)警告标签输入框描边颜色 - 默认
$color-tagInput_warning-bg-hovervar(--semi-color-warning-light-hover)警告标签输入框背景颜色 - 悬浮
$color-tagInput_warning-border-hovervar(--semi-color-warning-light-hover)警告标签输入框描边颜色 - 悬浮
$color-tagInput_warning-bg-focusvar(--semi-color-warning-light-default)警告标签输入框背景颜色 - 选中
$color-tagInput_warning-border-focusvar(--semi-color-warning)警告标签输入框描边颜色 - 选中
$color-tagInput_danger-bg-defaultvar(--semi-color-danger-light-default)危险标签输入框背景颜色 - 默认
$color-tagInput_danger-border-defaultvar(--semi-color-danger-light-default)危险标签输入框描边颜色 - 默认
$color-tagInput_danger-bg-hovervar(--semi-color-danger-light-hover)危险标签输入框背景颜色 - 悬浮
$color-tagInput_danger-border-hovervar(--semi-color-danger-light-hover)危险标签输入框描边颜色 - 悬浮
$color-tagInput_danger-bg-focusvar(--semi-color-danger-light-default)危险标签输入框背景颜色 - 选中
$color-tagInput_danger-border-focusvar(--semi-color-danger)危险标签输入框描边颜色 - 选中
$color-tagInput_sortable_item_over-bgvar(--semi-color-primary)拖拽经过的元素前竖线背景色
$color-tagInput_handler-icon-defaultvar(--semi-color-text-2)可拖拽的标签拖拽按钮颜色
$spacing-tagInput_small-Y1px小尺寸标签输入框标签顶部外边距
$spacing-tagInput_default-Y$spacing-super-tight默认尺寸标签输入框标签顶部外边距
$spacing-tagInput_large-Y3px大尺寸标签输入框标签顶部外边距
$spacing-tagInput_wrapper_n_paddingX$spacing-tight标签输入框标签容器水平内边距
$spacing-tagInput_drag_handler-marginRight4px拖拽handler icon的右外边距
$spacing-tagInput_tag_icon_paddingLeft4pxtag中有handler icon时tag的左内边距
$spacing-tagInput_prefix_suffix-marginX$spacing-base-tight标签输入框 prefix/suffix 水平外边距
$height-tagInput-large$height-control-large大尺寸标签输入框高度
$height-tagInput-default$height-control-default默认尺寸标签输入框高度
$height-tagInput-small$height-control-small小尺寸标签输入框高度
$height-tagInput_input_small20px小尺寸标签输入框Input框高度
$height-tagInput_input_default24px默认尺寸标签输入框Input框高度
$height-tagInput_input_large24px大尺寸标签输入框Input框高度
$width-tagInput-clear-medium$width-icon-medium * 2标签输入框清空按钮宽度
$width-tagInput-border-default$border-thickness-control标签输入框描边描边宽度 - 默认
$width-tagInput-border-hover$width-tagInput-border-default标签输入框描边描边宽度 - 悬浮
$width-tagInput-border-focus$border-thickness-control-focus标签输入框描边宽度 - 选中态
$width-tagInput_sortable_item_over2px拖拽经过的元素前竖线宽度
$radius-tagInputvar(--semi-border-radius-small)标签输入框圆角
$font-tagInput_prefix_suffix-fontWeight$font-weight-bold标签输入框 prefix/suffix 文字字重