TagsInput 标签输入框

输入并以标签形式保存多个值。

Galgame轻小说
'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。

KeyType-MoonNitroplus
'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
—
全部清空