Draft 旨在解决直接的富文本界面(如评论和聊天消息)的问题,但它也为 Facebook Notes 等丰富的编辑器体验提供了动力。

用户可以从其现有的 Facebook 照片中加载图像,也可以通过从桌面上载新图像来将图像嵌入 Notes 中。为此,Draft 框架支持在块级别进行自定义呈现,以呈现诸如富媒体之类的内容来代替纯文本。

Draft 资料库中的 TeX editor 提供了一个自定义块渲染的实时示例,其中 TeX 语法通过 KaTeX library 即时转换为可编辑的嵌入式公式渲染 。

这里有一个可参考的 媒体示例,演示了自定义音频、图片和视频的块渲染。

通过使用自定义块渲染器,可以在编辑器框架内引入复杂的丰富交互。

自定义块组件

Editor 组件中, 你可以指定一个 blockRendererFn 属性. 该属性函数允许一个更高级别的组件根据block type ,text,或者其它标准来为 ContentBlock 对象自定义 React 渲染。

例如,我们可能希望用一个自定义的 MediaComponent 来渲染 ‘atomic’ 类型的 ContentBlock 对象。

  1. function myBlockRenderer(contentBlock) {
  2. const type = contentBlock.getType();
  3. if (type === 'atomic') {
  4. return {
  5. component: MediaComponent,
  6. editable: false,
  7. props: {
  8. foo: 'bar',
  9. },
  10. };
  11. }
  12. }
  13. // Then...
  14. import {Editor} from 'draft-js';
  15. class EditorWithMedia extends React.Component {
  16. ...
  17. render() {
  18. return <Editor ... blockRendererFn={myBlockRenderer} />;
  19. }
  20. }

如果该 blockRendererFn 函数未返回任何自定义渲染器对象, Editor 则将渲染默认 EditorBlock 文本块组件。

component 属性定义要使用的组件,while the optional props object includes props that will be passed through to the rendered custom component via the props.blockProps sub property object. In addition, the optional editable property determines whether the custom component is contentEditable.

如果自定义组件不包含文本,则强烈建议您使用 editable: false

如果您的组件包含所提供的文本 ContentState,则您的自定义组件应构成一个 EditorBlock 组件。这将允许 Draft 框架在您的内容中正确维护光标行为。

通过在更高级别的组件的上下文中定义此功能,可以将此自定义组件的 prop 绑定到该组件,从而允许使用自定义组件道具的实例方法。

自定义 block 组件

MediaComponent 中, 很可能有一个使用案例,就是你想取出一个 entity metadata 来渲染你的自定义 block。你可能先在 EditorState 的管理期间,把一个 entity key 应用到 ‘atomic’ 块中的文本,接着在自定义组件的 render() 代码中取出这个 key 的 metadata 。

class MediaComponent extends React.Component {
  render() {
    const {block, contentState} = this.props;
    const {foo} = this.props.blockProps;
    const data = contentState.getEntity(block.getEntityAt(0)).getData();
    // Return a <figure> or some other content using this data.
  }
}

在这个自定义组件中, ContentBlock 对象和 ContentState 记录以及在顶层定义的 props,都是可用的。 通过从 ContentBlockEntity map(映射?) 中提取 entity 信息, 你可以获得渲染自定义组件所需的 metadata。

诚然,从 block 中提取 entity 是个稍显笨拙的 API,值得再次讨论。

建议与备注

如果你的自定义 block 渲染需要用到鼠标交互,通常明智的做法就是在交互过程中把 Editor 设置为 readOnly={true} 。这样,用户在与自定义 block 进行交互时不会在编辑器内触发任何选择更改。就编辑器行为而言,这应该不是问题,因为与自定义 block 组件进行交互很可能与编辑器中的文本更改互斥。

上面的建议对于涉及文本输入的自定义块渲染器尤其重要,例如 TeX编辑器示例。

还值得注意的是,在 Facebook Notes 编辑器中,我们尚未尝试对嵌入式媒体执行任何特定的 SelectionState 渲染或管理,例如在选择嵌入式照片时在其上渲染高亮。部分原因是媒体本身提供了丰富的交互作用,而调整大小的手柄和其他控件则暴露了鼠标行为。

由于使用 Draft 的工程师完全了解编辑器的选择状态并完全控制本机 Selection API,因此,如果需要,可以在静态嵌入式媒体上构建选择行为。不过,到目前为止,我们还没有尝试在 Facebook上 解决此问题,因此我们目前还没有将此用例的解决方案打包到 Draft 项目中。