如何在不丢失文本选区的情况下为Slate-React实现超链接插入弹窗
核心解决思路是不依赖浏览器原生选区,提前存储Slate内部独立维护的选区状态,具体操作步骤如下:
- 点击工具栏插入链接按钮、弹窗打开前,第一时间将当前
editor.selection的值存入ref中,Slate的选区状态独立于浏览器原生逻辑,不会因为编辑器失焦被自动清空。 - 给自定义弹窗的根元素绑定
onMouseDown事件,回调中执行e.preventDefault(),可以阻止点击弹窗时触发编辑器的原生失焦逻辑,选中文本的高亮状态也会暂时保留,和Google Docs的交互效果一致。 - 用户输入链接确认插入时,先调用
Transforms.select(editor, 之前存储的选区)恢复编辑器选中状态,再执行链接节点的包裹逻辑即可正常插入。
如果需要在编辑器失焦后依然展示选中区域的高亮效果,可以通过Slate的decorate API,给落在存储选区范围内的文本节点添加自定义高亮样式,完全复刻Google Docs的交互表现。
参考实现代码:
import { Transforms, Editor } from 'slate' import { useRef, useState } from 'react' import { useSlate, Editable } from 'slate-react' // 编辑器组件内逻辑 const RichEditor = () => { const [editor] = useSlate() const [linkModalOpen, setLinkModalOpen] = useState(false) // 存储选区的ref,重渲染不会丢失值 const savedSelectionRef = useRef(null) // 点击插入链接按钮的回调 const handleLinkBtnClick = () => { savedSelectionRef.current = editor.selection setLinkModalOpen(true) } // 弹窗确认插入链接的回调 const handleLinkConfirm = (inputUrl) => { if (!savedSelectionRef.current || !inputUrl) return // 恢复之前保存的选区 Transforms.select(editor, savedSelectionRef.current) // 构造链接节点插入 const linkNode = { type: 'link', url: inputUrl, children: [{ text: Editor.string(editor, savedSelectionRef.current) }] } Transforms.wrapNodes(editor, linkNode, { split: true }) // 重置状态 savedSelectionRef.current = null setLinkModalOpen(false) } return ( <div> {/* 编辑器工具栏 */} <button onClick={handleLinkBtnClick}>插入超链接</button> {/* 编辑器主体 */} <Editable placeholder="请输入内容" /> {/* 自定义弹窗 */} {linkModalOpen && ( <div className="link-modal" // 阻止mousedown默认行为,避免编辑器失焦 onMouseDown={(e) => e.preventDefault()} > <input type="text" placeholder="请输入链接地址" /> <button onClick={(e) => handleLinkConfirm(e.target.previousSibling.value)}>确认</button> </div> )} </div> ) }
内容的提问来源于stack exchange,提问作者Dorian Fabre
相关产品推荐
相关产品推荐

