2020.12.20
mt:BlockEditorBlocks ループ内のテキストボックスのtypeキーの間違いを修正
誤 core-text -> 正 sixapart-input
これは「Movable Type Advent Calendar 2020」の14日目の記事です。
MovableType.netの「カスタムブロック」のチュートリアルは、入力項目が1、2個のブロックまでとなっていますが、複数、かつ種類が混在した入力項目を持っているブロックの作成について解説します。
ポイントとなる過程の解説のみなので、全体の流れについては公式ドキュメントを参照してください。
ちなみにこれは「レベルと外観を自由に変更できる見出しブロック」です。こういうの大好物です。
基本的な考え方
ブロックエディタのカスタマイズをしたことがある人は経験があると思いますが、ブロックエディタで入力された内容を開発者がイメージしたHTMLに組み上げるには、入力値を「変数」として個別に取得し、そのCMSのスクリプトで処理するのが、基本的な考え方となります。
WordPressであればPHPですし、Movable TypeであればMTMLとJavaScriptです。
カスタムスクリプトの場合
エディタ内のプレビュー画面は、そのブロックのみ有効になる「カスタムスクリプト」を、JavaScriptで書いて整形します。
この画像は、冒頭の「マークアップとデザインを変更できる見出しブロック」から、カスタムスクリプトや設定を一旦削除した状態の編集画面です。ドロップダウンで選択した内容が、そのまま文字列としてつながってiframe内のbody要素に出力されます。このままでは、各値をJSの変数として取得することができません。
クラスで判定する
ですが、カスタムブロックの設定画面では「入力内容を包括するHTML要素」と「付与するクラス属性値」を指定できます。これを利用すればQuerySelecterで各項目を抽出することができます。
以下はexample-block-designというクラス名を付与したドロップダウンの値を抽出、判定しているカスタムスクリプトです。値さえ取れればあとはHTMLを生成するだけです。
<script>
document.addEventListener('DOMContentLoaded', function () {
var
html = document.body,
design;
design = html.querySelector('.example-block-design').innerText;
if( design.match( /装飾なし中央/ ) ) {
console.log('装飾なし中央');
} else if( design.match( /下線左/ ) ) {
console.log('下線左');
} else if( design.match( /下線中央/ ) ) {
console.log('下線中央');
} else if( design.match( /網掛け/ ) ) {
console.log('網掛け');
} else if( design.match( /マーク付き/ ) ) {
console.log('マーク付き');
} else {
console.log('装飾なし左');
}
});
</script>
ただし、複数の画像やテーブルが混在している場合や、ブロックの追加・削除が自由にできるタイプのブロックの場合は、抽出がかなり難しくなります。現時点ではテキストボックスや、値が固定されるドロップダウンを主体に組んだ方が良いです。
MTML
さて、 MTBlockEditorSetCompiledHtml メソッドでエディタ内のHTMLを直接書き換えてよいのであれば、カスタムスクリプトだけを気にすればよいのですが、テンプレート側で実際の表示を整えたい場合は、MTMLでも同様の判定が必要です。
仕様が不安定な現時点では、整形後のHTMLで保存してしまうのは避けたいという本音もあります。
typeでの判別
各ブロックには「type」というキーがあります。ブロック内の入力項目がすべて異なるタイプであれば、公式ドキュメントにもある通り mt:If で判別できます。
<mt:BlockEditorBlocks tag="PageBody">
<mt:If name="type" eq="custom-example">
<mt:BlockEditorBlocks>
<mt:If name="type" eq="sixapart-select">
ドロップダウン:<br><mt:Var name="__value__" /><hr>
<mt:ElseIf name="type" eq="sixapart-input">
テキスト:<br><mt:Var name="__value__" /><hr>
<mt:ElseIf name="type" eq="core-html">
HTML:<br><mt:Var name="__value__" /><hr>
</mt:If>
</mt:BlockEditorBlocks>
<mt:Else>
<mt:Var name="__value__" />
</mt:If>
</mt:BlockEditorBlocks>
判別ができないケース
ですが、ここで問題が出てきます。
以下は「example」という識別子のカスタムブロック内にある、A/Bふたつのドロップダウン(sixapart-select)のうちの、Aの値を抽出しようとしています。
しかし、これではブロック内のすべてのドロップダウンが出力されてしまいます。
<mt:BlockEditorBlocks tag="PageBody">
<mt:If name="type" eq="custom-example">
<mt:BlockEditorBlocks>
<mt:If name="type" eq="sixapart-select">
<mt:Var name="__value__" /><br>
</mt:If>
</mt:BlockEditorBlocks>
<mt:Else>
<mt:Var name="__value__" />
</mt:If>
</mt:BlockEditorBlocks>
いろいろ調べたのですが、どうも現時点でMTML側では、同じタイプの入力項目を判別する手段がないのです。もしかして配列になってやしないかとindexにクラスやラベルを投げたりしましたがだめでした。教えてシックス・アパートの偉い人。
値をmt:Ifで判別する
少々荒っぽい方法ですが、以下の手順で複数のドロップダウンの判別が可能になりました。
- ドロップダウンを作成する際に、選択肢の値すべてに接頭辞を付ける
- ドロップダウンの選択内容(__value__)にA/Bどちらの接頭辞が含まれているか判定する
- 選択内容の値の判定をする
1は、以下のような値に同じ文字列が入ったドロップダウンを作成するという意味です。選択肢の数はいくつでもいいです。エディタに接頭辞が見えてしまうので簡潔にしたいところです。
A:選択肢1
A:選択肢2
A:選択肢3
B:選択肢1
B:選択肢2
B:選択肢3
MTMLは以下です。
<mt:BlockEditorBlocks tag="PageBody">
<mt:If name="type" eq="custom-example">
<mt:BlockEditorBlocks>
<mt:If name="type" eq="sixapart-select">
<mt:If name="__value__" like="A:">
<mt:If name="__value__" like="選択肢2">
Aの選択肢は2 <mt:Var name="__value__" /><br>
<mt:ElseIf name="__value__" like="選択肢3">
Aの選択肢は3 <mt:Var name="__value__" /><br>
<mt:Else>
Aの選択肢は1 <mt:Var name="__value__" /><br>
</mt:If>
<mt:If name="__value__" like="B:">
<mt:If name="__value__" like="選択肢2">
Bの選択肢は2 <mt:Var name="__value__" /><br>
<mt:ElseIf name="__value__" like="選択肢3">
Bの選択肢は3 <mt:Var name="__value__" /><br>
<mt:Else>
Bの選択肢は1 <mt:Var name="__value__" /><br>
</mt:If>
</mt:If>
</mt:If>
</mt:BlockEditorBlocks>
<mt:Else>
<mt:Var name="__value__" />
</mt:If>
</mt:BlockEditorBlocks>
この例では判定のみとしてしていますが、 mt:SetVar や mt:SetVarBlock で__value__を保持しておけば、好きなようにHTMLを整形できます。
注意点
判定がlikeなので、かなりあいまいになります。できるだけ異なる値にしなければなりません。例の「選択肢1」「選択肢2」というのも文字列が似通っていて事故が起きそうです。
また、 mt:If は判定後のbreakができないので、以下の順番で判定すると「大」のところで「特大」もtrueになってしまいます。
中
大
特大
複数項目を持ったカスタムブロックの例
冒頭の「高度な見出し」ブロックのインポートデータをGistに置いておきます。
以下のような見出しが生成されます。CSSはお好みで追加してください。
<div class="mt-be-title">
<h1 class="mt-title mt-title-style-c mt-title-level-2">見出しの文字列を入力してください</h1>
</div>
まとめ
以上です。
これはMovableType.net(ASP版)の解説です。私はパッケージ版のブロックエディタは触っていないので、この方法ではできないかもしれません。あしからずご了承ください。
まだ不自由なところはありますが、MTのブロックエディタはページ・記事単位でオン/オフを切り替えられ、Gutenbergのように編集画面のレイアウトが変わってしまうこともないので、かなり使い勝手が良いと思います。
個人的には、入力内容の整形はカスタムスクリプトではなくテンプレート側でしたいので、テンプレートの方もブロック単位でエクスポートできればいいのになあと思っています。