0
点赞
收藏
分享

微信扫一扫

risc-v编译飞浆

命令

在编辑富文本内容时,您的用户将执行诸如插入文本、删除文本、拆分段落、添加格式等操作。在底层,这些编辑操作使用转换和操作来表达。但在高级别上,我们将它们称为 “命令”。

命令是表示用户特定意图的高级别操作。它们表示为编辑器接口上的辅助函数。核心中包含了一些常见富文本行为的帮助程序,但鼓励您编写自己的命令来模拟您特定领域的行为。

例如,以下是一些内置命令:

Editor.insertText(editor, '要插入的新文本字符串。')

Editor.deleteBackward(editor, { unit: 'word' })

Editor.insertBreak(editor)

但您可以(而且应该!)定义自己的自定义命令,以模拟您的领域。例如,您可能想要定义一个 formatQuote 命令,或者一个 insertImage 命令,或者一个 toggleBold 命令,具体取决于您允许的内容类型。

命令总是描述要执行的操作,就好像用户正在执行操作一样。因此,它们永远不需要定义执行命令的位置,因为它们始终作用于用户当前的选择。

在底层,Slate负责将每个命令转换为一组低级别的 “操作”,这些操作被应用以产生新值。这就是协同编辑实现成为可能的原因。但是您不必担心这一点,因为它会自动发生。

自定义命令

在定义自定义命令时,您可以创建自己的命名空间:


const MyEditor = {
  ...Editor,

  insertParagraph(editor) {
    // ...
  },
}

在编写自己的命令时,您经常会使用Slate提供的Transforms帮助程序。

转换

转换是一组特定的帮助程序,允许您对文档执行各种特定的更改,例如:

// 在范围内的所有文本节点上设置 "bold" 格式。
// 通常,您会使用Editor.addMark()命令应用类似粗体的样式。
// addMark()命令执行类似的setNodes转换,但它使用了更复杂的匹配函数,以便在markableVoid元素中应用标记。
Transforms.setNodes(
  editor,
  { bold: true },
  {
    at: range,
    match: node => Text.isText(node),
    split: true,
  }
)

// 在文档中某一点的最低块外面包装引用块。
Transforms.wrapNodes(
  editor,
  { type: 'quote', children: [] },
  {
    at: point,
    match: node => Editor.isBlock(editor, node),
    mode: 'lowest',
  }
)

// 插入新文本以替换特定路径处节点中的文本。
Transforms.insertText(editor, '要插入的新文本字符串。', { at: path })

// ... 还有许多其他转换!

转换帮助程序设计成可以一起组合使用。因此,您可能会为每个命令使用一些帮助程序。

编辑器 Editor

Slate编辑器的所有行为、内容和状态都被整合到一个单独的顶级 Editor 对象中。它的接口如下:

interface Editor {
  // 当前编辑器状态
  children: Node[]
  selection: Range | null
  operations: Operation[]
  marks: Omit<Text, 'text'> | null
  // 模式特定的节点行为。
  isInline: (element: Element) => boolean
  isVoid: (element: Element) => boolean
  markableVoid: (element: Element) => boolean
  normalizeNode: (entry: NodeEntry) => void
  onChange: (options?: { operation?: Operation }) => void
  // 可重写的核心操作。
  addMark: (key: string, value: any) => void
  apply: (operation: Operation) => void
  deleteBackward: (unit: 'character' | 'word' | 'line' | 'block') => void
  deleteForward: (unit: 'character' | 'word' | 'line' | 'block') => void
  deleteFragment: () => void
  insertBreak: () => void
  insertSoftBreak: () => void
  insertFragment: (fragment: Node[]) => void
  insertNode: (node: Node) => void
  insertText: (text: string) => void
  removeMark: (key: string) => void
}

它比其他对象稍微复杂一些,因为它包含了定义您自定义的、特定领域行为的所有顶级函数。

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

  • selection 属性包含用户当前的选择,如果有的话。不要直接设置它;使用 Transforms.select

  • operations 属性包含自上次 "change" 被刷新以来应用的所有操作。(因为 Slate 将操作批处理成事件循环的时刻。)

  • marks 属性存储在编辑器插入文本时要应用的格式。如果 marksnull,则格式将从当前选择中获取。不要直接设置它;使用 Editor.addMark 和 Editor.removeMark。

覆盖行为

在之前的指南中我们已经提到过,但是您可以通过覆盖其函数属性来覆盖编辑器的任何行为。

例如,如果您想要定义内联节点的链接元素:

const { isInline } = editor

editor.isInline = element => {
  return element.type === 'link' ? true : isInline(element)
}

或者也许您想要覆盖 insertText 行为以将 URL “链接化”:

const { insertText } = editor

editor.insertText = text => {
  if (isUrl(text)) {
    // ...
    return
  }

  insertText(text)
}

如果您有可以接受粗体或斜体等标记的空 “mention” 元素:

const { isVoid, markableVoid } = editor

editor.isVoid = element => {
  return element.type === 'mention' ? true : isVoid(element)
}

editor.markableVoid = element => {
  return element.type === 'mention' || markableVoid(element)
}

或者您甚至可以定义自定义的 “normalizations” 来确保链接遵循某些约束:

const { normalizeNode } = editor

editor.normalizeNode = entry => {
  const [node, path] = entry

  if (Element.isElement(node) && node.type === 'link') {
    // ...
    return
  }

  normalizeNode(entry)
}

每当您覆盖行为时,请确保调用现有的函数作为默认行为的后备机制。除非您确实想要完全删除默认行为(这很少是一个好主意)。

辅助函数

Editor 接口像所有 Slate 接口一样,公开了在实现某些行为时有用的辅助函数。有许多,许多与编辑器相关的辅助函数。例如:

// 获取指定路径节点的起始点。
const point = Editor.start(editor, [0, 0])

// 在范围内获取片段(文档的一部分)。
const fragment = Editor.fragment(editor, range)

还有许多基于迭代器的辅助函数,例如:

// 遍历范围内的每个节点。
for (const [node, path] of Editor.nodes(editor, { at: range })) {
  // ...
}

// 遍历当前选择中每个文本节点中的每个点。
for (const point of Editor.positions(editor)) {
  // ...
}

举报

相关推荐

0 条评论