独自の「リンク」ブロックを追加する方法

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

Cocoon 2.9.1.3でサポート

はじめに

Gutenbergエディター

Gutenbergエディターでブロック全体をリンク化するには、「カスタムHTML」ブロックを使用して <a>タグでブロックを囲む必要があります(図1参照)。
Cocoonには、プラグイン「VK Blocks」のようにグループ全体をリンクで囲む機能はありません。

そこで、ブロック全体をリンク化するブロックを作成します。

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

完成イメージ

実装手順

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

// ブロックを登録
add_action('init', function () {
  ob_start();
  ?>
  .wp-block-custom-link-wrapper,
  .wp-block-custom-link-wrapper:hover {
    color: unset;
    text-decoration: none;
  }
  <?php
  wp_register_style($handle, false);
  wp_add_inline_style($handle, ob_get_clean());

  ob_start();
  ?>
  (function (wp) {
    const { registerBlockType } = wp.blocks;
    const { __ } = wp.i18n;
    const { InspectorControls, useBlockProps, InnerBlocks } = wp.blockEditor;
    const { PanelBody, TextControl, ToggleControl } = wp.components;
    const { createElement: el, Fragment } = wp.element;

    registerBlockType('custom/link-wrapper', {
      title: __('リンクラッパー', 'link-wrapper'),
      icon: 'admin-links',
      category: 'cocoon-block',

      attributes: {
        url:          { type: 'string', default: '' },
        openInNewTab: { type: 'boolean', default: false }
      },

      /**
       * エディター描画コンポーネント
       *
       * @param {Object} props ブロックのProps
       * @return {import('react').ReactElement} Reactエレメント
       */
      edit: function (props) {
        const { attributes, setAttributes } = props;
        const { url, openInNewTab } = attributes;

        const blockProps = useBlockProps({
          style: {
            border: '1px dashed #ccc',
            padding: '10px'
          }
        });

        return el(Fragment, null,
          el(InspectorControls, null,
            el(PanelBody, {
              title: __('リンク設定', 'link-wrapper'),
              initialOpen: true
            },
              el(TextControl, {
                label: __('リンク先URL', 'link-wrapper'),
                value: url,
                onChange: (value) => setAttributes({ url: value })
              }),
              el(ToggleControl, {
                label: __('新しいタブで開く', 'link-wrapper'),
                checked: openInNewTab,
                onChange: (value) => setAttributes({ openInNewTab: value })
              })
            )
          ),

          el('div', blockProps,
            el(InnerBlocks)
          )
        );
      },

      /**
       * 保存用コンポーネント
       *
       * @param {Object} props ブロックのProps
       * @return {import('react').ReactElement} Reactエレメント
       */
      save: function (props) {
        const { attributes } = props;
        const { url, openInNewTab } = attributes;

        const target = openInNewTab ? '_blank' : '_self';

        const blockProps = useBlockProps.save({
          href: url,
          target: target
        });

        return el('a', blockProps,
          el('div', null,
            el(InnerBlocks.Content)
          )
        );
      }
    });
  })(window.wp);
  <?php

  $handle = 'link-wrapper-block';

  wp_register_script($handle, false, ['wp-blocks', 'wp-element', 'wp-i18n', 'wp-components', 'wp-block-editor'], false, true);
  wp_add_inline_script($handle, ob_get_clean());

  register_block_type('custom/link-wrapper', [
    'editor_script' => $handle,
    'style'         => $handle,
  ]);
});

参考:「グループ」ブロックにリンクメニューを追加する方法

「グループ」ブロックのツールバーにリンクメニューを追加します。

完成イメージ

実装手順

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

// エディター用JS・CSSを追加
add_action('enqueue_block_editor_assets', function () {
  ob_start();
  ?>
  (function (wp) {
    // Gutenberg APIから必要なコンポーネントを抽出
    const { addFilter } = wp.hooks;
    const { createHigherOrderComponent } = wp.compose;
    const { Fragment, useState, createElement: el } = wp.element;
    const { BlockControls, __experimentalLinkControl: LinkControl } = wp.blockEditor;
    const { ToolbarButton, Popover } = wp.components;

    // ブロックエディタの既存コンポーネントを拡張する高階コンポーネントを作成
    const withToolbar = createHigherOrderComponent((BlockEdit) => {
      return (props) => {
        // 「グループ」ブロック以外はそのまま描画
        if (props.name !== 'core/group') {
          return el(BlockEdit, props);
        }

        // 属性と状態の管理
        const { attributes, setAttributes } = props;
        const { dataLink, dataTarget } = attributes;
        const [open, setOpen] = useState(false);

        return el(Fragment, null,
          // ツールバーにリンク追加ボタンを配置
          el(BlockControls, { group: 'block' },
            el(ToolbarButton, {
              icon: 'admin-links',
              label: 'リンク',
              isPressed: !!dataLink,
              onClick: () => setOpen(!open)
            })
          ),
          // ボタン押下時に表示されるリンク設定用UI
          open && el(Popover, { position: 'bottom center', onClose: () => setOpen(false) },
            el(LinkControl, {
              value: { url: dataLink, opensInNewTab: dataTarget },
              onChange: (val) => setAttributes({ dataLink: val.url, dataTarget: val.opensInNewTab }),
              onRemove: () => setAttributes({ dataLink: '', dataTarget: false })
            })
          ),
          // ブロックを囲み、リンク設定済みであることを示すラッパーを追加
          el('div', { className: dataLink ? 'group-has-link' : '' },
            el(BlockEdit, props)
          )
        );
      };
    }, 'withToolbar');

    // エディタの編集機能に上記ツールバー処理をフック
    addFilter('editor.BlockEdit', 'custom/group-link-toolbar', withToolbar);
  })(window.wp);
  <?php

  $handle = 'my-group-link';

  wp_register_script($handle, false, ['wp-blocks', 'wp-element', 'wp-components', 'wp-compose', 'wp-hooks', 'wp-block-editor'], false, true);
  wp_add_inline_script($handle, ob_get_clean());
  wp_enqueue_script($handle);
});


// ブロックに属性追加
add_filter('block_type_metadata_settings', function ($settings, $metadata) {
  if ($metadata['name'] !== 'core/group') {
    return $settings;
  }

  // リンク先URLを保持
  $settings['attributes']['dataLink'] = [
    'type'    => 'string',
    'default' => ''
  ];

  // 別タブで開くフラグを保持
  $settings['attributes']['dataTarget'] = [
    'type'    => 'boolean',
    'default' => false
  ];

  return $settings;
}, 10, 2);


// ブロックに反映
add_filter('render_block', function ($content, $block) {
  if ($block['blockName'] !== 'core/group' || empty($block['attrs']['dataLink'])) {
    return $content;
  }

  $url    = esc_url($block['attrs']['dataLink']);
  $target = !empty($block['attrs']['dataTarget']) ? '_blank' : '';

  // data属性を追加
  $processor = new WP_HTML_Tag_Processor($content);
  if ($processor->next_tag()) {
    $processor->set_attribute('data-link', $url);
    if ($target) {
      $processor->set_attribute('data-target', $target);
    }
  }

  return $processor->get_updated_html();
}, 10, 2);


// フロント・エディタ共通のCSSを追加
add_action('enqueue_block_assets', function () {
  $handle = 'my-group-link';

  wp_register_style($handle, false);
  wp_enqueue_style($handle);

  ob_start();
  ?>
  .group-has-link {
    border: 2px dashed #2271b1;
  }

  [data-link] {
    cursor: pointer;
  }

  [data-link] a {
    pointer-events: none;
  }
  <?php
  $css = ob_get_clean();

  wp_add_inline_style($handle, $css);
});


// フロント用JS・CSSを追加
add_action('wp_enqueue_scripts', function () {
  ob_start();
  ?>
  document.addEventListener('click', function (e) {
    const card = e.target.closest('[data-link]');
    if (!card) return;

    const { link, target } = card.dataset;
    if (!link) {
      return;
    }

    // target指定に応じて遷移方法を切り替え
    target === '_blank' ? window.open(link, '_blank') : location.href = link;
  });
  <?php
  wp_add_inline_script('jquery', ob_get_clean());
});

さいごに

今回紹介した方法を使えば、Gutenbergエディターで任意のブロックをリンク全体で囲む「リンクラッパー」ブロックを簡単に追加できます。
これまでのようにカスタムHTMLで <a>タグを手動で書いてブロックを囲む必要がなく、エディター上から直感的にリンク化できるのが大きなメリットです。

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