Tips

react-draft-wysiwygを使ったエディタの作り方とカスタマイズの方法

  • このエントリーをはてなブックマークに追加

Reactでリッチテキストエディタを実装する場合、react-draft-wysiwygという便利なライブラリがあります。

react-draft-wysiwygはDraft.jsというライブラリをベースに作られていてテキストエディタを無料で簡単に実装することができます。

また様々なカスタマイズも可能で、自分が作っているアプリやサイトに合わせたものにできます。

今回は自分のReact環境にreact-draft-wysiwygを導入する方法とカスタマイズについて解説していきます。

動画もあるので必要な方はご覧になってください。

react-draft-wysiwygのインストール方法

npmのサイトにてインストールコマンドやコンポーネントの雛形があるので以下よりアクセスします。

https://www.npmjs.com/package/react-draft-wysiwyg

npm install --save react-draft-wysiwyg draft-js

インストールが正しく完了したかはpackage.jsonにて確認しましょう。

{
  "name": "editor-js",
  "version": "0.1.0",
  "private": true,
  "dependencies": {
    "@testing-library/jest-dom": "^5.16.5",
    "@testing-library/react": "^13.4.0",
    "@testing-library/user-event": "^13.5.0",
    // 追加されている
    "draft-js": "^0.11.7",
    "react": "^18.2.0",
    "react-dom": "^18.2.0",
    // 追加されている
    "react-draft-wysiwyg": "^1.15.0",
    "react-scripts": "5.0.1",
    "web-vitals": "^2.1.4"
  },
  "scripts": {
    "start": "react-scripts start",
    "build": "react-scripts build",
    "test": "react-scripts test",
    "eject": "react-scripts eject"
  },
  "eslintConfig": {
    "extends": [
      "react-app",
      "react-app/jest"
    ]
  },
  "browserslist": {
    "production": [
      ">0.2%",
      "not dead",
      "not op_mini all"
    ],
    "development": [
      "last 1 chrome version",
      "last 1 firefox version",
      "last 1 safari version"
    ]
  }
}

またコンポーネントファイルにテンプレートをコピーします。

本記事ではTextEditor.jsという名前のコンポーネントファイルを作成しました。

import React, { useState } from "react";
// ここを追加
import { Editor } from "react-draft-wysiwyg";
import "react-draft-wysiwyg/dist/react-draft-wysiwyg.css";

const TextEditor = () => {

  return (
    <div className="editor">
      // ここを追加
      <Editor
        editorState={editorState}
        toolbarClassName="toolbarClassName"
        wrapperClassName="wrapperClassName"
        editorClassName="editorClassName"
        {/* ここを修正 */}
        onEditorStateChange={this.onEditorStateChange}
      />
    </div>
  );
};

export default TextEditor;

ちなみに現在は関数コンポーネントで書くのが主流ですが、上記のテンプレートはクラスコンポーネントでの書き方になっているので少し修正が必要です。

またステートについての記載も無いので以下のようにコンポーネントを書き換えましょう。

// ここを追加
import { EditorState } from "draft-js";
import React, { useState } from "react";
import { Editor } from "react-draft-wysiwyg";
import "react-draft-wysiwyg/dist/react-draft-wysiwyg.css";

const TextEditor = () => {
  // ここを追加
  const [editorState, setEditorState] = useState(() =>
    EditorState.createEmpty()
  );

  return (
    <div className="editor">
      <Editor
        editorState={editorState}
        toolbarClassName="toolbarClassName"
        wrapperClassName="wrapperClassName"
        editorClassName="editorClassName"
        {/* ここを修正 */}
        onEditorStateChange={setEditorState}
      />
    </div>
  );
};

export default TextEditor;

ステートの管理にはuseStateを使用し、ステート変数editorStateとステート関数setEditorStateを作成しました。

useStateの初期値は関数でEditorStateというクラスに対してcreateEmpyメソッドを実行することで、エディタの中身を初期化するようになります。

またEditorStateを使うにはimport文が必要です。

続いてCSSで簡単にスタイルを追加します。

.App {
  text-align: center;
}

.App-logo {
  height: 40vmin;
  pointer-events: none;
}

@media (prefers-reduced-motion: no-preference) {
  .App-logo {
    animation: App-logo-spin infinite 20s linear;
  }
}

.App-header {
  background-color: #282c34;
  min-height: 100vh;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  /* ここを修正 */
  font-size: calc(6px + 2vmin);
  color: white;
}

.App-link {
  color: #61dafb;
}

@keyframes App-logo-spin {
  from {
    transform: rotate(0deg);
  }
  to {
    transform: rotate(360deg);
  }
}
/* ここを追加 */
.editor {
  width: 1000px;
  margin: 0 auto;
  border: 1px solid #333;
}
.wrapperClassName {
  background: #e9e9e9;
  padding: 16px;
}
.toolbarClassName {
  padding: 16px;
}
.editorClassName {
  background: #fff;
  padding: 0 8px;
  height: 200px;
}
.App .rdw-editor-main {
  height: 200px;
}

上図のようにエディタが表示されていて文字を入力することができるようになっているはずです。

CSSについては一部テンプレートで決まっているクラス名があるのと、それ以外は検証ツールで確認することでクラス名を確認することができます。

react-draft-wysiwygのカスタタマイズ方法

それでは便利なカスタマイズ方法を紹介します。

カスタマイズについては公式ドキュメントを確認してやるようにしましょう。

https://jpuri.github.io/react-draft-wysiwyg/#/docs

react-draft-wysiwygにメンション、ハッシュタグ、日本語化を追加

まずツールバーが英語になっているので日本語にしましょう。

import { EditorState } from "draft-js";
import React, { useState } from "react";
import { Editor } from "react-draft-wysiwyg";
import "react-draft-wysiwyg/dist/react-draft-wysiwyg.css";

const TextEditor = () => {

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

  return (
    <div className="editor">
      <Editor
        editorState={editorState}
        toolbarClassName="toolbarClassName"
        wrapperClassName="wrapperClassName"
        editorClassName="editorClassName"
        onEditorStateChange={setEditorState}
        {/* ここを追加 */}
        localization={{
          locale: "ja",
        }}
      />
    </div>
  );
};

export default TextEditor;

日本向けに作るアプリやサイトなら日本語表記にした方が便利ですよね。

続いてハッシュタグの追加です。

import { EditorState } from "draft-js";
import React, { useState } from "react";
import { Editor } from "react-draft-wysiwyg";
import "react-draft-wysiwyg/dist/react-draft-wysiwyg.css";

const TextEditor = () => {

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

  return (
    <div className="editor">
      <Editor
        editorState={editorState}
        toolbarClassName="toolbarClassName"
        wrapperClassName="wrapperClassName"
        editorClassName="editorClassName"
        onEditorStateChange={setEditorState}
        localization={{
          locale: "ja",
        }}
        {/* ここを追加 */}
        hashtag={{
          separator: " ",
          trigger: "#",
        }}
      />
    </div>
  );
};

export default TextEditor;

「#」を先頭につけて文字を入力すると、通常の入力時と違ったテキストになりました。

他にもメンションも追加できます。

import { EditorState } from "draft-js";
import React, { useState } from "react";
import { Editor } from "react-draft-wysiwyg";
import "react-draft-wysiwyg/dist/react-draft-wysiwyg.css";

const TextEditor = () => {

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

  return (
    <div className="editor">
      <Editor
        editorState={editorState}
        toolbarClassName="toolbarClassName"
        wrapperClassName="wrapperClassName"
        editorClassName="editorClassName"
        onEditorStateChange={setEditorState}
        localization={{
          locale: "ja",
        }}
        hashtag={{
          separator: " ",
          trigger: "#",
        }}
        {/* ここを追加 */}
        mention={{
          separator: " ",
          trigger: "@",
          suggestions: [
            { text: "田中太郎", value: "田中太郎", url: "tanakataro" },
            { text: "山田花子", value: "山田花子", url: "yamadahanako" },
            { text: "鈴木順平", value: "鈴木順平", url: "suuzukijunpei" },
          ],
        }}
      />
    </div>
  );
};

export default TextEditor;

メンションについては宛先を準備する必要があり、一人一人をオブジェクトで管理する形になります。

主要なカスタマイズは以上ですが細かいものは何点かあり、例えばテキスト入力についてプレースホルダーを入れることもできます。

import { EditorState } from "draft-js";
import React, { useState } from "react";
import { Editor } from "react-draft-wysiwyg";
import "react-draft-wysiwyg/dist/react-draft-wysiwyg.css";

const TextEditor = () => {

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

  return (
    <div className="editor">
      <Editor
        editorState={editorState}
        toolbarClassName="toolbarClassName"
        wrapperClassName="wrapperClassName"
        editorClassName="editorClassName"
        onEditorStateChange={setEditorState}
        localization={{
          locale: "ja",
        }}
        hashtag={{
          separator: " ",
          trigger: "#",
        }}
        mention={{
          separator: " ",
          trigger: "@",
          suggestions: [
            { text: "田中太郎", value: "田中太郎", url: "tanakataro" },
            { text: "山田花子", value: "山田花子", url: "yamadahanako" },
            { text: "鈴木順平", value: "鈴木順平", url: "suuzukijunpei" },
          ],
        }}
        {/* ここを追加 */}
        placeholder="ここに入力してください"
      />
    </div>
  );
};

export default TextEditor;

ちなみにツールバーの表示内容も自分で変更することができます。

正直ツールバーに表示される機能のなかには「これを使うタイミングあるんだろうか?」というものって結構ありますよね。

import { EditorState } from "draft-js";
import React, { useState } from "react";
import { Editor } from "react-draft-wysiwyg";
import "react-draft-wysiwyg/dist/react-draft-wysiwyg.css";

const TextEditor = () => {

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

  return (
    <div className="editor">
      <Editor
        editorState={editorState}
        toolbarClassName="toolbarClassName"
        wrapperClassName="wrapperClassName"
        editorClassName="editorClassName"
        onEditorStateChange={setEditorState}
        localization={{
          locale: "ja",
        }}
        hashtag={{
          separator: " ",
          trigger: "#",
        }}
        mention={{
          separator: " ",
          trigger: "@",
          suggestions: [
            { text: "田中太郎", value: "田中太郎", url: "tanakataro" },
            { text: "山田花子", value: "山田花子", url: "yamadahanako" },
            { text: "鈴木順平", value: "鈴木順平", url: "suuzukijunpei" },
          ],
        }}
        placeholder="ここに入力してください"
        {/* ここを追加 */}
        toolbar={{
          options: ["inline", "blockType"],
        }}
      />
    </div>
  );
};

export default TextEditor;

今回は「inline」「blockType」のみをツールバーとして表示させるようにしています。

ご自身が作られるアプリの仕様に合わせて作ってみましょう。

react-draft-wysiwygを使ってHTMLに変換する方法

最後に少し変わったカスタマイズについて紹介します。

テキストエディタに入力した文字をHTML形式に変換するものです。

こちら専用のライブラリを追加でインストールします。

https://www.npmjs.com/package/draft-html

npm i draft-html
{
  "name": "editor-js",
  "version": "0.1.0",
  "private": true,
  "dependencies": {
    "@testing-library/jest-dom": "^5.16.5",
    "@testing-library/react": "^13.4.0",
    "@testing-library/user-event": "^13.5.0",
    // 追加されている
    "draft-html": "^1.0.1-test2",
    "draft-js": "^0.11.7",
    "react": "^18.2.0",
    "react-dom": "^18.2.0",
    "react-draft-wysiwyg": "^1.15.0",
    "react-scripts": "5.0.1",
    "web-vitals": "^2.1.4"
  },
  "scripts": {
    "start": "react-scripts start",
    "build": "react-scripts build",
    "test": "react-scripts test",
    "eject": "react-scripts eject"
  },
  "eslintConfig": {
    "extends": [
      "react-app",
      "react-app/jest"
    ]
  },
  "browserslist": {
    "production": [
      ">0.2%",
      "not dead",
      "not op_mini all"
    ],
    "development": [
      "last 1 chrome version",
      "last 1 firefox version",
      "last 1 safari version"
    ]
  }
}

それでは変換されたHTMLを表示する場所をコンポーネントの中でtextareaタグで用意します。

import { EditorState } from "draft-js";
import React, { useState } from "react";
import { Editor } from "react-draft-wysiwyg";
import "react-draft-wysiwyg/dist/react-draft-wysiwyg.css";

const TextEditor = () => {

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

  return (
    <div className="editor">
      <Editor
        editorState={editorState}
        toolbarClassName="toolbarClassName"
        wrapperClassName="wrapperClassName"
        editorClassName="editorClassName"
        onEditorStateChange={setEditorState}
        localization={{
          locale: "ja",
        }}
        hashtag={{
          separator: " ",
          trigger: "#",
        }}
        mention={{
          separator: " ",
          trigger: "@",
          suggestions: [
            { text: "田中太郎", value: "田中太郎", url: "tanakataro" },
            { text: "山田花子", value: "山田花子", url: "yamadahanako" },
            { text: "鈴木順平", value: "鈴木順平", url: "suuzukijunpei" },
          ],
        }}
        placeholder="ここに入力してください"
        toolbar={{
          options: ["inline", "blockType"],
        }}
      />
      {/* ここを追加 */}
      <div>
        <textarea rows="30" cols="100" disabled></textarea>
      </div>
    </div>
  );
};

export default TextEditor;

先ほどインストールしたdraft-htmlと冒頭から使っているdraft.jsにはテキストをHTMLに変換するための専用のメソッドがあり、以下のように使います。

// ここを追加
import { draftToHtml } from "draft-html";
// ここを追加
import { EditorState, convertToRaw } from "draft-js";
import React, { useState } from "react";
import { Editor } from "react-draft-wysiwyg";
import "react-draft-wysiwyg/dist/react-draft-wysiwyg.css";

const TextEditor = () => {

  const [editorState, setEditorState] = useState(() =>
    EditorState.createEmpty()
  );
  // ここを追加
  let textToHtml = draftToHtml(convertToRaw(editorState.getCurrentContent()));

  return (
    <div className="editor">
      <Editor
        editorState={editorState}
        toolbarClassName="toolbarClassName"
        wrapperClassName="wrapperClassName"
        editorClassName="editorClassName"
        onEditorStateChange={setEditorState}
        localization={{
          locale: "ja",
        }}
        hashtag={{
          separator: " ",
          trigger: "#",
        }}
        mention={{
          separator: " ",
          trigger: "@",
          suggestions: [
            { text: "田中太郎", value: "田中太郎", url: "tanakataro" },
            { text: "山田花子", value: "山田花子", url: "yamadahanako" },
            { text: "鈴木順平", value: "鈴木順平", url: "suuzukijunpei" },
          ],
        }}
        placeholder="ここに入力してください"
        toolbar={{
          options: ["inline", "blockType"],
        }}
      />
      <div>
        <textarea rows="30" cols="100" disabled></textarea>
      </div>
    </div>
  );
};

export default TextEditor;

まずステート変数editorStateにはgetCurrentContentというメソッドを使って、現在のエディタに入力されているものを取得します。

取得した値をconvertToRawメソッドの引数にするとHTML形式に変換できます。

そしてHTMLタグを表示するためにdraftToHtmlというメソッドの引数に渡します。

上図のような形で入力した内容が自動でHTML形式に変換されました。

テキストで入力したものをWordPressなどのCMSツールに入稿するときに、HTMLタグを手動でつけていくのは大変ですよね。

テキストエディタ自体も便利ですが、ちょっとしたアイデアで他の用途にも転用できることを知ってもらえたかと思います。

みなさんもぜひ使ってみてください。

また今回参考にした本は以下になりますのでよければどうぞ。

今回参考にした本はこちら

  • このエントリーをはてなブックマークに追加