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

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

はじめに

ブロックに独自のスタイルを追加する場合、大きく分けて二つの手法があります。
それぞれの特徴や利点、欠点を理解したうえで、用途に応じて使い分けることが重要です。

  • register_block_style用いた標準スタイルパネルの利用
  • JSとInspectorControls用いた独自パネルの構築

register_block_styleを用いた場合

register_block_styleはブロックにスタイルを追加するためのAPI です。

スタイルを登録すると、Gutenbergエディターの標準スタイルパネルにボタンとして表示されます。
ユーザーがボタンにカーソルを合わせるとプレビューがポップアップ表示され、クリックすると対応するクラスが「段落」ブロックに付与されます。
フロントおよびエディター用CSSを用意すれば、選択したスタイルをそのまま反映できます。

完成イメージ

実装手順

以下のコードをfunctions.phpに追加します。

// 「段落」ブロックにカスタムスタイルを登録
add_action('init', function () {
  $styles = [
    ['name' => 'border-solid',  'label' => '実線'],
    ['name' => 'border-dashed', 'label' => '破線'],
    ['name' => 'border-dotted', 'label' => '点線'],
  ];

  foreach ($styles as $style) {
    register_block_style('core/paragraph', $style);
  }
});


// フロント・エディタ共通のCSSを追加
add_action('enqueue_block_assets', function () {
  ob_start();
  ?>
  .is-style-border-solid { border: 2px solid #000; padding: 5px; }
  .is-style-border-dashed { border: 2px dashed #000; padding: 5px; }
  .is-style-border-dotted { border: 2px dotted #000; padding: 5px; }
  <?php
  wp_add_inline_style('wp-block-library',ob_get_clean());
});

JS+InspectorControlsを用いた場合

JS+InspectorControlsの手法では、Gutenbergエディターのサイドバーに独自パネルを作成し、「段落」ブロックが選択されているときだけ表示されます。

完成イメージ

パネル内に任意の数のスタイルボタンを設置でき、各ボタンには線の種類、角丸、色、アイコンなどのサンプルを自由に表示可能です。
クリックするとclassNameが更新され、フロント・エディター双方にスタイルを反映できます。

実装手順

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

  • cocoon-child-master
    • functions.php
    • custom-editor.js
  • ラベル
    PHPを追加

    以下のコードをfunctions.phpに追加します。

    /**
     * カスタム段落スタイルの一覧を取得
     *
     * @return array スタイル識別子(クラス名)をキーとし、ラベルとCSS文字列を持つ連想配列
     */
    if (!function_exists('get_my_custom_paragraph_styles')):
    function get_my_custom_paragraph_styles() {
      return [
        'is-style-border-solid'  => ['label' => '実線', 'css' => 'border:2px solid #000; padding:5px;'],
        'is-style-border-dashed' => ['label' => '破線', 'css' => 'border:2px dashed #000; padding:5px;'],
        'is-style-border-dotted' => ['label' => '点線', 'css' => 'border:2px dotted #000; padding:5px;'],
      ];
    }
    endif;
    
    
    /**
     * 指定されたスタイルハンドルにカスタム段落のインラインCSSを追加
     *
     * @param string $handle スタイルハンドル名
     * @return void
     */
    if (!function_exists('enqueue_my_custom_paragraph_inline_styles')):
    function enqueue_my_custom_paragraph_inline_styles($handle) {
      $paragraph_styles = get_my_custom_paragraph_styles();
      $generated_css    = '';
    
      foreach ($paragraph_styles as $class_name => $data) {
        $generated_css .= '.' . esc_attr($class_name) . ' {' . $data['css'] . "}\n";
      }
    
      wp_add_inline_style($handle, $generated_css);
    }
    endif;
    
    
    // エディター用JS・CSSを追加
    add_action('enqueue_block_editor_assets', function () {
      // JSファイルを登録
      wp_register_script('custom-paragraph-controls', get_theme_file_uri('/custom-editor.js'), ['wp-blocks', 'wp-element', 'wp-components', 'wp-block-editor', 'wp-hooks'], null, true);
    
      // スタイル一覧をJavaScriptへ渡す
      $paragraph_styles = get_my_custom_paragraph_styles();
      wp_add_inline_script('custom-paragraph-controls', 'const paragraphStyles = ' . wp_json_encode($paragraph_styles) . ';', 'before');
    
      // エディター用JS・CSSを追加
      enqueue_my_custom_paragraph_inline_styles('wp-edit-blocks');
      wp_enqueue_script('custom-paragraph-controls');
    });
    
    
    // フロント用JS・CSSを追加
    add_action('wp_enqueue_scripts', function () {
      enqueue_my_custom_paragraph_inline_styles('wp-block-library');
    });
  • ラベル
    JavaScript

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

    /**
     * ブロックエディターの段落ブロックにカスタムスタイル選択パネルを追加
     */
    (function () {
      var addFilter         = wp.hooks.addFilter;
      var InspectorControls = wp.blockEditor.InspectorControls;
      var PanelBody         = wp.components.PanelBody;
      var el                = wp.element.createElement;
    
      /**
       * CSS文字列をReactのstyleオブジェクトに変換
       *
       * @param {string} cssString CSS文字列
       * @return {Object} 変換されたstyleオブジェクト
       */
      var parseCssToObj = function (cssString) {
        if (!cssString) {
          return {};
        }
    
        return cssString.split(';').reduce(function (acc, rule) {
          var parts    = rule.split(':');
          var property = parts[0];
          var value    = parts[1];
    
          if (property && value) {
            var formattedProperty = property.trim().replace(/-([a-z])/g, function (g) {
              return g[1].toUpperCase();
            });
            acc[formattedProperty] = value.trim();
          }
    
          return acc;
        }, {});
      };
    
      /**
       * 段落ブロックにカスタムコントロールを追加
       *
       * @param {Function} BlockEdit 既存のBlockEditコンポーネント
       * @return {Function} 拡張されたコンポーネント
       */
      var withCustomControls = function (BlockEdit) {
        return function (props) {
          var attributes    = props.attributes;
          var setAttributes = props.setAttributes;
          var isSelected    = props.isSelected;
    
          // 対象外のブロックは終了
          if (props.name !== 'core/paragraph') {
            return el(BlockEdit, props);
          }
    
          // カスタムスタイル一覧を取得
          var styles = typeof paragraphStyles !== 'undefined' ? paragraphStyles : {};
    
          var inspectorControls = null;
    
          // ブロック選択時のみパネルを生成
          if (isSelected) {
            // スタイル選択ボタンを生成
            var buttons = Object.keys(styles).map(function (key) {
            var isCurrent = attributes.className === key;
              
              // プレビューを生成
              var previewDiv = el('div', {
                style: Object.assign({
                  marginBottom: '8px',
                  minHeight:    '20px'
                }, parseCssToObj(styles[key].css))
              }, 'サンプル');
    
              // ラベルを生成
              var labelDiv = el('div', {
                style: {
                  fontSize: '12px',
                  color:    '#333'
                }
              }, styles[key].label);
    
              // ボタンを生成
              return el('button', {
                key: key,
                type: 'button',
                onClick: function () {
                  setAttributes({ className: isCurrent ? '' : key });
                },
                style: {
                  padding:      '10px',
                  cursor:       'pointer',
                  background:   isCurrent ? '#f0f0f0' : '#fff',
                  border:       isCurrent ? '2px solid #00a0d2' : '1px solid #ccc',
                  borderRadius: '4px',
                  textAlign:    'center'
                }
              }, previewDiv, labelDiv);
            });
    
            // インスペクターパネルを生成
            inspectorControls = el(InspectorControls, null,
              el(PanelBody, { title: '段落スタイル', initialOpen: true },
                el('div', {
                  style: {
                    display:             'grid',
                    gridTemplateColumns: 'repeat(2, 1fr)',
                    gap:                 '10px'
                  }
                }, buttons)
              )
            );
          }
    
          // ブロック編集画面とカスタムパネルを表示
          return el(wp.element.Fragment, null,
            el(BlockEdit, props),
            inspectorControls
          );
        };
      };
    
      addFilter('editor.BlockEdit', 'custom/paragraph-controls', withCustomControls);
    })();

ブロックスタイルの追加方法比較

表1に、それぞれの手法の特徴と比較を示します。

比較
手法利点注意点
register_block_style
  • JS不要で、コードがシンプル

  • 標準UIでマウスオーバー時にプレビュー表示
  • ボタン内にカスタムプレビューやアイコンを表示できない

  • パネルの分類や複雑なUIは作れない
JS + InspectorControls
  • ボタン内にサンプル表示やアイコンが可能

  • 複数分類のパネルを作れる
  • JS必須

  • コード量が増える

さいごに

今回紹介した方法を使えば、段落ブロックに簡単に独自スタイルを追加できます。
register_block_styleを使えば標準パネルで軽量に、JSとInspectorControlsを使えばカスタムパネルで複雑なスタイルやサンプル表示も可能です。
用途に応じて使い分けることで、効率的にデザイン性の高いコンテンツを作成できます。

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