📝 Little Draft.js helpers to make rich text editors “just work”
Switch branches/tags
Clone or download
Fetching latest commit…
Cannot retrieve the latest commit at this time.
Permalink
Type Name Latest commit message Commit time
Failed to load latest commit information.
.githooks
.github
public
src
.editorconfig
.env
.env.production
.eslintignore
.eslintrc.js
.flowconfig
.gitignore
.nvmrc
.prettierignore
.travis.yml
CHANGELOG.md
LICENSE
README.md
commitlint.config.js
dangerfile.js
package-lock.json
package.json
prettier.config.js
release.config.js
rollup.config.js

README.md

Draft.js conductor npm Build Status Coverage Status Wagtail

📝 Little Draft.js helpers to make rich text editors just work. Built for Draftail and Wagtail.

Photoshop’s Magic Wand selection tool applied on a WYSIWYG editor interface

Check out the online demo!

Features


Infinite list nesting

By default, Draft.js only provides support for 5 list levels for bulleted and numbered lists. While this is often more than enough, some editors need to go further.

Instead of manually writing and maintaining the list nesting styles, use this little helper:

import { ListNestingStyles, blockDepthStyleFn } from "draftjs-conductor";

<Editor blockStyleFn={blockDepthStyleFn} />
<ListNestingStyles max={6} />

ListNestingStyles will generate the necessary CSS for your editor’s lists. blockDepthStyleFn will then apply classes to blocks based on their depth, so the styles take effect. Voilà!

Relevant Draft.js issues:


Idempotent copy-paste between editors

The default Draft.js copy-paste handlers lose a lot of the formatting when copy-pasting between Draft.js editors. While this might be ok for some use cases, sites with multiple editors on the same page need them to reliably support copy-paste.

Relevant Draft.js issues:

To make it just work:

import {
  registerCopySource,
  handleDraftEditorPastedText,
} from "draftjs-conductor";

class MyEditor extends Component {
  constructor(props: Props) {
    super(props);

    this.state = {
      editorState: EditorState.createEmpty(),
    };

    this.onChange = this.onChange.bind(this);
    this.handlePastedText = this.handlePastedText.bind(this);
  }

  componentDidMount() {
    this.copySource = registerCopySource(this.editorRef);
  }

  onChange(nextState: EditorState) {
    this.setState({ editorState: nextState });
  }

  handlePastedText(text: string, html: ?string, editorState: EditorState) {
    let newState = handleDraftEditorPastedText(html, editorState);

    if (newState) {
      this.onChange(newState);
      return true;
    }

    return false;
  }

  componentWillUnmount() {
    if (this.copySource) {
      this.copySource.unregister();
    }
  }

  render() {
    const { editorState } = this.state;

    return (
      <Editor
        ref={(ref) => {
          this.editorRef = ref;
        }}
        editorState={editorState}
        onChange={this.onChange}
        handlePastedText={this.handlePastedText}
      />
    );
  }
}

registerCopySource will ensure the clipboard contains a full representation of the Draft.js content state on copy, while handleDraftEditorPastedText retrieves Draft.js content state from the clipboard. Voilà! This also changes the HTML clipboard content to be more semantic, with less styles copied to other word processors/editors.

Note: IE11 isn’t supported, as it doesn't support storing HTML in the clipboard.

Contributing

See anything you like in here? Anything missing? We welcome all support, whether on bug reports, feature requests, code, design, reviews, tests, documentation, and more. Please have a look at our contribution guidelines.

Credits

View the full list of contributors. MIT licensed. Website content available as CC0.