如何为Plate富文本编辑器创建自定义插件(工具)
解决Plate富文本编辑器自定义元素工具栏按钮无效问题
核心问题
你使用了MarkToolbarButton来触发块级元素(Column)的插入,但这个组件是专门为**行内标记(如粗体、斜体)**设计的,不适用于块级元素。这就是点击按钮无反应的根本原因。
解决步骤
1. 替换工具栏组件(核心修复)
将MarkToolbarButton替换为块级元素专用的ElementToolbarButton,或者自定义按钮实现插入逻辑:
方式1:使用ElementToolbarButton(推荐)
如果你的模板提供了ElementToolbarButton组件,直接替换即可:
// fixed-toolbar-buttons.tsx import { ElementToolbarButton } from '@udecode/plate-toolbar'; import { ELEMENT_COLUMN } from './columnsPlugin'; <ElementToolbarButton tooltip="双列布局" nodeType={ELEMENT_COLUMN}> <Icons.borderAll /> </ElementToolbarButton>
方式2:自定义插入按钮
如果没有ElementToolbarButton,可以通过useEditor钩子手动实现插入逻辑:
// fixed-toolbar-buttons.tsx import { useEditor } from '@udecode/plate-common'; import { insertNodes } from '@udecode/plate-common'; import { ELEMENT_COLUMN } from './columnsPlugin'; const editor = useEditor(); const insertColumns = () => { // 插入预定义结构的双列元素,确保内部有可编辑的段落 editor.insertNodes({ type: ELEMENT_COLUMN, children: [ { type: 'p', children: [{ text: '' }] }, { type: 'p', children: [{ text: '' }] }, ], }); }; <button onClick={insertColumns} title="双列布局" className="toolbar-button"> <Icons.borderAll /> </button>
2. 完善插件的插入逻辑(可选)
可以在插件中添加快捷键或默认插入规则,增强可用性:
// columnsPlugin.tsx import { createPluginFactory, insertNodes } from '@udecode/plate-common'; import { ColumnsElement } from '@/components/plate-ui/columns-element'; export const ELEMENT_COLUMN = 'column'; export const createColumnsPlugin = createPluginFactory({ key: ELEMENT_COLUMN, isElement: true, component: ColumnsElement, handlers: { // 添加快捷键:Ctrl+Shift+C 插入双列布局 onKeyDown: (editor, event) => { if (event.ctrlKey && event.shiftKey && event.key === 'C') { event.preventDefault(); insertNodes(editor, { type: ELEMENT_COLUMN, children: [ { type: 'p', children: [{ text: '' }] }, { type: 'p', children: [{ text: '' }] }, ], }); } }, }, });
3. 修复元素可编辑性
原代码中columns-wrapper设置了contentEditable={false},会导致整个双列区域无法编辑,需要调整结构:
// columns-element.tsx import { cn, withRef } from '@udecode/cn'; import { PlateElement } from '@udecode/plate-common'; import { useFocused, useSelected } from 'slate-react'; import './column.scss'; export const ColumnsElement = withRef<typeof PlateElement>( ({ className, nodeProps, ...props }, ref) => { const { children } = props; const selected = useSelected(); const focused = useFocused(); // 将子节点拆分为两列,默认补空段落 const [col1, col2] = Array.isArray(children) ? children : [children, { type: 'p', children: [{ text: '' }] }]; return ( <PlateElement ref={ref} {...props} className={cn( 'columns-wrapper', selected && focused && 'ring-2 ring-ring ring-offset-2', className )} > <div className="column-block">{col1}</div> <div className="column-block">{col2}</div> </PlateElement> ); } );
关键说明
- Mark vs Element:Plate中
Mark对应行内格式,Element对应块级结构,两者的工具栏组件和处理逻辑完全不同,不能混用。 - 节点结构:插入块级元素时,必须确保节点包含可编辑的子元素(如
<p>),否则插入后无法输入内容。
内容的提问来源于stack exchange,提问作者Eshan Rajapakshe
相关产品推荐
相关产品推荐

