WordPressのオリジナルブロック開発の手法を深堀りするため、Book Entryブロックを作成してみます。、作品名、読了日を登録できる機能を持っています。
環境構築
- デスクトップにMy Blocksフォルダを作成し、VSCodeで開く
- コマンド、ブロックの雛形を作成する
npx @wordpress/create-block@latest --wp-env
- 対話モードでプラグイン情報を入力
- コマンド、Dockerを起動する
cd book-entry/
npx wp-env start
- WPが起動する
http://localhost:8888/
User admin/Password password - ウォッチモード開始コマンド
npm start
ブロックを作成する
block.jsonの解説
{
"attributes": {
"bookTitle": {
"type": "string",
"source": "html",
"selector": ".wp-block-my-blocks-book-entry__title"
},
"fallbackCurrentDate": {
"type": "string",
"selector": ".wp-block-my-blocks-book-entry__currentDate"
}
}
}
block.json メタデータファイル
HP (サーバーサイド) と JavaScript (クライアントサイド) の両方でブロックタイプを登録するファイル
attributes
ブロックが必要なカスタムデータとデータベースへの保存方法を記述する属性
最低でも type または enum のいずれかが必要
ここで指定した値は、attributes prop を通して、ブロックの Edit コンポーネントと save 関数に渡される
“attributes”: {“bookTitle”: {}}
編集画面で入力するコードの文字列を保持するための属性
- type:string
- default(初期値): “”(空文字列)
- source:html
- データが何であるかを表す
- 保存された投稿コンテンツから、どのように属性値を取り出すかを定義
- (値なし) – source が指定されていない場合、データはブロックのコメントデリミタに保存
- attribute – データは、HTML 要素の属性に保存
- text – データは、HTML テキストに保存
- html – データは、HTML に保存。この典型的な使用例は RichText
- query – データは、オブジェクトの配列に保存
- meta – データは、投稿メタに保存 (非推奨)
- selector:
- コンテンツのどこにデータが保存されるか
- selector が指定されていない場合、ソース定義はブロックのルートノードに対して実行されます。selector がある場合は、ブロック内の合致する要素に対して実行
- HTMLタグのほか、class や id 属性などを指定できる
参考 block.json、block.json のメタデータ、属性
edit.jsの解説
import { useEffect } from "react";
import { useState } from "@wordpress/element";
import { RichText, useBlockProps } from "@wordpress/block-editor";
import { Button, Modal, DatePicker } from "@wordpress/components";
export default function Edit({ attributes, setAttributes }) {
console.log({ attributes });
// propsを分割代入
const { bookTitle, fallbackCurrentDate } = attributes;
// 現在の日付を取得し、文字列であることを確認する
const currentYear = new Date().toLocaleDateString().replace(/\//g, ".");
// モーダルの初期値をfalseに設定
const [isOpen, setIsOpen] = useState(false);
// 日付の初期値をカレンダーの選択日によって更新する関数
const updateSelectedDateFunc = (selected) => {
const updateSelected = new Date(selected)
.toLocaleDateString()
.replace(/\//g, ".");
setAttributes({ fallbackCurrentDate: updateSelected });
};
// ブロックロードのとき、fallbackCurrentYear がまだ設定されていなければ、
// 現在の年に設定する
useEffect(() => {
if (!fallbackCurrentDate) {
setAttributes({ fallbackCurrentDate: currentYear });
}
}, [fallbackCurrentDate]);
return (
<div {...useBlockProps()}>
<RichText
placeholder="Enter text..."
tagName="p"
className="wp-block-my-blocks-book-entry__title"
value={bookTitle}
onChange={(value) => {
setAttributes({ bookTitle: value });
}}
/>
<div className="wp-block-my-blocks-book-entry__body">
<p
className="wp-block-my-blocks-book-entry__currentDate"
value={fallbackCurrentDate}
onChange={(value) => {
setAttributes({ fallbackCurrentDate: value });
}}
>
{fallbackCurrentDate}
</p>
<Button
variant="secondary"
className="wp-block-my-blocks-book-entry__btn"
onClick={() => setIsOpen(true)}
>
更新
</Button>
{isOpen && (
<Modal onRequestClose={() => setIsOpen(false)}>
<p>日付選択</p>
<DatePicker
onChange={(selected) => updateSelectedDateFunc(selected)}
/>
</Modal>
)}
</div>
</div>
);
}
useBlockProps()
ブロックラッパー内に、エディターで必要とされるすべてのクラスとスタイル(ブロックの動作の有効化に必要な属性とイベントハンドラ)を出力
save.jsの解説
import { RichText, useBlockProps } from "@wordpress/block-editor";
export default function save({ attributes }) {
const { bookTitle, fallbackCurrentDate } = attributes;
return (
<div {...useBlockProps.save()}>
<RichText.Content
tagName="p"
className="wp-block-my-blocks-book-entry__title"
value={bookTitle}
/>
<div className="wp-block-my-blocks-book-entry__body">
<p
className="wp-block-my-blocks-book-entry__currentDate"
value={fallbackCurrentDate}
>
{fallbackCurrentDate}
</p>
</div>
</div>
);
}
editor.jsから変更した点
- RichText → RichText.Content
- …useBlockProps() → …useBlockProps.save()
コードエディタ
<!-- wp:my-blocks/book-entry {"fallbackCurrentDate":"2024.7.15"} -->
<div class="wp-block-my-blocks-book-entry">
<p class="wp-block-my-blocks-book-entry__title">サンプルテキスト</p>
<div class="wp-block-my-blocks-book-entry__body">
<p class="wp-block-my-blocks-book-entry__currentDate" value="2024.7.15">2024.7.15</p>
</div>
</div>
<!-- /wp:my-blocks/book-entry -->
style.scss
.wp-block-my-blocks-book-entry {
box-sizing: border-box;
box-shadow: 2px 2px 3px 2px rgba(0, 0, 0, 0.1);
border-radius: 5px;
padding: 1em;
margin: 1em;
& > * + * {
margin-block-start: 0.5em;
}
&__title {
font-size: 1.5em;
margin: 0;
}
&__body {
display: flex;
align-items: center;
& > * + * {
margin-inline-start: 1em;
}
}
&__currentDate {
margin: 0;
}
&__btn {
position: relative;
text-align: center;
font-size: 12px;
font-weight: 700;
line-height: 1;
border: 0;
background: #f6f9fc;
box-shadow: #d9e8f2 0 0 0 1px inset;
padding: 0.5em 2em;
display: inline;
}
}
レンダリング結果
エディター

フロントエンド

エディター
// エディター
<div
tabindex="0"
class="block-editor-block-list__block wp-block is-selected wp-block-my-blocks-book-entry"
id="block-0f91ffd1-6e5d-4594-9ce2-222d1001ab6d"
role="document"
aria-label="Block: Book Entry"
data-block="0f91ffd1-6e5d-4594-9ce2-222d1001ab6d"
data-type="my-blocks/book-entry"
data-title="Book Entry"
>
<p
role="textbox"
aria-multiline="true"
aria-label="Enter text..."
aria-readonly="false"
class="block-editor-rich-text__editable wp-block-my-blocks-book-entry__title rich-text"
contenteditable="true"
data-wp-block-attribute-key="0"
style="white-space: pre-wrap; min-width: 1px"
>
サンプルテキスト
</p>
<div class="wp-block-my-blocks-book-entry__body">
<p class="wp-block-my-blocks-book-entry__currentDate" value="2024.7.15">
2024.7.15
</p>
<button
type="button"
class="components-button wp-block-my-blocks-book-entry__btn is-secondary"
>
更新
</button>
</div>
</div>
// フロントエンド
<div class="wp-block-my-blocks-book-entry">
<p class="wp-block-my-blocks-book-entry__title">サンプルテキスト</p>
<div class="wp-block-my-blocks-book-entry__body">
<p class="wp-block-my-blocks-book-entry__currentDate" value="2024.7.15">
2024.7.15
</p>
</div>
</div>
ポイント
レンダリングタグの指定は3ファイル(block.json、edit.js、save.js)で揃えること。下記の例ではタグをレンダリング<p>で統一している。。また、block.jsonにてsourceとselectorはセットで指定すること。
block.json | “source”: “html”, “selector”: “p” |
edit.js | tagName=”p” |
save.js | tagName=”p” |
ビルド
ビルドコマンド
npm run build
npm run plugin-zip