Gutenberg カスタムブロックの追加方法の具体例:「Map」
以前Gutenbergのカスタムブロックの追加方法を紹介しましたが、今回は具体例と一緒に解説です。
先に解説に役立つ知識として以下の記事を書いていますので、その紹介をしておきます。
- ワードプレスのエディター「Gutenberg」にカスタムブロックを追加する方法
- 初めてのNode.jsとnpm – インストールと使用方法
- 初めてのWebpack – インストールと使用方法
- ワードプレスのエディタGutenbergのカスタムブロック用にJSXを変換する方法 – WebpackとBabel
入門書のような内容ですので、タイトルを見て中身が分かりそうならスキップしても良いと思います。
「JSX」を「Gutenberg」用に変換できる環境をセットアップ出来ている前提ですが、今回は具体例がある方が分かりやすいと思いますので、構成するファイルのコードも具体例として、テーマ「Ace」に追加している「Map」ブロックの作成方法を、構成するファイルを一緒に紹介していきます。
こういうブロックで、場所には「Osaka」をセットしています。
まずは基本的な形
ブロックを追加するための基本的な作り方です。
最初にブロックとして登録する際に、保存すべき設定項目も登録する必要がありますので、必要になりそうな設定項目を挙げていきましょう。
今回具体例で紹介するグーグルマップとして使用するのは、クエリとなる検索キーワード1つ。
また、必要とされるパラメータとしてGoogleのAPIキーがありますが、これは個別にブロックの設定項目として保存する必要がありません。
ワードプレスのPHP側で保存しておくなどして、JS側で使用できるようにしておきましょう。
配布しないことが前提の自分だけのブロックを作成しているのであれば、フロントエンドのHTMLに直接APIキーを入力してもいいかもしれません。
この他横幅などのサイズやスタイルなども設定値として保存できますので、オプションとして設定項目を用意するのもいいと思いますよ。
では、ブロックを登録しましょう。
とりあえず、必要なデータなどを列挙したモデルの作成です。
const { registerBlock } = wp.block;
import Block from "./block.jsx";
import Frontend from "./frontend.jsx";
// Append Style with webpack
import './editor-style.scss';
const registeredBlockParams = {
title: __( 'Map', 'textdomain' ),
icon: 'location-alt',
category: 'widget',
keywords: [
__( 'Map', 'textdomain' ),
],
description: __(
'Map Block.',
'textdomain'
),
// List to be saved by block editor
attributes: {
/**
* Used for Output
**/
/**
* Text for the product link.
**/
mapQuery: {
type: 'string',
default: __( 'Map Location Query', 'textdomain' ),
},
},
/**
* Renders and manages the block.
**/
edit( props ) {
return <Block { ...props } />;
},
/**
* Save the block content in the post content. Block content is saved as a products shortcode.
*
* @return string
**/
save( props ) {
return (
<Frontend { ...props } />
);
},
};
registerBlock( 'your-widget/map', registeredBlockParams );
別に拡張子を「jsx」にする必要はありませんが、JSXを使用しているファイルがどれか分かりやすいように書いているだけです。
「.js」でも「.ts」でもBabelなどのトランスパイルがちゃんと機能していれば問題ありません。
「block.jsx」はエディタ側のビューに使用され、「frontend-mode.jsx」は実際にフロントエンドで使用されるHTMLを構成します。
属性に登録した「mapQuery」では、マップのロケーションのキーワードとして保存するようにしておきます。
Frontend.jsx
こちらは実際に出力されるHTMLを構成するファイルです。
エディタ側のプレビューでも使用しますので、先にこちらを作成しておきます。
/**
* External dependencies
*/
const { Component } = wp.element;
class FrontendModeMap extends Component {
constructor( props ) {
super( props );
}
render() {
if ( 'undefined' === typeof googleMapAPIKey || '' === googleMapAPIKey ) {
return;
}
const { mapQuery } = this.props.attributes;
const iframeSrc = `https://www.google.com/maps/embed/v1/place?q=${ encodeURI( mapQuery ) }&key=${ googleMapAPIKey }&zoom=15`;
return (
<div className="widget-map block-widget-map">
<div className="block-widget-map-inner">
<iframe className="block-map-frame"
src={ iframeSrc }
/>
</div>
</div>
);
}
}
export default FrontendModeMap;
冒頭にも書きましたが、前提として「googleMapAPIKey」が定義できている、あるいは直接APIキーを入力する必要があります。
エラー無くiframeさえちゃんと出力できていれば問題ないはずです。
DIVタグもサイズをCSSでセットしやすいように使用しているだけで必要ではありません。
Block.jsx
エディタ側のビューを構成するものです。
実際のファイル名は小文字です。
/**
* External dependencies
*/
const { __ } = wp.i18n;
const {
Placeholder,
TextControl,
} = wp.components;
const { Component } = wp.element;
import PreviewMode from './frontend-mode.jsx';
class Map extends Component {
constructor( props ) {
super( props );
}
/**
* Rendering
* @param {} props
**/
render()
{
const { mapQuery } = this.props.attributes;
return (
<Placeholder
icon="star-filled"
label={ __( '"Map Block"', 'ace' ) }
className={ `widget-map placeholder` }
>
{ ( 'undefined' === typeof googleMapKeys
|| '' === googleMapKeys
) && (
<p>{ __( 'Please set the Google API key for Map in setting page.', 'textdomain' ) }</p>
) }
<TextControl
className="edit-map-query"
placeholder="Location"
value={ mapQuery }
onChange={ newMapQuery => this.props.setAttributes({ mapQuery: newMapQuery }) }
/>
{ <PreviewMode { ...this.props } />
}
</Placeholder>
);
}
}
export default Map;
ワードプレスが定義してくれるコンポーネントなどがありますので、それを使用すると楽に編集ができます。
ここでは「TextControl」や「Placeholder」がそれです。
「onChange」で変更時の値を取得できますので、設定値のアップデートに使用します。
また、今回はマップを表示するだけの単純なHTMLを出力するだけですので、プレビュー用に「frontend.jsx」をそのまま使用しており、「<PreviewMode { …this.props }/>」としてメソッド「render()」内で使用しています。
{ <PreviewMode { ...this.props }/>
}
このように括弧で囲った上で2行に分けて書いているのは、エディタ部分のみを編集したい時、プレビューが邪魔になる場合にコメントアウトですぐに外すことができるようにしているだけで、深い意味はありません。
ブロック作成は応用も簡単
今回使用したのは、設定値がマップクエリだけでしたが、他のブロックを使用する場合はそれが複数になるだけですので、必要なデータが何になるのかがはっきりしていれば難しい問題にはならないでしょう。
JavaScript側のAPIに関しては、WordPressの参照ページが少し少ない気もしますが、結構便利なものが揃っており、restAPIを使用して投稿データなども簡単に拾ってくることができます。
componentDidMountなどのReactのコンポーネントの基本となるメソッドの知識があれば、もっと柔軟にブロック毎の編集ビューも使いやすくなると思います。
Gutenbergでデータを保存するのに使用しない「setState」も、編集時に切り替えなどを行うのには便利ですしね。