Reka UI TagsInputInput 组件:标签输入框的 Props、键盘交互与源码级解析
2026/9/18 3:42:36 网站建设 项目流程

Reka UI TagsInputInput 组件:标签输入框的 Props、键盘交互与源码级解析

【免费下载链接】radix-vueAn open-source UI component library for building high-quality, accessible design systems and web apps for Vue. Previously Radix Vue项目地址: https://gitcode.com/GitHub_Trending/ra/radix-vue

导读

TagsInputInput是 Reka UI(即本仓库 radix-vue,组件库源码位于 packages/core/src/TagsInput)标签输入组件(Tags Input)中的核心文本输入部件:它负责接收用户键入的标签文本,并把"回车添加标签、粘贴批量添加、失焦添加、Tab 添加"等交互转化为根组件TagsInputRoot的标签状态更新。读完本文,你将掌握TagsInputInput的每一个 Props 的含义与默认值、它与根组件上下文(Context)的协作方式,以及Enter / Tab / Backspace / Delete / 方向键等键盘交互在源码中是如何实现的,从而能在自己的 Vue 项目中正确、高效地使用并定制标签输入。

定位与职责:输入部件如何与根组件协作

Tags Input 组件由多个部件组成:TagsInputRoot(容器,管理标签数组状态)、TagsInputItem(单个标签)、TagsInputItemText(标签文本)、TagsInputItemDelete(删除按钮)、TagsInputInput(文本输入框)以及TagsInputClear(清空按钮)。组件整体结构可参考文档 docs/content/docs/components/tags-input.md 中的 Anatomy 一节:

<script setup> import { TagsInputClear, TagsInputInput, TagsInputItem, TagsInputItemDelete, TagsInputItemText, TagsInputRoot } from 'reka-ui' </script> <template> <TagsInputRoot> <TagsInputItem> <TagsInputItemText /> <TagsInputItemDelete /> </TagsInputItem> <TagsInputInput /> <TagsInputClear /> </TagsInputRoot> </template>

从源码结构看,TagsInputInput本身不保存标签数据,它通过injectTagsInputRootContext()注入根组件通过 provideTagsInputRootContext 提供的上下文(Context),然后调用其中的onAddValueonInputKeydown等回调,把输入事件转化为根组件的modelValue更新。这种"子部件只负责输入事件采集、根组件负责状态管理"的架构,与 Reka UI 其他复合组件(如 Combobox、Select)保持一致。

Props 完整说明

TagsInputInput的 Props 定义位于 TagsInputInput.vue,继承自PrimitiveProps,共 5 个属性:

NameDescriptionTypeRequiredDefault
as组件要渲染成的元素或组件,可被asChild覆盖。AsTag \| ComponentNo"input"
asChild将默认渲染元素替换为传入的子元素,并合并其 props 与行为。booleanNo-
autoFocus挂载时自动聚焦该元素。booleanNo-
maxLength允许输入的最大字符数。numberNo-
placeholder空标签输入时的占位字符。stringNo-

asasChild:Composition(组合)能力

这是 Reka UI 所有基础部件的通用能力。as默认值为'input',即默认渲染一个原生<input>;通过as可改渲染成其他标签或组件,通过asChild则可以把自定义子元素作为实际渲染节点(例如用无样式的input子元素包裹自定义图标)。这一机制在 Primitive 中实现,细节可参考仓库文档 docs/content/docs/guides 下的 Composition 指南。

autoFocus:挂载后自动聚焦

源码中,autoFocusonMounted钩子里实现(TagsInputInput.vue):先查找实际渲染的<input>节点(考虑到asChild场景),然后通过setTimeout(..., 1)延迟一个宏任务,确保 DOM 完全刷新后再调用inputEl.focus()

onMounted(() => { const inputEl = currentElement.value.nodeName === 'INPUT' ? currentElement.value : currentElement.value.querySelector('input') if (!inputEl) return setTimeout(() => { // make sure all DOM was flush then only capture the focus if (props.autoFocus) inputEl?.focus() }, 1) })

maxLength:单次输入长度上限

直接透传给原生inputmaxlength属性(模板中:maxlength="maxLength")。注意它与根组件的max(标签总数上限)是两个不同的维度:maxLength限制正在输入的文本长度max限制已添加的标签数量

placeholder:空输入提示

透传给原生inputplaceholder。仓库中的示例用法见 TagsInput/story/_TagsInput.vue:

<TagsInputInput placeholder="Anything..." class="focus:outline-none flex-1 rounded bg-transparent text-white placeholder:text-mauve10 px-1" />

数据属性(Data Attributes)

TagsInputInput会向渲染节点输出以下数据属性,便于样式定制与测试:

AttributeValues
[data-invalid]Present when input value is invalid

当输入重复标签或超过max上限时,根组件会把isInvalidInput置为trueTagsInputInput的模板中对应渲染:data-invalid="context.isInvalidInput.value ? '' : undefined"(TagsInputInput.vue)。搭配 CSS 选择器[data-invalid]即可实现输入非法值的红色描边等反馈。

输入交互的源码级解析

TagsInputInput的核心价值在于它把 5 类输入事件与根组件的标签管理逻辑挂钩(模板绑定见 TagsInputInput.vue)。

Enter 回车添加标签

@keydown.enter="handleCustomKeydown"在按下回车时调用。handleCustomKeydown(TagsInputInput.vue)的逻辑:

  1. 若正处于输入法组合(isComposing)阶段则跳过(避免拼音/日文输入法候选选中干扰);
  2. await nextTick()后检查event.defaultPrevented——若用户自定义了@keydown.enter.prevent,则尊重用户逻辑,不再自动添加;
  3. 输入值为空则直接返回;
  4. 调用context.onAddValue(target.value)尝试添加,成功(返回true)后清空输入框;
  5. event.preventDefault()阻止默认行为,避免组件处于<form>中时触发表单提交。

Tab 添加标签

@keydown.tab="handleTab":当根组件开启addOnTab时,Tab 键行为与回车一致(调用同一个handleCustomKeydown);否则走原生 Tab 焦点切换(TagsInputInput.vue)。

Blur 失焦添加

@blur="handleBlur"(TagsInputInput.vue)在根组件开启addOnBlur时生效,且有两处防御逻辑:

  • 先清除当前选中的标签(selectedElement);
  • 如果失焦是因为点击了下拉内容(如与 Combobox 组合时的候选项)导致(通过aria-controlsrelatedTarget.closest判断),则不触发添加——因为被点击的候选项应作为新标签添加,而不是把输入框当前值添加进去;
  • 输入为空则直接返回。

Paste 粘贴批量添加

@paste="handlePaste"(TagsInputInput.vue)在根组件开启addOnPaste时,阻止默认粘贴行为,读取剪贴板文本,并用delimiter(分隔符)切分后逐段调用onAddValue批量添加标签。例如粘贴"vue,react,svelte",在默认逗号分隔符下会一次生成三个标签。

Delimiter 分隔符即时触发

@input="handleInput"(TagsInputInput.vue)监听每次输入:若当前输入字符匹配delimiter(支持字符串或正则),先把该字符从输入值中剔除,再尝试把前面已输入的内容作为标签添加。这样用户输入"vue,"时,vue会立即成为标签,而逗号不会残留在输入框里。输入法组合期间该逻辑同样被跳过。

方向键与删除键:标签键盘导航

@keydown="handleInputKeydown"把键盘事件转发给根组件的onInputKeydown(TagsInputRoot.vue),实现了完整的标签键盘导航:

  • Backspace:光标位于输入框首位时,先选中最后一个标签;若已有选中标签则删除它并把选中态移到左侧标签(左无标签则回到输入框);
  • Delete:标签处于激活态时删除当前标签,并把激活态移到右侧标签;
  • ArrowLeft:光标在首位时选中上一个标签(RTL 下方向反转);
  • ArrowRight:在最后一个标签上按右方向键则取消选中、回到输入框;
  • Home/End:激活第一个 / 最后一个标签。

这些行为与文档 docs/content/docs/components/tags-input.md 的 Keyboard Interactions 一节描述的键位一一对应。仓库测试 TagsInput.test.ts 中覆盖了"回车添加标签后清空输入框、addTag事件发出、ArrowLeft 选中标签、data-state 在 active/inactive 间切换"等行为,可当作行为契约参考。

与根组件 Props 的联动要点

TagsInputInput的行为受根组件TagsInputRoot若干 Props 驱动,使用时应组合配置(完整参数见 TagsInputRoot.vue 与 docs/content/meta/TagsInputRoot.md):

Root 参数类型默认值对 Input 的影响
addOnBlurboolean-失焦时把输入框内容添加为标签
addOnPasteboolean-粘贴时按分隔符切分并批量添加
addOnTabboolean-Tab 键等同回车添加标签
delimiterstring \| RegExp","输入/粘贴时的分隔触发与切分规则
disabledboolean-禁用输入框(透传disabled并输出data-invalid相关状态)
duplicateboolean-允许重复标签;否则重复输入会被判定为 invalid
maxnumber0标签数量上限(0表示不限),超限触发invalid事件
convertValue(value: string) => T-使用对象作标签值时,把输入字符串转换为对象,必填
displayValue(value: T) => stringvalue.toString()自定义标签显示文本
modelValue/defaultValueT[][]受控 / 非受控的标签数组

典型组合示例

粘贴自动添加(来自文档 docs/content/docs/components/tags-input.md 的 Paste behavior 示例):

<script setup lang="ts"> import { TagsInputInput, TagsInputItem, TagsInputItemDelete, TagsInputItemText, TagsInputRoot } from 'reka-ui' </script> <template> <TagsInputRoot v-model="modelValue" add-on-paste> … </TagsInputRoot> </template>

多分隔符正则(来自文档的 Multiple delimiters 示例,逗号、分号、空格、制表符、换行均可触发添加):

<script setup lang="ts"> import { TagsInputInput, TagsInputItem, TagsInputItemDelete, TagsInputItemText, TagsInputRoot } from 'reka-ui' // split by space, comma, semicolon, tab, or newline const delimiter = /[ ,;\t\n\r]+/ </script> <template> <TagsInputRoot v-model="modelValue" :delimiter="delimiter" add-on-paste> … </TagsInputRoot> </template>

对象标签值:当标签值是对象时(参考 story 目录下的 TagsInputObject.story.vue),必须同时提供convertValue(把输入字符串转成对象)与displayValue(把对象渲染成可读文本);否则根组件会抛出错误:You must provide a convertValue function when using objects as values.(见 TagsInputRoot.vue)。

键盘交互一览

TagsInputInput聚焦时支持的完整键盘操作(对应 docs/content/docs/components/tags-input.md 的 Accessibility 章节):

按键行为
Enter把当前输入内容添加为标签(输入法组合期间忽略)
Tab开启addOnTab时等同回车添加
Backspace光标在首位时:先选中最后一个标签;标签激活时删除并激活左侧标签
Delete标签激活时删除当前标签并激活右侧标签
ArrowLeft/ArrowRight在标签间移动激活态(RTL 方向自动反转),最后一个标签上按右方向回到输入框
Home/End激活第一个 / 最后一个标签

总结

TagsInputInput是 Reka UI Tags Input 的"输入入口":它自身只负责事件采集与转发,通过 Context 把回车、Tab、失焦、粘贴、分隔符输入、方向键等交互交给根组件TagsInputRoot统一处理标签状态。实际使用时,请记住三组关键配置:

  1. 添加时机addOnTab/addOnBlur/addOnPaste三个布尔 Props 控制何时把输入内容转为标签;
  2. 分隔规则delimiter(字符串或正则)同时作用于输入即时触发与粘贴批量切分;
  3. 约束校验maxLength限制单次输入长度,根组件的max限制标签总数,duplicate控制是否允许重复,非法输入会通过[data-invalid]invalid事件暴露给开发者。

深入源码可继续阅读 TagsInputInput.vue、TagsInputRoot.vue 以及行为测试 TagsInput.test.ts,或参考完整组件文档 docs/content/docs/components/tags-input.md。

【免费下载链接】radix-vueAn open-source UI component library for building high-quality, accessible design systems and web apps for Vue. Previously Radix Vue项目地址: https://gitcode.com/GitHub_Trending/ra/radix-vue

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询