WordPressでカスタムブロックBook Entryを作成する

WordPressのオリジナルブロック開発の手法を深堀りするため、Book Entryブロックを作成してみます。、作品名、読了日を登録できる機能を持っています。

環境構築

  1. デスクトップにMy Blocksフォルダを作成し、VSCodeで開く
  2. コマンド、ブロックの雛形を作成する
    npx @wordpress/create-block@latest --wp-env
  3. 対話モードでプラグイン情報を入力
  4. コマンド、Dockerを起動する
    cd book-entry/
    npx wp-env start
  5. WPが起動する
    http://localhost:8888/
    User admin/Password password
  6. ウォッチモード開始コマンド
    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.jsonblock.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.jstagName=”p”
save.jstagName=”p”

ビルド

ビルドコマンド

npm run build
npm run plugin-zip

参考