Previous:
添加事件回调


定制 Block 节点

在之前的示例中,我们使用了一个 paragraph 段落,不过我们实际上没有告诉 Slate 关于 paragragph block 类型的相关信息。我们只是让编辑器使用内置的默认 <div> 渲染器来渲染节点而已。

不过我们能做的并不止于此。你可以通过 Slate 定义任何类型的自定义 block 节点,比如引用、代码块、列表项等。

下面我们将展示如何实现这一点。让我们从之前的应用开始吧:

class App extends React.Component {

  state = {
    state: initialState
  }

  onChange = ({ state }) => {
    this.setState({ state })
  }

  onKeyDown = (event, data, change) => {
    if (event.which != 55 || !event.shiftKey) return

    event.preventDefault()

    change.insertText('and');
    return true
  }

  render() {
    return (
      <Editor
        state={this.state.state}
        onChange={this.onChange}
        onKeyDown={this.onKeyDown}
      />
    )
  }

}

现在让我们为编辑器添加【代码块】支持。

这里主要的问题是,代码块并不像一个普通的段落那样渲染,它们需要不同的渲染方式。为了实现这一点,我们需要为 code 节点定义一个 "renderer"。

节点(译者注,Node 对象)的 renderer 就是简单的 React 组件,像这样:

// 为代码块定义 React 组件以作为 renderer。
function CodeNode(props) {
  return <pre {...props.attributes}><code>{props.children}</code></pre>
}

非常简单。

看到了 props.attributes 吗?Slate 将需要在 block 顶层渲染的属性通过这种方式传入,这样你就不用自己重复实现了。注意,你 必须 将属性混入组件中。

以及,看到了 props.children 吗?Slate 会自动为你渲染好 block 的所有子节点,并通过 props.children 的形式让你按照使用其它 React 组件的方式来使用。通过这种方式,你就不需要纠结于如何为节点渲染合适的文字子节点或其它内容了。注意,你 必须 将 children 作为组件末层的叶子节点渲染。

现在,我们为 Editor 添加这个 renderer:

function CodeNode(props) {
  return <pre {...props.attributes}><code>{props.children}</code></pre>
}

class App extends React.Component {

  state = {
    state: initialState,
    // 为应用 state 添加 "schema" 以传入编辑器
    schema: {
      nodes: {
        code: CodeNode
      }
    }
  }

  onChange = ({ state }) => {
    this.setState({ state })
  }

  onKeyDown = (event, data, change) => {
    if (event.which != 55 || !event.shiftKey) return

    event.preventDefault()

    change.insertText('and')
    return true
  }

  render() {
    return (
      // 传入 `schema` 属性…
      <Editor
        schema={this.state.schema}
        state={this.state.state}
        onChange={this.onChange}
        onKeyDown={this.onKeyDown}
      />
    )
  }

}

可以了,不过我们现在需要让用户以某种方式真正地将一个普通的 block 节点转换为一个代码块。为此,我们在 onKeyDown 回调中添加一个 ⌘-Alt-C 快捷键即可:

function CodeNode(props) {
  return <pre {...props.attributes}><code>{props.children}</code></pre>
}

class App extends React.Component {

  state = {
    state: initialState,
    schema: {
      nodes: {
        code: CodeNode
      }
    }
  }

  onChange = ({ state }) => {
    this.setState({ state })
  }

  onKeyDown = (event, data, change) => {
    // 按下的不是 "`" + cmd + ctrl 时不返回 change。
    if (event.which != 67 || !event.metaKey || !event.altKey) return

    // 阻止插入 "`" 字符的默认行为。
    event.preventDefault()

    // 否则,将当前选中 block 设置为 "code"。
    change.setBlock('code')
    return true
  }

  render() {
    return (
      <Editor
        schema={this.state.schema}
        state={this.state.state}
        onChange={this.onChange}
        onKeyDown={this.onKeyDown}
      />
    )
  }

}

现在,当你按下 ⌘-Alt-C 时,光标所在的 block 就会转换为代码块了!神奇吧!

不过这里我们遗漏了一点。当你再次按下 ⌘-Alt-C 时,应当将代码块转换回段落。要实现这个特性,我们需要添加一些判断,以根据当前选中的 block 是否为代码块来变换 block 类型:

function CodeNode(props) {
  return <pre {...props.attributes}><code>{props.children}</code></pre>
}

class App extends React.Component {

  state = {
    state: initialState,
    schema: {
      nodes: {
        code: CodeNode
      }
    }
  }

  onChange = ({ state }) => {
    this.setState({ state })
  }

  onKeyDown = (event, data, change) => {
    if (event.which != 67 || !event.metaKey || !event.altKey) return

    event.preventDefault()

    // 判断当前选中 block 是否为代码块。
    const isCode = change.state.blocks.some(block => block.type == 'code')

    // 根据 `isCode` 设置 block 类型。
    change.setBlock(isCode ? 'paragraph' : 'code')
    return true
  }

  render() {
    return (
      <Editor
        schema={this.state.schema}
        state={this.state.state}
        onChange={this.onChange}
        onKeyDown={this.onKeyDown}
      />
    )
  }

}

搞定了!现在如果你在代码块中按下 ⌘-Alt-C,就会将代码块转换回普通段落了!


Next:
应用自定义格式


results matching ""

    No results matching ""