'use client'
import { useState } from 'react'
import { TagsInput } from '@hina-ui/react'
export default function Demo() {
const [tags, setTags] = useState(['Galgame', '轻小说'])
return (
<TagsInput
value={tags}
onValueChange={setTags}
placeholder="添加标签"
aria-label="作品标签"
className="max-w-sm"
/>
)
}
tsx
用法
import { TagsInput } from '@hina-ui/react'
ts
标签输入框在一个输入面里保存多个值,每个值显示为一个可移除的标签。value / onValueChange 绑定字符串数组。在输入区输入文字后按 Enter,或者输入分隔符,文字就会添加为标签;标签一行放不下时,输入面的高度随之增加。未声明的属性都会传给内部的文本输入,请用 aria-label 或者 aria-labelledby 命名。
还没有标签
'use client'
import { useState } from 'react'
import { Stack, TagsInput, Text } from '@hina-ui/react'
export default function Demo() {
const [tags, setTags] = useState<string[]>([])
return (
<Stack gap="sm" className="w-full max-w-sm">
<TagsInput
value={tags}
onValueChange={setTags}
placeholder="输入后按 Enter"
aria-label="关键词"
/>
<Text tone="muted" size="sm">
{tags.length ? tags.join('、') : '还没有标签'}
</Text>
</Stack>
)
}
tsx
它与 MultiSelect 的区别在于值的来源:多选选择器从给定的选项中挑选,标签输入框接受任意文字。
示例
数量上限与重复
max 限制标签数量,duplicate 决定是否允许重复。被拒绝的输入会调用 onInvalid,参数是被拒绝的文字。
最多三个,不允许重复
'use client'
import { useState } from 'react'
import { Stack, TagsInput, Text } from '@hina-ui/react'
export default function Demo() {
const [tags, setTags] = useState(['日常', '恋爱'])
const [rejected, setRejected] = useState('')
return (
<Stack gap="sm" className="w-full max-w-sm">
<TagsInput
value={tags}
onValueChange={setTags}
max={3}
placeholder="最多三个"
aria-label="题材"
onInvalid={setRejected}
/>
<Text tone="muted" size="sm">
{rejected ? `「${rejected}」未加入` : '最多三个,不允许重复'}
</Text>
</Stack>
)
}
tsx
分隔符与粘贴
delimiter 默认为逗号,可以换成其他字符或者正则表达式。addOnPaste 默认开启,粘贴的文字按分隔符拆分为多个标签;addOnBlur 让输入区失去焦点时也把剩余文字添加为标签。
'use client'
import { useState } from 'react'
import { TagsInput } from '@hina-ui/react'
export default function Demo() {
const [tags, setTags] = useState<string[]>([])
return (
<TagsInput
value={tags}
onValueChange={setTags}
delimiter=" "
addOnBlur
placeholder="以空格分隔,可以整段粘贴"
aria-label="别名"
className="max-w-sm"
/>
)
}
tsx
可清空
clearable 在末尾显示清除按钮,一次移除全部标签,并调用 onClear。
'use client'
import { useState } from 'react'
import { TagsInput } from '@hina-ui/react'
export default function Demo() {
const [tags, setTags] = useState(['Key', 'Type-Moon', 'Nitroplus'])
return (
<TagsInput
value={tags}
onValueChange={setTags}
clearable
placeholder="添加制作公司"
aria-label="制作公司"
className="max-w-sm"
/>
)
}
tsx
尺寸
size 有 sm、md、lg 三档,没有标签时的高度与同档输入框相等。
import { Stack, TagsInput } from '@hina-ui/react'
export default function Demo() {
return (
<Stack gap="sm" className="w-full max-w-sm">
<TagsInput size="sm" defaultValue={['小号']} aria-label="小号" />
<TagsInput size="md" defaultValue={['中号']} aria-label="中号" />
<TagsInput size="lg" defaultValue={['大号']} aria-label="大号" />
</Stack>
)
}
tsx
状态
invalid 给输入面加上警示色,disabled 禁用整组,标签不可移除。variant="secondary" 是放在 surface 之内的扁平形态。
import { Stack, TagsInput } from '@hina-ui/react'
export default function Demo() {
return (
<Stack gap="sm" className="w-full max-w-sm">
<TagsInput defaultValue={['校验未通过']} invalid aria-label="校验未通过" />
<TagsInput defaultValue={['已禁用']} disabled aria-label="已禁用" />
<TagsInput defaultValue={['扁平形态']} variant="secondary" aria-label="扁平形态" />
</Stack>
)
}
tsx
在表单中
放进 FormField 后,标签指向输入区,说明与错误信息由字段渲染;校验规则与提交交给 Form。值是字符串数组,数量与单个标签的长度分别写在数组层与元素层。
'use client'
import { useState } from 'react'
import * as v from 'valibot'
import { Button, Form, FormField, TagsInput, Text } from '@hina-ui/react'
const schema = v.object({
aliases: v.pipe(
v.array(v.pipe(v.string(), v.maxLength(20, '别名不超过 20 个字'))),
v.minLength(1, '至少填写一个别名'),
v.maxLength(5, '最多五个别名'),
),
})
export default function Demo() {
const [values, setValues] = useState({ aliases: [] as string[] })
const [saved, setSaved] = useState('')
async function save(data: unknown) {
await new Promise(resolve => setTimeout(resolve, 600))
setSaved(JSON.stringify(data))
}
return (
<Form values={values} rules={schema} className="w-80" onSubmit={save}>
{({ submitting }) => (
<>
<FormField name="aliases" label="别名" description="回车添加,最多五个" required>
<TagsInput
value={values.aliases}
onValueChange={aliases => setValues({ ...values, aliases })}
/>
</FormField>
<Button type="submit" loading={submitting} className="self-start">
保存
</Button>
{saved && (
<Text tone="muted" size="sm">
已保存:{saved}
</Text>
)}
</>
)}
</Form>
)
}
tsx
行为
- 按 Enter 或者输入分隔符,把当前文字添加为标签并清空输入区。
- 输入区为空时按退格移除末尾的标签。
- 点击标签或者输入面的空白处即聚焦输入区。
- 每个标签末尾有移除按钮,点击即移除。
- 超过
max或者重复的值不会加入,调用onInvalid。
无障碍
- 文本输入通过
aria-label或者aria-labelledby命名,未声明的属性都会传给它。 - 每个标签的移除按钮带有语言包给出的名称。
invalid会同时在文本输入上设置aria-invalid。
API
Props
| 属性 | 类型 | 默认值 | 说明 |
|---|---|---|---|
value | string[] | [] | 标签列表 |
placeholder | string | — | 输入区的占位文字 |
max | number | 0 | 标签数量上限, 0 表示不限 |
duplicate | boolean | false | 是否允许重复的标签 |
delimiter | string | RegExp | ',' | 分隔符 |
addOnPaste | boolean | true | 粘贴时是否按分隔符拆分为标签 |
addOnBlur | boolean | false | 失去焦点时是否把剩余文字添加为标签 |
clearable | boolean | false | 是否显示清除按钮 |
name | string | — | 表单字段名 |
variant | 'primary' | 'secondary' | 'primary' | 形态 |
size | 'sm' | 'md' | 'lg' | 'md' | 尺寸 |
disabled | boolean | false | 是否禁用 |
invalid | boolean | false | 是否处于校验未通过状态 |
className | string | — | 追加至根元素的类名 |
回调
| 回调 | 参数 | 说明 |
|---|---|---|
onValueChange | value: string[] | 标签列表变化 |
onInvalid | value: string | 输入被拒绝 |
onClear | — | 全部清空 |