自定义样式

在之前的指南中我们学习了怎么创建一个自定义的 block 类型,以在不同的容器中呈现文本块。但是 Slate 支持自定义的不仅仅是 blocks

在这篇指南中,我们会像你展示如何添加自定义格式选项,如粗体, 斜体, 代码 或者 删除线

让我们从之前的应用程序继续吧:

  1. const App = () => {
  2. const editor = useMemo(() => withReact(createEditor()), [])
  3. const [value, setValue] = useState([
  4. {
  5. type: 'paragraph',
  6. children: [{ text: 'A line of text in a paragraph.' }],
  7. },
  8. ])
  9. const renderElement = useCallback(props => {
  10. switch (props.element.type) {
  11. case 'code':
  12. return <CodeElement {...props} />
  13. default:
  14. return <DefaultElement {...props} />
  15. }
  16. }, [])
  17. return (
  18. <Slate editor={editor} value={value} onChange={value => setValue(value)}>
  19. <Editable
  20. renderElement={renderElement}
  21. onKeyDown={event => {
  22. if (event.key === '`' && event.ctrlKey) {
  23. event.preventDefault()
  24. const { selection } = editor
  25. const [match] = Editor.nodes(editor, {
  26. match: n => n.type === 'code',
  27. })
  28. Transforms.setNodes(
  29. editor,
  30. { type: match ? 'paragraph' : 'code' },
  31. { match: n => Editor.isBlock(editor, n) }
  32. )
  33. }
  34. }}
  35. />
  36. </Slate>
  37. )
  38. }

现在,我们编辑 onKeyDown 函数,当你按下 ctrl + B 时,它会添加一个粗体格式到你所选择的文本上:

  1. const App = () => {
  2. const editor = useMemo(() => withReact(createEditor()), [])
  3. const [value, setValue] = useState([
  4. {
  5. type: 'paragraph',
  6. children: [{ text: 'A line of text in a paragraph.' }],
  7. },
  8. ])
  9. const renderElement = useCallback(props => {
  10. switch (props.element.type) {
  11. case 'code':
  12. return <CodeElement {...props} />
  13. default:
  14. return <DefaultElement {...props} />
  15. }
  16. }, [])
  17. return (
  18. <Slate editor={editor} value={value} onChange={value => setValue(value)}>
  19. <Editable
  20. renderElement={renderElement}
  21. onKeyDown={event => {
  22. if (!event.ctrlKey) {
  23. return
  24. }
  25. switch (event.key) {
  26. // 当按下 "`" ,保留我们代码块存在的逻辑
  27. case '`': {
  28. event.preventDefault()
  29. const [match] = Editor.nodes(editor, {
  30. match: n => n.type === 'code',
  31. })
  32. Transforms.setNodes(
  33. editor,
  34. { type: match ? 'paragraph' : 'code' },
  35. { match: n => Editor.isBlock(editor, n) }
  36. )
  37. break
  38. }
  39. // 当按下 "B" ,加粗所选择的文本
  40. case 'b': {
  41. event.preventDefault()
  42. Transforms.setNodes(
  43. editor,
  44. { bold: true },
  45. // 应用到文本节点上,
  46. // 如果所选内容仅仅是全部文本的一部分,则拆分它们。
  47. { match: n => Text.isText(n), split: true }
  48. )
  49. break
  50. }
  51. }
  52. }}
  53. />
  54. </Slate>
  55. )
  56. }

非常棒,我们设置好了这个按键处理函数。但是!如果你已经尝试选择文本并按下 ctrl + B,你并不会看到任何的变化。这是因为我们还没有告诉 Slate 如何去渲染 bold 格式。

对于你添加的所有格式,Slate 都会把文本打散到叶子 (leaves)节点,然后你需要告诉 Slate 如何去理解它,就像是对待 block 元素那样。所以让我们来定义一个 Leaf 组件:

  1. // Define a React component to render leaves with bold text.
  2. const Leaf = props => {
  3. return (
  4. <span
  5. {...props.attributes}
  6. style={{ fontWeight: props.leaf.bold ? 'bold' : 'normal' }}
  7. >
  8. {props.children}
  9. </span>
  10. )
  11. }

看起来很熟悉,对吗?

接下来,让我们告诉 Slate 这个叶子节点。为了做到这点,我们会通过 prop 传递一个名为 renderLeaf 的属性到编辑器。同时,让我们添加主动检查逻辑来改变格式

  1. const App = () => {
  2. const editor = useMemo(() => withReact(createEditor()), [])
  3. const [value, setValue] = useState([
  4. {
  5. type: 'paragraph',
  6. children: [{ text: 'A line of text in a paragraph.' }],
  7. },
  8. ])
  9. const renderElement = useCallback(props => {
  10. switch (props.element.type) {
  11. case 'code':
  12. return <CodeElement {...props} />
  13. default:
  14. return <DefaultElement {...props} />
  15. }
  16. }, [])
  17. // 通过 useCallback 定义一个可以记忆的渲染叶子节点的函数
  18. const renderLeaf = useCallback(props => {
  19. return <Leaf {...props} />
  20. }, [])
  21. return (
  22. <Slate editor={editor} value={value} onChange={value => setValue(value)}>
  23. <Editable
  24. renderElement={renderElement}
  25. // 传递渲染叶子节点函数
  26. renderLeaf={renderLeaf}
  27. onKeyDown={event => {
  28. if (!event.ctrlKey) {
  29. return
  30. }
  31. switch (event.key) {
  32. case '`': {
  33. event.preventDefault()
  34. const [match] = Editor.nodes(editor, {
  35. match: n => n.type === 'code',
  36. })
  37. Transforms.setNodes(
  38. editor,
  39. { type: match ? null : 'code' },
  40. { match: n => Editor.isBlock(editor, n) }
  41. )
  42. break
  43. }
  44. case 'b': {
  45. event.preventDefault()
  46. Transforms.setNodes(
  47. editor,
  48. { bold: true },
  49. { match: n => Text.isText(n), split: true }
  50. )
  51. break
  52. }
  53. }
  54. }}
  55. />
  56. </Slate>
  57. )
  58. }
  59. const Leaf = props => {
  60. return (
  61. <span
  62. {...props.attributes}
  63. style={{ fontWeight: props.leaf.bold ? 'bold' : 'normal' }}
  64. >
  65. {props.children}
  66. </span>
  67. )
  68. }

现在,如果你尝试选择一些文本然后按下 ctrl + B,你会看到它变成了粗体!再一次让人惊叹!