【@wordpress/scripts】でインライン単位で文字サイズ色を変更できる(リッチテキストツールバー)オリジナルブロック作成

パッケージインストール

npm プロジェクトの初期化

package.json ファイルを作成します。

npm init -y

@wordpress/scriptsのインストール

(WordPress のブロックエディター用のパッケージ)

npm install @wordpress/scripts @wordpress/blocks @wordpress/i18n @wordpress/block-editor @wordpress/components @wordpress/data react react-dom

package.jsonにスクリプトが追記

{
  "name": "my-theme-custom-block",
  "version": "1.0.0",
  "description": "My first WordPress custom block in a theme",
  "main": "build/index.js",
  "scripts": {
    "build": "wp-scripts build",
    "start": "wp-scripts start"
  },
  "keywords": [],
  "author": "",
  "license": "ISC",
  "devDependencies": {
    "@wordpress/scripts": "^x.x.x"
  }
}

@wordpress パッケージの型定義ファイルインストール

npm install --save-dev @types/wordpress__blocks
npm install --save-dev @types/wordpress__block-editor @types/wordpress__components
npm install @wordpress/data @wordpress/rich-text
npm install --save-dev @types/react @types/wordpress__rich-text

webpack

  • ES6+やTypeScriptのコードを古いブラウザでも動作するES5に変換できます。
  • モジュール間の依存関係を自動的に解析し、複数のJavaScriptファイルを1つのファイルにまとめます。
webpackでのTypeScript処理フロー TypeScript .ts ファイル ts-loader TypeScript → JS Babel ES6+ → ES5 出力 bundle.js

webpack.config.jsの設定

webpack.config.jsは、webpackの設定ファイルです。

webpack.config.js の主要構造 entry アプリケーションの 開始点を指定 output バンドルファイルの 出力先と名前を指定 module.rules ファイルの変換方法を ローダーで指定 plugins 追加の処理や最適化を プラグインで指定 resolve モジュールの解決方法を カスタマイズ mode development, production などの環境を指定

webpack.config.js

const defaultConfig = require('@wordpress/scripts/config/webpack.config');
const path = require('path');

module.exports = {
    ...defaultConfig,

    // エントリーポイントの設定
    entry: {
        lead: path.resolve(__dirname, 'wp-content/themes/originaltheme/src/lead/index.tsx'),
    },

    // 出力の設定
    output: {
        path: path.resolve(__dirname, 'wp-content/themes/originaltheme/build'),
        filename: '[name].js', // [name] には entry で指定したキーが入る
    },

    // ファイルの拡張子を省略できるようにする
    resolve: {
        extensions: ['.ts', '.tsx', '.js']
    },


    module: {

        // ローダーの設定
        rules: [
            {
                test: /\.tsx?$/,
                use: 'ts-loader',
                exclude: /node_modules/
            }
        ]
    }
};
npm install ts-loader --save-dev

tsconfig.json

{
  "compilerOptions": {
    "target": "es5",                          // 出力するJavaScriptのバージョン
    "module": "commonjs",                     // モジュールシステム
    "strict": true,                           // 厳格な型チェックオプション
    "esModuleInterop": true,                  // ESモジュールとの互換性
    "skipLibCheck": true,                     // ライブラリの型チェックをスキップ
    "forceConsistentCasingInFileNames": true, // ファイル名の大文字小文字の一貫性を強制
    "jsx": "react",                           // JSXのサポート
    "moduleResolution": "node",               // モジュール解決方法
    "resolveJsonModule": true,                // JSONモジュールのインポートを許可
    "outDir": "./build",                      // 出力ディレクトリ
    "rootDir": "./wp-content/themes/originaltheme/src" // ソースファイルのルートディレクトリ
  },
  "include": [
    "wp-content/themes/originaltheme/src/**/*" // コンパイル対象のファイル
  ],
  "exclude": [
    "node_modules"                            // コンパイル対象外のファイル
  ]
}

ブロックのソースファイルを作成

リッチテキストツールバーを使用

インライン単位で文字サイズ文字色を変更できるブロック

index.tsx

import { registerBlockType } from '@wordpress/blocks';
import { __ } from '@wordpress/i18n';
import {
    useBlockProps,
    RichText,
    RichTextToolbarButton,
} from '@wordpress/block-editor';
import {
    registerFormatType,
    applyFormat,
    removeFormat,
    type RichTextValue
} from '@wordpress/rich-text';
import React from 'react';

// ブロックの属性の型定義
interface BlockAttributes {
    content: string;
}

// フォーマットの型定義
interface FormatProps {
    isActive: boolean;
    value: RichTextValue;
    onChange: (value: RichTextValue) => void;
}

// WPFormat の型定義
interface WPFormatType {
    name: string;
    title: string;
    tagName: string;
    className: string;
    interactive: boolean;
    edit: (props: FormatProps) => JSX.Element;
}

// フォーマット名の定義
const FONT_SIZE_FORMAT = 'custom-format/font-size';
const TEXT_COLOR_FORMAT = 'custom-format/text-color';

// フォントサイズフォーマットの登録
const fontSizeFormat: WPFormatType = {
    name: FONT_SIZE_FORMAT,
    title: __('Font Size', 'custom-format'),
    tagName: 'span',
    className: 'custom-font-size',
    interactive: false,
    edit: ({ isActive, value, onChange }: FormatProps) => {
        const fontSizes = ['0.75rem', '1rem', '2rem', '3rem', '4rem'];

        // フォントサイズの変更の処理の関数
        const onChangeFontSize = (size: string) => {
            const newFormat = {
                type: FONT_SIZE_FORMAT,
                attributes: {
                    style: `font-size: ${size};`,
                },
            };

            if (isActive) {
                onChange(removeFormat(value, FONT_SIZE_FORMAT));
            }
            onChange(applyFormat(value, newFormat));
        };

        return (
            <React.Fragment>
                {fontSizes.map((size) => (
                    <RichTextToolbarButton
                        key={size}
                        icon="editor-textcolor"
                        title={`${size} ${__('Font Size', 'custom-format')}`}
                        onClick={() => onChangeFontSize(size)}
                        isActive={isActive}
                    />
                ))}
            </React.Fragment>
        );
    }
};

// 文字色フォーマットの登録
const textColorFormat: WPFormatType = {
    name: TEXT_COLOR_FORMAT,
    title: __('Red Text', 'custom-format'),
    tagName: 'span',
    className: 'custom-text-color',
    interactive: false,
    edit: ({ isActive, value, onChange }: FormatProps) => {

        // 文字色の変更の処理の関数
        const onToggleColor = () => {
            if (isActive) {
                onChange(removeFormat(value, TEXT_COLOR_FORMAT));
            } else {
                const newFormat = {
                    type: TEXT_COLOR_FORMAT,
                    attributes: {
                        style: 'color: red;',
                    },
                };
                onChange(applyFormat(value, newFormat));
            }
        };

        return (
            <RichTextToolbarButton
                icon="editor-textcolor"
                title={__('Red Text', 'custom-format')}
                onClick={onToggleColor}
                isActive={isActive}
            />
        );
    }
};

// フォーマットの登録
registerFormatType(FONT_SIZE_FORMAT, fontSizeFormat);
registerFormatType(TEXT_COLOR_FORMAT, textColorFormat);

// ブロックを登録
registerBlockType<BlockAttributes>('customtheme/cardlead', {
    title: __('カード記事リード文', 'custom-block'),
    icon: 'editor-textcolor',
    category: 'text',

    attributes: {
        content: {
            type: 'string',
            source: 'html',
            selector: 'p',
        },
    },

    edit: ({ attributes, setAttributes }) => {
        const { content } = attributes;
        const blockProps = useBlockProps({
            className: 'my-custom-class',
            style: {
                borderTop: '8px solid #ffda00',
                borderBottom: '8px solid #ffda00',
                textAlign: 'center' as 'center',
            }
        });

        return (
            <div {...blockProps}>
                <RichText
                    tagName="p"
                    value={content}
                    onChange={(newContent: string) => setAttributes({ content: newContent })}
                    placeholder={__('Select text and choose font size or color...', 'custom-block')}
                    allowedFormats={[
                        'core/bold',
                        'core/italic',
                        FONT_SIZE_FORMAT,
                        TEXT_COLOR_FORMAT
                    ]}
                />
            </div>
        );
    },

    save: ({ attributes }) => {
        const { content } = attributes;
        const blockProps = useBlockProps.save({
            className: 'my-custom-class',
            style: {
                borderTop: '8px solid #ffda00',
                borderBottom: '8px solid #ffda00',
                textAlign: 'center' as 'center',
            }
        });

        return (
            <div {...blockProps}>
                <RichText.Content
                    tagName="p"
                    value={content}
                />
            </div>
        );
    },
});

functions.phpで読み込み

functions.php

<?php
// カスタムブロックの登録
function my_theme_custom_block_init() {
    // register_block_type()の第一引数は「ドメイン名/ブロック名」でregisterBlockType()の第一引数と一致させる
    register_block_type( 'customtheme/cardlead', array(
        'editor_script' => 'cardlead-script',
    ) );
}
add_action( 'init', 'my_theme_custom_block_init' );

// ブロックのスクリプトを読み込む
function my_theme_custom_block_enqueue_assets() {
    $asset_file_cardlead = include( get_template_directory() . '/build/cardlead.asset.php' );

    wp_enqueue_script(
        'cardlead-script',
        get_template_directory_uri() . '/build/cardlead.js',
        $asset_file_cardlead['dependencies'],
        $asset_file_cardlead['version']
    );
}
add_action( 'enqueue_block_editor_assets', 'my_theme_custom_block_enqueue_assets' );