编辑器

Slate 编辑器所有的行为,内容,和状态都会汇总到到一个顶级的 Editor 对象中。它的接口是这样的:

  1. interface Editor {
  2. children: Node[]
  3. selection: Range | null
  4. operations: Operation[]
  5. marks: Record<string, any> | null
  6. [key: string]: unknown
  7. // Schema-specific node behaviors.
  8. isInline: (element: Element) => boolean
  9. isVoid: (element: Element) => boolean
  10. normalizeNode: (entry: NodeEntry) => void
  11. onChange: () => void
  12. // Overrideable core actions.
  13. addMark: (key: string, value: any) => void
  14. apply: (operation: Operation) => void
  15. deleteBackward: (unit: 'character' | 'word' | 'line' | 'block') => void
  16. deleteForward: (unit: 'character' | 'word' | 'line' | 'block') => void
  17. deleteFragment: () => void
  18. insertBreak: () => void
  19. insertFragment: (fragment: Node[]) => void
  20. insertNode: (node: Node) => void
  21. insertText: (text: string) => void
  22. removeMark: (key: string) => void
  23. }

看起来比其他接口要稍微复杂一点,因为它包含了所有你定义的顶级函数和自定义域的操作。

children 属性包含了组成编辑器内容的节点的文档树。

selection 属性包含了用户当前选择的文档片段 (如果有的话)。

operations 属性包含了所有自上次更改以来所应用的操作。(Slate 在事件循环两个 tick 之间的分批操作`

marks 属性存储了光标附带的格式,该格式将应用到插入的文本上。

重写行为(Overriding Behaviors)

在之前的指南中我们已经暗示了这一点,你可以通过重写编辑器的函数属性来重写它的任何行为。

比如,如果你想定义链接元素是一个行内元素:

  1. const { isInline } = editor
  2. editor.isInline = element => {
  3. return element.type === 'link' ? true : isInline(element)
  4. }

或者你可能想要重写 insertText 行为来 “链接” 地址:

  1. const { insertText } = editor
  2. editor.insertText = text => {
  3. if (isUrl(text)) {
  4. // ...
  5. return
  6. }
  7. insertText(text)
  8. }

或者你甚至想要自定义“规范”,用来确保链接遵从某些约束:

  1. const { normalizeNode } = editor
  2. editor.normalizeNode = entry => {
  3. const [node, path] = entry
  4. if (Element.isElement(node) && node.type === 'link') {
  5. // ...
  6. return
  7. }
  8. normalizeNode(entry)
  9. }

无论你怎样重写行为,确保调用默认的函数作为后备行为。除非你真的想要完全移除默认行为。(这不是一个好主意!)

辅助函数(Helper Functions)

Editor 接口和所有的 Slate 接口一样,暴露了一些有用的辅助函数用来实现某些行为。比如说:

  1. // 获取路径上指定节点的起点
  2. const point = Editor.start(editor, [0, 0])
  3. // 从一个选择范围(range)获取文档片段
  4. const fragment = Editor.fragment(editor, range)

还有一些基于迭代的辅助函数,比如:

  1. // 迭代选择范围(range)的每一个节点
  2. for (const [node, path] of Editor.nodes(editor, { at: range })) {
  3. // ...
  4. }
  5. // 迭代当前文档片段(selection)的文本节点的每一点(point)
  6. for (const [point] of Editor.positions(editor)) {
  7. // ...
  8. }