Uweb

【第1回】Gutenbergにオリジナルブロックを追加する

こんにちは。やまぐちなな(@nana_winter_web )です。

WordPressの新エディタ「Gutenberg」が登場してからしばらく経ちましたね。

今回から、複数回に分けて、比較的自由に設定することができる、カスタマイズ性の高いボタンのブロックを追加します。(ブロックを入れ子にできるブロックの追加の記事も書きたいと思っています。)

GutenbergReact というJavaScript(以下、JS)のフレームワークで作られていますが、Reactが分からない人でも問題なく進められるはず。

この連載に沿ってブロックを追加するために、環境構築は必要ありません。

前提条件

テーマもしくは、プラグインを制作することができる人を対象に書いていきます。
ローカル環境などのテスト環境を用意するのがおすすめです。

動作確認は、WordPressバージョン5.3.2、PHPバージョン7.3.2で行いました。
Twenty Twentyの子テーマを作成し、そこにブロックを追加していきますが、子テーマでなくても良いし、プラグインでも良いです。
プラグインにすると、テーマを変えても、ブロックはそのまま使い続けることができます。

ブロックを登録するカテゴリーを作る

Gutenbergはブロックがカテゴリーに分かれています。

自分でブロックを作るときは、既存のカテゴリー(WordPressが用意しているものなど)にブロックを追加するか、カテゴリーを追加するかを選ぶことができます。

今回は、せっかくなので、自分でグループを作りますが、既存のカテゴリーにブロックを追加する場合は飛ばして問題ありません。

functions.phpに下記のコードを追加します。

functions.php
/**
 * ブロックエディタにブロックカテゴリーを追加.
 *
 * @param Array  $categories 現在登録済みのカテゴリー.
 * @param String $post 記事.
 */
function original_block_categories( $categories, $post ) {
	return array_merge(
		$categories,
		array(
			array(
				'slug'  => 'original-block', // 投稿などのslugのようなもの(他とかぶらないように注意)
				'title' => 'オリジナルブロック', // カテゴリーの表示名(アコーディオンの開閉部分に表示される)
				'icon'  => 'edit', // titleの右隣に表示されるアイコン
			),
		)
	);
}
add_filter( 'block_categories', 'original_block_categories', 10, 2 );

引数の$postは、投稿の情報が入るので、特定の投稿タイプの場合のみブロックカテゴリーを追加するなどの条件も設定できます。

2020 03 10 01

slugは、ブロックを追加するときに指定するカテゴリー名のようなものなので半角英数字がベスト。
他とかぶらない必要があります。

titleは、ブロックを選択する時に出てくるアコーディオンパネルに表示されるものです。

iconは、titleの右隣に表示されます。
指定しなくても問題ありませんが、指定すると目立ちます。

指定できるアイコンについて

WordPressが用意しているアイコンを利用すると手軽ですが、自分で用意したアイコンを利用することもできます。

WordPressが用意したアイコンを利用する

2020 03 10 02

アイコンの一覧 にアクセスし、使いたいアイコンをクリックします。

すると、ページの始めのあたりに該当のアイコンの情報が表示されるので、「Copy CSS」の上の文字(上の画像の色枠の部分)からdashicons-を除いた文字をコピーし、iconに指定します。

WordPressのアイコンなら、dashicons-wordpressからdashicons-を除いたwordpressiconに指定します。

自分で用意したアイコンを利用する

2020 03 10 03

今回は、上の画像のようなアイコンを作ったので、それを指定します。
環境構築をしたり、Babelを利用した場合はもっとシンプルに書くこともできますが、今回は省略。

即時関数の中に下のコードを追加することで、好きなアイコンを設定できます。

var originalIcon = element.createElement('svg',
	{
		width: 24,
		height: 24,
		viewBox: '0 0 300 300'
	},
	element.createElement('g',
		{
			'stroke-width': 6,
			stroke: '#000',
			fill: 'none',
			transform: 'translate(83.056 121.552)'
		},
		element.createElement('circle',
			{
				cx: 68.5,
				cy: 68.5,
				r: 65.5,
				stroke: 'none'
			}
		),
		element.createElement('circle',
			{
				cx: 68.5,
				cy: 68.5,
				r: 65.5,
				fill: 'none'
			}
		),
	),
	element.createElement('path',
		{
			d: 'M5.976-19.224C5.328-13.1,4.032-6.7,1.944-2.376A26.371,26.371,0,0,1,6.7-.072C8.856-4.608,10.44-11.52,11.3-18.288Zm17.5-5.328a34.274,34.274,0,0,1,1.08,4.32L29.52-22.32a60.539,60.539,0,0,0-5.976-15.264l-4.536,1.8a58.893,58.893,0,0,1,2.52,5.688l-7.92.432c4.464-5.9,9.36-13.608,13.248-20.016l-5.4-2.448a137.518,137.518,0,0,1-6.768,12.384,45.121,45.121,0,0,0-3.1-3.816c2.448-3.96,5.472-9.792,7.992-14.76l-5.76-2.088a86.255,86.255,0,0,1-5.76,13.176c-.72-.72-1.368-1.368-2.088-2.016l-3.312,4.32A70.824,70.824,0,0,1,11.376-34.56C10.152-32.616,8.856-30.888,7.7-29.3l-5.4.288.648,5.832c3.024-.144,6.552-.432,10.224-.648V5.616h5.76v-29.88ZM57.456-12.744v-7.2h4.608v7.2Zm-8.784,0v-7.2h4.392v7.2Zm-8.568,0v-7.2h4.32v7.2Zm19.512-28.3v6.48H36.792v-6.48Zm7.992,15.912H36.648c.072-1.44.144-2.808.144-4.1H66.024V-46.44H30.816V-31.1c0,6.624-.288,14.976-2.664,22.608A58.283,58.283,0,0,0,24.84-19.512l-4.608,1.44A53.686,53.686,0,0,1,23.616-5.04L27.36-6.192A32.162,32.162,0,0,1,23.472,1.3a23.832,23.832,0,0,1,4.9,3.6c3.6-5.112,5.688-11.448,6.912-17.856V5.688H40.1V-7.776h4.32V5.04h4.248V-7.776h4.392V5.04h4.392V-7.776h4.608V-.432c0,.576-.144.72-.648.792-.5,0-1.8,0-3.312-.072a18.446,18.446,0,0,1,1.728,5.328c2.592,0,4.536-.144,5.976-1.008,1.44-.936,1.8-2.592,1.8-4.9ZM28.08-56.232v5.76H67.824v-5.76ZM90.792-29.88H106.2V-25.7H90.792Zm15.408-13.1H90.792v-3.96H106.2Zm0,8.64H90.792v-4.1H106.2ZM139.968-11.3v-5.4h-28.8V-20.52h26.856V-25.7H112.608V-29.88h19.944v-4.464H112.608v-4.1h19.8v-4.536h-19.8v-3.96h22.968v-5.328H112.968c1.368-2.16,2.736-4.68,4.032-7.2l-7.416-.936a63.3,63.3,0,0,1-3.528,8.136H93.816c1.584-2.3,2.952-4.68,4.248-6.912L91.08-60.552A59.866,59.866,0,0,1,74.16-40.176a23.575,23.575,0,0,1,4.752,4.536,64.7,64.7,0,0,0,5.616-5.184v20.3h20.016V-16.7H76.032v5.4H99.36A80.32,80.32,0,0,1,74.088-.5a38.985,38.985,0,0,1,4.32,5.472A81.128,81.128,0,0,0,104.544-8.424V5.616h6.624V-8.568c7.2,5.688,17.28,10.512,26.208,13.176a27.18,27.18,0,0,1,4.1-5.472c-8.5-2.016-18.144-5.9-24.84-10.44Zm70.848-37.368v-6.84H149.184v6.84h30.384a79.737,79.737,0,0,1-32.4,29.952,37.688,37.688,0,0,1,4.608,5.976,89.608,89.608,0,0,0,24.552-19.44v37.8H183.6V-41.256c1.728-2.448,3.24-4.968,4.752-7.416ZM183.888-33.408c8.208,5.832,18.864,14.4,23.688,19.944l5.616-5.184c-5.256-5.544-16.2-13.68-24.264-19.08Zm66.312.432v14.4H233.64v-14.4Zm6.624,20.736V-39.312H227.16V-6.624h6.48V-12.24Zm27.216-43.2H220.1v6.7h48.384v45.36c0,1.512-.5,1.944-2.088,2.016-1.728,0-7.7.072-13.176-.216a29.786,29.786,0,0,1,2.808,7.2c7.2,0,12.24-.072,15.336-1.3,3.024-1.008,4.1-3.24,4.1-7.56v-45.5h8.568Z',
			fill: '#000',
			transform: 'translate(7.056 101.552)'
		}
	),
	element.createElement('path',
		{
			d: 'M1208,119.556h26L1221,146Zm0,0V48h26v71.556Z',
			fill: '#7e7e7e',
			transform: 'translate(-857.103 -504.017) rotate(30)'
		}
	),
	element.createElement('rect',
		{
			width: 127,
			height: 6,
			fill: '#000',
			transform: 'translate(99.92 152.204) rotate(30)'
		}
	),
);
// PHPで追加したカテゴリーの情報を上書き
blocks.updateCategory( 'original-block', { icon: originalIcon } );

基本のコードの構造はシンプルです。
このコード一式を、変数に入れています。

element.createElement('タグ名',
	{
		属性名:, // 属性名に-(ハイフン)を使っている場合はクオーテーションで囲む
	},
	// 指定したいタグの中に入れたいタグがあれば入れ子にする
),

ブロックを追加するためファイルの作成と読み込み

GutenbergReactで作られているのでJSのファイルをメインで編集していきますが、コードを書く前にファイルを読み込む設定をする必要があります。
PHPでブロックを追加することもできますが、今回は省略します。

2020 03 10 04

テーマフォルダー直下にjsフォルダーを作成し、その中にblock-eidtor.jsというファイルを追加しました。

ファイルを作っただけでは読み込まれないので、functions.phpに下記のコードを追加します。

functions.php
/**
 * ブロックエディタ用のファイル読み込み.
 */
function original_block_editor() {
	wp_enqueue_script( 'original-block', get_theme_file_uri( 'js/block-editor.js' ), array( 'wp-blocks' ), wp_get_theme()->get( 'Version' ), true );
}
add_action( 'enqueue_block_editor_assets', 'original_block_editor' );

ブロックを追加するための即時関数を用意する

( function( blocks, element ) {
	// ここにブロックの内容を追加.
} (
	window.wp.blocks,
	window.wp.element
) );

編集不可能なブロックの追加

きちんとファイルが読み込まれたかを確認するために編集不可能な文章を表示するブロックを追加してみます。
先ほど追加した即時関数の中に、下記のコードを追加します。

var el = element.createElement;

blocks.registerBlockType('original-block/original-block', { // ブロックカテゴリー名/ブロックの名前
	title: '編集不可能なブロック', // ブロック名
	icon: 'editor-alignleft', // ブロック名の上に表示するアイコン
	category: 'original-block', // カテゴリー名
	attributes: {
		content: {
			type: 'array',
			source: 'children',
			selector: 'p'
		}
	},
	edit: function (props) {
		return el(
			'p',
			{
				className: props.className,
			},
			'初めてのオリジナルブロック'
		);
	},
	save: function (props) {
		return el(
			'p',
			{
				className: props.className,
			},
			'初めてのオリジナルブロック'
		);
	}
});
2020 03 10 05

第1引数には、ブロックカテゴリーのslug/ブロックのslugを指定します。
「ブロックのslug」はここで決める項目です。他とかぶらないようにします。

第2引数は、今回編集する必要がある部分以外は、次回以降に紹介します。

titleはブロックの名前です。
今回はわかりやすいように「編集不可能なブロック」にしました。

iconは、titleの上に表示するアイコンです。
指定方法は、ブロックカテゴリーの追加のときと同じです。
省略すると、画像のようなアイコンが表示されます。

categoryで、どのカテゴリーにブロックを追加するかを決めます。
既存のカテゴリーにブロックを追加する場合は、既存のカテゴリーのslugを指定すれば良いです。

自分で用意したアイコンを指定したい場合

2020 03 10 06

カテゴリー追加するときに紹介した、アイコンを追加するコードから、blocks.updateCategory( 'original-block', { icon: originalIcon } );を削除し、icon: 'edit'の部分をicon: originalIconに変更します。

まとめ

2020 03 10 07

ブロックを使ってみて、上の画像のようになれば、完成です。
今回追加したブロックは、編集ができないブロックなので、表示するテキストの変更はできません。

コードを追加してみて困ったことがあればこちらを参考にしてみてください。

(function (blocks, element) {
	// ここにブロックの内容を追加.
    var el = element.createElement;

    // アイコンを追加する
    var originalIcon = element.createElement('svg',
        {
            width: 24,
            height: 24,
            viewBox: '0 0 300 300'
        },
        element.createElement('g',
            {
                'stroke-width': 6,
                stroke: '#000',
                fill: 'none',
                transform: 'translate(83.056 121.552)'
            },
            element.createElement('circle',
                {
                    cx: 68.5,
                    cy: 68.5,
                    r: 65.5,
                    stroke: 'none'
                }
            ),
            element.createElement('circle',
                {
                    cx: 68.5,
                    cy: 68.5,
                    r: 65.5,
                    fill: 'none'
                }
            ),
        ),
        element.createElement('path',
            {
                d: 'M5.976-19.224C5.328-13.1,4.032-6.7,1.944-2.376A26.371,26.371,0,0,1,6.7-.072C8.856-4.608,10.44-11.52,11.3-18.288Zm17.5-5.328a34.274,34.274,0,0,1,1.08,4.32L29.52-22.32a60.539,60.539,0,0,0-5.976-15.264l-4.536,1.8a58.893,58.893,0,0,1,2.52,5.688l-7.92.432c4.464-5.9,9.36-13.608,13.248-20.016l-5.4-2.448a137.518,137.518,0,0,1-6.768,12.384,45.121,45.121,0,0,0-3.1-3.816c2.448-3.96,5.472-9.792,7.992-14.76l-5.76-2.088a86.255,86.255,0,0,1-5.76,13.176c-.72-.72-1.368-1.368-2.088-2.016l-3.312,4.32A70.824,70.824,0,0,1,11.376-34.56C10.152-32.616,8.856-30.888,7.7-29.3l-5.4.288.648,5.832c3.024-.144,6.552-.432,10.224-.648V5.616h5.76v-29.88ZM57.456-12.744v-7.2h4.608v7.2Zm-8.784,0v-7.2h4.392v7.2Zm-8.568,0v-7.2h4.32v7.2Zm19.512-28.3v6.48H36.792v-6.48Zm7.992,15.912H36.648c.072-1.44.144-2.808.144-4.1H66.024V-46.44H30.816V-31.1c0,6.624-.288,14.976-2.664,22.608A58.283,58.283,0,0,0,24.84-19.512l-4.608,1.44A53.686,53.686,0,0,1,23.616-5.04L27.36-6.192A32.162,32.162,0,0,1,23.472,1.3a23.832,23.832,0,0,1,4.9,3.6c3.6-5.112,5.688-11.448,6.912-17.856V5.688H40.1V-7.776h4.32V5.04h4.248V-7.776h4.392V5.04h4.392V-7.776h4.608V-.432c0,.576-.144.72-.648.792-.5,0-1.8,0-3.312-.072a18.446,18.446,0,0,1,1.728,5.328c2.592,0,4.536-.144,5.976-1.008,1.44-.936,1.8-2.592,1.8-4.9ZM28.08-56.232v5.76H67.824v-5.76ZM90.792-29.88H106.2V-25.7H90.792Zm15.408-13.1H90.792v-3.96H106.2Zm0,8.64H90.792v-4.1H106.2ZM139.968-11.3v-5.4h-28.8V-20.52h26.856V-25.7H112.608V-29.88h19.944v-4.464H112.608v-4.1h19.8v-4.536h-19.8v-3.96h22.968v-5.328H112.968c1.368-2.16,2.736-4.68,4.032-7.2l-7.416-.936a63.3,63.3,0,0,1-3.528,8.136H93.816c1.584-2.3,2.952-4.68,4.248-6.912L91.08-60.552A59.866,59.866,0,0,1,74.16-40.176a23.575,23.575,0,0,1,4.752,4.536,64.7,64.7,0,0,0,5.616-5.184v20.3h20.016V-16.7H76.032v5.4H99.36A80.32,80.32,0,0,1,74.088-.5a38.985,38.985,0,0,1,4.32,5.472A81.128,81.128,0,0,0,104.544-8.424V5.616h6.624V-8.568c7.2,5.688,17.28,10.512,26.208,13.176a27.18,27.18,0,0,1,4.1-5.472c-8.5-2.016-18.144-5.9-24.84-10.44Zm70.848-37.368v-6.84H149.184v6.84h30.384a79.737,79.737,0,0,1-32.4,29.952,37.688,37.688,0,0,1,4.608,5.976,89.608,89.608,0,0,0,24.552-19.44v37.8H183.6V-41.256c1.728-2.448,3.24-4.968,4.752-7.416ZM183.888-33.408c8.208,5.832,18.864,14.4,23.688,19.944l5.616-5.184c-5.256-5.544-16.2-13.68-24.264-19.08Zm66.312.432v14.4H233.64v-14.4Zm6.624,20.736V-39.312H227.16V-6.624h6.48V-12.24Zm27.216-43.2H220.1v6.7h48.384v45.36c0,1.512-.5,1.944-2.088,2.016-1.728,0-7.7.072-13.176-.216a29.786,29.786,0,0,1,2.808,7.2c7.2,0,12.24-.072,15.336-1.3,3.024-1.008,4.1-3.24,4.1-7.56v-45.5h8.568Z',
                fill: '#000',
                transform: 'translate(7.056 101.552)'
            }
        ),
        element.createElement('path',
            {
                d: 'M1208,119.556h26L1221,146Zm0,0V48h26v71.556Z',
                fill: '#7e7e7e',
                transform: 'translate(-857.103 -504.017) rotate(30)'
            }
        ),
        element.createElement('rect',
            {
                width: 127,
                height: 6,
                fill: '#000',
                transform: 'translate(99.92 152.204) rotate(30)'
            }
        ),
    );

    // PHPで追加したカテゴリーの情報を上書き
    blocks.updateCategory( 'original-block', { icon: originalIcon } );

	blocks.registerBlockType('original-block/original-block', {
		title: '編集不可能なブロック', // ブロック名
        icon: 'editor-alignleft', // ブロック名の上に表示するアイコン
        // アイコンをカスタマイズしたい場合は、上の行を削除し、下のコードのコメントアウトを解除
		// icon: originalIcon, // ブロック名の上に表示するアイコン
		category: 'original-block', // カテゴリー名
		attributes: {
			content: {
				type: 'array',
				source: 'children',
				selector: 'p'
			}
		},
		edit: function (props) {
			return el(
				'p',
				{
					className: props.className,
				},
				'初めてのオリジナルブロック'
			);
		},
		save: function (props) {
			return el(
				'p',
				{
					className: props.className,
				},
				'初めてのオリジナルブロック'
			);
		}
	});
}(
	window.wp.blocks,
	window.wp.element
));

シェア

最新記事