@douyinfe/semi-ui
Version:
A modern, comprehensive, flexible design system and UI library. Connect DesignOps & DevOps. Quickly build beautiful React apps. Maintained by Douyin-fe team.
463 lines (377 loc) • 17.1 kB
Markdown
---
localeCode: zh-CN
order: 49
category: 输入类
title: TagInput 标签输入框
icon: doc-tagInput
brief: 标签输入框能够将输入的内容生成标签。
---
## 代码演示
### 如何引入
```jsx import
import { TagInput } from '@douyinfe/semi-ui';
```
### 基本演示
敲击回车键后,输入内容将成为标签。标签内容如果为空串或者纯空格时,则会被过滤。
```jsx live=true
import React from 'react';
import { TagInput } from '@douyinfe/semi-ui';
() => (
<TagInput
defaultValue={['抖音', '火山', '西瓜视频']}
placeholder='请输入...'
onChange={v => console.log(v)}
/>
);
```
### 批量添加
可以使用 `separator` 设置分隔符,来实现批量输入,它的默认值为英文逗号。支持多个分隔符以 string[] 格式传入。
```jsx live=true
import React from 'react';
import { TagInput } from '@douyinfe/semi-ui';
() => (
<>
<TagInput
separator='-'
placeholder='使用 - 进行批量输入'
onChange={v => console.log(v)}
/>
<br/><br/>
<TagInput
separator={['-', '/', '|', '++']}
placeholder='支持多个分隔符进行批量输入'
onChange={v => console.log(v)}
/>
</>
);
```
### 批量删除
可使用 `showClear` 设置是否支持一键删除所有标签和输入框内容。
```jsx live=true
import React from 'react';
import { TagInput } from '@douyinfe/semi-ui';
() => (
<TagInput
showClear
defaultValue={['抖音', '火山']}
placeholder='请输入...'
onChange={v => console.log(v)}
/>
);
```
### 禁用
```jsx live=true
import React from 'react';
import { TagInput } from '@douyinfe/semi-ui';
() => (
<TagInput
disabled
showClear
defaultValue={['抖音', '火山', '西瓜视频']}
placeholder='请输入...'
/>
);
```
### 尺寸大小
通过 `size` 控制标签输入框的大小尺寸,可选: `small` 、 `default` 、 `large`。
```jsx live=true
import React from 'react';
import { TagInput } from '@douyinfe/semi-ui';
() => (
<>
<TagInput size='small' placeholder='small'/>
<br/><br/>
<TagInput placeholder='default'/>
<br/><br/>
<TagInput size='large' placeholder='large'/>
</>
);
```
### 不同校验状态样式
可以使用 `validateStatus` 设置不同校验状态的样式,它仅影响背景颜色等样式表现,可选值: `default` 、 `warning` 、 `error`。
```jsx live=true
import React from 'react';
import { TagInput } from '@douyinfe/semi-ui';
() => (
<>
<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中自行设置。
```jsx live=true
import React from 'react';
import { TagInput } from '@douyinfe/semi-ui';
import { IconVigoLogo, IconGift } from '@douyinfe/semi-icons';
() => (
<>
<TagInput prefix={<IconVigoLogo />} />
<br/><br/>
<TagInput prefix="Prefix" />
<br/><br/>
<TagInput suffix={<IconGift />} />
<br/><br/>
<TagInput suffix="Suffix" />
</>
);
```
### 失焦后自动创建标签
可使用 `addOnBlur`,设置是否在 blur 事件触发时,将当前 input 的值自动创建成 tag。
```jsx live=true
import React from 'react';
import { TagInput } from '@douyinfe/semi-ui';
() => (
<TagInput
defaultValue={['抖音', '火山', '西瓜视频']}
addOnBlur={true}
placeholder='请输入...'
onChange={v => console.log(v)}
/>
);
```
### 过滤重复标签
可使用 `allowDuplicates`,设置是否允许创建相同 tag,默认为 true。
```jsx live=true
import React from 'react';
import { TagInput } from '@douyinfe/semi-ui';
() => (
<TagInput
defaultValue={['抖音', '火山', '西瓜视频']}
allowDuplicates={false}
placeholder='请输入...'
onChange={v => console.log(v)}
/>
);
```
### 输入限制
可使用 `max` 限制输入的标签数量,超出后将不允许再输入,并且触发 `onExceed()` 回调。
可使用 `maxLength` 限制单个标签的最大长度,超出后将不允许再输入,并且触发 `onInputExceed()` 回调。
```jsx live=true
import React from 'react';
import { TagInput } from '@douyinfe/semi-ui';
() => (
<>
<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`。
```jsx live=true
import React from 'react';
import { TagInput } from '@douyinfe/semi-ui';
() => (
<TagInput
maxTagCount={2}
showRestTagsPopover={true}
restTagsPopoverProps={{ position: 'top' }}
defaultValue={['抖音', '火山', '西瓜视频']}
onChange={v => console.log(v)}
/>
);
```
### 标签受控
可使用 `value` 设置标签内容,并配合 `onChange` 实现标签内容受控。
```jsx live=true hideInDSM
import React, { useState } from 'react';
import { TagInput } from '@douyinfe/semi-ui';
function TagInputDemo() {
const [value, setValue] = useState(['抖音']);
const handleChange = v => setValue(v);
return <TagInput value={value} onChange={handleChange} />;
}
```
### 输入受控
可使用 `inputValue` 设置输入框内容,并配合 `onInputChange` 实现输入内容受控。
```jsx live=true hideInDSM
import React, { useState } from 'react';
import { TagInput } from '@douyinfe/semi-ui';
function TagInputDemo() {
const [value, setValue] = useState('abc');
const handleInputChange = (v, e) => setValue(v);
return <TagInput inputValue={value} onInputChange={handleInputChange} />;
}
```
### 回调
```jsx live=true
import React from 'react';
import { TagInput } from '@douyinfe/semi-ui';
() => (
<TagInput
defaultValue={['抖音']}
placeholder='请输入...'
showClear
onFocus={e => {console.log(`onFocus`);}}
onBlur={e => {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, e) => {console.log(`onInputChange,当前输入内容:${input}`);}}
/>
);
```
### 焦点管理
可以使用 `blur()` 和 `focus()` 方法对焦点进行管理。
```jsx live=true hideInDSM
import React, { useRef } from 'react';
import { TagInput, Button } from '@douyinfe/semi-ui';
function TagInputDemo() {
const ref = useRef();
const handleTagInputFocus = () => {
ref.current && 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` 第三个参数 `onClose` 于 2.23.0 版本开始提供。
```jsx live=true
import React, { useMemo, useState } from 'react';
import { TagInput, Avatar } from '@douyinfe/semi-ui';
import { IconClose } from '@douyinfe/semi-ui-icons';
function CustomRender() {
const [value, setValue] = useState(['夏可漫']);
const list = useMemo(() => ([
{ "name": "夏可漫", "avatar": "https://sf6-cdn-tos.douyinstatic.com/obj/eden-cn/ptlz_zlp/ljhwZthlaukjlkulzlp/root-web-sites/avatarDemo.jpeg" },
{ "name": "申悦", "avatar": "https://sf6-cdn-tos.douyinstatic.com/obj/eden-cn/ptlz_zlp/ljhwZthlaukjlkulzlp/root-web-sites/bf8647bffab13c38772c9ff94bf91a9d.jpg" },
{ "name": "曲晨一", "avatar": "https://sf6-cdn-tos.douyinstatic.com/obj/eden-cn/ptlz_zlp/ljhwZthlaukjlkulzlp/root-web-sites/dbf7351bb779433d17c4f50478cf42f7.jpg" },
{ "name": "文嘉茂", "avatar": "https://sf6-cdn-tos.douyinstatic.com/obj/eden-cn/ptlz_zlp/ljhwZthlaukjlkulzlp/root-web-sites/7abf810ff060ac3387bd027ead92c4e0.jpg" },
]), []);
const mapList = useMemo(() => new Map(list.map(item => [item.name, item])), [list]);
const renderTagItem = (value, index, onClose) => {
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 : 'https://lf3-static.bytednsdoc.com/obj/eden-cn/ptlz_zlp/ljhwZthlaukjlkulzlp/root-web-sites/dy.png'}
size="extra-small"
/>
<span style={{ marginLeft: 8 }}>
{`${value}@semi.com`}
</span>
<IconClose style={{ paddingLeft: 4, color: 'var( --semi-color-text-3)' }} size="small" onClick={onClose} />
</div>
);
};
return (
<TagInput
value={value}
onChange={setValue}
renderTagItem={renderTagItem}
/>
);
}
```
### 拖拽排序
将 `draggable`设为 true,开启拖拽排序功能。v2.17.0 后支持。拖拽排序下不允许添加相同 Tag, 因此需要将 `allowDuplicates` 设置为 false。
拖拽功能开启后,点击 TagInput,Tag 可拖拽。点击 TagInput 外任意区域,Tag 不可拖拽。
```jsx live=true
import React from 'react';
import { TagInput } from '@douyinfe/semi-ui';
() => (
<TagInput
draggable
allowDuplicates={false}
defaultValue={['抖音', '火山', '西瓜视频']}
placeholder='请输入...'
onChange={v => console.log(v)}
/>
);
```
## API 参考
|属性 |说明 |类型 |默认值 |版本 |
|-------------|-------------------------------------------------|-------------------------------|----------|--------|
|addOnBlur |是否在 blur 事件触发时,将当前 input 的值自动创建成 tag |boolean | false |-|
|allowDuplicates|是否允许添加相同 tag |boolean | true |-|
|autoFocus |初始渲染时是否自动 focus |boolean | false |-|
|className |样式类名 |string | - |-|
|defaultValue |初始标签 |string[] | - |-|
|disabled |是否禁用 |boolean |false |-|
|inputValue |当前输入框,配合 onInputChange 实现受控 |string | - |-|
|maxLength |单个标签的最大长度 |number | - |-|
|max |允许标签的最大数量 |number | - |-|
|maxTagCount |标签的最大展示数量,超出后将以 +N 形式展示 |number | - |-|
|showRestTagsPopover |当超过 maxTagCount,hover 到 +N 时,是否通过 Popover 显示剩余内容 |boolean | true |-|
|restTagsPopoverProps |Popover 的配置属性,可以控制弹出方向、zIndex、trigger等,具体参考[Popover](/zh-CN/show/popover#API_参考) |PopoverProps | {} |-|
|showContentTooltip |当标签长度过长发生截断时,hover 标签的时候,是否通过 Tooltip 显示全部内容, 支持 Tooltip\|Popover;opts,其他需要透传给浮层组件的属性 |boolean\|{type: 'tooltip'\|'popover', opts: object} | true |-|
|placeholder |占位默认值 |string | - |-|
|prefix |前缀标签 |ReactNode |- |-|
| preventScroll | 指示浏览器是否应滚动文档以显示新聚焦的元素,作用于组件内的 focus 方法 | boolean | | |
|renderTagItem|自定义标签渲染, 参数 onClose 于版本2.23.0版本提供 | <ApiType detail='(value: string, index: number, onClose: function) => React.ReactNode'>(params) => React.ReactNode</ApiType> | - |-|
|separator |设置批量输入时的分隔符 |string\|string[] |, |-|
|showClear |是否支持一键删除所有标签和输入内容 |boolean |false |-|
|size |设置输入框尺寸,可选: `small`、`large`、`default` |string |`default` |-|
|split |自定义分隔符处理函数 |(value: string, separator: string) => string[] | - |2.90.0|
|style |内联样式 |React.CSSProperties | - |-|
|suffix |后缀标签 |ReactNode |- |-|
|validateStatus|设置校验状态样式,可选: `default`、`warning`、`error` |string |`default` |-|
|value |当前标签,配合 onChange 实现受控 |string[] \| undefined | - |-|
|draggable |设置是否可拖拽 |boolean |false |2.17.0|
|expandRestTagsOnClick| 在不可拖拽的情况下,在 TagInput 被点击后是否展开多余的 Tag |boolean |true |2.17.0|
|onAdd |添加标签时的回调 |(addedValue: string[]) => void | - |-|
|onBlur |输入框失去焦点时的回调 |(e:React.MouseEvent) => void | - |-|
|onChange |标签变化时的回调 |(value:string[]) => void | - |-|
|onExceed |超过 max 时的回调 |(value:string[]) => void | - |-|
|onFocus |输入框获取焦点时的回调 |(e:React.MouseEvent) => void | - |-|
|onInputChange|输入框内容变化时的回调 |(value:string,e: React.KeyboardEvent) => void) | - |-|
|onInputExceed|超过 maxLength 时的回调 |(value:string) => void | - |-|
|onKeyDown |keydown 回调 |(e: React.KeyboardEvent) => void | - |2.1.0|
|onRemove |移除标签时的回调 |(removedValue: string, idx: number) => void | - |-|
## Methods
绑定在组件实例上的方法,可以通过 ref 调用实现某些特殊交互
|名称 |描述 |版本 |
|-------|------|---------|
|blur() |移出焦点|-|
|focus()|获取焦点|-|
## Accessibility
### ARIA
- TagInput 支持传入 `aria-label` 来表示该 TagInput 作用;
- TagInput 会依据 disabled 及 validateStatus props 来分别设置 `aria-disabled`、`aria-invalid`;
- TagInput 的输入框和清空按钮均具有 `aria-label` 来表明元素作用。
## 设计变量
<DesignToken/>
<!-- ## 相关物料
```material
192,176
``` -->