Gutenberg Handbook 読書メモ (1)

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/

editsave関数によって、ブロックがどのようにレンダリングされるか定義できる。
※以下のサンプルコードでは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>;
}

ディスカッションに参加

2件のコメント

コメントをどうぞ

コメントを残す