我正在参加「掘金·启航计划」
前言
在上一篇文章中,我们学习了如何在富文本编辑器中添加自定义格式,在不同容器中展示不同的文本,但 Slate 不仅仅可以改变「区块」。
在本篇文章,你将学会如何添加自己想要的文本格式,例如粗体、斜体、代码或删除线。
所以我们从之前的栗子开始,你可以先回顾一下之前的文章:
文本加粗
在很多文本编辑器中,都有这么快捷键:ctrl + B
-- 文本加粗。接下来,我们通过 Slate 的 onKeyDown
监听键盘动作,当用户按下 control + B
时,让编辑器将用户选中的文本替换为加粗格式:
<Editable
renderElement={renderElement}
onKeyDown={(event) => {
if (!event.ctrlKey) {
return;
}
switch (event.key) {
// 当 "`" 按下时, 替换区块类型
case "`": {
event.preventDefault();
const [match] = Editor.nodes(editor, {
match: (n) => n.type === "code",
});
Transforms.setNodes(
editor,
{ type: match ? "paragraph" : "code" },
{
match: (n) =>
Editor.isBlock(editor, n),
}
);
break;
}
// 当 "B" 按下时,将选中的文本加粗
case "b": {
event.preventDefault();
Transforms.setNodes(
editor,
{ bold: true },
// 应用到文本节点中,如果选中部分和文本节点一部分重叠,将文本节点拆分
{
match: (n) => Text.isText(n),
split: true,
}
);
break;
}
}
}}
/>
现在,我们已经添加成功,但是,如果你选择文本后按下 Ctrl + B
会发现富文本并没有发生任何变化。因为现在还没有告诉 Slate 如何渲染「粗体」标记。
Slate 认为每个段落 paragraph
是一颗树,对于你添加的任何格式,Slate 都会将其解析为「叶子」,一个段落可以包含不同格式的文本,就像一片片不同的叶子。你需要做的就是告诉 Slate 如何将不同的「叶子」渲染出来,就像之前的渲染区块元素一样。
定义一个 Leaf
组件:
// 定义一个 React 组件来渲染带有粗体文本的"叶子"
const Leaf = props => {
return (
<span
{...props.attributes}
style={{ fontWeight: props.leaf.bold ? 'bold' : 'normal' }}
>
{props.children}
</span>
)
}
定义完成后,需要告诉 Slate 关于「粗体」叶子的事情,我们将 renderLeaf
属性传递给富文本编辑器:
const renderLeaf = useCallback(props => {
return <Leaf {...props} />
}, [])
//...
<Editable
renderLeaf={renderLeaf}
/>
现在再次按下 ctrl + B
,你就能看到文本变为粗体了。
别忘了,当用户加粗选中文本后,再次按下 ctrl + B
时,选中文本应该回到上一次的状态,即「未加粗」标记。
为此,先判断选中文本的加粗标记状态,如果已加粗,设置为 false
;如果未加粗,设置为 true
:
const marks = Editor.marks(editor);
Transforms.setNodes(
editor,
{ bold: marks.bold ? false : true },
{
match: (n) => Text.isText(n),
split: true,
}
);
这样就能根据选中内容当前的标记状态来更新加粗标记状态,效果如下:
完整代码如下:
const initialValue = [
{
type: "paragraph",
children: [{ text: "这是一段初始文字。" }],
},
];
const CodeElement = (props) => {
return (
<pre {...props.attributes} style={{ background: "#dadada" }}>
<code>{props.children}</code>
</pre>
);
};
const DefaultElement = (props) => {
return <p {...props.attributes}>{props.children}</p>;
};
const Leaf = (props) => {
return (
<span
{...props.attributes}
style={{ fontWeight: props.leaf.bold ? "bold" : "normal" }}
>
{props.children}
</span>
);
};
const App = () => {
// 创建一个在渲染中保持不变的 slate editor 对象
const [editor] = useState(() => withReact(createEditor()));
const renderElement = useCallback((props) => {
console.log("renderElement-props", props);
switch (props.element.type) {
case "code":
return <CodeElement {...props} />;
default:
return <DefaultElement {...props} />;
}
}, []);
const renderLeaf = useCallback((props) => {
return <Leaf {...props} />;
}, []);
return (
<div className="container">
<h3>Slate 编辑器</h3>
<div className="editor-wrapper">
<Slate
editor={editor}
value={initialValue}
>
<Editable
renderElement={renderElement}
renderLeaf={renderLeaf}
onKeyDown={(event) => {
if (!event.ctrlKey) {
return;
}
switch (event.key) {
case "`": {
event.preventDefault();
const [match] = Editor.nodes(editor, {
match: (n) => n.type === "code",
});
Transforms.setNodes(
editor,
{ type: match ? "paragraph" : "code" },
{
match: (n) =>
Editor.isBlock(editor, n),
}
);
break;
}
case "b": {
event.preventDefault();
const marks = Editor.marks(editor);
Transforms.setNodes(
editor,
{ bold: marks.bold ? false : true },
{
match: (n) => Text.isText(n),
split: true,
}
);
break;
}
}
}}
/>
</Slate>
</div>
</div>
);
};
最后
看到这里,相信你对 Slate 编辑器的「自定义标记」已经有了一定的了解。接下来,你可以参考上面的例子,尝试着去实现一个「文本倾斜」的自定义格式。