Draft的目的是为简单的富文本界面(如注释和聊天消息)解决问题,但它也支持更丰富的编辑体验(如Facebook Notes)。

用户可以在他们的笔记中嵌入图片,或者从他们现有的Facebook照片中加载,亦或是从桌面上上传新的图片。为此,
Draft框架支持块级别的自定义渲染,以渲染富媒体等内容来代替文本。

Draft代码库中的TeX编辑器提供了一个自定义块渲染的例子,它通过KaTeX库将Tex语法即时转化为可编辑的嵌入式公式来进行渲染。

还有一个媒体示例,它展示了音频、图像和视频的自定义块渲染。

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

Custom Block Components(自定义块组件)

在编辑器组件中,可以指定blockRendererFn函数的props。这个prop function允许定义一个高级组件来作为 基于block type,文本或者其他条件的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函数没有返回自定义渲染器对象,编辑器将渲染默认的EditorBlock文本块组件。

The component property defines the component to be used, 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.

component属性定义要使用的组件,而可选的props对象,包含了props属性,通过props.blockProps子属性对象,传递到自定义渲染的组件上。此外,这个可选的editable属性决定了自定义组件是否是contentEditable

It is strongly recommended that you use editable: false if your custom component will not contain text.

强烈建议您使用editable: false(如果您的自定义组件不包含文本)。

If your component contains text as provided by your ContentState, your custom component should compose an EditorBlock component. This will allow the Draft framework to properly maintain cursor behavior within your contents.

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

By defining this function within the context of a higher-level component, the props for this custom component may be bound to that component, allowing instance methods for custom component props.

通过声明这个函数在一个更高阶的组件的context中,这个自定义的组件的props将会跟那个组件绑定,从而允许通过自定义组件的props访问那个组件的实例方法。

Defining custom block components

Within MediaComponent, the most likely use case is that you will want to retrieve entity metadata to render your custom block. You may apply an entity key to the text within a 'atomic' block during EditorState management, then retrieve the metadata for that key in your custom component render() code.

针对MediaComponent,最有可能的使用案例是你将想要检索实体的metadata来渲染你的自定义block。在EditorState管理期间,你可以应用一个实体key在一个'atomic'块中文本上,然后在自定义组件的render()代码中检索该键的元数据。

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

The ContentBlock object and the ContentState record are made available within the custom component, along with the props defined at the top level. By extracting entity information from the ContentBlock and the Entity map, you can obtain the metadata required to render your custom component.

ContentBlock 对象和 ContentState 记录在自定义组件上能够被访问到,通过自定义组件的顶级的props定义。并且通过从ContentBlock 对象和实体map映射中提取实体信息,你能够获取到渲染你的自定义组件的元数据``metadata

Retrieving the entity from the block is admittedly a bit of an awkward API, and is worth revisiting.

从块中检索实体确实是一种笨拙的API,值得再次回顾。

Recommendations and other notes

If your custom block renderer requires mouse interaction, it is often wise to temporarily set your Editor to readOnly={true} during this interaction. In this way, the user does not trigger any selection changes within the editor while interacting with the custom block. This should not be a problem with respect to editor behavior, since interacting with your custom block component is most likely mutually exclusive from text changes within the editor.

如果你的自定义块渲染器包含了鼠标交互,通常明智的做法是在此交互期间将编辑器临时设置为readOnly={true}.
通过这种方式,用户在与自定义块交互时不会在编辑器中触发任何选择更改。对于编辑器行为而言,这应该不是问题,因为与自定义块组件交互很可能与编辑器中的文本更改相互排斥。

The recommendation above is especially important for custom block renderers that involve text input, like the TeX editor example.

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

It is also worth noting that within the Facebook Notes editor, we have not tried to perform any specific SelectionState rendering or management on embedded media, such as rendering a highlight on an embedded photo when selecting it. This is in part because of the rich interaction provided on the media itself, with resize handles and other controls exposed to mouse behavior.

同样值得注意的是,在Facebook Notes编辑器中,我们没有尝试在嵌入式媒体上执行任何特定的SelectionState 渲染或管理,例如在选择嵌入式照片时呈现突出显示。这在一定程度上是因为媒体本身提供了丰富的交互功能,可以根据鼠标的行为调整手柄和其他控件的大小。

Since an engineer using Draft has full awareness of the selection state of the editor and full control over native Selection APIs, it would be possible to build selection behavior on static embedded media if desired. So far, though, we have not tried to solve this at Facebook, so we have not packaged solutions for this use case into the Draft project at this time.

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