draft.js-机翻

概述

js是一个用于在React中构建富文本编辑器的框架,由一个不变的模型提供支持,并对跨浏览器差异进行抽象。

js允许您构建任何类型的富文本输入,无论您只是希望支持一些内联文本样式,还是构建用于撰写长格式文章的复杂文本编辑器。

安装

js使用了一些IE11无法使用的现代ECMAScript功能,这些功能不是CreateReact应用程序的默认babel配置的一部分。如果您遇到了开箱即用的问题,请尝试在out-of-the-box安装shim或polyfill。

npm install draft-js react react-dom babel-polyfill
# or
yarn add draft-js react react-dom es6-shim

API Changes Notice

在开始之前,请注意,我们最近在草案中改变了实体存储的API。Draft.js版本v0.10.0和v0.11.0同时支持新旧API。接下来将是v0.12.0,它将删除旧API。

Usage

import React from 'react';
import ReactDOM from 'react-dom';
import {Editor, EditorState} from 'draft-js';
import 'draft-js/dist/Draft.css';

class MyEditor extends React.Component {
  constructor(props) {
    super(props);
    this.state = {editorState: EditorState.createEmpty()};
    this.onChange = editorState => this.setState({editorState});
  }

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

ReactDOM.render(<MyEditor />, document.getElementById('container'));

自从React 16.8发布以来,你可以使用Hooks来处理EditorState,而不需要使用类。

import React from 'react';
import ReactDOM from 'react-dom';
import {Editor, EditorState} from 'draft-js';
import 'draft-js/dist/Draft.css';

function MyEditor() {
  const [editorState, setEditorState] = React.useState(
    () => EditorState.createEmpty(),
  );

  return <Editor editorState={editorState} onChange={setEditorState} />;
}

ReactDOM.render(<MyEditor />, document.getElementById('container'));

因为Draft.js支持unicode,你必须在你的HTML文件的块中有以下的meta标签:

<meta charset="utf-8" />

在渲染编辑器时应该包括Draft.css。

API Basics

Controlled Inputs

Editor React组件被构建为一个受控的contentteditable组件,其目标是提供一个基于熟悉的React受控输入API的顶级API。

简单回顾一下,受控输入包括两个关键部分:

  • 表示输入状态的值

  • 一个onChange prop函数来接收对输入的更新

这种方法允许组成输入的组件严格控制输入的状态,同时仍然允许对DOM进行更新,以提供关于用户编写的文本的信息。

const MyInput = () => {
  const [value, setValue] = useState('');
  const onChange = (evt) => setValue(evt.target.value);

  return <input value={value} onChange={onChange} />;
};

顶级组件可以通过value state属性维护对输入状态的控制。

Controlling Rich Text

然而,在React富文本场景中,有两个明显的问题:

  • 纯文本字符串不足以表示富编辑器的复杂状态。
  • 对于contentteditable元素,没有这样的onChange事件可用。

State因此表示为单个不可变的EditorState对象,而onChange在Editor核心中实现,以向顶层提供该状态值。

EditorState对象是编辑器状态的完整快照,包括内容、光标和撤消/重做历史记录。编辑器中对内容和选择的所有更改都将创建新的EditorState对象。注意,由于跨不可变对象的数据持久性,这仍然是有效的。

import {Editor, EditorState} from 'draft-js';

const MyInput = () => {
  const [editorState, setEditorState] = useState(() =>
    EditorState.createEmpty(),
  );

  return <Editor editorState={editorState} onChange={setEditorState} />;
};

对于编辑器DOM中发生的任何编辑或选择更改,onChange处理程序将根据这些更改使用最新的EditorState对象执行。

Rich Styling

现在我们已经建立了顶级API的基础,我们可以进一步研究如何将基本的富样式添加到Draft编辑器中。

EditorState: Yours to Command

上一篇文章介绍了EditorState对象,它是编辑器完整状态的快照,由editor核心通过onChange道具提供。

然而,由于您的顶级React组件负责维护状态,您也可以自由地以您认为合适的任何方式将更改应用到EditorState对象。

例如,对于内联样式和块样式的行为,RichUtils模块提供了许多有用的函数来帮助操作状态。

类似地,Modifier模块还提供了一些常见的操作,允许您进行编辑,包括对文本、样式等的更改。这个模块是一组编辑函数,由更简单、更小的编辑函数组成,以返回所需的EditorState对象。

对于本例,我们将继续使用RichUtils来演示如何在顶级组件中应用基本的富样式。

RichUtils and Key Commands

RichUtils有关于网络编辑器可用的核心关键命令的信息,如Cmd+B(粗体),Cmd+I(斜体),等等。

我们可以通过handleKeyCommand道具观察和处理键命令,并将这些命令挂接到RichUtils中,以应用或删除所需的样式。

import {Editor, EditorState, RichUtils} from 'draft-js';

class MyEditor extends React.Component {
  constructor(props) {
    super(props);
    this.state = {editorState: EditorState.createEmpty()};
    this.onChange = editorState => this.setState({editorState});
    this.handleKeyCommand = this.handleKeyCommand.bind(this);
  }

  handleKeyCommand(command, editorState) {
    const newState = RichUtils.handleKeyCommand(editorState, command);

    if (newState) {
      this.onChange(newState);
      return 'handled';
    }

    return 'not-handled';
  }

  render() {
    return (
      <Editor
        editorState={this.state.editorState}
        handleKeyCommand={this.handleKeyCommand}
        onChange={this.onChange}
      />
    );
  }
}

handleKeyCommand

提供给handleKeyCommand的命令参数是一个字符串值,即要执行的命令的名称。这是从DOM键事件映射的。editorState参数表示最新的编辑器状态,因为它可能在处理键时由draft内部更改。在handleKeyCommand中使用编辑器状态的这个实例。有关这方面的更多信息,以及函数为什么返回已处理或未处理的详细信息,请参见高级主题-键绑定。

Styling Controls in UI

在React组件中,您可以添加按钮或其他控件,以允许用户在编辑器中修改样式。在上面的示例中,我们使用了已知的关键命令,但是我们可以添加更复杂的UI来提供这些丰富的特性。

下面是一个超基本的例子,它有一个“Bold”按钮来切换Bold样式。

class MyEditor extends React.Component {
  // ...

  _onBoldClick() {
    this.onChange(RichUtils.toggleInlineStyle(this.state.editorState, 'BOLD'));
  }

  render() {
    return (
      <div>
        <button onClick={this._onBoldClick.bind(this)}>Bold</button>
        <Editor
          editorState={this.state.editorState}
          handleKeyCommand={this.handleKeyCommand}
          onChange={this.onChange}
        />
      </div>
    );
  }
}

Entities

本文讨论Entity系统,Draft使用该系统用元数据注释文本范围。实体引入了超越样式文本的丰富程度。链接、提及和嵌入内容都可以使用实体实现。

在Draft存储库中,链接编辑器和实体演示提供了实时代码示例,以帮助阐明如何使用实体以及它们的内置行为。

实体API参考提供了在创建、检索或更新实体对象时使用的静态方法的详细信息。

有关Entity API的最新更改以及如何更新应用程序的示例的信息,请参阅我们的v0.10 API迁移指南。

Introduction

实体是表示Draft编辑器中文本范围的元数据的对象。它有三个属性:

  • type:表示实体类型的字符串,例如:“链接”、“提”,“照片”。
  • mutability:不要与immutable-js中的immutability混淆,这个属性表示在编辑器中编辑文本范围时,用这个实体对象注释的文本范围的行为。下面将更详细地说明这一点。
  • data:包含实体元数据的可选对象。例如,一个'LINK'实体可能包含一个数据对象,该对象包含该链接的href值。

所有实体都存储在ContentState记录中。这些实体是通过ContentState和React组件中的键引用的,这些组件用于装饰带注释的范围。(我们目前正在弃用以前用于访问实体的API;见问题# 839。)

使用装饰器或自定义块组件,您可以根据实体元数据向编辑器添加丰富的呈现。

Creating and Retrieving Entities

应该使用contentState.createEntity创建实体。,它接受上述三个属性作为参数。这个方法返回一个更新的ContentState记录,以包含新创建的实体,然后您可以调用ContentState。getLastCreatedEntityKey获取新创建的实体记录的密钥。

该键是在将实体应用到内容时应该使用的值。例如,Modifier模块包含一个applyEntity方法:

const contentState = editorState.getCurrentContent();
const contentStateWithEntity = contentState.createEntity('LINK', 'MUTABLE', {
  url: 'http://www.zombo.com',
});
const entityKey = contentStateWithEntity.getLastCreatedEntityKey();
const contentStateWithLink = Modifier.applyEntity(
  contentStateWithEntity,
  selectionState,
  entityKey,
);
const newEditorState = EditorState.set(editorState, {
  currentContent: contentStateWithLink,
});

对于给定的文本范围,您可以通过在ContentBlock对象上使用getEntityAt()方法提取其关联的实体键,并传入目标偏移值。

const contentState = editorState.getCurrentContent();
const blockWithLinkAtBeginning = contentState.getBlockForKey('...');
const linkKey = blockWithLinkAtBeginning.getEntityAt(0);
const linkInstance = contentState.getEntity(linkKey);
const {url} = linkInstance.getData();

Mutability:可变性

实体可能有三个“可变”值之一。它们之间的区别在于,当用户编辑它们时,它们的行为方式。

请注意,DraftEntityInstance对象总是不可变的Records,这个属性只是用来指示注释文本在编辑器中可能如何“变异”。(未来的更改可能会重命名该属性,以避免在命名方面的潜在混淆。)

Immutable:不可变的

如果不从文本中删除实体注释,就无法更改此文本。具有这种可变类型的实体实际上是原子的。

例如,在Facebook输入中,添加一个页面的提及(如巴拉克•奥巴马)。然后,要么在提到的文本中添加一个字符,要么尝试删除一个字符。注意,当添加或删除字符时,实体将被删除。

在文本必须完全匹配相关元数据且不能被更改的情况下,这个可变值非常有用。

Mutable:可变的

这篇文章可以随意修改。例如,链接文本通常是“可变的”,因为href和链接文本不是紧密耦合的。

Segmented:分段

被“分段”的实体以与“不可变”实体相同的方式紧密耦合到它们的文本上,但允许通过删除进行定制。

例如,在Facebook输入中,添加对朋友的提及。然后,在文本中添加一个字符。注意,实体从整个字符串中被删除,因为你提到的朋友可能不会在你的文本中更改他们的名字。

接下来,尝试删除提及的字符或单词。注意,只有您已删除的提及部分被删除。这样,我们可以允许提到的名字很短。

Modifying Entities

由于DraftEntityInstance记录是不可变的,所以您不能直接更新实例上的data属性。

相反,有两个Entity方法可用于修改实体:mergeData和replaceData。前者允许通过传入一个对象进行合并来更新数据,而后者则完全交换新数据对象。

Using Entities for Rich Content

本节的下一篇文章将介绍装饰器对象的使用,它可用于检索实体以实现呈现目的。

链接编辑器示例提供了实体创建和装饰的工作示例。

https://github.com/facebook/draft-js/tree/master/examples/draft-0-10-0/link

v0.10 API Migration

js v0.10版本包含了一个用于管理DraftEntity数据的API更改;全局的“DraftEntity”模块已被弃用,而DraftEntity实例将作为ContentState的一部分进行管理。这意味着以前在DraftEntity上访问的方法现在被移动到ContentState记录。

旧API在v0.11中被设置为永久删除,但现在将在v0.12中删除。一定要迁移过来!

这个API改进为v0.12中提供的许多好处解锁了路径:

DraftEntity实例和存储将是不可变的。

DraftEntity将不再是全局可访问的。

对实体数据的任何更改都会触发重新渲染。

Quick Overview

以下是一个快速列表,列出了更改的内容以及如何更新应用程序:

Creating an Entity

Old Syntax

const entityKey = Entity.create(urlType, 'IMMUTABLE', {src: urlValue});

New Syntax

const contentStateWithEntity = contentState.createEntity(urlType, 'IMMUTABLE', {
  src: urlValue,
});
const entityKey = contentStateWithEntity.getLastCreatedEntityKey();

Getting an Entity

Old Syntax

const entityInstance = Entity.get(entityKey);
// entityKey is a string key associated with that entity when it was created

New Syntax

const entityInstance = contentState.getEntity(entityKey);
// entityKey is a string key associated with that entity when it was created

Decorator strategy arguments change:装饰器策略参数改变

Old Syntax

const compositeDecorator = new CompositeDecorator([
  {
    strategy: (contentBlock, callback) =>
      exampleFindTextRange(contentBlock, callback),
    component: ExampleTokenComponent,
  },
]);

New Syntax

const compositeDecorator = new CompositeDecorator([
  {
    strategy: (contentBlock, callback, contentState) => (
      contentBlock, callback, contentState
    ),
    component: ExampleTokenComponent,
  },
]);

注意,ExampleTokenComponent将接收contentState作为一个道具。

为什么现在要将“contentState”传递到decorator策略中?因为如果我们的策略是在contentBlock中找到某些实体,我们可能需要它:

const mutableEntityStrategy = function(contentBlock, callback, contentState) {
  contentBlock.findEntityRanges(character => {
    const entityKey = character.getEntity();
    if (entityKey === null) {
      return false;
    }
    // To look for certain types of entities,
    // or entities with a certain mutability,
    // you may need to get the entity from contentState.
    // In this example we get only mutable entities.
    return contentState.getEntity(entityKey).getMutability() === 'MUTABLE';
  }, callback);
};

Decorator Strategies that find Entities:找到实体的装饰策略

Old Syntax

function findLinkEntities(contentBlock, callback) {
  contentBlock.findEntityRanges(character => {
    const entityKey = character.getEntity();
    return entityKey !== null && Entity.get(entityKey).getType() === 'LINK';
  }, callback);
}

New Syntax

function findLinkEntities(contentBlock, callback, contentState) {
  contentBlock.findEntityRanges(character => {
    const entityKey = character.getEntity();
    return (
      entityKey !== null &&
      contentState.getEntity(entityKey).getType() === 'LINK'
    );
  }, callback);
}

Decorators:修饰器

内联样式和块样式并不是我们想要添加到编辑器中的唯一一种丰富样式。例如,Facebook的评论输入为提到和话题标签提供蓝色背景高亮显示。

为了支持自定义富文本的灵活性,Draft提供了一个“装饰器”系统。这个tweet示例提供了一个实际的装饰器示例。

CompositeDecorator

装饰器的概念是基于扫描给定ContentBlock的内容,找出与定义的策略匹配的文本范围,然后用指定的React组件呈现它们。

您可以使用CompositeDecorator类来定义所需的装饰器行为。这个类允许您提供多个DraftDecorator对象,并使用每种策略依次搜索文本块。

装饰器存储在EditorState记录中。当创建一个新的EditorState对象时,例如通过EditorState. createempty(),可以选择提供一个装饰器。

Under the hood:

当内容在Draft编辑器中发生变化时,生成的EditorState对象将用它的装饰器评估新的ContentState,并标识要装饰的范围。此时将形成一个完整的块、装饰器和内联样式树,并作为呈现输出的基础。

通过这种方式,我们总是确保当内容更改时,呈现的装饰与我们的EditorState同步。装饰器的概念是基于扫描给定ContentBlock的内容,找出与定义的策略匹配的文本范围,然后用指定的React组件呈现它们。

您可以使用CompositeDecorator类来定义所需的装饰器行为。这个类允许您提供多个DraftDecorator对象,并使用每种策略依次搜索文本块。

装饰器存储在EditorState记录中。当创建一个新的EditorState对象时,例如通过EditorState. createempty(),可以选择提供一个装饰器。

例如,在“Tweet”编辑器的例子中,我们使用一个复合装饰器来搜索@-handle字符串和hashtag字符串:

const compositeDecorator = new CompositeDecorator([
  {
    strategy: handleStrategy,
    component: HandleSpan,
  },
  {
    strategy: hashtagStrategy,
    component: HashtagSpan,
  },
]);

这个复合装饰器将首先扫描给定的文本块以查找@-handle匹配,然后查找hashtag匹配。

// Note: these aren't very good regexes, don't use them!
const HANDLE_REGEX = /\@[\w]+/g;
const HASHTAG_REGEX = /\#[\w\u0590-\u05ff]+/g;

function handleStrategy(contentBlock, callback, contentState) {
  findWithRegex(HANDLE_REGEX, contentBlock, callback);
}

function hashtagStrategy(contentBlock, callback, contentState) {
  findWithRegex(HASHTAG_REGEX, contentBlock, callback);
}

function findWithRegex(regex, contentBlock, callback) {
  const text = contentBlock.getText();
  let matchArr, start;
  while ((matchArr = regex.exec(text)) !== null) {
    start = matchArr.index;
    callback(start, start + matchArr[0].length);
  }
}

策略函数使用匹配文本范围的开始值和结束值执行提供的回调。

Decorator Components

对于修饰过的文本范围,必须定义一个React组件来呈现它们。这些往往是带有CSS类或样式的普通span元素。

在我们当前的示例中,CompositeDecorator对象将handlesspan和HashtagSpan作为用于装饰的组件。这些是基本的无状态组件:

const HandleSpan = props => {
  return (
    <span {...props} style={styles.handle}>
      {props.children}
    </span>
  );
};

const HashtagSpan = props => {
  return (
    <span {...props} style={styles.hashtag}>
      {props.children}
    </span>
  );
};

Decorator组件将在props中接收各种元数据,包括contentState的副本、entityKey(如果有的话)和blockKey。要了解提供给Decorator组件的完整的道具列表,请参阅DraftDecoratorComponentProps类型。

注意,道具。子元素传递到呈现的输出。这样做是为了确保文本在修饰的范围内呈现。

您可以对链接使用相同的方法,如我们的链接示例所示。

Beyond CompositeDecorator

提供给EditorState的装饰器对象只需要匹配DraftDecoratorType Flow类型定义的期望,这意味着你可以创建任何你想要的装饰器类,只要它们匹配预期的类型——你不需要被CompositeDecorator绑定。

Setting new decorators

此外,在正常状态传播期间,可以通过不可变的方法动态地在EditorState上设置新的装饰器值。

这意味着在你的应用程序工作流中,如果你的装饰器失效或需要修改,你可以创建一个新的装饰器对象(或使用null删除所有装饰)和EditorState.set()来使用新的装饰器设置。

例如,如果出于某种原因,我们希望在用户与编辑器交互时禁用@-handle装饰的创建,那么可以执行以下操作:

function turnOffHandleDecorations(editorState) {
  const onlyHashtags = new CompositeDecorator([
    {
      strategy: hashtagStrategy,
      component: HashtagSpan,
    },
  ]);
  return EditorState.set(editorState, {decorator: onlyHashtags});
}

这个editorState的ContentState将用新的装饰器重新评估,@-handle装饰将不再出现在下一个渲染通道中。

同样,由于跨不可变对象的数据持久性,这保持了内存效率。

Key Bindings

Editor组件提供了通过keyBindingFn prop为编辑器定义自定义键绑定的灵活性。这允许您在编辑器组件中匹配关键命令和行为。

Defaults

默认的键绑定函数是getDefaultKeyBinding。

由于Draft框架保持了对DOM呈现和行为的严格控制,必须通过键绑定系统捕获和路由基本的编辑命令。

getDefaultKeyBinding将已知的os级编辑器命令映射到DraftEditorCommand字符串,然后对应组件处理程序中的行为。

例如,Ctrl+Z (Win)和Cmd+Z (OSX)映射到'undo'命令,然后路由我们的处理程序执行EditorState.undo()。

Customization:定制

您可以提供自己的键绑定函数来提供自定义命令字符串。

建议您的函数使用getDefaultKeyBinding作为失败案例,以便您的编辑器可以从默认命令中受益。

通过自定义命令字符串,您可以实现handleKeyCommand prop函数,该函数允许您将命令字符串映射到所需的行为。如果handleKeyCommand返回'handled',则认为该命令已处理。如果它返回'not-handled',命令将失败。

Example

假设我们有一个编辑器,它应该具有“保存”机制,定期将您的内容作为草稿副本写入服务器。

首先,让我们定义键绑定函数:

import {getDefaultKeyBinding, KeyBindingUtil} from 'draft-js';
const {hasCommandModifier} = KeyBindingUtil;

function myKeyBindingFn(e: SyntheticKeyboardEvent): string | null {
  if (e.keyCode === 83 /* `S` key */ && hasCommandModifier(e)) {
    return 'myeditor-save';
  }
  return getDefaultKeyBinding(e);
}

我们的函数接收一个键事件,并检查它是否符合我们的标准:它必须是一个S键,并且它必须有一个命令修饰符,即OSX的命令键,或者其他的控制键。

如果命令是匹配的,返回一个命名命令的字符串。否则,使用默认的键绑定。

在我们的编辑器组件中,我们可以通过handleKeyCommand道具来使用这个命令:

import {Editor} from 'draft-js';
class MyEditor extends React.Component {

  constructor(props) {
    super(props);
    this.handleKeyCommand = this.handleKeyCommand.bind(this);
  }
  // ...

  handleKeyCommand(command: string): DraftHandleValue {
    if (command === 'myeditor-save') {
      // Perform a request to save your contents, set
      // a new `editorState`, etc.
      return 'handled';
    }
    return 'not-handled';
  }

  render() {
    return (
      <Editor
        editorState={this.state.editorState}
        handleKeyCommand={this.handleKeyCommand}
        keyBindingFn={myKeyBindingFn}
        ...
      />
    );
  }
}

' myeeditor -save'命令可以用于我们的自定义行为,并且返回'handled'指示编辑器该命令已经被处理,不需要做更多的工作。

通过在所有其他情况下返回'not-handled',默认命令就可以变成默认处理程序行为。

Managing Focus

在React组件中管理文本输入焦点可能是一项棘手的任务。浏览器的焦点/模糊API是强制性的,所以纯粹通过render()的声明性方法设置或删除焦点往往会让人感到尴尬和不正确,并且需要尝试控制焦点状态。

考虑到这一点,在Facebook,我们经常选择将focus()方法暴露在包装文本输入的组件上。这打破了声明式范例,但它也简化了工程师在应用程序中成功管理焦点行为所需的工作。

Editor组件遵循这种模式,因此组件上有一个公共focus()方法可用。这允许您在高级组件中使用ref来在需要时直接调用组件上的focus()。

组件中的事件监听器将观察焦点更改,并按照预期通过onChange传播它们,因此状态和DOM将正确地保持同步。

Translating container clicks to focus

您的高级组件很可能将Editor组件包装在某种容器中,可能会使用填充来匹配您的应用程序。

默认情况下,如果用户在试图聚焦编辑器时单击此容器内但在呈现的编辑器之外,编辑器将不会意识到单击事件。因此,建议您在容器组件上使用单击侦听器,并使用上面描述的focus()方法将焦点应用到编辑器。

例如,明文编辑器示例就使用这种模式。

Block Styling

在Editor中,一些块类型被赋予默认的CSS样式,以限制工程师使用定制编辑器启动和运行所需的基本配置的数量。

通过为编辑器定义blockStyleFn prop函数,可以指定在呈现时应用于块的类。

DraftStyleDefault.css

Draft库在DraftStyleDefault.css中包含了默认的块CSS样式。(注意,CSS类名上的注释是Facebook内部CSS模块管理系统的产物。)

这些CSS规则主要用于为列表项提供默认样式,否则调用者将负责管理自己的默认列表样式。

blockStyleFn

编辑器上的blockStyleFn道具允许你在渲染时定义CSS类来样式块。例如,您可能希望为'blockquote'类型的块设置花哨的斜体文本样式。

function myBlockStyleFn(contentBlock) {
  const type = contentBlock.getType();
  if (type === 'blockquote') {
    return 'superFancyBlockquote';
  }
}

// Then...
import {Editor} from 'draft-js';
class EditorWithFancyBlockquotes extends React.Component {
  render() {
    return <Editor ... blockStyleFn={myBlockStyleFn} />;
  }
}

Then, in your own CSS:

.superFancyBlockquote {
  color: #999;
  font-family: 'Hoefler Text', Georgia, serif;
  font-style: italic;
  text-align: center;
}

Custom Block Rendering

本文讨论如何自定义Draft默认块呈现。块呈现用于定义支持的块类型及其各自的呈现器,以及将粘贴的内容转换为已知的Draft块类型。

当粘贴内容或调用convertFromHTML时,Draft将通过将Draft块呈现映射与匹配的标记匹配,将粘贴的内容转换为相应的块呈现类型。

Draft default block render map

HTML element Draft block type
<h1/> header-one
<h2/> header-two
<h3/> header-three
<h4/> header-four
<h5/> header-five
<h6/> header-six
<blockquote/> blockquote
<pre/> code-block
<figure/> atomic
<li/> unordered-list-item,ordered-list-item**
<div/> unstyled***

** - Block type will be based on the parent <ul/> or <ol/>

*** - Any block that is not recognized by the block rendering mapping will be treated as unstyled

Configuring block render map

通过向编辑器blockRender道具传递一个不可变的map,可以覆盖草案的默认块渲染映射。

覆盖默认块渲染地图的例子:

// The example below deliberately only allows
// 'heading-two' as the only valid block type and
// updates the unstyled element to also become a h2.
const blockRenderMap = Immutable.Map({
  'header-two': {
    element: 'h2'
  },
  'unstyled': {
    element: 'h2'
  }
});

class RichEditor extends React.Component {
  render() {
    return (
      <Editor
        ...
        blockRenderMap={blockRenderMap}
      />
    );
  }
}

有些情况下,我们只想添加新的块类型,而不是覆盖默认值。这可以通过使用DefaultDraftBlockRenderMap引用来创建一个新的blockRenderMap

扩展默认块渲染地图的例子:

const blockRenderMap = Immutable.Map({
  'section': {
    element: 'section'
  }
});

// Include 'paragraph' as a valid block and updated the unstyled element but
// keep support for other draft default block types
const extendedBlockRenderMap = Draft.DefaultDraftBlockRenderMap.merge(blockRenderMap);

class RichEditor extends React.Component {
  render() {
    return (
      <Editor
        ...
        blockRenderMap={extendedBlockRenderMap}
      />
    );
  }
}

当Draft解析粘贴的HTML时,它将HTML元素映射回Draft块类型。如果希望指定映射到特定块类型的其他HTML元素,可以将数组aliasedElements添加到块配置中。

使用无样式块类型别名的示例:

'unstyled': {
  element: 'div',
  aliasedElements: ['p'],
}

Custom block wrappers

默认情况下,html元素用于包装块类型。但是,也可以向blockRenderMap提供一个react组件来包装EditorBlock。

在粘贴期间,或调用convertFromHTML时,html将被扫描以寻找匹配的标记元素。当在blockRenderMap上有一个包装器的定义来包装特定的块类型时,就会使用包装器。例如:

Draft使用包装器在


中包装
,但包装器也可以用于包装任何其他自定义块类型。

扩展默认块渲染图的例子,为自定义块使用react组件:

class MyCustomBlock extends React.Component {
  constructor(props) {
    super(props);
  }

  render() {
    return (
      <div className='MyCustomBlock'>
        {/* here, this.props.children contains a <section> container, as that was the matching element */}
        {this.props.children}
      </div>
    );
  }
}

const blockRenderMap = Immutable.Map({
  'MyCustomBlock': {
    // element is used during paste or html conversion to auto match your component;
    // it is also retained as part of this.props.children and not stripped out
    element: 'section',
    wrapper: <MyCustomBlock />,
  }
});

// keep support for other draft default block types and add our myCustomBlock type
const extendedBlockRenderMap = Draft.DefaultDraftBlockRenderMap.merge(blockRenderMap);

class RichEditor extends React.Component {
  ...
  render() {
    return (
      <Editor
        ...
        blockRenderMap={extendedBlockRenderMap}
      />
    );
  }
}

Custom Block Components

Draft旨在解决简单的富文本界面(如评论和聊天消息)的问题,但它也提供了更丰富的编辑体验,如Facebook Notes。

用户可以在Notes中嵌入图片,可以从现有的Facebook照片中加载图片,也可以从桌面上传新的图片。为此,Draft框架在块级别支持自定义呈现,以呈现富媒体等内容来代替纯文本。

Draft存储库中的TeX编辑器提供了一个定制块呈现的实时示例,TeX语法通过KaTeX库被动态转换为可编辑的嵌入式公式呈现。

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

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

Custom Block Components

在Editor组件中,可以指定blockRendererFn prop。这个prop函数允许高级组件根据块类型、文本或其他标准为ContentBlock对象定义自定义React呈现。

例如,我们可能希望使用自定义的mediaccomponent来渲染“原子”类型的ContentBlock对象。

function myBlockRenderer(contentBlock) {
  const type = contentBlock.getType();
  if (type === 'atomic') {
    return {
      component: MediaComponent,
      editable: false,
      props: {
        foo: 'bar',
      },
    };
  }
}

// Then...
import {Editor} from 'draft-js';
class EditorWithMedia extends React.Component {
  ...
  render() {
    return <Editor ... blockRendererFn={myBlockRenderer} />;
  }
}

如果blockRendererFn函数没有返回自定义渲染器对象,编辑器将呈现默认的EditorBlock文本块组件。

component属性定义要使用的组件,而可选的props对象包括将通过props传递给呈现的自定义组件的道具。blockProps子属性对象。另外,可选的edititable属性决定自定义组件是否contentteditable。

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

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

通过在高级组件的上下文中定义此函数,可以将此定制组件的道具绑定到该组件,从而允许定制组件道具的实例方法。

Defining custom block components

在MediaComponent中,最可能的用例是,您希望检索实体元数据来呈现自定义块。你可以在EditorState管理过程中对“原子”块中的文本应用一个实体键,然后在自定义组件render()代码中检索该键的元数据。

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记录在自定义组件中可用,以及在顶层定义的道具。通过从ContentBlock和entity映射中提取实体信息,您可以获得呈现自定义组件所需的元数据。

诚然,从块中检索实体是一个有点笨拙的API,值得重新访问。

Recommendations and other notes

如果您的自定义块渲染器需要鼠标交互,通常明智的做法是在此交互期间临时将Editor设置为readOnly={true}。这样,用户在与自定义块交互时不会在编辑器中触发任何选择更改。这应该不是编辑器行为的问题,因为与自定义块组件的交互很可能与编辑器中的文本更改是互斥的。

上面的建议对于包含文本输入的自定义块呈现器特别重要,比如TeX编辑器的例子。

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

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

Complex Inline Styles

在编辑器中,您可能希望提供广泛的内联样式行为,远远超出粗体/斜体/下划线的基础。例如,您可能希望支持颜色、字体族、字体大小等多种类型。此外,您想要的样式可能重叠或相互排斥。

富编辑器和彩色编辑器示例实际演示了复杂的内联样式行为。

Model

在Draft模型中,内联样式是在字符级别表示的,使用不可变的OrderedSet来定义应用于每个字符的样式列表。这些样式由字符串标识。(详情请参阅CharacterMetadata。)

例如,考虑文本“Hello world”。字符串的前六个字符由空集OrderedSet()表示。最后5个字符由OrderedSet.of('BOLD')表示。为了方便起见,我们可以将这些OrderedSet对象视为数组,尽管实际上我们积极地重用相同的不可变对象。

本质上,我们的风格是:

[
  [], // H
  [], // e
  // ...
  ['BOLD'], // w
  ['BOLD'], // o
  // etc.
];

Overlapping Styles

现在让我们假设我们希望把字符的中间部分也变成斜体:Hello world。这个操作可以通过Modifier API执行。

最终结果将通过在相关的OrderedSet对象中也包含'ITALIC'来容纳重叠。

[
  [], // H
  [], // e
  ['ITALIC'], // l
  // ...
  ['BOLD', 'ITALIC'], // w
  ['BOLD', 'ITALIC'], // o
  ['BOLD'], // r
  // etc.
];

当决定如何呈现内联样式的文本时,Draft将确定样式相同的字符的连续范围,并将这些字符一起呈现在样式跨度节点中。

Mapping a style string to CSS

默认情况下,Editor提供了对基本内联样式列表的支持:'BOLD'、'ITALIC'、'UNDERLINE'和'CODE'。它们被映射到普通的CSS样式对象,这些对象用于将样式应用到相关的范围。

对于您的编辑器,您可以定义自定义样式字符串来包含这些默认值,或者您可以覆盖基本样式的默认样式对象。

在编辑器用例中,您可以提供customStyleMap道具来定义样式对象。(参见多彩的编辑器以获取实际示例。)

例如,您可能想添加一个“strike - through”样式。为此,定义一个自定义样式映射:

import {Editor} from 'draft-js';

const styleMap = {
  'STRIKETHROUGH': {
    textDecoration: 'line-through',
  },
};

class MyEditor extends React.Component {
  // ...
  render() {
    return (
      <Editor
        customStyleMap={styleMap}
        editorState={this.state.editorState}
        ...
      />
    );
  }
}

当渲染时,textDecoration: line-through样式将应用于所有具有strike -through样式的字符范围。

Nested Lists:嵌套列表

Draft框架提供了对嵌套列表的支持,如Facebook Notes编辑器所示。在那里,您可以使用Tab和Shift+Tab来添加或删除列表项的深度。

Implementation

RichUtils模块提供了一个方便的onTab方法来管理这种行为,应该足以满足大多数嵌套列表的需求。您可以使用编辑器上的onTab道具来使用这个实用程序。

默认情况下,通过DraftStyleDefault.css将样式应用于列表项,以设置适当的间距和列表样式行为。

注意,除了'ordered-list-item'和'unordered-list-item',目前还不支持对任何类型的块进行深度处理。

Text Direction

Facebook支持数十种语言,这意味着我们的文本输入需要足够灵活,以处理相当多的多样性。

例如,我们希望RTL语言(如阿拉伯语和希伯来语)的输入行为能够满足用户的期望。我们还希望能够支持带有LTR和RTL文本的编辑器内容。

为此,Draft使用bidi算法在每个块的基础上确定适当的文本对齐和方向。

当用户键入时,文本会自动呈现为LTR或RTL方向。你不需要做任何事来确定自己的方向。

Text Alignment

虽然语言在合成过程中会按照内容字符的定义自动向左或向右对齐,但工程师也可以手动设置编辑器内容的文本对齐方式。

这可能是有用的,例如,如果编辑器要求内容严格居中,或需要保持文本与另一个UI元素对齐。

因此,Editor组件提供了一个textAlignment道具,带有一小组值:'left'、'center'和'right'。使用这些值,编辑器的内容将与指定的方向对齐,而不管语言和字符集。

EditorState Race Conditions

Draft Editor是一个受控的输入组件(你可以在API基础部分详细阅读),这意味着对Editor状态的更改将通过onChange向上传播,并由应用程序将其反馈给Editor组件。

这个周期通常是这样的:

...
this.onChange = function(editorState) {
  this.setState({editorState: editorState});
}
...
<Editor
  editorState={this.state.editorState}
  onChange={this.onChange}
  placeholder="Enter some text..."
/>

不同的浏览器事件可以触发编辑器创建一个新的状态并调用onChange。例如,当用户将文本粘贴到其中时,Draft将解析新内容并创建必要的数据结构来表示它。

这个循环工作得很好,但是,由于setState调用,它是一个异步操作。这在设置状态和用新状态呈现编辑器之间引入了延迟。在此期间可以执行其他JS代码。

像这样的非原子操作可能会引入竞争条件。下面是一个例子:假设您想要删除来自粘贴的所有文本样式。这可以通过监听onPaste事件并从EditorState中移除所有样式来实现:

this.onPaste = function() {
  this.setState({
    editorState: removeEditorStyles(this.state.editorState),
  });
};

然而,这不会像预期的那样工作。现在有两个事件处理程序在相同的浏览器事件中设置新的EditorState。因为事件处理程序一个接一个地运行,所以最后一个setState将占优势。这是它在JS时间轴中的样子:

如您所见,由于setState是一个异步操作,第二个setState将覆盖在第一个setState上设置的任何内容,从而使Editor失去粘贴文本的所有内容。

您可以在这个正在运行的示例中观察和研究竞态条件。该示例还包含了突出显示JS时间轴的日志记录,因此请确保打开开发人员工具。

作为经验法则,避免为同一个事件使用不同的事件处理程序来操作EditorState。使用setTimeout运行setState也可能使您陷入相同的情况。任何时候,当你觉得你正在“失去状态”,确保你没有覆盖它之前的编辑器重新呈现。

Best Practices

既然您了解了这个问题,那么您能做些什么来避免它呢?一般来说,要注意从哪里获取EditorState。如果您使用的是本地的(存储在this.state中),那么它有可能不是最新的。为了最小化这个问题,Draft在其大多数回调函数中提供了最新的EditorState实例。在您的代码中,您应该使用提供的EditorState而不是本地的EditorState,以确保您是基于最新的更改。下面是编辑器支持的回调列表:

editorState handleReturn(事件)

editorState handleKeyCommand(命令)

editorState handleBeforeInput(字符)

handlePastedText(文本、html、editorState)

然后可以使用下面的方法在竞争条件下重写paste示例:

this.handlePastedText = (text, styles, editorState) => {
  this.setState({
    editorState: removeEditorStyles(text, editorState),
  });
};
//...
<Editor
  editorState={this.state.editorState}
  onChange={this.onChange}
  handlePastedText={this.handlePastedText}
  placeholder="Enter some text..."
/>;

使用handlepastetext,你可以自己实现粘贴行为。

注意:如果你需要在你的编辑器中有这个行为,你可以通过设置编辑器的strippasstedstyles属性为true来实现。

Issues and Pitfalls

本文讨论了Draft编辑器框架的一些已知问题,以及我们在Facebook使用该框架时遇到的一些常见缺陷。

Common Pitfalls

Delayed state updates

单向数据管理的一个常见模式是使用setTimeout或其他机制批处理或延迟对数据存储的更新。更新存储,然后对相关React组件发出更改,以传播重新呈现。

然而,当使用Draft编辑器的React应用程序出现延迟时,可能会导致严重的交互问题。这是因为编辑器期望即时更新和呈现与用户的输入行为保持同步。延迟可能会阻止更新通过编辑器组件树传播,这可能会导致击键和更新之间的断开。

为了在仍然使用延迟或批处理机制的情况下避免这种情况,您应该将延迟行为与编辑器状态传播分开。也就是说,必须始终允许EditorState毫不延迟地传播到Editor组件,并独立地执行不影响Editor组件状态的批处理更新。

修改翻译结果

Missing Draft.css

Draft框架包含了一些用于编辑器的CSS资源,可以通过一个构建文件Draft. CSS获得。

在呈现编辑器时应该包含这种CSS,因为这些样式设置了文本对齐、空格和其他重要特性的默认值。如果没有它,您可能会遇到块定位、对齐和游标行为方面的问题。

如果您选择独立于Draft.css编写自己的CSS,则很可能需要复制许多默认样式。

Known Issues

Custom OSX Keybindings#

因为浏览器不能访问os级别的自定义键绑定,所以不可能拦截没有映射到默认系统键绑定的编辑意图行为。

这样做的结果是,使用自定义键绑定的用户可能会遇到Draft编辑器的问题,因为他们的键命令可能不会按照预期的方式运行。

Browser plugins/extensions

与任何React应用程序一样,修改DOM的浏览器插件和扩展可能会导致Draft编辑器崩溃。

例如,语法检查器可以修改contentteditable元素中的DOM,添加下划线和背景等样式。因为如果浏览器不符合React的预期,则React无法协调DOM,所以编辑器状态可能无法与DOM保持同步。

某些旧的广告拦截器也会破坏原生DOM Selection API——无论如何都是个坏主意!——而且由于Draft依赖于这个API来维护受控的选择状态,这可能会给编辑器交互带来麻烦。

IME and Internet Explorer

从IE11开始,ie浏览器在某些国际输入法上出现了明显的问题,尤其是韩语输入法。

Polyfills

Draft的一些代码及其依赖项使用了ES2015语言特性。在构建Draft时,像class这样的语法特性会通过Babel编译掉,但它不包括现在许多现代浏览器中包含的api的腻子填充(例如:String.prototype.startsWith)。我们希望您的浏览器能够支持这些api,或者在polyfill的帮助下。一个这样的polyfill是es6-shim,我们在许多例子中使用,但你可以自由使用babel-polyfill,如果这更适合你的场景。

当使用任何一种polyfill/shim时,您应该尽早将其包含在您的应用程序的入口点(最低限度,在您导入Draft之前)。例如,使用create-react-app并针对IE11, src/index.js可能是导入polyfill的好地方:

src/index.js

import 'babel-polyfill';
// or
import 'es6-shim';

import React from 'react';
import ReactDOM from 'react-dom';
import App from './App';
import './index.css';

ReactDOM.render(<App />, document.getElementById('root'));

Mobile Not Yet Supported

js正朝着完全支持移动浏览器的方向发展,但目前还没有正式支持移动浏览器。有一些已知的问题影响Android和iOS -看到问题标签' Android '或' iOS '的当前状态。

Editor Component

本文讨论核心受控contentteditable组件本身Editor的API和支持。Props在DraftEditorProps中定义。

Props

Basics

See API Basics for an introduction.

editorState

editorState: EditorState;

onChange

onChange: (editorState: EditorState) => void

当编辑和选择发生变化时,编辑器将执行onChange函数。

Presentation (Optional)

placeholder

placeholder?: string

当编辑器为空时显示的可选占位符字符串。

注意:您可以根据需要使用CSS来设置或隐藏占位符。例如,在富编辑器示例中,当用户在空编辑器中更改块样式时,占位符是隐藏的。这是因为当样式改变时,占位符可能不会与光标对齐。

textAlignment

textAlignment?: DraftTextAlignment

可以选择设置此编辑器的覆盖文本对齐方式。无论输入文本的默认文本方向如何,此对齐值将应用于整个内容。

如果您希望将文本居中或在一个方向对齐,以使其适合于您的UI设计,您可以使用此功能。

如果未设置此值,则文本对齐将基于编辑器中的字符,以每个块为基础。

textDirectionality

textDirectionality?: DraftTextDirectionality

可以选择设置此编辑器的覆盖文本方向。值包括'RTL'用于从右到左的文本,如希伯来语或阿拉伯语,以及'LTR'用于从左到右的文本,如英语或西班牙语。无论输入文本的默认文本方向如何,此方向性将应用于整个内容。

如果没有设置这个值,文本方向将基于编辑器中的字符,以每个块为基础。

blockRendererFn

blockRendererFn?: (block: ContentBlock) => ?Object

Optionally set a function to define custom block rendering. See Advanced Topics: Block Components for details on usage.

blockRenderMap

blockRenderMap?: DraftBlockRenderMap

提供块呈现配置的映射。每个块类型映射到元素标记和一个可选的react元素包装器。此配置用于渲染和粘贴处理。

See Advanced Topics: Custom Block Rendering for details on usage.

blockStyleFn

blockStyleFn?: (block: ContentBlock) => string

可选地设置一个函数来定义在呈现给定块时应用于该块的类名。

See Advanced Topics: Block Styling for details on usage.

customStyleMap

customStyleMap?: Object

可选地定义一个内联样式映射,以应用于具有指定样式的文本范围

See Advanced Topics: Inline Styles for details on usage.

customStyleFn

customStyleFn?: (style: DraftInlineStyle, block: ContentBlock) => ?Object

可以选择定义一个函数来将内联样式转换为应用于文本范围的CSS对象

See Advanced Topics: Inline Styles for details on usage.

Behavior

autoCapitalize

autoCapitalize?: string

设置是否启用自动大写以及它的行为。

More about platform availability and usage can be found on mdn.

autoComplete

autoComplete?: string

设置是否开启自动完成以及它的行为。

More about platform availability and usage can be found on mdn.

autoCorrect

autoCorrect

设置是否开启自动更正以及它的行为。

More about platform availability and usage can be found on MDN.

readOnly

readOnly?: boolean

设置编辑器是否应该呈现为静态DOM,并禁用所有可编辑性。

当支持自定义块组件中的交互时,或者当您只想显示静态用例的内容时,这是非常有用的。

within custom block components

spellCheck

spellCheck?: boolean

设置编辑器的拼写检查是否打开。

注意,在OSX Safari中,如果用户打开了拼写检查,那么启用拼写检查也会启用自动更正功能。还要注意,IE中总是禁用拼写检查,因为观察拼写检查事件所需的事件不会在IE中触发。

stripPastedStyles

stripPastedStyles?: boolean

设置是否从粘贴内容中删除除明文外的所有信息。

如果你的编辑器不支持丰富的样式,就应该使用这个。

默认是假的。

DOM and Accessibility (Optional)

tabIndex

ARIA props

这些道具允许您在编辑器上设置可访问性属性。有关支持的属性的详尽列表,请参阅DraftEditorProps。

DraftEditorProps

editorKey

editorKey?: string

您可能不会在上手动设置editorKey,除非您正在呈现Draft组件的服务器端。如果是,则必须设置此道具以避免服务器/客户端不匹配。

如果没有设置该键,则在组件呈现并作为Editor的组件的支柱分配时自动生成该键。

如果你设置了这个道具,这个键在每个编辑器中应该是唯一的,因为它用于确定在编辑器中粘贴文本时是否应该保留样式。

Cancelable Handlers

提供这些prop函数是为了允许对一小组有用的事件进行自定义事件处理。通过从处理程序返回'handled',表明事件已被处理,Draft核心不应该再对它进行任何操作。通过返回'not-handled',您将委托Draft来处理事件。

handleReturn

handleReturn?: (
  e: SyntheticKeyboardEvent,
  editorState: EditorState,
) => DraftHandleValue

处理RETURN键下事件。示例用法:从呈现的结果列表中选择提及标签,以触发将提及实体应用到内容中。

handleKeyCommand

handleKeyCommand?: (
  command: string,
  editorState: EditorState,
  eventTimeStamp: number,
) => DraftHandleValue

处理命名编辑器命令

See Advanced Topics: Key Bindings for details on usage.

handleBeforeInput

handleBeforeInput?: (
  chars: string,
  editorState: EditorState,
  eventTimeStamp: number,
) => DraftHandleValue

处理要从beforeInput事件插入的字符。返回'handled'导致beforeInput事件的默认行为被阻止(也就是说,它与在事件上调用preventDefault方法相同)。示例用法:在用户输入了一个新块的开始部分后,您可以将该ContentBlock转换为一个无序列表项。

在Facebook,我们也使用这个功能将键入的ASCII引号转换为“智能”引号,并将键入的表情符号转换为图像。

handlePastedText

handlePastedText?: (
  text: string,
  html?: string,
  editorState: EditorState,
) => DraftHandleValue

处理直接粘贴到编辑器中的文本和html(用于富文本)。返回true将阻止默认的粘贴行为。

handlePastedFiles

handlePastedFiles?: (files: Array<Blob>) => DraftHandleValue

handleDrop

处理直接粘贴到编辑器中的文件。

handleDroppedFiles?: (
  selection: SelectionState,
  files: Array<Blob>,
) => DraftHandleValue

处理其他drop操作。

Key Handlers (Optional)

Draft允许您提供一个自定义的keyDown处理程序来包装或覆盖其默认的处理程序。

keyBindingFn

keyBindingFn?: (e: SyntheticKeyboardEvent) => ?string

这个prop函数将keyDown事件公开给您选择的处理程序。如果发生了感兴趣的事件,您可以执行自定义逻辑和/或返回一个与DraftEditorCommand或您自己创建的自定义编辑器命令相对应的字符串。例如:在Facebook,这是用来提供键盘交互,当输入朋友的名字时,会出现提及自动完成菜单。你可以在这里找到更详细的解释。

here.

Mouse events

onFocus

onFocus?: (e: SyntheticFocusEvent) => void

onBlur

onBlur?: (e: SyntheticFocusEvent) => void

Methods

focus

focus(): void

强制焦点回到编辑器节点上。

blur

blur(): void

从编辑器节点删除焦点。

EditorChangeType

EditorChangeType是一个枚举,列出了可以在Draft模型中处理的可能的更改操作集。它被表示为Flow类型,即字符串的联合。

它作为参数传递给EditorState。,并表示通过转换到新的ContentState而执行的更改操作的类型。

在幕后,这个值用于确定适当的撤销/重做处理、拼写检查行为等等。因此,虽然可以在这里提供任意字符串值作为changeType参数,但您应该避免这样做。

我们强烈建议您安装Flow来对项目执行静态类型检查。流将强制使用适当的EditorChangeType值。

Values

adjust-depth

一个或多个ContentBlock对象的深度值正在被更改。

apply-entity

一个实体正在被应用(或通过null删除)到一个或多个字符。

backspace-character

一个字符被向后删除。

change-block-data

一个或多个ContentBlock对象的类型值正在被更改。

change-inline-style

正在为一个或多个字符应用或删除内联样式。

move-block

一个块正在BlockMap中移动。

BlockMap.

delete-character

一个字符正在被向前删除。

insert-characters

正在选择状态插入一个或多个字符。

insert-fragment

一个“片段”的内容(例如一个BlockMap)在选择状态被插入。

redo

正在执行重做操作。因为重做行为是由Draft核心处理的,所以你不太可能需要显式地使用它。

remove-range

多个字符或方块被移除。

spellcheck-change

正在执行拼写检查或自动更正更改。这用于通知核心编辑器是否尝试允许本机撤消行为。

split-block

一个ContentBlock被分割成两个,例如当用户按回车键时。

undo

正在执行撤消操作。由于撤销行为是由Draft核心处理的,因此不太可能需要显式地使用它。

EditorState

ditorState是编辑器的顶级状态对象。

它是一个Immutable Record,代表了Draft编辑器的整个状态,包括:

当前文本内容状态

当前选择状态

内容的完全装饰表示

撤销/重做栈

内容的最新变化类型

请注意

在使用EditorState对象时,不应该使用Immutable API。相反,使用下面的实例getter和静态方法。

Overview

常见的实例方法

下面的列表包括了EditorState对象最常用的实例方法。

  • getCurrentContent(): ContentState
  • getSelection(): SelectionState
  • getCurrentInlineStyle(): DraftInlineStyle
  • getBlockTree(): OrderedMap

Static Methods

  • static createEmpty(?decorator): EditorState
  • static createWithContent(contentState, ?decorator): EditorState
  • static create(config): EditorState
  • static push(editorState, contentState, changeType): EditorState
  • static undo(editorState): EditorState
  • static redo(editorState): EditorState
  • static acceptSelection(editorState, selectionState): EditorState
  • static forceSelection(editorState, selectionState): EditorState
  • static moveSelectionToEnd(editorState): EditorState
  • static moveFocusToEnd(editorState): EditorState
  • static setInlineStyleOverride(editorState, inlineStyleOverride): EditorState
  • static set(editorState, EditorStateRecordType): EditorState

Properties

Note

Use the static EditorState methods to set properties, rather than using the Immutable API directly. This means using EditorState.set to pass new options to an EditorState instance.

Example

const editorState = EditorState.createEmpty();
const editorStateWithoutUndo = EditorState.set(editorState, {
  allowUndo: false,
});
  • allowUndo
  • currentContent
  • decorator
  • directionMap
  • forceSelection
  • inCompositionMode
  • inlineStyleOverride
  • lastChangeType
  • nativelyRenderedContent
  • redoStack
  • selection
  • treeMap
  • undoStack

Common Instance Methods

getCurrentContent

getCurrentContent(): ContentState

返回编辑器的当前内容。

getSelection

getSelection(): SelectionState

返回编辑器的当前光标/选择状态。

getCurrentInlineStyle

getCurrentInlineStyle(): DraftInlineStyle

返回一个OrderedSet,表示编辑器的“当前”内联样式。

如果为当前ContentState和SelectionState插入字符,将使用这个内联样式值,并考虑应该应用的任何内联样式重写。

getBlockTree

getBlockTree(blockKey: string): List;

返回装饰和样式化范围的不可变列表。这用于呈现目的,并基于currentContent和装饰器生成。

在呈现时,此对象用于将内容分解为适当的块、装饰器和样式范围组件。

Static Methods

createEmpty

static createEmpty(decorator?: DraftDecoratorType): EditorState

返回一个带有空ContentState和默认配置的新EditorState对象。

createWithContent

static createWithContent(
  contentState: ContentState,
  decorator?: DraftDecoratorType
): EditorState

根据提供的ContentState和装饰器返回一个新的EditorState对象。

create

static create(config: EditorStateCreationConfig): EditorState

根据配置对象返回一个新的EditorState对象。如果您需要通过上述方法不可用的自定义配置,请使用此方法。

push

static push(
  editorState: EditorState,
  contentState: ContentState,
  changeType: EditorChangeType
): EditorState

返回一个新的EditorState对象,其中指定的ContentState应用为新的currentContent。基于changeType,这个ContentState可以被视为撤销/重做行为的边界状态。

必须使用此方法将所有内容更改应用到EditorState。

要重命名。

undo

static undo(editorState: EditorState): EditorState

返回一个新的EditorState对象,并将撤销堆栈的顶部应用为新的currentContent。

现有的currentContent被推入重做堆栈。

redo

static redo(editorState: EditorState): EditorState

返回一个新的EditorState对象,其中重做堆栈的顶部应用为新的currentContent。

现有的currentContent被推到undo堆栈上。

acceptSelection

static acceptSelection(
  editorState: EditorState,
  selectionState: SelectionState
): EditorState

返回一个新的EditorState对象,其中应用了指定的SelectionState,但不要求呈现选择。

例如,当DOM选择发生了超出我们控制范围的变化,不需要重新呈现时,这就很有用。

forceSelection

static forceSelection(
  editorState: EditorState,
  selectionState: SelectionState
): EditorState

返回一个新的EditorState对象,其中应用了指定的SelectionState,强制呈现选择。

当需要在正确的位置手动呈现选择以保持对呈现输出的控制时,这是非常有用的。

moveSelectionToEnd

static moveSelectionToEnd(editorState: EditorState): EditorState

返回一个新的EditorState对象,并在末尾显示所选内容。

将选择移到编辑器的末尾,而不强制聚焦。

moveFocusToEnd

static moveFocusToEnd(editorState: EditorState): EditorState

返回一个新的EditorState对象,其末尾带有选择并强制聚焦。

这在我们希望以编程方式关注输入的场景中很有用,并且允许用户继续无缝地工作是有意义的。

setInlineStyleOverride

static setInlineStyleOverride(editorState: EditorState, inlineStyleOverride: DraftInlineStyle): EditorState

返回一个新的EditorState对象,其中指定的DraftInlineStyle应用于要应用于下一个插入字符的内联样式集。

set

static set(editorState: EditorState, options: EditorStateRecordType): EditorState

返回一个新的EditorState对象,并传入新的选项。该方法继承自Immutable记录API。

Properties and Getters

在大多数情况下,上面的实例和静态方法应该足以管理Draft编辑器的状态。

下面是由EditorState跟踪的属性的完整列表,以及它们对应的getter方法,以便更好地提供关于该对象中跟踪的状态范围的详细信息。

请注意

在使用EditorState对象时,不应该使用Immutable API。相反,使用上面的实例getter和静态方法

allowUndo

allowUndo: boolean;
getAllowUndo();

是否在此编辑器中允许撤消/重做行为。默认是正确的。

由于撤销/重做栈是内存保留的主要来源,如果你有一个不需要撤销/重做行为的编辑器UI,你可以考虑设置为false。

Whether to allow undo/redo behavior in this editor. Default is true.

Since the undo/redo stack is the major source of memory retention, if you have an editor UI that does not require undo/redo behavior, you might consider setting this to false.

currentContent

currentContent: ContentState;
getCurrentContent();

The currently rendered ContentState. See getCurrentContent().

decorator

decorator: ?DraftDecoratorType;
getDecorator()

当前装饰器对象(如果有)。

注意,ContentState独立于您的装饰器。如果提供了装饰器,它将用于装饰文本的呈现范围。

directionMap

directionMap: BlockMap;
getDirectionMap();

由UnicodeBidiService确定的每个块及其文本方向的映射。

您不应该手动管理它。

forceSelection

forceSelection: boolean;
mustForceSelection();

是否强制呈现当前的SelectionState。

您不应该手动设置此属性——请参见forceSelection()。see forceSelection().

inCompositionMode

inCompositionMode: boolean;
isInCompositionMode();

用户是否处于IME组合模式。这对于为IME用户呈现适当的UI非常有用,即使没有向编辑器提交任何内容更改。不应手动设置此属性。

inlineStyleOverride

inlineStyleOverride: DraftInlineStyle;
getInlineStyleOverride();

要应用于下一个插入字符的内联样式值。当使用键盘命令或样式按钮为折叠的选择范围应用内联样式时使用。

DraftInlineStyle是不可变的OrderedSet字符串的类型别名,每个字符串对应一个内联样式。

lastChangeType

lastChangeType: EditorChangeType;
getLastChangeType();

为了将我们带到当前ContentState而发生的内容更改的类型。这在确定撤销/重做的边界状态时使用。

nativelyRenderedContent

nativelyRenderedContent: ?ContentState;
getNativelyRenderedContent()

在编辑行为期间,编辑器可能允许某些操作在本地呈现。例如,在基于contentteditable的组件的正常键入行为期间,我们通常可以允许键事件失败而在DOM中打印字符。这样做,我们可以避免额外的重新渲染,并保持拼写检查高亮显示。

当允许本机呈现行为时,可以使用本机renderedcontent属性来指示此EditorState不需要重新呈现。

redoStack

redoStack: Stack<ContentState>;
getRedoStack()

一个不可变的ContentState对象堆栈,可以在重做操作时恢复。当执行撤消操作时,当前的ContentState被推到redoStack上。

您不应该手动管理此属性。如果您想禁用撤消/重做行为,请使用allowUndo属性。See also undoStack.

selection

selection: SelectionState;
getSelection();

当前呈现的SelectionState。请参阅acceptSelection()和forceSelection()。

您不应该手动管理此属性。

treeMap

treeMap: OrderedMap<string, List>;

要在编辑器组件中呈现的完全装饰和样式化的范围树。treeMap对象是基于ContentState和一个可选的装饰器(DraftDecoratorType)生成的。

在渲染时,组件应该使用getBlockTree()方法遍历treeMap对象,以渲染修饰的范围和样式化的范围。

您不应该手动管理此属性。

undoStack

undoStack: Stack<ContentState>;
getUndoStack()

一个不可变的ContentState对象堆栈,可以为撤消操作恢复。

当执行修改内容的操作时,我们决定是否应该将当前的ContentState视为用户通过执行撤消操作可以到达的“边界”状态。如果是这样,ContentState被推到撤消堆栈上。如果不是,输出的ContentState将被丢弃。

您不应该手动管理此属性。如果您想禁用撤消/重做行为,请使用allowUndo属性。

也看到redoStack。


RichUtils

RichUtils模块是一组用于富文本编辑的静态实用程序函数。

在每种情况下,这些方法都接受带有相关参数的EditorState对象并返回EditorState对象。

Static Methods

currentBlockContainsLink(
  editorState: EditorState
): boolean

getCurrentBlockType

getCurrentBlockType(
  editorState: EditorState
): string

handleKeyCommand

handleKeyCommand(
  editorState: EditorState,
  command: string
): ?EditorState

insertSoftNewline

insertSoftNewline(
  editorState: EditorState
): EditorState

onBackspace

onBackspace(
  editorState: EditorState
): EditorState?

onDelete

onDelete(
  editorState: EditorState
): EditorState?

onTab

onTab(
  event: SyntheticEvent,
  editorState: EditorState,
  maxDepth: integer
): EditorState

toggleBlockType

toggleBlockType(
  editorState: EditorState,
  blockType: string
): EditorState

toggleCode

toggleCode(
  editorState: EditorState
): EditorState

toggleInlineStyle

toggleInlineStyle(
  editorState: EditorState,
  inlineStyle: string
): EditorState

切换所选内容的指定内联样式。如果用户的选择是折叠的,则应用或删除内部状态的样式。如果没有折叠,则直接将更改应用到文档状态。

toggleLink(
  editorState: EditorState,
  targetSelection: SelectionState,
  entityKey: string
): EditorState

tryToRemoveBlockStyle

tryToRemoveBlockStyle(
  editorState: EditorState
): ContentState?

AtomicBlockUtils

AtomicBlockUtils模块是一组用于原子块编辑的静态实用函数。

在每种情况下,这些方法都接受带有相关参数的EditorState对象并返回EditorState对象。

Static Methods

insertAtomicBlock

insertAtomicBlock: function(
  editorState: EditorState,
  entityKey: string,
  character: string
): EditorState

moveAtomicBlock

moveAtomicBlock: function(
  editorState: EditorState,
  atomicBlock: ContentBlock,
  targetRange: SelectionState,
  insertionMode?: DraftInsertionType
): EditorState

KeyBindingUtil

KeyBindingUtil模块是一组静态实用函数,用于定义键绑定。

Static Methods

isCtrlKeyCommand

isCtrlKeyCommand: function(
  e: SyntheticKeyboardEvent
): boolean

检查ctrlKey修饰符是否没有与altKey修饰符一起使用。如果将它们组合在一起,则结果是altGraph键修饰符,这组键绑定不处理该修饰符。

isOptionKeyCommand

isOptionKeyCommand: function(
  e: SyntheticKeyboardEvent
): boolean

usesMacOSHeuristics

usesMacOSHeuristics: function(): boolean

检查仅适用于macOS的试探法是否在内部使用,例如在确定用作命令修饰符的密钥组合时。

hasCommandModifier

hasCommandModifier: function(
  e: SyntheticKeyboardEvent
): boolean

Modifier

Modifier模块是一组静态实用函数,封装了ContentState对象上的通用编辑操作。强烈建议您使用这些方法进行编辑操作。

这些方法还负责适当地删除或修改实体范围(给定任何受影响实体的可变性类型)。

在每种情况下,这些方法都接受带有相关参数的ContentState对象并返回ContentState对象。如果没有实际执行编辑,返回的ContentState将与输入对象相同。

Overview

Methods

  • replaceText(...): ContentState
  • insertText(...): ContentState
  • moveText(...): ContentState
  • replaceWithFragment(...): ContentState
  • removeRange(...): ContentState
  • splitBlock(...): ContentState
  • applyInlineStyle(...): ContentState
  • removeInlineStyle(...): ContentState
  • setBlockType(...): ContentState
  • setBlockData(...): ContentState
  • mergeBlockData(...): ContentState
  • applyEntity(...): ContentState

Static Methods

replaceText

replaceText(
  contentState: ContentState,
  rangeToReplace: SelectionState,
  text: string,
  inlineStyle?: DraftInlineStyle,
  entityKey?: ?string
): ContentState

将此ContentState的指定范围替换为所提供的字符串,并将内联样式和实体键应用于整个插入字符串。

例如:在Facebook上,当用提及亚伯拉罕林肯替换@abraham lincoln时,整个旧范围都是要替换的目标,提及实体应该应用到插入的字符串中。

insertText

insertText(
  contentState: ContentState,
  targetRange: SelectionState,
  text: string,
  inlineStyle?: DraftInlineStyle,
  entityKey?: ?string
): ContentState

与replaceText相同,但强制收缩目标范围,以便不替换字符。这只是为了方便,因为文本编辑经常是插入而不是替换。

moveText

moveText(
  contentState: ContentState,
  removalRange: SelectionState,
  targetRange: SelectionState
): ContentState

移动“移除”范围到“目标”范围,替换目标文本。

replaceWithFragment

replaceWithFragment(
  contentState: ContentState,
  targetRange: SelectionState,
  fragment: BlockMap
): ContentState

一个“片段”是一个块映射的一部分,实际上只是一个OrderedMap与ContentState对象的完整块映射非常相似。

这个方法将用片段替换“目标”范围。

示例:当粘贴内容时,我们将粘贴转换为要插入到编辑器中的片段,然后使用此方法添加它。

removeRange

removeRange(
  contentState: ContentState,
  rangeToRemove: SelectionState,
  removalDirection: DraftRemovalDirection
): ContentState

从编辑器中删除整个文本范围。移除方向对于正确的实体删除行为是重要的。

splitBlock

splitBlock(
  contentState: ContentState,
  selectionState: SelectionState
): ContentState

将选定的块分割为两个块。这应该只在选择折叠时使用。

applyInlineStyle

applyInlineStyle(
  contentState: ContentState,
  selectionState: SelectionState,
  inlineStyle: string
): ContentState

将指定的内联样式应用于整个选定范围。

removeInlineStyle

removeInlineStyle(
  contentState: ContentState,
  selectionState: SelectionState,
  inlineStyle: string
): ContentState

从整个选定范围删除指定的内联样式。

setBlockType

setBlockType(
  contentState: ContentState,
  selectionState: SelectionState,
  blockType: DraftBlockType
): ContentState

为所有选定的块设置块类型。

setBlockData

setBlockData(
  contentState: ContentState,
  selectionState: SelectionState,
  blockData: Map<any, any>
): ContentState

为所有选定的块设置块数据。

mergeBlockData

mergeBlockData(
  contentState: ContentState,
  selectionState: SelectionState,
  blockData: Map<any, any>
): ContentState

更新所有选定块的块数据。

applyEntity

applyEntity(
  contentState: ContentState,
  selectionState: SelectionState,
  entityKey: ?string
): ContentState

将实体应用于整个选定范围,或者如果entityKey为空,则从范围中删除所有实体。

posted @ 2021-10-01 16:56  code汤  阅读(812)  评论(0)    收藏  举报