标签输入 tags-input
数据录入组件。三层同源:无头内核给出解剖与状态机,Vue 组件与自定义元素只是它的两层外壳,行为完全一致。
示例
基础用法
框里打字按 Enter 落一个标签;标签由作者按当前值渲染,v-for 的 key 必须给
当前:Vue、TypeScript
<script setup lang="ts">
import { ref } from "vue";
import {
XhTagsInputControl,
XhTagsInputInput,
XhTagsInputItem,
XhTagsInputItemDeleteTrigger,
XhTagsInputItemPreview,
XhTagsInputItemText,
XhTagsInputLabel,
XhTagsInputRoot,
} from "@xihan-ui/vue";
const tags = ref<string[]>(["Vue", "TypeScript"]);
</script>
<template>
<XhTagsInputRoot
v-slot="{ value }"
v-model:value="tags"
placeholder="回车落一个"
style="max-inline-size: 420px"
>
<XhTagsInputLabel>技术栈</XhTagsInputLabel>
<XhTagsInputControl>
<XhTagsInputItem v-for="t in value" :key="t" :value="t">
<XhTagsInputItemPreview>
<XhTagsInputItemText>{{ t }}</XhTagsInputItemText>
<XhTagsInputItemDeleteTrigger>×</XhTagsInputItemDeleteTrigger>
</XhTagsInputItemPreview>
</XhTagsInputItem>
<XhTagsInputInput />
</XhTagsInputControl>
</XhTagsInputRoot>
<p>当前:{{ tags.length ? tags.join("、") : "(无)" }}</p>
</template>上限与粘贴拆分
add-on-paste 让粘进来的一串按分隔符拆成多个标签;顶到 max 后再打再粘都进不去
<script setup lang="ts">
import { ref } from "vue";
import {
XhTagsInputClearTrigger,
XhTagsInputControl,
XhTagsInputInput,
XhTagsInputItem,
XhTagsInputItemDeleteTrigger,
XhTagsInputItemPreview,
XhTagsInputItemText,
XhTagsInputLabel,
XhTagsInputRoot,
} from "@xihan-ui/vue";
const tags = ref<string[]>(["Vue"]);
</script>
<template>
<XhTagsInputRoot
v-slot="{ value, count, atMax }"
v-model:value="tags"
:max="4"
add-on-paste
delimiter=","
placeholder="试试粘贴 React,Svelte,Solid"
style="max-inline-size: 420px"
>
<XhTagsInputLabel>技术栈(最多 4 个)</XhTagsInputLabel>
<XhTagsInputControl>
<XhTagsInputItem v-for="t in value" :key="t" :value="t">
<XhTagsInputItemPreview>
<XhTagsInputItemText>{{ t }}</XhTagsInputItemText>
<XhTagsInputItemDeleteTrigger>×</XhTagsInputItemDeleteTrigger>
</XhTagsInputItemPreview>
</XhTagsInputItem>
<XhTagsInputInput />
<XhTagsInputClearTrigger>⨯</XhTagsInputClearTrigger>
</XhTagsInputControl>
<span>{{ count }} / 4{{ atMax ? " · 已到上限" : "" }}</span>
</XhTagsInputRoot>
</template>就地编辑
editable 打开后双击任一标签改写它:Enter 提交、Escape 撤销,改成空白等于删掉这个标签
当前:前端、组件库、无障碍
<script setup lang="ts">
import { ref } from "vue";
import {
XhTagsInputControl,
XhTagsInputInput,
XhTagsInputItem,
XhTagsInputItemDeleteTrigger,
XhTagsInputItemInput,
XhTagsInputItemPreview,
XhTagsInputItemText,
XhTagsInputLabel,
XhTagsInputRoot,
} from "@xihan-ui/vue";
const tags = ref<string[]>(["前端", "组件库", "无障碍"]);
</script>
<template>
<XhTagsInputRoot
v-slot="{ value }"
v-model:value="tags"
editable
placeholder="回车落一个"
style="max-inline-size: 420px"
>
<XhTagsInputLabel>标签</XhTagsInputLabel>
<XhTagsInputControl>
<XhTagsInputItem v-for="t in value" :key="t" :value="t">
<XhTagsInputItemPreview>
<XhTagsInputItemText>{{ t }}</XhTagsInputItemText>
<XhTagsInputItemDeleteTrigger>×</XhTagsInputItemDeleteTrigger>
</XhTagsInputItemPreview>
<!-- 编辑框常挂不卸载,不编辑时由组件收起 -->
<XhTagsInputItemInput />
</XhTagsInputItem>
<XhTagsInputInput />
</XhTagsInputControl>
</XhTagsInputRoot>
<p>当前:{{ tags.length ? tags.join("、") : "(无)" }}</p>
</template>禁用与只读
disabled 整个控件退出 Tab 序列;read-only 仍可聚焦浏览,但加不进也删不掉
<script setup lang="ts">
import {
XhTagsInputControl,
XhTagsInputInput,
XhTagsInputItem,
XhTagsInputItemDeleteTrigger,
XhTagsInputItemPreview,
XhTagsInputItemText,
XhTagsInputLabel,
XhTagsInputRoot,
} from "@xihan-ui/vue";
</script>
<template>
<div style="display: flex; flex-direction: column; gap: 20px; max-inline-size: 420px">
<XhTagsInputRoot v-slot="{ value }" :default-value="['Vue', 'Vite']" disabled>
<XhTagsInputLabel>禁用</XhTagsInputLabel>
<XhTagsInputControl>
<XhTagsInputItem v-for="t in value" :key="t" :value="t">
<XhTagsInputItemPreview>
<XhTagsInputItemText>{{ t }}</XhTagsInputItemText>
<XhTagsInputItemDeleteTrigger>×</XhTagsInputItemDeleteTrigger>
</XhTagsInputItemPreview>
</XhTagsInputItem>
<XhTagsInputInput />
</XhTagsInputControl>
</XhTagsInputRoot>
<XhTagsInputRoot v-slot="{ value }" :default-value="['Vue', 'Vite']" read-only>
<XhTagsInputLabel>只读</XhTagsInputLabel>
<XhTagsInputControl>
<XhTagsInputItem v-for="t in value" :key="t" :value="t">
<XhTagsInputItemPreview>
<XhTagsInputItemText>{{ t }}</XhTagsInputItemText>
<XhTagsInputItemDeleteTrigger>×</XhTagsInputItemDeleteTrigger>
</XhTagsInputItemPreview>
</XhTagsInputItem>
<XhTagsInputInput />
</XhTagsInputControl>
</XhTagsInputRoot>
</div>
</template>形态
variant 只改控件与胶囊的颜色槽位,落标签与删标签的行为三档一致
<script setup lang="ts">
import {
XhTagsInputControl,
XhTagsInputInput,
XhTagsInputItem,
XhTagsInputItemDeleteTrigger,
XhTagsInputItemPreview,
XhTagsInputItemText,
XhTagsInputLabel,
XhTagsInputRoot,
} from "@xihan-ui/vue";
const variants = ["outline", "subtle", "ghost"] as const;
</script>
<template>
<div style="display: flex; flex-direction: column; gap: 20px; max-inline-size: 420px">
<XhTagsInputRoot
v-for="v in variants"
:key="v"
v-slot="{ value }"
:variant="v"
:default-value="['Vue', 'TypeScript']"
placeholder="回车落一个"
>
<XhTagsInputLabel>{{ v }}</XhTagsInputLabel>
<XhTagsInputControl>
<XhTagsInputItem v-for="t in value" :key="t" :value="t">
<XhTagsInputItemPreview>
<XhTagsInputItemText>{{ t }}</XhTagsInputItemText>
<XhTagsInputItemDeleteTrigger>×</XhTagsInputItemDeleteTrigger>
</XhTagsInputItemPreview>
</XhTagsInputItem>
<XhTagsInputInput />
</XhTagsInputControl>
</XhTagsInputRoot>
</div>
</template>语气
tone 决定用哪族颜色,与 variant 正交;这里固定 outline 只看语气的差别
<script setup lang="ts">
import {
XhTagsInputControl,
XhTagsInputInput,
XhTagsInputItem,
XhTagsInputItemDeleteTrigger,
XhTagsInputItemPreview,
XhTagsInputItemText,
XhTagsInputLabel,
XhTagsInputRoot,
} from "@xihan-ui/vue";
const tones = ["brand", "neutral", "success", "warning", "danger", "info"] as const;
</script>
<template>
<div style="display: grid; gap: 16px; grid-template-columns: repeat(2, minmax(0, 1fr))">
<XhTagsInputRoot
v-for="t in tones"
:key="t"
v-slot="{ value }"
variant="outline"
:tone="t"
:default-value="['标签']"
placeholder="回车落一个"
>
<XhTagsInputLabel>{{ t }}</XhTagsInputLabel>
<XhTagsInputControl>
<XhTagsInputItem v-for="v in value" :key="v" :value="v">
<XhTagsInputItemPreview>
<XhTagsInputItemText>{{ v }}</XhTagsInputItemText>
<XhTagsInputItemDeleteTrigger>×</XhTagsInputItemDeleteTrigger>
</XhTagsInputItemPreview>
</XhTagsInputItem>
<XhTagsInputInput />
</XhTagsInputControl>
</XhTagsInputRoot>
</div>
</template>尺寸
控件高度、胶囊与输入文字一起换档,不传 size 即默认档
<script setup lang="ts">
import {
XhTagsInputControl,
XhTagsInputInput,
XhTagsInputItem,
XhTagsInputItemDeleteTrigger,
XhTagsInputItemPreview,
XhTagsInputItemText,
XhTagsInputLabel,
XhTagsInputRoot,
} from "@xihan-ui/vue";
// 中间一档不写 size,用 undefined 表达
const sizes = [
{ size: "sm", label: "小" },
{ size: undefined, label: "默认" },
{ size: "lg", label: "大" },
] as const;
</script>
<template>
<div style="display: flex; flex-direction: column; gap: 20px; max-inline-size: 420px">
<XhTagsInputRoot
v-for="s in sizes"
:key="s.label"
v-slot="{ value }"
:size="s.size"
:default-value="['Vue', 'TypeScript']"
placeholder="回车落一个"
>
<XhTagsInputLabel>{{ s.label }}</XhTagsInputLabel>
<XhTagsInputControl>
<XhTagsInputItem v-for="t in value" :key="t" :value="t">
<XhTagsInputItemPreview>
<XhTagsInputItemText>{{ t }}</XhTagsInputItemText>
<XhTagsInputItemDeleteTrigger>×</XhTagsInputItemDeleteTrigger>
</XhTagsInputItemPreview>
</XhTagsInputItem>
<XhTagsInputInput />
</XhTagsInputControl>
</XhTagsInputRoot>
</div>
</template>随表单提交
写了 name 与 hidden-input 才参与提交,整份标签按断词符拼成一串;框里没内容时回车留给表单
<script setup lang="ts">
import { ref } from "vue";
import {
XhButton,
XhTagsInputControl,
XhTagsInputHiddenInput,
XhTagsInputInput,
XhTagsInputItem,
XhTagsInputItemDeleteTrigger,
XhTagsInputItemPreview,
XhTagsInputItemText,
XhTagsInputLabel,
XhTagsInputRoot,
} from "@xihan-ui/vue";
const tags = ref<string[]>(["Vue", "TypeScript"]);
const submitted = ref("");
function onSubmit(event: Event) {
const data = new FormData(event.target as HTMLFormElement);
submitted.value = String(data.get("skills") ?? "");
}
</script>
<template>
<form
style="display: flex; flex-direction: column; gap: 12px; max-inline-size: 420px"
@submit.prevent="onSubmit"
>
<XhTagsInputRoot
v-slot="{ value }"
v-model:value="tags"
name="skills"
delimiter=","
placeholder="回车落一个"
>
<XhTagsInputLabel>技术栈</XhTagsInputLabel>
<XhTagsInputControl>
<XhTagsInputItem v-for="t in value" :key="t" :value="t">
<XhTagsInputItemPreview>
<XhTagsInputItemText>{{ t }}</XhTagsInputItemText>
<XhTagsInputItemDeleteTrigger>×</XhTagsInputItemDeleteTrigger>
</XhTagsInputItemPreview>
</XhTagsInputItem>
<XhTagsInputInput />
</XhTagsInputControl>
<XhTagsInputHiddenInput />
</XhTagsInputRoot>
<XhButton type="submit" variant="outline" style="align-self: start">提交</XhButton>
<span>表单收到:{{ submitted || "(还没提交)" }}</span>
</form>
</template>入库前统一改写
给了 value 就由宿主说了算:组件只发变更意图,写回什么形状在这里定
当前:#vue
<script setup lang="ts">
import { ref } from "vue";
import {
XhTagsInputControl,
XhTagsInputInput,
XhTagsInputItem,
XhTagsInputItemDeleteTrigger,
XhTagsInputItemPreview,
XhTagsInputItemText,
XhTagsInputLabel,
XhTagsInputRoot,
} from "@xihan-ui/vue";
const tags = ref<string[]>(["#vue"]);
// 统一成小写并补上井号,重复的那一份丢掉
function onValueChange(details: { value: string[] }) {
const next: string[] = [];
for (const raw of details.value) {
const tag = raw.trim().toLowerCase();
const normalized = tag.startsWith("#") ? tag : `#${tag}`;
if (normalized !== "#" && !next.includes(normalized)) {
next.push(normalized);
}
}
tags.value = next;
}
</script>
<template>
<XhTagsInputRoot
v-slot="{ value }"
:value="tags"
placeholder="打 Vue 回车,落进去的是 #vue"
style="max-inline-size: 420px"
@value-change="onValueChange"
>
<XhTagsInputLabel>话题</XhTagsInputLabel>
<XhTagsInputControl>
<XhTagsInputItem v-for="t in value" :key="t" :value="t">
<XhTagsInputItemPreview>
<XhTagsInputItemText>{{ t }}</XhTagsInputItemText>
<XhTagsInputItemDeleteTrigger>×</XhTagsInputItemDeleteTrigger>
</XhTagsInputItemPreview>
</XhTagsInputItem>
<XhTagsInputInput />
</XhTagsInputControl>
</XhTagsInputRoot>
<p>当前:{{ tags.length ? tags.join("、") : "(无)" }}</p>
</template>候选词一键添加
根插槽给出 addValue 与 atMax:输入框之外再开一条加标签的路,上限一样管得住
<script setup lang="ts">
import { ref } from "vue";
import {
XhButton,
XhTagsInputControl,
XhTagsInputInput,
XhTagsInputItem,
XhTagsInputItemDeleteTrigger,
XhTagsInputItemPreview,
XhTagsInputItemText,
XhTagsInputLabel,
XhTagsInputRoot,
} from "@xihan-ui/vue";
const suggestions = ["文档", "无障碍", "设计令牌", "组件库"];
const tags = ref<string[]>(["文档"]);
</script>
<template>
<XhTagsInputRoot
v-slot="{ value, addValue, atMax }"
v-model:value="tags"
:max="3"
placeholder="回车落一个"
style="max-inline-size: 420px"
>
<XhTagsInputLabel>话题(最多 3 个)</XhTagsInputLabel>
<XhTagsInputControl>
<XhTagsInputItem v-for="t in value" :key="t" :value="t">
<XhTagsInputItemPreview>
<XhTagsInputItemText>{{ t }}</XhTagsInputItemText>
<XhTagsInputItemDeleteTrigger>×</XhTagsInputItemDeleteTrigger>
</XhTagsInputItemPreview>
</XhTagsInputItem>
<XhTagsInputInput />
</XhTagsInputControl>
<div style="display: flex; flex-wrap: wrap; gap: 8px">
<XhButton
v-for="s in suggestions"
:key="s"
size="sm"
variant="outline"
:disabled="atMax || value.includes(s)"
@click="addValue(s)"
>
{{ s }}
</XhButton>
</div>
</XhTagsInputRoot>
</template>外部触发的输入会话
输入部件平时收起,按「添加」才露面并聚焦;打字时给候选,选中即落标签,失焦按 blur-behavior 收尾
<script setup lang="ts">
import { nextTick, ref } from "vue";
import {
XhButton,
XhTagsInputControl,
XhTagsInputInput,
XhTagsInputItem,
XhTagsInputItemDeleteTrigger,
XhTagsInputItemPreview,
XhTagsInputItemText,
XhTagsInputLabel,
XhTagsInputRoot,
} from "@xihan-ui/vue";
const domains = ["@qq.com", "@163.com", "@gmail.com"];
const mails = ref<string[]>(["hi@xihan.dev"]);
const typing = ref(false);
const input = ref<InstanceType<typeof XhTagsInputInput> | null>(null);
// 输入框由外部按钮开合,露面后焦点要自己送进去
function start() {
typing.value = true;
nextTick(() => {
(input.value?.$el as HTMLInputElement | undefined)?.focus();
});
}
// 候选:拿已经打出来的前缀拼几个完整地址
function options(text: string): string[] {
const prefix = text.split("@")[0] ?? "";
return prefix ? domains.map((domain) => prefix + domain) : [];
}
</script>
<template>
<XhTagsInputRoot
v-slot="{ value, inputValue, addValue, setInputValue, atMax }"
v-model:value="mails"
:max="4"
blur-behavior="add"
placeholder="打前缀选后缀"
style="max-inline-size: 420px"
>
<XhTagsInputLabel>通知邮箱(最多 4 个)</XhTagsInputLabel>
<XhTagsInputControl>
<XhTagsInputItem v-for="t in value" :key="t" :value="t">
<XhTagsInputItemPreview>
<XhTagsInputItemText>{{ t }}</XhTagsInputItemText>
<XhTagsInputItemDeleteTrigger>×</XhTagsInputItemDeleteTrigger>
</XhTagsInputItemPreview>
</XhTagsInputItem>
<XhTagsInputInput v-if="typing" ref="input" @blur="typing = false" />
<XhButton
v-else
size="sm"
variant="outline"
:disabled="atMax"
@click="start"
>
+ 添加
</XhButton>
</XhTagsInputControl>
<!-- 候选面板是作者自己的节点:按下不放焦点,点完把框里的半截文本清掉 -->
<div
v-if="typing && options(inputValue).length"
style="
display: flex;
flex-direction: column;
margin-block-start: 4px;
border: 1px solid var(--xh-border-subtle);
border-radius: var(--xh-radius-md);
overflow: hidden;
"
>
<button
v-for="opt in options(inputValue)"
:key="opt"
type="button"
style="
padding: 6px 10px;
border: 0;
background: none;
color: var(--xh-fg-default);
font: inherit;
text-align: start;
cursor: pointer;
"
@mousedown.prevent
@click="
addValue(opt);
setInputValue('');
"
>
{{ opt }}
</button>
</div>
</XhTagsInputRoot>
</template>标签用对象
组件里存的是标识那一份,显示哪一份由作者定:条目文本渲染 label,提交仍按标识拼串
提交出去的是标识:u-1
框里看到的是名字:张三
<script setup lang="ts">
import { ref } from "vue";
import {
XhTagsInputControl,
XhTagsInputHiddenInput,
XhTagsInputInput,
XhTagsInputItem,
XhTagsInputItemDeleteTrigger,
XhTagsInputItemPreview,
XhTagsInputItemText,
XhTagsInputLabel,
XhTagsInputRoot,
} from "@xihan-ui/vue";
interface Option {
value: string;
label: string;
}
// 显示名与标识的对照表由宿主自己拿着,组件只认标识
const options = ref<Option[]>([
{ value: "u-1", label: "张三" },
{ value: "u-2", label: "李四" },
{ value: "u-3", label: "王五" },
]);
const value = ref<string[]>(["u-1"]);
let seq = 0;
function labelOf(id: string): string {
return options.value.find((option) => option.value === id)?.label ?? id;
}
// 组件报回来的是框里打的那串文本:同名的换成它的标识,没见过的现造一条对照
function onValueChange(details: { value: string[] }) {
const next: string[] = [];
for (const raw of details.value) {
const known =
options.value.find((option) => option.value === raw) ??
options.value.find((option) => option.label === raw);
if (known) {
if (!next.includes(known.value)) {
next.push(known.value);
}
continue;
}
seq += 1;
const created = { value: `u-new-${seq}`, label: raw };
options.value = [...options.value, created];
next.push(created.value);
}
value.value = next;
}
</script>
<template>
<XhTagsInputRoot
v-slot="{ value: tags }"
:value="value"
name="reviewers"
placeholder="打名字回车"
style="max-inline-size: 420px"
@value-change="onValueChange"
>
<XhTagsInputLabel>评审人</XhTagsInputLabel>
<XhTagsInputControl>
<XhTagsInputItem v-for="t in tags" :key="t" :value="t">
<XhTagsInputItemPreview>
<XhTagsInputItemText>{{ labelOf(t) }}</XhTagsInputItemText>
<XhTagsInputItemDeleteTrigger>×</XhTagsInputItemDeleteTrigger>
</XhTagsInputItemPreview>
</XhTagsInputItem>
<XhTagsInputInput />
</XhTagsInputControl>
<XhTagsInputHiddenInput />
</XhTagsInputRoot>
<p>提交出去的是标识:{{ value.join(",") || "(无)" }}</p>
<p>框里看到的是名字:{{ value.map(labelOf).join("、") || "(无)" }}</p>
</template>产物
| 层 | 值 |
|---|---|
| 自定义元素 | <xh-tags-input> |
| Vue 组件 | XhTagsInputClearTrigger XhTagsInputControl XhTagsInputHiddenInput XhTagsInputInput XhTagsInputItem XhTagsInputItemDeleteTrigger XhTagsInputItemInput XhTagsInputItemPreview XhTagsInputItemText XhTagsInputLabel XhTagsInputRoot |
| 组合式函数 | useTagsInput |
| 状态机 | tagsInputMachine |
| 皮肤 | @xihan-ui/styles/tags-input.css |
解剖
部件名即 data-part 属性值,也是皮肤的选择器。加粗的是必备部件,不渲染它组件不工作(Web Components 适配器会在诊断通道上报 wc.missing-part)。
data-scope="tags-input":root · label · control · input · item · item-preview · item-text · item-delete-trigger · item-input · clear-trigger · hidden-input
Props
| 属性 | 类型 | 必填 | 说明 |
|---|---|---|---|
value | string[] | 受控标签集合;给了就由宿主说了算,机器不自改,只发 onValueChange。 | |
defaultValue | string[] | 非受控初始标签集合。 | |
inputValue | string | 受控输入文本;与 value 各自独立受控。 | |
defaultInputValue | string | 非受控初始输入文本。 | |
max | number | 最多几个标签。缺省不限;写 0 表示一个也不许加。 | |
allowOverflow | boolean | 允许越过 max。 关(默认):顶到上限后这一次输入整体不生效,文本原样留在框里,绝不悄悄吞掉。 开:照加不误,只在 root / control 上打出 data-overflow 供样式与提示使用。 | |
disabled | boolean | ||
readOnly | boolean | ||
invalid | boolean | ||
name | string | 表单字段名;给了 hidden-input 才带 name,此时整份标签按 delimiter 拼成一串提交。 | |
placeholder | string | ||
delimiter | string | 断词符,默认逗号。打字打出它即断词成标签,粘贴时也按它拆。 显式给空串即关掉断词:此时只有 Enter 能把文本变成标签。 | |
addOnPaste | boolean | 粘贴时接管:按 delimiter 拆成多个标签。默认关(交给浏览器照常粘进框里)。 | |
editable | boolean | 允许双击标签就地改。默认关。 | |
blurBehavior | TagsInputBlurBehavior | null | 焦点离开整个组件时怎么处置输入框里的残留文本。 | |
variant | ControlVariant | 形态:outline / subtle / ghost,决定颜色怎么用。 | |
tone | Tone | 语气:brand / neutral / success / warning / danger / info,决定用哪族颜色。 | |
size | Size | 尺寸:sm / md / lg。 | |
translations | Partial<TagsInputTranslations> | ||
onValueChange | (details: TagsInputValueChangeDetails) => void | ||
onInputValueChange | (details: TagsInputInputValueChangeDetails) => void |
状态机
状态:idle · navigating · editing
事件:VALUE.SET · TAG.ADD · VALUE.CLEAR · INPUT.CHANGE · INPUT.COMMIT · INPUT.BLUR · TAG.HIGHLIGHT · TAG.DELETE · TAG.EDIT · EDIT.CHANGE · EDIT.SUBMIT · EDIT.CANCEL · ITEM.FOCUS_LOST · FORM.RESET
判据:canEdit · canEditTag · canDeleteWithPrev · hasHighlightTarget
connect API
useTagsInput 产出的对象。getXxxProps() 铺到对应部件的宿主元素上,其余是可读状态与操作入口。
| 成员 | 类型 | 说明 |
|---|---|---|
value | string[] | |
count | number | 标签个数,等于 value.length;作者常拿它做"3 / 5"这类计数提示。 |
inputValue | string | |
empty | boolean | 一个标签都没有。 |
disabled | boolean | |
readOnly | boolean | |
invalid | boolean | |
atMax | boolean | 已顶到 max:再加进不去(allowOverflow 开时只是提示,不拦)。 |
overflow | boolean | 已经越过 max(只有 allowOverflow 开着才可能为真)。 |
highlightedValue | string | null | 光标停着的标签;没在标签间走时为 null。 |
editedValue | string | null | 正被就地改写的标签;不在编辑态时为 null。 |
canClear | boolean | 清空按钮此刻是否可用(可编辑,且标签或输入文本至少有一样)。 |
setValue | (next: string[]) => void | 整份替换,去重去空白,不受 max 约束。 |
addValue | (next: string) => void | 追加一个标签,受 max 与 allowOverflow 约束。 |
deleteValue | (value: string) => void | |
clear | () => void | |
setInputValue | (next: string) => void | |
highlight | (value: string | null) => void | 把光标挪到某个标签上;传 null 即交回输入框。 |
edit | (value: string) => void | 进入就地编辑;未开 editable 时被守卫挡下。 |
getRootProps | () => T['element'] | |
getLabelProps | () => T['label'] | |
getControlProps | () => T['element'] | |
getInputProps | () => T['input'] | |
getItemProps | (item: TagsInputItemProps) => T['element'] | |
getItemPreviewProps | (item: TagsInputItemProps) => T['element'] | |
getItemTextProps | (item: TagsInputItemProps) => T['element'] | |
getItemDeleteTriggerProps | (item: TagsInputItemProps) => T['button'] | |
getItemInputProps | (item: TagsInputItemProps) => T['input'] | |
getClearTriggerProps | () => T['button'] | |
getHiddenInputProps | () => T['input'] |
键盘
规格出处:W3C APG
| 按键 | 生效条件 | 行为 |
|---|---|---|
Enter | focus in input, 框里有能成标签的内容, not disabled/readOnly | 把输入框里的文本变成标签(含 delimiter 时一次进多个);框里只有空白时不接管,Enter 留给表单提交 |
delimiter(默认 ,) | focus in input, not disabled/readOnly | 断词:分隔符之前的每一段各成一个标签,最后一段留在框里接着打 |
Backspace | 输入框为空且没有标签被高亮, 至少有一个标签 | 高亮最后一个标签(这一下不删任何东西) |
Backspace | 输入框为空且已有标签被高亮 | 删掉高亮的标签,光标落到前一个上;删的是第一个就交回输入框 |
Delete | 已有标签被高亮 | 同上,删掉高亮的标签 |
ArrowLeft | focus in input 且光标贴着最左端(无选区), 至少有一个标签 | 往左走一格;还没走进标签时从最后一个起步,已经在第一个就停住 |
ArrowRight | 已有标签被高亮 | 往右走一格;走出末尾即交回输入框。光标还在框里时不接管 |
Home | 已有标签被高亮 | 跳到第一个标签 |
End | 已有标签被高亮 | 交回输入框 |
Escape | 已有标签被高亮 | 取消高亮,光标交回输入框;没在标签间走时不接管该键 |
Enter | 已有标签被高亮, editable 开着 | 就地编辑这个标签,焦点进编辑框并整段选中 |
Enter | focus in item-input(就地编辑中) | 提交改写;改成空白等于删掉这个标签,改成另一个已有标签则并成一个。焦点交回输入框 |
Escape | focus in item-input(就地编辑中) | 撤销这次改写,标签保持原样,焦点交回输入框 |
