これまで紹介してきたように、GutenbergではブロックにRichTextコンポーネントを追加することができる。たとえば、インライン要素がそれに該当する。このサイトSnow Monkey Blocksでは、「蛍光ペン」という機能があり、このようにハイライトを施すことができるが、要するにそういうものだ。
ある程度HTMLに詳しい方ならば、「なんらかのクラス名をつけたspanタグを挿入できるようにしているのだろう」と気づくだろう。今回はそのやり方についてリソースを提供したい。
単一のタグ
上で紹介した蛍光ペンのようなものはそれほど難しくない。Snow Monkey Blocksでは span.smb-highlighter
を付与することで対応している。このためのAPIがregisterFormatType
というAPIなのだが、まだ文書化されていないため、サンプルコードなどを頼りに実装することになる。以下の4つが参考になるだろう。
- Advanced Rich Text Tools for Gutenberg – Gutenbergのコミッターであるiseuldeによるサンプルプラグイン。
code
タグで選択した要素を包むサンプルがある。 - Gutenberg(ブロックエディタ)メモ: 任意のHTMLタグを範囲選択で挿入可能なボタンをRichTextのツールバーに追加する – 上記を参考にした日本語ブログ記事。
- Snow Monkey Blocksのコード – 上述した通り、
span.smb-highlighter
をつけるボタン。 - WP-Yomiganaのコード – 筆者が作成したプラグインで、選択したテキストを
small
タグで包む。
上記のコードを参考にすれば、できるはずだ。
複雑なタグ
さて、筆者は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ではあるが、まだドキュメントされておらず、なおかつ高度な内容であるため、かなり難しい。サンプルコードなどを頼りに実装していくしかない状態だ。この部分の知見は日本語情報でほとんどシェアされていないので、我こそはと思われる方は積極的にブログなどで公開していってほしい。