Before Gutenberg – リッチテキストのツールバーにボタンを追加する

これまで紹介してきたように、GutenbergではブロックにRichTextコンポーネントを追加することができる。たとえば、インライン要素がそれに該当する。このサイトSnow Monkey Blocksでは、「蛍光ペン」という機能があり、このようにハイライトを施すことができるが、要するにそういうものだ。

ある程度HTMLに詳しい方ならば、「なんらかのクラス名をつけたspanタグを挿入できるようにしているのだろう」と気づくだろう。今回はそのやり方についてリソースを提供したい。

単一のタグ

上で紹介した蛍光ペンのようなものはそれほど難しくない。Snow Monkey Blocksでは span.smb-highlighter を付与することで対応している。このためのAPIがregisterFormatTypeというAPIなのだが、まだ文書化されていないアンドキュメンテッドため、サンプルコードなどを頼りに実装することになる。以下の4つが参考になるだろう。

上記のコードを参考にすれば、できるはずだ。

複雑なタグ

さて、筆者はWP-Yomiganaというルビを挿入できるプラグインを公開しているのだが、このプラグインではおまけで色々追加しており、全部で5つの要素が存在する。

small
HTML5の定義に乗っ取り、文中での注釈(以前は字義通り「小さい」だった)として機能する。

q
あまり使われないが、文中引用として機能する。例:吾輩は猫である。名前はまだ無い。
dl
このブロックのように、「用語-定義」の組み合わせで表現されるリスト。
cite
引用元のソースを示すタグ。作品名などもこのciteタグでマークアップする。例・吾輩は猫である
ruby
ルビをふるタグ。聖剣エクスカリバーといったような感じで使う。

このうちdlについてはすでに紹介した通り、InnerBlockとして実装済みだ。そして、smallタグおよびciteタグは「単一のタグ」で紹介した方法で実装できる。困るのがqタグとrubyタグである。

属性の指定

ちょうどaタグがhref属性をリンク先として持つように、qタグは引用元を示すcite属性を入力できる。もちろん、qタグまでブラウザデフォルトでない実装を行なっているケースはほとんどないだろうが、とにかく、選択した要素に対してインタラクティブに入力を求める機能が必要だ。

これをどのように実装するかというと、スタイルの適用を決めるtoggleFormatメソッドにwindow.promptで取得した文字列を渡す方法だ。

registerFormatType( 'wp-yomigana/q', {

  title: __( 'Inline Quote', 'wp-yomigana' ),

  tagName: 'q',
  // ここで属性を定義
  attributes: {
    cite: '',
  },

  className: null,

  edit ({ isActive, value, onChange }) {
    // ショートカットキーおよびボタンが押された時のハンドラ
    const onToggle = () => {
      let cite = '';
      if ( ! isActive ) {
        cite = window.prompt( __( 'Enter source information(optional)', 'wp-yomigana' ) ) || '';
      }
      return onChange( toggleFormat( value, {
        type: 'wp-yomigana/q',
        attributes: {
          cite: cite,
        }
      } ) );
    };

    const shortcutType = 'primaryShift';
    const shortcutCharacter ='q';
    const icon = ''; // 略
    return (
      <Fragment>
        <RichTextShortcut type={shortcutType} character={shortcutCharacter} onUse={onToggle}  />
        <RichTextToolbarButton icon={icon} title={__( 'Inline Quotation', 'wp-yomigana' )} onClick={onToggle}
          isActive={isActive} shorcutType={shortcutType} shorcutCharacter={shortcutCharacter} />
      </Fragment>
    )
  }
} );

実際のソースコードはこちら。モーダルウィンドウなどの方法を使うには、もう少し複雑な実装にする必要があるだろう。

複数のタグの組み合わせ

rubyタグのマーックアップは次のようになる。

<ruby>聖剣<rt>エクスカリバー</rt></ruby>

つまり、rubyノードの中にテキストノードとrtノードが続くイレギュラーなマークアップだ。これを実現するためには、APIによって提供されているtoggleFormatメソッドだけではダメで、その前にもうちょっと頑張らなければならなくなる。次のコードをご覧いただきたい。これはショートカットやボタンが押された時に実行されるメソッドである。

const onToggle = () => {
  // ルビ文字を定義
  let ruby = '';
  if ( ! isActive ) {
    // ルビが設定済みでない場合
    ruby = window.prompt( __( 'Enter ruby characters', 'wp-yomigana' ) ) || value.text.substr( value.start, value.end - value.start );
    // ルビの親文字の位置を調べる
    const rubyEnd   = value.end;
    const rubyStart = value.start;
    // valueは選択部分の位置情報を持った、親段落内要素の配列。
    value = insert( value, ruby, rubyEnd );
    // ルビの開始位置、終了位置を記録
    value.start = rubyStart;
    value.end   = rubyEnd + ruby.length;
    // rubyタグを適用。
    value = applyFormat( value, {
      type: 'wp-yomigana/ruby'
    }, rubyStart, rubyEnd + ruby.length );
    // rubyタグ内のrtタグを適用
    value = applyFormat( value, {
      type: 'wp-yomigana/rt'
    }, rubyEnd, rubyEnd + ruby.length );
  } else {
    // ルビが設定済みの場合、単に外すだけ。
    value = toggleFormat( value, {
      type: 'wp-yomigana/ruby'
    } );
  }
  return onChange( value );
};

Format APIでは、DOMではない独自の要素の配列が渡ってくるので、insertメソッドなどのAPIを利用して要素を手作業で追加していく必要がある。

また、rtタグはフォーマット要素として認識されていないので、それもあらかじめ登録しておく必要がある。この場合、編集UIを持たないので、要素の中身はFragmentだけだ。

registerFormatType( 'wp-yomigana/rt', {

  title: __( 'Ruby Character', 'wp-yomigana' ),

  tagName: 'rt',

  className: null,

  edit( {isActive, value, onChange} ) {
    return <Fragment></Fragment>;
  }
} );

このフォーマットをあらかじめ宣言しておかないと、applyFormatは適用されない。また、フォーマットの適用はruby -> rtの順番でなければならず、逆だと動かなかった。全体のソースコードはこちらである。


RichText APIは便利なAPIではあるが、まだドキュメントされておらず、なおかつ高度な内容であるため、かなり難しい。サンプルコードなどを頼りに実装していくしかない状態だ。この部分の知見は日本語情報でほとんどシェアされていないので、我こそはと思われる方は積極的にブログなどで公開していってほしい。

コメントを残す

このサイトはスパムを低減するために Akismet を使っています。コメントデータの処理方法の詳細はこちらをご覧ください