@4nduril/react-rte 中文文档教程

发布于 7年前 浏览 26 项目主页 更新于 3年前

React Rich Text Editor

Build Status

这是一个完全用 React 构建的 UI 组件,旨在成为一个完整的- 类似于 CKEditorTinyMCE 和其他富文本“所见即所得”编辑器。 它基于来自 Facebook 的出色的开源 Draft.js,该文件经过了性能和生产测试。

Demo

在此处试用编辑器:react-rte.org/demo

截图1

Getting Started

$ npm install --save react-rte

RichTextEditor为主编辑器成分。 它由 Draft.js 、一些 UI 组件(例如工具栏)和一些有关使用 HTML/Markdown 获取和设置内容的有用抽象组成。

RichTextEditor 被设计成像 textarea 一样使用,除了 value 不是一个字符串,它是一个带有 toString就可以了。 使用 createValueFromString(markup, 'html') 从字符串创建一个 value 也很容易。

Browser Compatibility

脚本由 Babel 转译为 ES6。 此外,该软件包的至少一个依赖项不支持 IE。 因此,为了支持 IE 和 back-plat,您需要在 HTML 中包含一些 polyfill(#74、#196、#203):

Required Webpack configuration

如果你没有使用Webpack,你可以跳过这部分。 Node.js 环境中的同构/服务器端渲染支持需要 Webpack。

'react-rte' 包含一个已经使用 webpack 构建(使用 CSS)的包,并且不打算被 webpack 再次使用。 因此,如果您使用的是 webpack,则必须从 react-rte/lib/RichTextEditor 导入 RichTextEditor,以便获得 webpack 可以与您的应用程序捆绑的未捆绑脚本。

如果你正在使用 webpack,你必须添加一个 css 加载器,否则你的 webpack 构建将失败。 例如:

  {
    test: /\.css$/,
    loaders: [
      'style-loader',
      'css-loader?modules'
    ]
  },

Example Usage:

这个例子使用了更新的 JavaScript 和 JSX。 有关旧 JavaScript 中的示例,请参见下文

import React, {Component, PropTypes} from 'react';
import RichTextEditor from 'react-rte';

class MyStatefulEditor extends Component {
  static propTypes = {
    onChange: PropTypes.func
  };

  state = {
    value: RichTextEditor.createEmptyValue()
  }

  onChange = (value) => {
    this.setState({value});
    if (this.props.onChange) {
      // Send the changes up to the parent component as an HTML string.
      // This is here to demonstrate using `.toString()` but in a real app it
      // would be better to avoid generating a string on each change.
      this.props.onChange(
        value.toString('html')
      );
    }
  };

  render () {
    return (
      <RichTextEditor
        value={this.state.value}
        onChange={this.onChange}
      />
    );
  }
}

Toolbar Customization

render() {
  // The toolbarConfig object allows you to specify custom buttons, reorder buttons and to add custom css classes.
  // Supported inline styles: https://github.com/facebook/draft-js/blob/master/docs/Advanced-Topics-Inline-Styles.md
  // Supported block types: https://github.com/facebook/draft-js/blob/master/docs/Advanced-Topics-Custom-Block-Render.md#draft-default-block-render-map
  const toolbarConfig = {
    // Optionally specify the groups to display (displayed in the order listed).
    display: ['INLINE_STYLE_BUTTONS', 'BLOCK_TYPE_BUTTONS', 'LINK_BUTTONS', 'BLOCK_TYPE_DROPDOWN', 'HISTORY_BUTTONS'],
    INLINE_STYLE_BUTTONS: [
      {label: 'Bold', style: 'BOLD', className: 'custom-css-class'},
      {label: 'Italic', style: 'ITALIC'},
      {label: 'Underline', style: 'UNDERLINE'}
    ],
    BLOCK_TYPE_DROPDOWN: [
      {label: 'Normal', style: 'unstyled'},
      {label: 'Heading Large', style: 'header-one'},
      {label: 'Heading Medium', style: 'header-two'},
      {label: 'Heading Small', style: 'header-three'}
    ],
    BLOCK_TYPE_BUTTONS: [
      {label: 'UL', style: 'unordered-list-item'},
      {label: 'OL', style: 'ordered-list-item'}
    ]
  };
  return (
    <RichTextEditor toolbarConfig={toolbarConfig} />
  );
}

Motivation

简而言之,这是一种 2016 年的富文本编辑方法,它建立在久经沙场的现代组件之上,重要的是,我们不将文档状态存储在 DOM 中,从而消除了整类常见的“所见即所得”问题。

此编辑器基于来自 Facebook 的 Draft.js 构建。 Draft.js 更像是一个低级框架(contentEditable 抽象),但是这个组件旨在成为一个完全完善的 UI 组件,当您需要替换 < 时,您可以使用它;textarea/> 在您的应用程序中支持粗体、斜体、链接、列表等

。Draft.js 中的数据模型允许我们以一种几乎与视图/渲染层无关的方式来表示文档或您选择的文本表示形式(html/markdown)。 此数据模型封装了编辑器的内容/状态,并且基于 Immutable.js 以兼顾性能和易于使用理由。

Features

  • Pure React and fully declarative
  • Supported formats: HTML and Markdown (coming soon: extensible support for custom formats)
  • Document Model represents your document in a sane way that will deterministically convert to clean markup regardless of your format choice
  • Takes full advantage of Immutable.js and the excellent performance characteristics that come with it.
  • Reliable undo/redo without a large memory footprint
  • Modern browser support

Deterministic Output

不同于典型的富文本编辑器(例如 CKEditorTinyMCE) 我们将内容状态保存在结构良好的数据模型中,而不是在视图中。 将我们的数据模型与我们的视图分开的一个重要优势是确定性输出。

比方说,您选择了一些文本并添加了粗体样式。 然后你添加斜体样式。 或者,如果您先添加斜体然后再添加粗体会怎么样。 结果应该是相同的:文本范围同时具有粗体和斜体样式。 但是在浏览器的视图(文档对象模型)中,这是用 内部的 表示的,反之亦然? 这是否取决于您添加样式的顺序? 在许多基于网络的编辑器中,HTML 输出确实取决于您的操作顺序。 这意味着您的输出是不确定的。 在编辑器中看起来完全相同的两个文档将具有不同的、有时是不可预测的 HTML 表示。

在此编辑器中,我们使用纯确定性函数将文档状态转换为 HTML 输出。 无论您如何到达该状态,输出都是可预测的。 这使得一切都更容易推理。 在我们的例子中, 每次都会进入

API

Required Props

  • value: Used to represent the content/state of the editor. Initially you will probably want to create an instance using a provided helper such as RichTextEditor.createEmptyValue() or RichTextEditor.createValueFromString(markup, 'html').
  • onChange: A function that will be called with the "value" of the editor whenever it is changed. The value has a toString method which accepts a single format argument (either 'html' or 'markdown').

Other Props

您可以传递给 Draft.js Editor 的所有道具都可以传递给 RichTextEditoreditorState 除外,它将根据内部生成value 属性)。

  • autoFocus: Setting this to true will automatically focus input into the editor when the component is mounted
  • placeholder: A string to use as placeholder text for the RichTextEditor.
  • readOnly: A boolean that determines if the RichTextEditor should render static html.

EditorValue Class

在 Draft.js 中,EditorState 不仅包含文档内容,还包含编辑器的整个状态,包括光标位置和选择。 这有很多好处,包括撤消/重做。 为了让您更轻松,我们将编辑器的状态包装在一个 EditorValue 实例中,并使用有用的方法将其转换为 HTML 或 Markdown。 此类的实例应传递给 value 属性中的 RichTextEditor

EditorValue 类内置了某些优化。假设您要在视图中显示编辑器内容的 HTML。 如果您更改光标位置,将触发 onChange 事件(因为请记住,光标位置是 EditorState 的一部分)并且您需要调用 toString( ) 来呈现您的视图。 但是,EditorValue 足够聪明,知道自上次 toString() 以来 content 实际上并没有改变,因此它将返回缓存版本HTML。

优化提示:尝试仅在实际需要将其转换为字符串时才调用 editorValue.toString()。 如果您可以在不调用 toString 的情况下继续传递 editorValue,它将非常高效。

Example with ES5 and no JSX

var React = require('react');
var RichTextEditor = require('react-rte');

React.createClass({
  propTypes: {
    onChange: React.PropTypes.func
  },

  getInitialState: function() {
    return {
      value: RichTextEditor.createEmptyValue()
    };
  },

  render: function() {
    return React.createElement(RichTextEditor, {
      value: this.state.value,
      onChange: this.onChange
    });
  },

  onChange: function(value) {
    this.setState({value: value});
    if (this.props.onChange) {
      // Send the changes up to the parent component as an HTML string.
      // This is here to demonstrate using `.toString()` but in a real app it
      // would be better to avoid generating a string on each change.
      this.props.onChange(
        value.toString('html')
      );
    }
  }

});

TODO

  • Support images
  • Better test coverage
  • Documentation for using this editor in your projects
  • Fix some issues with Markdown parsing (migrate to remark parser)
  • Internationalization
  • Better icons and overall design

Known Limitations

目前最大的限制是不支持图片。 有计划支持内联图像(使用装饰器)并最终支持中型块级图像(使用自定义块渲染器)。

其他限制包括缺少的功能,例如:文本对齐和文本颜色。 这些即将推出。

React 之前的 v15 将记录以下多余的警告:

一个组件是 contentEditable 并且包含由 反应。 现在你有责任保证没有 这些节点被意外修改或复制。 这是 可能不是故意的。

由于所有节点均由 Draft 内部管理,因此这不是问题,并且可以安全地忽略此警告。 您可以在实例化您的组件之前通过鸭子打孔 console.error 完全抑制此警告的显示:

console.error = (function(_error) {
  return function(message) {
    if (typeof message !== 'string' || message.indexOf('component is `contentEditable`') === -1) {
      _error.apply(console, arguments);
    }
  };
})(console.error);

Contribute

我很乐意接受错误修复和改进(和测试)的拉取请求。 如果你有一个你想要实现的特性,那么首先打开一个问题看看它是否已经在处理中可能是个好主意。 请匹配项目其余部分的代码风格(ESLint 应强制执行此操作)并请包含测试。 谢谢!

Run the Demo

克隆这个项目。 运行 <代码>npm 安装。 运行 npm run build-dist 然后将您选择的服务器(如 serv)指向 <代码>/demo.html。

License

此软件已获得 ISC 许可

React Rich Text Editor

Build Status

This is a UI component built completely in React that is meant to be a full-featured textarea replacement similar to CKEditor, TinyMCE and other rich text "WYSIWYG" editors. It's based on the excellent, open source Draft.js from Facebook which is performant and production-tested.

Demo

Try the editor here: react-rte.org/demo

Screenshot 1

Getting Started

$ npm install --save react-rte

RichTextEditor is the main editor component. It is comprised of the Draft.js <Editor>, some UI components (e.g. toolbar) and some helpful abstractions around getting and setting content with HTML/Markdown.

RichTextEditor is designed to be used like a textarea except that instead of value being a string, it is an object with toString on it. Creating a value from a string is also easy using createValueFromString(markup, 'html').

Browser Compatibility

The scripts are transpiled by Babel to ES6. Additionally, at least one of this package's dependencies does not support IE. So, for IE and back-plat support you will need to include some polyfill in your HTML (#74, #196, #203): <script src="https://cdn.polyfill.io/v2/polyfill.min.js?features=String.prototype.startsWith,Array.from,Array.prototype.fill,Array.prototype.keys,Array.prototype.findIndex,Number.isInteger&flags=gated"></script>

Required Webpack configuration

If you are not using Webpack, you can skip this section. Webpack is required for isomorphic/server-side rendering support in a Node.js environment.

'react-rte' contains a bundle that is already built (with CSS) using webpack and is not intended to be consumed again by webpack. So, if you are using webpack you must import RichTextEditor from react-rte/lib/RichTextEditor in order to get the un-bundled script which webpack can bundle with your app.

If you are using webpack you must add a css loader or else your webpack build will fail. For example:

  {
    test: /\.css$/,
    loaders: [
      'style-loader',
      'css-loader?modules'
    ]
  },

Example Usage:

This example uses newer JavaScript and JSX. For an example in old JavaScript, see below.

import React, {Component, PropTypes} from 'react';
import RichTextEditor from 'react-rte';

class MyStatefulEditor extends Component {
  static propTypes = {
    onChange: PropTypes.func
  };

  state = {
    value: RichTextEditor.createEmptyValue()
  }

  onChange = (value) => {
    this.setState({value});
    if (this.props.onChange) {
      // Send the changes up to the parent component as an HTML string.
      // This is here to demonstrate using `.toString()` but in a real app it
      // would be better to avoid generating a string on each change.
      this.props.onChange(
        value.toString('html')
      );
    }
  };

  render () {
    return (
      <RichTextEditor
        value={this.state.value}
        onChange={this.onChange}
      />
    );
  }
}

Toolbar Customization

render() {
  // The toolbarConfig object allows you to specify custom buttons, reorder buttons and to add custom css classes.
  // Supported inline styles: https://github.com/facebook/draft-js/blob/master/docs/Advanced-Topics-Inline-Styles.md
  // Supported block types: https://github.com/facebook/draft-js/blob/master/docs/Advanced-Topics-Custom-Block-Render.md#draft-default-block-render-map
  const toolbarConfig = {
    // Optionally specify the groups to display (displayed in the order listed).
    display: ['INLINE_STYLE_BUTTONS', 'BLOCK_TYPE_BUTTONS', 'LINK_BUTTONS', 'BLOCK_TYPE_DROPDOWN', 'HISTORY_BUTTONS'],
    INLINE_STYLE_BUTTONS: [
      {label: 'Bold', style: 'BOLD', className: 'custom-css-class'},
      {label: 'Italic', style: 'ITALIC'},
      {label: 'Underline', style: 'UNDERLINE'}
    ],
    BLOCK_TYPE_DROPDOWN: [
      {label: 'Normal', style: 'unstyled'},
      {label: 'Heading Large', style: 'header-one'},
      {label: 'Heading Medium', style: 'header-two'},
      {label: 'Heading Small', style: 'header-three'}
    ],
    BLOCK_TYPE_BUTTONS: [
      {label: 'UL', style: 'unordered-list-item'},
      {label: 'OL', style: 'ordered-list-item'}
    ]
  };
  return (
    <RichTextEditor toolbarConfig={toolbarConfig} />
  );
}

Motivation

In short, this is a 2016 approach to rich text editing built on modern, battle-hardened components and, importantly, we do not store document state in the DOM, eliminating entire classes of common "WYSIWYG" problems.

This editor is built on Draft.js from Facebook. Draft.js is more of a low-level framework (contentEditable abstraction), however this component is intended to be a fully polished UI component that you can reach for when you need to replace a <textarea/> in your application to support bold, italic, links, lists, etc.

The data model in Draft.js allows us to represent the document in a way that is mostly agnostic to the view/render layer or the textual representation (html/markdown) you choose. This data model encapsulates the content/state of the editor and is based on Immutable.js to be both performant and easy to reason about.

Features

  • Pure React and fully declarative
  • Supported formats: HTML and Markdown (coming soon: extensible support for custom formats)
  • Document Model represents your document in a sane way that will deterministically convert to clean markup regardless of your format choice
  • Takes full advantage of Immutable.js and the excellent performance characteristics that come with it.
  • Reliable undo/redo without a large memory footprint
  • Modern browser support

Deterministic Output

Unlike typical rich text editors (such as CKEditor and TinyMCE) we keep our content state in a well-architected data model instead of in the view. One important advantage of separating our data model from our view is deterministic output.

Say, for instance, you select some text and add bold style. Then you add italic style. Or what if you add italic first and then bold. The result should be the same either way: the text range has both bold and italic style. But in the browser's view (Document Object Model) is this represented with a <strong> inside of an <em> or vice versa? Does it depend on the order in which you added the styles? In many web-based editors the HTML output does depend on the order of your actions. That means your output is non-deterministic. Two documents that look exactly the same in the editor will have different, sometimes unpredictable, HTML representations.

In this editor we use a pure, deterministic function to convert document state to HTML output. No matter how you arrived at the state, the output will be predictable. This makes everything easier to reason about. In our case, the <strong> will go inside the <em> every time.

API

Required Props

  • value: Used to represent the content/state of the editor. Initially you will probably want to create an instance using a provided helper such as RichTextEditor.createEmptyValue() or RichTextEditor.createValueFromString(markup, 'html').
  • onChange: A function that will be called with the "value" of the editor whenever it is changed. The value has a toString method which accepts a single format argument (either 'html' or 'markdown').

Other Props

All the props you can pass to Draft.js Editor can be passed to RichTextEditor (with the exception of editorState which will be generated internally based on the value prop).

  • autoFocus: Setting this to true will automatically focus input into the editor when the component is mounted
  • placeholder: A string to use as placeholder text for the RichTextEditor.
  • readOnly: A boolean that determines if the RichTextEditor should render static html.

EditorValue Class

In Draft.js EditorState contains not only the document contents but the entire state of the editor including cursor position and selection. This is helpful for many reasons including undo/redo. To make things easier for you, we have wrapped the state of the editor in an EditorValue instance with helpful methods to convert to/from a HTML or Markdown. An instance of this class should be passed to RichTextEditor in the value prop.

The EditorValue class has certain optimizations built in. So let's say you are showing the HTML of the editor contents in your view. If you change your cursor position, that will trigger an onChange event (because, remember, cursor position is part of EditorState) and you will need to call toString() to render your view. However, EditorValue is smart enough to know that the content didn't actually change since last toString() so it will return a cached version of the HTML.

Optimization tip: Try to call editorValue.toString() only when you actually need to convert it to a string. If you can keep passing around the editorValue without calling toString it will be very performant.

Example with ES5 and no JSX

var React = require('react');
var RichTextEditor = require('react-rte');

React.createClass({
  propTypes: {
    onChange: React.PropTypes.func
  },

  getInitialState: function() {
    return {
      value: RichTextEditor.createEmptyValue()
    };
  },

  render: function() {
    return React.createElement(RichTextEditor, {
      value: this.state.value,
      onChange: this.onChange
    });
  },

  onChange: function(value) {
    this.setState({value: value});
    if (this.props.onChange) {
      // Send the changes up to the parent component as an HTML string.
      // This is here to demonstrate using `.toString()` but in a real app it
      // would be better to avoid generating a string on each change.
      this.props.onChange(
        value.toString('html')
      );
    }
  }

});

TODO

  • Support images
  • Better test coverage
  • Documentation for using this editor in your projects
  • Fix some issues with Markdown parsing (migrate to remark parser)
  • Internationalization
  • Better icons and overall design

Known Limitations

Currently the biggest limitation is that images are not supported. There is a plan to support inline images (using decorators) and eventually Medium-style block-level images (using a custom block renderer).

Other limitations include missing features such as: text-alignment and text color. These are coming soon.

React prior v15 will log the following superfluous warning:

A component is contentEditable and contains children managed by React. It is now your responsibility to guarantee that none of those nodes are unexpectedly modified or duplicated. This is probably not intentional.

As all nodes are managed internally by Draft, this is not a problem and this warning can be safely ignored. You can suppress this warning's display completely by duck-punching console.error before instantiating your component:

console.error = (function(_error) {
  return function(message) {
    if (typeof message !== 'string' || message.indexOf('component is `contentEditable`') === -1) {
      _error.apply(console, arguments);
    }
  };
})(console.error);

Contribute

I'm happy to take pull requests for bug-fixes and improvements (and tests). If you have a feature you want to implement it's probably a good idea to open an issue first to see if it's already being worked on. Please match the code style of the rest of the project (ESLint should enforce this) and please include tests. Thanks!

Run the Demo

Clone this project. Run npm install. Run npm run build-dist then point the server of your choice (like serv) to /demo.html.

License

This software is ISC Licensed.

    我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
    原文