こんにちは!Donyです。

今日は、Wordpress5.0から導入された新しいエディター「Gutenberg」のオリジナルブロックの作り方を説明したいと思います。

自分はまだReactに慣れてないので、React式ではなく一般Javascriptのコードでブロックを作ります。

今日の記事で作るブロックは、固定表示のタイトル1つに、他のブロックを自由に追加できるブロックです。以下のイメージのようなものです。

完成イメージ

完成コード

phpコード(functions.php)

/*
 * エディター用スクリプト
*/
add_action( 'enqueue_block_editor_assets', 'add_custom_block_style_script' );
function add_custom_block_style_script() {
    //Javascript
    $directory_url = get_template_directory_uri();
    $wp_js = array( 'wp-edit-post', 'wp-element', 'wp-blocks', 'wp-editor', 'wp-block-editor', );
    wp_enqueue_script( 'add-post-editor-block-script', $directory_url . '/original-blocks.js', $wp_js, '', true );
    
    //CSS
    wp_enqueue_style( 'dg-style-editor-style', $directory_url . '/original-blocks.css' );
}

/*
 * 専用カテゴリー追加
*/
add_filter( 'block_categories', 'add_editor_block_categories', 10, 2 );
function add_editor_block_categories( $categories, $post ) {
    return array_merge(
        array(
            array(
                'slug' => 'original-blocks', //Javascriptのカテゴリーで使う
                'title' => 'オリジナルブロック', //ブロックリストに表示される
                'icon'  => 'dashicons-layout', //ブロックリストにアイコン表示
            ),
        ),
        $categories
    );
}

Javascriptコード(original-blocks.js)

(function( wp ) {
    'use strict';
    const { blocks, element, editor, blockEditor } = wp;
    const el = element.createElement;
    const { RichText } = editor;
    const { InnerBlocks } = blockEditor;
    
    /*
     * アコーディオン
    */
    blocks.registerBlockType( 'original/new-block', {
        title: 'オリジナルブロック',
        icon: 'wordpress-alt',
        category: 'original-blocks', /* PHPで指定したslugを入力 */
        keywords: [ 'オリジナル', 'original', 'おりじなる' ],
        attributes: {/* リッチテキストの場合は、必須 */
            h2: {/* props.attributes.「content」と連動 */
                type: 'string',/* 取得する値のタイプ */
                source: 'html',/* 値のどこまで抽出するか */
                default: 'オリジナルブロックを作る',
                selector: 'h2'/* tagName(タグ名)かクラス名を一致させる */
            }
        },
        example: {
            attributes: {
                button: 'オリジナルブロックを作る!',
            },
            innerBlocks: [
                {
                    name: 'core/paragraph',
                    attributes: {
                        content: 'ブロックにコンテンツを入れる'
                    },
                },
            ],
        },
        supports: {
            anchor: true
        },
        edit: function( props ) {
            return el(
                'div',
                { className: 'block-area' },
                [
                    el(
                        RichText, {
                            tagName: 'h2', 
                            className: 'block-title',
                            value: props.attributes.h2,
                            formattingControls: [ 'bold', 'italic' ],
                            onChange: function( content ) {
                                props.setAttributes( { h2: content } );
                            },
                            placeholder: 'オリジナルブロックを作る',
                        }
                    ),
                    el(
                        'div',
                        { className: 'block-content' },
                        [
                            el(
                                InnerBlocks,
                                {
                                    template: [ ['core/paragraph'] ]
                                }
                            ),
                        ]
                    )
                ]
            );
        },
        save: function( props ) {
            return el(
                'div',
                { className: 'block-area' },
                [
                    el( RichText.Content, {
                        tagName: 'h2',
                        className: 'block-title',
                        value: props.attributes.h2,
                        type: 'h2'
                    } ),
                    el(
                        'div',
                        { className: 'block-content' },
                        [
                            el( InnerBlocks.Content, {} ),
                        ]
                    )
                ]
            );
        }
    });
} (
    window.wp
) );

CSS(original-blocks.css)

@charset "UTF-8";
.block-area {
    padding: 20px;
    border:1px solid #555;
}
.block-area .block-title {
    margin: 0 0 20px;
    padding: 0 0 5px;
    border-bottom: 1px dotted #555;
    font-size:24px;
}

コード説明

各コードの役割を見てみましょう。まずPHPから登録が必要です。

PHP

PHPでは、以下のことをやります。

  • JavascriptとCSSファイルの読み込み
  • Gutenbergカテゴリー追加

ファイルの読み込みコード

hook設定

まずGutenbergにファイルを追加できるように、hookをかけるようにしましょう。

「enqueue_block_editor_assets」のhookを使います。

add_action( 'enqueue_block_editor_assets', 'add_custom_block_style_script' );
function add_custom_block_style_script() {

}

このようにhook設定できたら、次はJavascriptとCSSを追加しましょう。

Javacriptの追加

Javascriptでは、「wp_enqueue_script」を利用して追加できます。

/*
 * エディター用スクリプト
*/
add_action( 'enqueue_block_editor_assets', 'add_custom_block_style_script' );
function add_custom_block_style_script() {
    //Javascript読み込み
    $directory_url = get_template_directory_uri(); //テーマまでのパス取得
    $wp_js = array( 'wp-edit-post', 'wp-element', 'wp-blocks', 'wp-editor', 'wp-block-editor', );
    wp_enqueue_script( 'add-post-editor-block-script', $directory_url . '/original-blocks.js', $wp_js, '', true );
}

使った関数はこのようになります。

  • get_template_directory_uri():テーマまでのパス取得
  • wp_enqueue_script( $handle, $src, $deps, $ver, $in_footer );
    • $handle:スクリプトID
    • $src:ファイルのURLパス
    • $deps:指定したファイルより後から読み込ませる場合、スクリプトIDを配列で入力
    • $ver:登録するファイルのバージョン
    • $in_footer:trueの場合</body>の前にファイル追加

「$deps」は、Gutenbergの場合他に使われているJavascriptの関数などを使えるように、指定したファイルの後に読み込まれるようにしてくれます。

今回のブロックで使うものは、「wp-edit-post、wp-element、wp-blocks、wp-editor、wp-block-editor」なるので、こちらの後から読み込むように配列に追加します。

CSSの追加

次はCSSを読み込むようにしましょう。

CSS追加で使う関数は「wp_enqueue_style」を使います。

/*
 * エディター用スクリプト
*/
add_action( 'enqueue_block_editor_assets', 'add_custom_block_style_script' );
function add_custom_block_style_script() {
    //Javascript読み込み
    $directory_url = get_template_directory_uri();
    $wp_js = array( 'wp-edit-post', 'wp-element', 'wp-blocks', 'wp-editor', 'wp-block-editor', );
    wp_enqueue_script( 'add-post-editor-block-script', $directory_url . '/original-blocks.js', $wp_js, '', true );
    
    //CSS読み込み
    wp_enqueue_style( 'dg-style-editor-style', $directory_url . '/original-blocks.css' );
}

「wp_enqueue_style」の説明は以下になります。

  • wp_enqueue_style( $handle, $src, $deps, $ver, $media );
    • $handle:スタイルID
    • $src:ファイルのURLパス
    • $deps:指定したファイルより後から読み込ませる場合、スタイルIDを配列で入力
    • $ver:登録するファイルのバージョン
    • $media:CSS-media-typesを指定(mediaなど)

オリジナルカテゴリー追加

次はオリジナルブロック用の専用カテゴリーを追加するコードです。

専用カテゴリーを使わない場合は、既存にあるコアカテゴリーに追加することもできます。

/*
 * 専用カテゴリー追加
*/
add_filter( 'block_categories', 'add_editor_block_categories', 10, 2 );
function add_editor_block_categories( $categories, $post ) {
    return array_merge(
        array(
            array(
                'slug' => 'original-blocks', //Javascriptのカテゴリーで使う
                'title' => 'オリジナルブロック', //ブロックリストに表示される
                'icon'  => 'dashicons-layout', //ブロックリストにアイコン表示
            ),
        ),
        $categories
    );
}
  • slug:ブロックのカテゴリーID(Javascriptでも使う)
  • title:カテゴリー名
  • icon:カテゴリーアイコン

Javascript

無名関数設定

最初は、Javascriptがすぐ実行できるように無名関数を設定します。

引数には、外部スクリプトが読み込まれるように、後ろに「window.wp」を設置して、それを無名関数の「wp」に引き継ぐようにします。

//wpの中身は、window.wpになる
(function( wp ) {
    'use strict';
    
} (
    window.wp
) );

変数定義

ブロック作成に必要なオブジェクトを使いやすいように、変数として定義します。

使い方は下で説明します。

  • blocks, element, editor, blockEditor:各オブジェクトを定義
  • el:ブロックの中に要素を追加できる関数を定義
  • RichText:入力できる要素を定義
  • InnerBlocks:既存ブロックを利用できる
(function( wp ) {
    'use strict';
    const { blocks, element, editor, blockEditor } = wp;
    const el = element.createElement;
    const { RichText } = editor;
    const { InnerBlocks } = blockEditor;
    
} (
    window.wp
) );

新しいブロックの追加

一旦コードを乗せて下で解説します。

(function( wp ) {
    'use strict';
    const { blocks, element, editor, blockEditor } = wp;
    const el = element.createElement;
    const { RichText } = editor;
    const { InnerBlocks } = blockEditor;
    
    /*
     * アコーディオン
    */
    blocks.registerBlockType( 'original/new-block', {
        title: 'オリジナルブロック',
        icon: 'wordpress-alt',
        category: 'original-blocks', /* PHPで指定したslugを入力 */
        keywords: [ 'オリジナル', 'original', 'おりじなる' ],
        attributes: {/* リッチテキストの場合は、必須 */
            h2: {/* props.attributes.「content」と連動 */
                type: 'string',/* 取得する値のタイプ */
                source: 'html',/* 値のどこまで抽出するか */
                default: 'オリジナルブロックを作る',
                selector: 'h2'/* tagName(タグ名)かクラス名を一致させる */
            }
        },
        example: {
            attributes: {
                button: 'オリジナルブロックを作る!',
            },
            innerBlocks: [
                {
                    name: 'core/paragraph',
                    attributes: {
                        content: 'ブロックにコンテンツを入れる'
                    },
                },
            ],
        },
        supports: {
            anchor: true
        },
        edit: function( props ) {
            
        },
        save: function( props ) {
            
        }
    });
} (
    window.wp
) );

理解のためシンプルに説明するので、各項目の詳しい説明は「正式マニュアル」を参考してください。

blocks.registerBlockType( 'original/new-block', {

ここで「original」は、テーマやプラグイン名を「new-block」には、ブロック名を入れれば大丈夫です。

また下の設定からブロック追加メニューでの表示を設定することができます。

title: 'オリジナルブロック', //ブロック名を入力
icon: 'wordpress-alt', //アイコン設定
category: 'original-blocks', /* PHPで指定したslugを入力 */
keywords: [ 'オリジナル', 'original', 'おりじなる' ], //ブロックを検索するためのキーワードを設定

「category」は、PHPで設定したカテゴリーIDを入力します。

ブロック追加メニューでの表示
attributes: {/* リッチテキストの場合は、必須 */
    h2: {/* props.attributes.「content」と連動 */
        type: 'string',/* 取得する値のタイプ */
        source: 'html',/* 値のどこまで抽出するか */
        default: 'オリジナルブロックを作る',
        selector: 'h2'/* tagName(タグ名)かクラス名を一致させる */
    }
},

「attributes」は要素のオプションを設定します。値のタイプは初期値などを設定できます。

example: {
    attributes: {
        button: 'オリジナルブロックを作る!',
    },
    innerBlocks: [
        {
            name: 'core/paragraph',
            attributes: {
                content: 'ブロックにコンテンツを入れる'
            },
        },
    ],
},

ブロックを追加する時に、マウスを上にするとプレビュー画面が表示されます。

注意すべきところは、「innerBlocks」を使う場合は上記のように「innerBlocks」も指定しないと、エラーになってしまいます。

うまく設定できない場合は、「example」を設定しないことでエラーを防げます。

supports: {
    anchor: true
},

「supports」はエディター内で使用される機能をオプションを設定できます。詳しくは「ブロックサポート」をご覧ください。

edit: function( props ) {
            
},

「edit」は、編集画面でのブロックを設定します。

save: function( props ) {
            
}

「save」は、実際のページで表示されるブロックを設定します。

editの設定
edit: function( props ) {
    return el(
        'div',
        { className: 'block-area' },
        [
            el(
                RichText,
               {
                    tagName: 'h2', 
                    className: 'block-title',
                    value: props.attributes.h2,
                    formattingControls: [ 'bold', 'italic' ],
                    onChange: function( content ) {
                        props.setAttributes( { h2: content } );
                    },
                    placeholder: 'オリジナルブロックを作る',
                }
            ),
            el(
                'div',
                { className: 'block-content' },
                [
                    el(
                        InnerBlocks,
                        {
                            template: [ ['core/paragraph'] ]
                        }
                    ),
                ]
            )
        ]
    );
},
「el」について

「el」はelement.createElementを定義したものですが、3つの引数を設定します。

element.createElement( tagName, attributes, value )

tagName:HTMLタグ名を入れる(div, h1など、InnerBlocks、RichTextも可能)
attributes:classNameやidなどを設定
value:文字列や他の「element.createElement」を単独、配列で設定
※InnerBlocksやRichTextの場合はvalue省略

attributesの詳しくは「公式マニュアル」をご覧ください。

「RichText」について

element.createElementにtagNameを指定する場合、表示する値は固定されるため、文字を入力して反映することはできません。

RichTextは、指定したタグ名のまま、文字を直接入力して保存できるようにしてくれます。

RichTextの詳細は「公式マニュアル」をご覧ください。

element.createElement( RichText, attributes )

tagName:RichText
attributes:既存のattributesに、tagNameやonChangeなどを追加できる
onChangeについて
onChange: function( content ) {
    props.setAttributes( { h2: content } );
}

※contentは、入力した内容が入っている

element.createElementでは、「onChange」などのイベント設定ができます。

「onChange」は、入力した時をフックに入力した内容を指定したオプションに定義することができます。

h2は上で設定した「attributes」の「h2」になります。

「InnerBlocks」について

まず重要な注意事項ですが、「InnerBlocks」はブロックに1回しか指定できません。2回以上使うとエラーは発生しないものの、処理がおかしくなります。

オプション説明

template:初期表示ブロックを指定
allowedBlocks:指定したブロックのみ追加できるようにする
templateLock:ブロックをロックする

オプションの詳しい説明は「公式マニュアル」をご覧ください。

saveの設定
save: function( props ) {
    return el(
        'div',
        { className: 'block-area' },
        [
            el( RichText.Content, {
                tagName: 'h2',
                className: 'block-title',
                value: props.attributes.h2,
                type: 'h2'
            } ),
            el(
                'div',
                { className: 'block-content' },
                [
                    el( InnerBlocks.Content, {} ),
                ]
            )
        ]
    );
}

基本的に、「edit」でやったことと変わりません。

「edit」で設定した値を表示させるだけなので、「save」は「edit」より簡単です。

ただ「RichText」を「RichText.Content」に、「InnerBlocks」は「InnerBlocks.Content」に設定します。

これで設定は完了です。実際の編集画面に行ってみるとサムネイルのようにオリジナルブロックが追加できます。

完成イメージ

今回のブロック以外にもスライドショーやタブコンテンツなど、色んなブロックを作ることができます。

TOP