Important: This documentation covers Yarn 1 (Classic).
For Yarn 2+ docs and migration guide, see yarnpkg.com.

Package detail

draftjs-conductor

thibaudcolas31.7kMIT3.0.0TypeScript support: included

📝✨ Little Draft.js helpers to make rich text editors “just work”

draftjs, draft-js, editor, react, wysiwyg, rich text, richtext, rte

readme

Draft.js conductor

npm Build status Coverage Status

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

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 those little helpers:

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

<style>
  {getListNestingStyles(6)}
</style>
<Editor blockStyleFn={blockDepthStyleFn} />

getListNestingStyles 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à!

If your editor’s maximum list nesting depth never changes, pre-render the styles as a fragment for better performance:

const listNestingStyles = <style>{getListNestingStyles(6)}</style>;

You can also leverage React.memo to speed up re-renders even if max was to change:

const NestingStyles = React.memo(ListNestingStyles);

<style>
  <NestingStyles max={max} />
</style>;

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:

All of those problems can be fixed with this library, which overrides the copy and cut events to transfer more of the editor’s content, and introduces a function to use with the Draft.js handlePastedText to retrieve the pasted content.

This will paste all copied content, even if the target editor might not support it. To ensure only supported content is retained, use filters like draftjs-filters.

Note: IE11 isn’t supported, as it doesn't support storing HTML in the clipboard, and we also use the Element.closest API.

With draft.js 0.11 and above

Here’s how to use the copy/cut override, and the paste handler:

import {
  onDraftEditorCopy,
  onDraftEditorCut,
  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);
  }

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

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

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

    return false;
  }

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

    return (
      <Editor
        editorState={editorState}
        onChange={this.onChange}
        onCopy={onDraftEditorCopy}
        onCut={onDraftEditorCut}
        handlePastedText={this.handlePastedText}
      />
    );
  }
}

The copy/cut event handlers will ensure the clipboard contains a full representation of the Draft.js content state on copy/cut, 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.

You can also use getDraftEditorPastedContent method and set new EditorState by yourself. It is useful when you need to do some transformation with content (for example filtering unsupported styles), before past it in the state.

With draft.js 0.10

The above code relies on the onCopy and onCut event handlers, only available from Draft.js v0.11.0 onwards. For Draft.js v0.10.5, use registerCopySource instead, providing a ref to the editor:

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

class MyEditor extends Component {
  componentDidMount() {
    this.copySource = registerCopySource(this.editorRef);
  }

  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}
      />
    );
  }
}

With draft-js-plugins

The setup is slightly different with draft-js-plugins (and React hooks) – we need to use the provided getEditorRef method:

// reference to the editor
const editor = useRef<Editor>(null);

// register code for copying
useEffect(() => {
  let unregisterCopySource: undefined | unregisterObject = undefined;
  if (editor.current !== null) {
    unregisterCopySource = registerCopySource(
      editor.current.getEditorRef() as any,
    );
  }
  return () => {
    unregisterCopySource?.unregister();
  };
});

See #115 for further details.

Editor state data conversion helpers

Draft.js has its own data conversion helpers, convertFromRaw and convertToRaw, which work really well, but aren’t providing that good of an API when initialising or persisting the content of an editor.

We provide two helper methods to simplify the initialisation and serialisation of content. createEditorStateFromRaw combines EditorState.createWithContent, EditorState.createEmpty and convertFromRaw as a single method:

import { createEditorStateFromRaw } from "draftjs-conductor";

// Initialise with `null` if there’s no preexisting state.
const editorState = createEditorStateFromRaw(null);
// Initialise with the raw content state otherwise
const editorState = createEditorStateFromRaw({ entityMap: {}, blocks: [] });
// Optionally use a decorator, like with Draft.js APIs.
const editorState = createEditorStateFromRaw(null, decorator);

To save content, serialiseEditorStateToRaw combines convertToRaw with checks for empty content – so empty content is saved as null, rather than a single text block with empty text as would be the case otherwise.

import { serialiseEditorStateToRaw } from "draftjs-conductor";

// Content will be `null` if there’s no textual content, or RawDraftContentState otherwise.
const content = serialiseEditorStateToRaw(editorState);

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. Image credit: FirefoxEmoji.

changelog

Changelog

All notable changes to this project will be documented in this file.

The format is based on Keep a Changelog and this project adheres to Semantic Versioning, enforced with semantic-release.

3.0.0 (2022-06-13)

Features

  • api: convert whole package API to TypeScript (3fca3fa)

BREAKING CHANGES

  • api: All helpers are now written in TypeScript.

    Flow types are no longer available, and TypeScript types are built-in.

2.2.0 (2021-04-14)

Features

  • lists: support specifying an arbitrary number of ol counter styles (921a5d3)

2.1.0 (2021-04-13)

Features

  • lists: add different numeral list styles per depth level (d309382)

2.0.0 (2020-11-19)

Features

  • api: replace list nesting APIs with a single getListNestingStyles (3703b2f)
  • deps: proactively declare support with Draft.js v0.12.0 (586b385)

BREAKING CHANGES

  • api: The <ListNestingStyles max={6} /> component has been removed, and the generateListNestingStyles method is now deprecated and will be removed in a future release.

Both are replaced with a getListNestingStyles method, which works exactly the same as generateListNestingStyles, but with a different parameter order, and with default values:

export const getListNestingStyles = (
  maxDepth: number,
  minDepth: number = DRAFT_DEFAULT_MAX_DEPTH + 1,
  selectorPrefix: string = DRAFT_DEFAULT_DEPTH_CLASS,
) => {
  return generateListNestingStyles(selectorPrefix, minDepth, maxDepth);
};

This small breaking change allows us to remove this package’s peerDependency on React, making it easier to upgrade to React 17, and other versions in the future.

1.2.0 (2020-11-19)

Features

1.1.0 (2020-08-16)

Features

  • api: add new getDraftEditorPastedContent method (#226) (fcaada5)

1.0.1 (2020-01-20)

Bug Fixes

  • deps: allow draft-js ^0.11.0 as a peer dependency (1b0cfa3)

1.0.0 (2019-08-14)

This release is functionally identical to v0.5.2.

The project has reached a high-enough level of stability to be used in production, and breaking changes will now be reflected via major version changes.

0.5.2 (2019-08-13)

Bug Fixes

  • release: prevent tarballs from being published in npm tarball (96d0765)

0.5.1 (2019-08-13)

Bug Fixes

  • api: add .flow typing file to restore type checks on CJS imports (cb73a81)

0.5.0 (2019-08-13)

Features

  • api: add new data conversion helper methods (355c88e)

0.4.5 (2019-07-04)

Bug Fixes

  • api: disable Flow types in CommonJS build (023f6b0)
  • package: use ES6 import instead of require for draft-js/lib deps (9bcea6b)

0.4.4 (2019-05-28)

Bug Fixes

  • copy-paste: fix partial copy from decorator text. Fix #12 (e043b74)
  • copy-paste: support copy from decorators. Fix #12 (d90bbbc)
  • release: remove unneeded react-dom peerDependency (3e59f05)

Performance Improvements

  • copy-paste: completely skip event handling operations in IE11 (9521758)

0.4.3 (2019-04-21)

Bug Fixes

  • selection: use getContentStateFragment for readonly copy. Fix #14 (0483d82)

0.4.2 (2019-04-21)

Bug Fixes

  • api: update typing so compiled code still validates with Flow (d065ce7)

0.4.1 (2019-01-25)

Bug Fixes

  • copy-paste: use explicit check for plain text pastes (02bdc94)

0.4.0 (2019-01-25)

Features

  • api: add WIP publication of flow types (fb7fa29)
  • api: convert ListNestingStyles from PureComponent to function (44f9a5f)
  • api: publish package with Flow annotations built in (d7e190f)
  • api: remove (undocumented) prefix prop on ListNestingStyles (774fe8a)

0.3.0 (2018-10-27)

Features

  • release: mark package as side-effects-free for Webpack (#11) (5923318)

0.2.1 (2018-06-04)

Bug Fixes

  • copy-paste: preserve line breaks for pasting into word processors (8a09efa)

0.2.0 (2018-06-03)

Features

  • copy-paste: override Draft.js copy-paste to preserve full editor content (#2)

0.1.0 (2018-02-24)

Features

  • api: add react and react-dom as peerDependencies (63acfb3)
  • lists: add list nesting styles api to package (8fb7073)
  • lists: remove whitespace filtering from list styles (2e29541)