draft-js で文字色を変更する

2022年8月14日日曜日

javascript

t f B! P L

enter image description here

draft-jsは、JavaScirptでリッチテキストエディタの実装をする非常にカスタマイズ性の高いフレームワークである。しかしながら、日本語の情報が少ないため、ちょっとした事でも調べるのに時間がかかることがある。

今回は、私自身もつまずいた、draft-jsで文字色を変更する方法を紹介します。

スポンサーリンク

customStyleMap(カスタムスタイルマップ)

太字、斜体、下線などの文字装飾は、draft-jsで既定のインラインスタイル名が定義されているため、BOLDITALICUNDERLINEのような名称を指定すれば、スタイルが適用できる。

const newEditorState = RichUtils.toggleInlineStyle(editorState, 'BOLD')
setEditorState(newEditorState)

ただ、今回やりたい文字色の変更などは、既定のインラインスタイル名は定義されていないため、CSSの classに似た方法で装飾を適用する customStyleMap を作る必要がある。

今回は、文字色を「赤」「黄色」「青」に変更する3つのカスタム・インラインスタイルを定義する。

const customStyleMap = {
  red: { color: "red" },
  yellow: { color: "yellow" },
  blue: { color: "blue" }
};

次に、上で宣言したオブジェクトを、draft-js の Editorコンポーネントに適用する。

<Editor
  editorState={editorState}
  onChange={setEditorState}
  customStyleMap={customStyleMap}
/>

文字色を変えてみる

実際に、上で作成した customStyleMap を使って文字色を変えてみよう。

使用方法は簡単で、適用させたいカスタム・インラインスタイル名を RichUtils.toggleInlineStyle などのインラインスタイルを適用する関数に指定するだけでよい。

//赤色の文字色を切り替える
const newEditorState = RichUtils.toggleInlineStyle(editorState, 'red')
setEditorState(newEditorState)

スポンサーリンク

文字色を複数切り替える

通常、エディタを作る場合は、文字色を複数の色に切り替えられるようにするだろう。

ある色から、別の色に文字色を変更する場合は、前に適用していたカスタム・インランインスタイルをクリアしてから、新しい色のカスタム・インランインスタイル名を設定してやる必要がある。

そのため、RichUtils.toggleInlineStyle のような関数は使えず、次のような少々面倒なコードで文字色を切り替える必要がある。

スタイルのクリア

新しい文字色のカスタム・インラインスタイルを適用する前に、一旦、全ての色のインラインスタイルをクリアする関数を作る。

  //スタイルのクリアする
  const removeInlineStyles = (editorState: EditorState) => {
    const contentState = editorState.getCurrentContent();
    const contentWithoutStyles = Object.keys(customStyleMap).reduce(
      (newContentState: any, key: string) => 
        Modifier.removeInlineStyle(
          newContentState,
          editorState.getSelection(),
          key
        ),
      contentState
    );
    return EditorState.push(
      editorState,
      contentWithoutStyles,
      'change-inline-style'
    );
  };

新しい色のカスタム・インラインスタイルの適用

次に、変更したい文字色のカスタム・インラインスタイルを適用する関数を作る。

  // 指定した文字色のスタイルを適用する
  const appyInlineStyle = (editorState: EditorState, style: string) => {
    const neeContentState = Modifier.applyInlineStyle(
      editorState.getCurrentContent(), 
      editorState.getSelection(), 
      style);
    return EditorState.push(
      editorState,
      neeContentState,
      'change-inline-style'
    );
  };

色を切り替えるボタンの実装

最後に、「赤」「黄色」「青」のボタンが押されたら、先ほど作った removeInlineStylesappyInlineStyle 関数を呼び出し、文字色を切り替えるボタンを実装する。

e.preventDefault() しているのは、ボタンを押すとエディタのフォーカスと選択範囲が解除されてしまうのの防止策である。

<button onMouseDown={(e) => {
  const state = removeInlineStyles(editorState);
  setEditorState(appyInlineStyle(state, "red"));
  e.preventDefault()
}}></button>
<button onMouseDown={(e) => {
  const state = removeInlineStyles(editorState);
  setEditorState(appyInlineStyle(state, "yellow"));
  e.preventDefault()
}}>黄色</button>
<button onMouseDown={(e) => {
  const state = removeInlineStyles(editorState);
  setEditorState(appyInlineStyle(state, "blue"));
  e.preventDefault()
}}></button>

実行すると、こんな感じに押されたボタンに対応する色で、エディタの選択範囲の文字色が切り替わる。

enter image description here

さいごに

さいごに、今回作成した文字色を切り替えるエディタのコード全文を載せておく。

import React, { useRef } from 'react';
import { useState } from 'react';
import { Editor, EditorState, Modifier, RichUtils } from 'draft-js';
import 'draft-js/dist/Draft.css';

// カスタムスタイル
const customStyleMap = {
  red: { color: "red" },
  yellow: { color: "yellow" },
  blue: { color: "blue" }
};

function SampleEditor() {
  const editorRef = useRef<Editor>(null);
  const [editorState, setEditorState] = useState(
    () => EditorState.createEmpty(),
  );

  //スタイルをクリアする
  const removeInlineStyles = (editorState: EditorState) => {
    const contentState = editorState.getCurrentContent();
    const contentWithoutStyles = Object.keys(customStyleMap).reduce(
      (newContentState: any, key: string) => 
        Modifier.removeInlineStyle(
          newContentState,
          editorState.getSelection(),
          key
        ),
      contentState
    );
    return EditorState.push(
      editorState,
      contentWithoutStyles,
      'change-inline-style'
    );
  };

  // スタイルを適用する
  const appyInlineStyle = (editorState: EditorState, style: string) => {
    const neeContentState = Modifier.applyInlineStyle(
      editorState.getCurrentContent(), 
      editorState.getSelection(), 
      style);
    return EditorState.push(
      editorState,
      neeContentState,
      'change-inline-style'
    );
  };

  return (
    <>
      <div>
        <button onMouseDown={(e) => {
          const state = removeInlineStyles(editorState);
          setEditorState(appyInlineStyle(state, "red"));
          e.preventDefault()
        }}></button>
        <button onMouseDown={(e) => {
          const state = removeInlineStyles(editorState);
          setEditorState(appyInlineStyle(state, "yellow"));
          e.preventDefault()
        }}>黄色</button>
        <button onMouseDown={(e) => {
          const state = removeInlineStyles(editorState);
          setEditorState(appyInlineStyle(state, "blue"));
          e.preventDefault()
        }}></button>
      </div>
      <div style={ {width: "600px", height: "300px", background: "#eee"} }>
        <Editor
          editorState={editorState}
          onChange={setEditorState}
          customStyleMap={customStyleMap}
        />
      </div>
    </>
  );
}

export default SampleEditor;
スポンサーリンク
スポンサーリンク

このブログを検索

Profile

自分の写真
Webアプリエンジニア。 日々新しい技術を追い求めてブログでアウトプットしています。
プロフィール画像は、猫村ゆゆこ様に書いてもらいました。

仕事募集もしていたり、していなかったり。

QooQ