Tiptap 提及功能:从触发到渲染的5个关键配置

Tiptap 提及功能:从触发到渲染的5个关键配置 Tiptap 提及功能从触发到渲染的5个关键配置【免费下载链接】tiptapThe headless rich text editor framework for web artisans.项目地址: https://gitcode.com/GitHub_Trending/ti/tiptapTiptap 的tiptap/extension-mention扩展解决一个具体问题用户在编辑器里输入的瞬间弹出可搜索的候选列表选中后把这段文字变成一个不可编辑、可挂样式、携带id的提及节点——它不再是一串普通字符而是能和你用户系统对上号的实体。想象一个协作工具的任务评论框你要 队友触发通知、# 一个标签用于归档。用户敲下Jen匹配项浮现按回车后文字变成蓝色胶囊后面自动补一个空格改主意按 Backspace 时胶囊是整体删除还是还原成Jennifer文本都由你的配置说了算。先跑起来下面的代码创建了一个启用 mention 扩展的编辑器items从本地列表里按 query 过滤出最多 5 个候选new Editor({ extensions: [ Document, Paragraph, Text, Mention.configure({ HTMLAttributes: { class: mention }, suggestion: { items: ({ query }) users .filter(u u.label.toLowerCase().includes(query.toLowerCase())) .slice(0, 5), }, }), ], })效果在编辑器里输入并继续敲名字下方出现下拉列表按回车后Jennifer Grey变成胶囊节点属性里存着data-id和data-label后面默认补一个空格。Mention 配置逐项拆解配置项作用典型取值不配会怎样必配suggestion.items返回候选列表接收{ query }({query}) [...]输入触发字符后没有任何弹窗常用suggestion.char触发字符默认就是HTMLAttributes节点 DOM 属性通常放 class{class:mention}节点只有data-typementionCSS 没法精准命中suggestion.minQueryLength查询多长才开始请求候选2刚按就弹出完整列表deleteTriggerWithBackspaceBackspace 是否整体删除提及true节点还原成标签纯文本进阶suggestions多触发数组每项一个触发字符[{char:},{char:#}]只认suggestion单数里的那一个suggestion.debounce防抖毫秒数300每敲一个字符都发一次请求suggestion.initialItems加载前先展示的候选最近联系过的 3 人异步请求期间弹层是空的renderText/renderHTML控制显示文本与 HTML自定义函数默认输出char (label ?? id)胶囊样式就命中HTMLAttributes里的 class几行 CSS 的事.tiptap .mention { background-color: #e5f3ff; border-radius: 4px; padding: 0 2px; color: #1a73e8; font-weight: 500; box-decoration-break: clone; }效果提及跨行时每一行都有独立的底色圆角而不是被裁成一块。开启多触发需求同一个编辑器提人、#提标签。关键差异是把单数suggestion换成复数suggestions数组每项带自己的触发字符、候选源和插入命令Mention.configure({ suggestions: [ { char: , items: ({ query }) fetchUsers(query), }, { char: #, items: ({ query }) fetchTags(query), command: ({ editor, range, props }) editor.chain().focus().insertContentAt(range, [ { type: mention, attrs: { ...props, mentionSuggestionChar: # } }, { type: text, text: }, ]).run(), }, ], })效果每个触发字符各挂一个独立的 suggestion 插件互不干扰节点上自动记mentionSuggestionChar属性序列化时据此区分这是人还是标签。两个实战组合候选来自后端。需求一句话搜索走接口不能每敲一个字打一次请求。关键差异是把items写成异步函数加两行防抖和预填充suggestion: { char: , minQueryLength: 2, debounce: 300, initialItems: [{ id: 1, label: 张三 }], items: async ({ query, signal }) { const res await fetch(/api/users?q${query}, { signal }) if (signal.aborted) return [] return (await res.json()).slice(0, 5) }, }效果弹窗打开先显示最近的人请求回来再替换用户继续输入时旧请求靠signal自动取消。弹层用框架组件。需求一句话列表项要带头像、描述行和键盘上下导航。关键差异是在render里用框架渲染器建组件把定位交给插件的mountrender: () { let component, unmount return { onStart: props { component new VueRenderer(MentionList, { props, editor: props.editor }) unmount props.mount(component.element) }, onUpdate: props component.updateProps(props), onExit: () unmount?.(), } }效果插件接管弹层的追加、跟随光标和销毁你不用自己写 scroll/resize 监听仓库里的 demos/src/Nodes/Mention/Vue/MentionList.vue 就是这样一个带键盘导航的列表实现。踩坑速查现象Backspace 删提及没删掉留下Jennifer一段文本。原因deleteTriggerWithBackspace默认false设计上就是还原为纯文本。修复Mention.configure({ deleteTriggerWithBackspace: true })。现象加了第二个#触发毫无反应。原因第二个配置塞进了单数suggestion里只有复数suggestions数组支持多触发。修复把两项都移到suggestions: [...]中。现象输入后列表经常为空。原因匹配写成了startsWithquery 不在开头就全部过滤掉。修复改用label.toLowerCase().includes(query.toLowerCase())。现象弹层被编辑区的overflow: hidden裁掉或滚动时不跟手。原因弹层节点挂在编辑器内部受滚动容器约束。修复改用props.mount(component.element)由插件负责定位与跟随。现象提及后面出现两个空格。原因默认command已插入一个尾部空格自定义 command 又插了一个。修复自定义insertContentAt的节点数组里去掉{ type: text, text: }。往哪走mention 只是tiptap/suggestion这套通用建议工具的一个具名用例同样的 options 能做出 emoji 选择器或斜杠命令。下一步可以读 packages/suggestion/src/suggestion.ts 里的触发匹配逻辑或者参考 demos/src/Examples/MultiMention/ 把双触发跑成完整项目。【免费下载链接】tiptapThe headless rich text editor framework for web artisans.项目地址: https://gitcode.com/GitHub_Trending/ti/tiptap创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考