Mentions
mentions@提及输入 · 复用 Textarea 皮肤 + 触发符唤起候选(镜像 div 测光标像素坐标) + aria-activedescendant 虚拟焦点(零依赖)
用法
基础用法
输入 @ 唤起候选浮层,方向键选、Enter/Tab 插入提及。
tsx
<Mentions
options={people}
placeholder="输入 @ 提及同事…"
onChange={setValue}
onSelect={(o) => console.log(o)}
/>自定义触发符
prefix 改为 "#" 关联工单等非人员实体。
tsx
<Mentions
prefix="#"
options={tickets}
placeholder="输入 # 关联工单…"
/>无效态
invalid 标红边框,配合表单校验提示缺少提及。
tsx
<Mentions options={people} invalid defaultValue="缺少 @负责人" />禁用态
disabled 禁止编辑,已提交提及仍套色展示。
tsx
<Mentions options={people} disabled defaultValue="禁用态 @林晓 " />何时用
多行评论/工单/动态里需要 @ 某人、# 关联工单这类「键入触发符 → 弹候选 → 插入」场景时用。它本质是带候选浮层的 Textarea。和 Combobox 的区别:Combobox 选「单值」整体替换输入框;Mentions 在自由文本中插入一个个提及片段,候选只在触发符后唤起,正文仍是普通多行文本。
导入
ts
import { Mentions, MentionText, type MentionTextProps, findTrigger, insertMention, defaultFilter, segmentMentions, type MentionSegment } from "@hulianui/ui"Props
| 名称 | 类型 | 默认 | 说明 |
|---|---|---|---|
| options* | MentionOption[] | — | 候选清单;每项 value/label,可带 description/startContent/disabled |
| value | string | — | 受控文本值(与 onChange 配套) |
| defaultValue | string | — | 非受控初始值(不传 value 时生效) |
| prefix | string | "@" | 触发符,可配多字符如 "@@"/"#";其前为行首或空白时唤起候选 |
| filter | false | ((option, query) => boolean) | 内置子串匹配 | false=关闭本地过滤交给 onSearch;函数=自定义;缺省=大小写不敏感子串(label/value) |
| size | "sm" | "md" | "lg" | "md" | 文本域皮肤尺寸(复用 Textarea 的 size 变体) |
| invalid | boolean | false | 独立使用时标红 |
| placeholder | string | — | 占位符(透传 textarea) |
| rows | number | — | 文本域行数(透传 textarea) |
| disabled | boolean | false | 禁用 |
| className | string | — | 容器类名 |
| popupClassName | string | — | 候选浮层额外类名 |
还透传Textarea的原生属性(除被 Omit 的size/value/defaultValue/onChange/onSelect/prefix)。
Events
| 事件 | 类型 | 说明 |
|---|---|---|
| onChange | (value: string) => void | 文本变化回调(受控必接) |
| onSearch | (query: string) => void | 查询变化通知(外部/异步过滤用,本身不返回结果) |
| onSelect | (option: MentionOption) => void | 选中候选回调(回传整条 option) |
禁忌 / 坑
- 受控用法
value必须配onChange,否则文本无法编辑(与原生受控 textarea 同理)。 - 插入文本是
prefix + label + " ",label即可见名也是写入正文的字面量;要展示和插入不一致请自行用onSelect接管。 - 外部/异步过滤时设
filter={false}并在onSearch里刷新options,否则内置子串过滤会再过滤一遍父级已筛好的结果。 - 暂无其它已知坑。
相关
SecretField · Combobox · Listbox · InputOTP · Rating · Upload
Playground
<Mentions
prefix="@"
size="md"
options={people}
placeholder="输入 @ 提及同事…"
rows={3}
onChange={setValue}
onSelect={(o) => console.log(o)}
/>