WordPressのエディタ画面のカスタマイズのため、新エディタ「Gutenberg」のドキュメントを読んでます。覚え書きを書き残しておきます。
Introduction
https://wordpress.org/gutenberg/handbook/
- “Gutenberg”とは、新しいWordPressエディタのコードネーム
- リッチなレイアウトを誰でも簡単に作れるようにするのが目的
- “Block” という概念を導入することで、ショートコードやカスタムHTMLを不要にする
The Language of Gutenberg
https://wordpress.org/gutenberg/handbook/language/
- Gutenbergの中心にあるのは「ブロック」の概念である
- Gutenbergでは、記事(post)はブロックの集合からなる
- ブロックは活版印刷における活字のようなもので、記事の編集時には必要だが、最終的に出力される記事には含まれない
ブロックはHTMLよりも高い次元にある
- ブロックはHTMLを出力するための機能だが、ユーザが編集を助けるための機能も伴っている
- ブロックは最終的なHTMLを出力するために必要な情報を全て保持している
2つのpost
- Gutenbergの記事(post)はブロックのことを知っている(block-aware)
- Gutenbergのpostは、
post_content
そのものではない
ブロックのツリー
- 実行時に、ブロックはメモリ上に保持される
- GutenbergのpostはHTMLではなく、オブジェクトのツリーである
- ルートノードは必要とせず、ノードのコレクションである
[
{
type: 'core/cover-image',
attributes: {
url: 'my-hero.jpg',
align: 'full',
hasParallax: false,
hasBackgroundDim: true
},
children: [
"Gutenberg posts aren't HTML"
]
},
{
type: 'core/paragraph',
children: [
"Lately I've been hearing plen…"
]
}
]
シリアライゼーションとHTMLコメントの目的
- Gutenbergのデータモデルは、記事の編集中のメモリに保持されるが、レンダリング結果からは痕跡が消える
- Gutenbergはデータモデルを
post_content
に保存できるようシリアライズする - シリアライズの過程では、ツリーをHTMLに変換する。その際、HTMLコメントをブロックの境界として用いる
- 編集時には、HTMLコメントからツリーを再構築する
- 仮に、Gutenbergに対応していないテーマを使用したとしても、最低限のコンテンツは描画されるようになっている(動的な要素は描画されない)
デリミタと式文法の構文解析
- HTMLコメントでは、二重のハイフン(–)を除いて、文法上の制約は無い
- ブロックの属性はJSONリテラルとしてコメントに埋め込まれる
シリアライズされたブロックの中身
ブロックがwp_content
に保存されると、その属性はコメント内に保存される
<!-- wp:image -->
<figure class="wp-block-image">
<img src="source.jpg" alt="" />
</figure>
<!-- /wp:image -->
サーバサイドでレンダリングされる動的なブロックは以下のようになる:
<!-- wp:latest-posts {"postsToShow":4,"displayPostDate":true} /-->
Gutenbergのライフサイクル
要約すると、Gutenbergの記事を編集する手順は、文書を保存してツリーを生成するところから始まる。最終的に、ブロックはpost_content
に保存される。編集中、全ての操作はブロックのツリーに変更を加える。
Extensibility
https://wordpress.org/gutenberg/handbook/extensibility/
ブロックの作成
ブロックAPIを使うことで、静的なブロック、サーバサイドでレンダリングされる動的ブロック、post_metaにデータを保存するブロックなどを作成できる。
※メモ:クライアントサイドでの動的なレンダリングはできない???
静的ブロックは以下のように作成できる。
var el = wp.element.createElement;
wp.blocks.registerBlockType( 'mytheme/red-block', {
title: 'Red Block',
icon: 'universal-access-alt',
category: 'layout',
edit: function() {
return el( 'div', { style: { backgroundColor: '#900', color: '#fff', padding: '20px' } }, 'I am a red block.' );
},
save: function() {
return el( 'div', { style: { backgroundColor: '#900', color: '#fff', padding: '20px' } }, 'I am a red block.' );
}
} );
ブロックの削除
ブラックリストの使用
以下のようなJavaScriptコードを書き、
// myplugin.js
wp.blocks.unregisterBlockType( 'core/verse' );
エディタ内で読み込むようにすればよい。
<?php
// myplugin.php
function myplugin_blacklist_blocks() {
wp_enqueue_script(
'myplugin-blacklist-blocks',
plugins_url( 'myplugin.js', __FILE__ ),
array( 'wp-blocks' )
);
}
add_action( 'enqueue_block_editor_assets', 'myplugin_blacklist_blocks' );
ホワイトリストの使用
特定のブロック以外を無効化したい場合、以下のように書く。
// myplugin.js
var allowedBlocks = [
'core/paragraph',
'core/image',
'core/html',
'core/freeform'
];
wp.blocks.getBlockTypes().forEach( function( blockType ) {
if ( allowedBlocks.indexOf( blockType.name ) === -1 ) {
wp.blocks.unregisterBlockType( blockType.name );
}
} );
inserterからブロックを隠す
inserterに表示するブロックはサーバサイドでフィルタリングできる。
// 許可するブロック型の名前のリスト
add_filter( 'allowed_block_types', function() {
return [ 'core/paragraph' ];
} );
ブロックの編集(実験的)
既存のブロックの挙動を変更するため、Gutenbergはフィルターを提供している:
registerBlockType
: ブロック設定のフィルタリングgetSaveContent.extraProps
:save
関数でWP elementを返す全てのブロックに適用されるフィルタ。ブロックにpropsを追加する場合に使用するBlockEdit
: ブロックのedit
関数で受け取ったWP elementを編集するのに使う
例
全てのブロックにデフォルトで背景を設定している。
// Our filter function
function addBackgroundProp( props ) {
return Object.assign( props, { backgroundColor: 'red' } );
}
// Adding the filter
wp.blocks.addFilter(
'getSaveContent.extraProps',
'myplugin\add-background',
addBackgroundProp
);
エディターUIの拡張(SlotとFill)
Coming soon. とのこと(2017/12/04現在)
Block API
https://wordpress.org/gutenberg/handbook/block-api/
プラグインやテーマは、エディタに対する独自の機能や追加の機能をブロックとして登録できる
ブロック型の登録
全てのブロックは、ブロック型定義の登録を行う必要がある。これには、registerBlockType
関数を使用する。
ブロック名
新しいブロック型の登録にはregisterBlockType
関数を使用する。この関数は、ブロック名と設定オブジェクトを引数に取る。
ブロック名は namespace/block-name
という構造になっていなければならない。namespaceはプラグインやテーマの名前である。
// Registering my block with a unique name
registerBlockType( 'my-plugin/book', {} );
注意:ブロック名には小文字のアルファベットと数字、ダッシュ(-)記号のみを含み、最初の1文字目はアルファベットでなければならない
ブロック設定オブジェクトの代表的な設定は下記の通り
- title(必須): ブロックのinserter上に表示する名前
- category(必須): inserter上の分類。
common
formatting
layout
widgets
embed
のいずれか - icon: ブロックのアイコン WordPressのDashiconsの名前を指定するか、独自のsvg要素を指定する
- keywords: 検索時のキーワード
- attributes: ブロックが使用する構造化されたデータ
- transforms: 説明なし。WIPらしい
- useOnce: 記事ごとに1個だけしか使えない場合はtrueにする
- supports: サポート機能を拡張する設定
- anchor(default: false): ページ内リンクできるようにする
- customClassName(default: true): ブロックのラッパー要素にブロック独自のクラス名をつける
- className(default: true):
.wp-block-bour-block-name
という形式のクラスをつけるか
- supportHTML(default: true): HTMLモードで編集可能か
Edit and Save
https://wordpress.org/gutenberg/handbook/block-edit-save/
edit
とsave
関数によって、ブロックがどのようにレンダリングされるか定義できる。
※以下のサンプルコードではESNextとJSXを使用している。
Edit
edit
関数はエディタを使用している際のブロックの構造を定義する。
// Defining the edit interface
edit() {
return <hr />;
}
この関数は、引数のオブジェクトから以下のプロパティを受け取ることができる。
attributes
利用可能な属性と、それに対応した値。以下のサンプルでは、content
という属性を定義して、ブロックのコンテンツとして使用している。
edit( { attributes } ) {
return <div>{ attributes.content }</div>;
}
className
ラッパー要素のクラス名。これらはsave
関数によって自動的に追加されるが、edit
の段階では自動的には追加されない。クラス名を明示的に受け取るにはclassNameプロパティを使用する。
edit( { attributes, className } ) {
return <div className={ className }>{ attributes.content }</div>;
}
focus
ブロックがフォーカスされている状態か否かを判定する。
edit( { attributes, className, focus } ) {
return (
<div className={ className }>
{ attributes.content }
{ focus &&
<span>Shows only when the block is focused.</span>
}
</div>
);
}
setAttributes
ユーザの操作に応じて属性の値を更新できる。
edit( { attributes, className, focus } ) {
const { content, mySetting } = attributes;
// ユーザがボタンをクリックしたら設定を切り替え
const toggleSetting = () => setAttributes( { mySetting: ! mySetting } );
return (
<div className={ className }>
{ content }
{ focus &&
<button onClick={ toggleSetting }>Toggle setting</button>
}
</div>
);
}
setFocus
ToDoらしい
Save
save
関数は、最終的なマークアップを決定する。これはGutenbergによってシリアライズされて、post_content
に保存される。
save() {
return <hr />;
}
save
関数はnull
を返すこともできる。この場合、属性のみがシリアライズされ、サーバサイドでHTMLの描画処理が行われる(これを動的(dynamic)ブロックと呼ぶらしい)。
save
関数もプロパティを受け取ることができる。
attributes
edit
と同様の使い方。保存時にコンテンツが確定し、サーバサイドでの描画が必要ない静的ブロックでは、attributesの値をsave
関数でマークアップに埋め込めば良い。
save( { attributes } ) {
return <div>{ attributes.content }</div>;
}
コメントをどうぞ