Gutenbergエディターのブロックに独自のスタイルパネルを追加する方法

Cocoonカスタマイズ集
この記事は約12分で読めます。
記事内に広告が含まれています。

はじめに

スタイルボタンの追加なんて、やってることは毎回同じで、結局CSSクラスを追加してるだけ。
デザインが変わらないなら、そもそもボタンなんて要らないし、本当に大事なのは「どんなCSSを書くか、どうデザインを当てるか」。
手段が目的になっちゃダメだわ。

以下の内容を実現します。

register_block_styleを使えば、ブロックにスタイルボタンを簡単に追加できます。

しかし、複数の条件を組み合わせたスタイルの設定には向いていません。
参考リンクのように、スタイルボタンが多数並ぶと、選択肢が煩雑に感じられることがあります。

そこで、独自のスタイルパネルを作成し、ユーザーが複数の条件に応じてCSSクラスを柔軟に追加できるUIを提供します。
これにより、より直感的かつ効率的にスタイルを適用できるようになります。

この記事では、この方法について説明します。

完成イメージ

実装手順

プラグインの形式で実装します。

以下の手順で実装します。

  • my-box-menu-style
    • box-menu-style.php
    • assets
      • css
        • style.css
      • js
        • admin.js
  • ラベル
    PHPを追加

    以下のコードをbox-menu-style.phpに追加します。

    <?php
    /**
     * Plugin Name: My Box Menu Style
     * Description: [C]ボックスメニューに、PC列数・モバイル列数・余白オプションを追加します。
     * Version: 1.0.0
     * Author: CHU-YA
     */
    
    if (!defined('ABSPATH')) {
      exit;
    }
    
    // エディター用JS・CSSを追加
    add_action('enqueue_block_editor_assets', function () {
      wp_enqueue_script('my-box-menu-style-editor', plugin_dir_url(__FILE__) . 'assets/js/admin.js', ['wp-blocks', 'wp-element', 'wp-editor', 'wp-components', 'wp-compose', 'wp-block-editor', 'wp-hooks'], filemtime(plugin_dir_path(__FILE__) . 'assets/js/admin.js'), true);
    });
    
    
    // フロント・エディター共通のCSSを追加
    add_action('enqueue_block_assets', function () {
      wp_enqueue_style('my-box-menu-style-css', plugin_dir_url(__FILE__) . 'assets/css/style.css', [], filemtime(plugin_dir_path(__FILE__) . 'assets/css/style.css'));
    });
  • ラベル
    JavaScriptを追加

    以下のコードをadmin.jsに追加します。

    (function (wp) {
      // WordPress APIから必要な関数・コンポーネントを取得
      var addFilter = wp.hooks.addFilter;
      var createHigherOrderComponent = wp.compose.createHigherOrderComponent;
      var Fragment = wp.element.Fragment;
      var InspectorControls = wp.blockEditor.InspectorControls;
      var PanelBody = wp.components.PanelBody;
      var SelectControl = wp.components.SelectControl;
      var CheckboxControl = wp.components.CheckboxControl;
    
      /**
       * PC表示の列数に対応するクラス
       *
       * @type {Array<{label: string, value: string}>}
       */
      var PC_CLASSES = [
        { label: '4列', value: '' },
        { label: '5列', value: 'is-pc5' },
        { label: '6列', value: 'is-pc6' },
      ];
    
      /**
       * モバイル表示の列数に対応するクラス
       *
       * @type {Array<{label: string, value: string}>}
       */
      var MOBILE_CLASSES = [
        { label: '2列', value: '' },
        { label: '3列', value: 'is-sp3' },
      ];
    
      /**
       * ブロック編集画面を拡張する高階コンポーネント
       *
       * @param {Function} BlockEdit 元のブロック編集コンポーネント
       * @return {Function} 拡張したブロック編集コンポーネント
       */
      var withCustomStyleClassControl = createHigherOrderComponent(function (BlockEdit) {
        /**
         * ブロック編集画面を描画
         *
         * @param {Object} props ブロックのプロパティ
         * @return {JSX.Element} ブロック編集画面
         */
        return function (props) {
          var name = props.name;
          var attributes = props.attributes;
          var setAttributes = props.setAttributes;
          var isSelected = props.isSelected;
    
          // 対象ブロック以外は元の編集画面を表示
          if (name !== 'cocoon-blocks/box-menu') {
            return wp.element.createElement(BlockEdit, props);
          }
    
          var currentClassName = attributes.className || '';
    
          // 現在のクラスから選択中のPC・モバイル設定と余白設定を取得
          var selectedPC = '';
          var selectedMobile = '';
          var hasGapClass = currentClassName.indexOf('is-gap') !== -1;
    
          PC_CLASSES.forEach(function (option) {
            if (option.value && currentClassName.indexOf(option.value) !== -1) {
              selectedPC = option.value;
            }
          });
    
          MOBILE_CLASSES.forEach(function (option) {
            if (option.value && currentClassName.indexOf(option.value) !== -1) {
              selectedMobile = option.value;
            }
          });
    
          /**
           * PC・モバイル・余白の設定に応じてクラス名を更新
           *
           * @param {string} newPC 新しいPC用クラス名
           * @param {string} newMobile 新しいモバイル用クラス名
           * @param {boolean} gap 余白用クラスを付与するかどうか
           * @return {void}
           */
          function updateClassName(newPC, newMobile, gap) {
            // PC・モバイル・余白用の既存クラスを除外
            var otherClasses = currentClassName
              .split(' ')
              .filter(function (cls) {
                var excludeClasses = PC_CLASSES.concat(MOBILE_CLASSES).map(function (opt) {
                  return opt.value;
                });
                excludeClasses.push('is-gap');
                return !excludeClasses.includes(cls);
              })
              .join(' ');
    
            // 既存クラスと新しい設定を組み合わせてクラス名を生成
            var newClassName = [otherClasses, newPC, newMobile]
              .filter(Boolean)
              .join(' ');
    
            // 余白設定が有効な場合はクラスを追加
            if (gap) {
              newClassName = (newClassName + ' is-gap').trim();
            }
    
            // ブロックのクラス属性を更新
            setAttributes({ className: newClassName });
          }
    
          // ブロック編集画面と設定パネルを描画
          return wp.element.createElement(
            Fragment,
            {},
            // 元のブロック編集画面を表示
            wp.element.createElement(BlockEdit, props),
    
            // ブロック選択時のみ設定パネルを表示
            isSelected &&
              wp.element.createElement(
                InspectorControls,
                {},
                wp.element.createElement(
                  PanelBody,
                  { title: 'カスタムスタイル', initialOpen: true },
    
                  // PC表示の列数を選択
                  wp.element.createElement(SelectControl, {
                    label: 'PCスタイル選択',
                    value: selectedPC,
                    options: PC_CLASSES,
                    onChange: function (value) {
                      selectedPC = value;
                      updateClassName(value, selectedMobile, hasGapClass);
                    },
                  }),
    
                  // モバイル表示の列数を選択
                  wp.element.createElement(SelectControl, {
                    label: 'モバイルスタイル選択',
                    value: selectedMobile,
                    options: MOBILE_CLASSES,
                    onChange: function (value) {
                      selectedMobile = value;
                      updateClassName(selectedPC, value, hasGapClass);
                    },
                  }),
    
                  // 余白設定を切り替え
                  wp.element.createElement(CheckboxControl, {
                    label: '余白を有効にする',
                    checked: hasGapClass,
                    onChange: function (checked) {
                      hasGapClass = checked;
                      updateClassName(selectedPC, selectedMobile, checked);
                    },
                  })
                )
              )
          );
        };
      }, 'withCustomStyleClassControl');
    
      // ブロック編集画面にカスタム設定を追加
      addFilter(
        'editor.BlockEdit',
        'my-plugin/my-box-menu',
        withCustomStyleClassControl
      );
    })(window.wp);
  • ラベル
    CSSを追加

    以下のコードをstyle.cssに追加します。

    :root {
      --box-menu: 4;
      --box-gap: 0;
    }
    
    .is-pc5 {
      --box-menu: 5;
    }
    
    .is-pc6 {
      --box-menu: 6;
    }
    
    .is-gap {
      --box-gap: 10px;
    }
    
    .box-menu-box {
      .box-menus {
        display: grid;
        gap: var(--box-gap);
        grid-template-columns: repeat(var(--box-menu), 1fr);
      }
    
      @media (max-width: 834px) {
        .box-menus {
          --box-menu: 2;
        }
    
        &.is-sp3 .box-menus {
          --box-menu: 3;
        }
      }
    }
    
    body .box-menus .box-menu {
      width: 100%;
    }

さいごに

今回紹介した方法を使えば、Cocoonのボックスメニューに対して柔軟なスタイル指定が可能になり、複数の条件を直感的なUIで設定できます。
register_block_styleでは表現しきれない複合スタイルの管理を、ひとつのパネルUIに集約することで、操作性と視認性を高めることができます。

タイトルとURLをコピーしました