WordPressのメニュー編集画面にFont Awesome 5のアイコンパレットを追加する方法

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

はじめに

Cocoonでは、ボックスメニューやヘッダー・フッターモバイルボタンなど、メニューを作成する際にアイコンを設定することがあります。
従来はFont Awesomeのホームページから使用するアイコンを探し、クラス名を確認してWordPressのメニュー項目に手入力する必要がありました。

そこで、WordPressのメニュー編集画面にFont Awesome 5のアイコンパレットを追加し、一覧からアイコンを選択できるようにします。
アイコン名による検索や選択したアイコンのプレビューにも対応します。

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

Cocoon設定→全体→サイトアイコンフォント「Font Awesome 5」設定すると、メニュー編集画面にアイコン選択ボタンが追加されます。

完成イメージ

実装手順

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

// メニュー項目に入力フォームを追加
add_action('wp_nav_menu_item_custom_fields', function ($item_id, $item) {
  // Font Awesome 5 が無効の場合は処理対象外
  if (!is_site_icon_font_font_awesome_5()) {
    return;
  }

  // メニュー項目のクラス一覧を取得
  $current = '';
  $classes = array_values((array) $item->classes);

  // 選択済みアイコンのクラスを取得
  foreach ($classes as $index => $class) {
    if (
      in_array($class, ['fas', 'far', 'fab'], true)
      && isset($classes[$index + 1])
      && preg_match('/^fa-[a-z0-9-]+$/', $classes[$index + 1])
    ) {
      $current = $class . ' ' . $classes[$index + 1];
      break;
    }
  }

  ?>
  <p class="description description-wide">
    <button type="button" class="button my-fa-open">アイコンを選択</button>
    <span class="my-fa-current"><?php if ($current) : ?><i class="<?php echo esc_attr($current); ?>" aria-hidden="true"></i><?php endif; ?></span>
  </p>
  <?php
}, 10, 2);


// アイコンパレットを管理画面に出力
add_action('admin_footer-nav-menus.php', function () {
  // Font Awesome 5 が無効の場合は処理対象外
  if (!is_site_icon_font_font_awesome_5()) {
    return;
  }

  // アイコン一覧のキャッシュキーを設定
  $cache_key = 'my_fa5_icon';

  // キャッシュ済みのアイコン一覧を取得
  $icons = get_transient($cache_key);

  // キャッシュが存在しない場合はアイコン一覧を取得
  if (false === $icons) {
    $response = wp_remote_get('https://raw.githubusercontent.com/FortAwesome/Font-Awesome/5.x/metadata/icons.json', ['timeout' => 15]);

    // 通信エラーまたは HTTP ステータスが 200 以外の場合は処理対象外
    if (!is_wp_error($response) && 200 === wp_remote_retrieve_response_code($response)) {
      // JSON データを連想配列として取得
      $data = json_decode(wp_remote_retrieve_body($response), true);

      // アイコンデータが配列の場合は処理を継続
      if (is_array($data)) {
        // アイコンスタイルとCSSクラスの対応を設定
        $prefixes = ['brands' => 'fab', 'regular' => 'far', 'solid' => 'fas'];

        // アイコン一覧を初期化
        $icons = [];

        // アイコン名とスタイルを取得
        foreach ($data as $name => $item) {
          // アイコンが対応する各スタイルを取得
          foreach ((array) ($item['styles'] ?? []) as $style) {
            // 対応するスタイルのアイコンを一覧に登録
            if (isset($prefixes[$style])) {
              $icons[] = $prefixes[$style] . ' fa-' . $name;
            }
          }
        }

        // 取得したアイコン一覧を30日間キャッシュに登録
        set_transient($cache_key, $icons, 30 * DAY_IN_SECONDS);
      }
    }

    // アイコン一覧を取得できない場合は空の配列を設定
    if (!is_array($icons)) {
      $icons = [];
    }
  }

  ?>
  <style>
  .my-fa-modal {
    border: 0;
    border-radius: 6px;
    padding: 0;
    width: min(900px, 94vw);

    &::backdrop {
      background: rgba(0, 0, 0, .6);
    }
  }

  .my-fa-modal__panel {
    box-sizing: border-box;
    display: flex;
    flex-direction: column;
    gap: 12px;
    max-height: 85vh;
    padding: 20px;
  }

  .my-fa-modal__header {
    align-items: center;
    display: flex;
    gap: 12px;
    justify-content: space-between;

    & h2 {
      margin: 0;
    }
  }

  .my-fa-modal__search {
    box-sizing: border-box;
    width: 100%;
  }

  .my-fa-modal__grid {
    display: grid;
    gap: 6px;
    grid-template-columns: repeat(auto-fill, minmax(100px, 1fr));
    min-height: 150px;
    overflow-y: auto;
  }

  .my-fa-modal__icon {
    align-items: center;
    background: #fff;
    border: 1px solid #ddd;
    border-radius: 4px;
    cursor: pointer;
    display: flex;
    flex-direction: column;
    gap: 8px;
    justify-content: center;
    min-height: 80px;
    padding: 8px 4px;

    &:hover,
    &:focus {
      background: #f0f6fc;
      border-color: #2271b1;
    }

    & i {
      font-size: 24px;
    }

    & span {
      font-size: 11px;
      text-align: center;
    }
  }

  .my-fa-current {
    margin-left: 8px;
    vertical-align: middle;

    & i {
      font-size: 20px;
    }
  }
  </style>

  <!-- アイコン選択用ダイアログを登録 -->
  <dialog id="my-fa-modal" class="my-fa-modal" aria-labelledby="my-fa-modal-title">
    <div class="my-fa-modal__panel">
      <div class="my-fa-modal__header">
        <h2 id="my-fa-modal-title">Font Awesome 5 アイコンパレット</h2>
        <button type="button" class="button my-fa-modal__close">閉じる</button>
      </div>

      <!-- アイコン検索用の入力欄を登録 -->
      <input type="search" class="my-fa-modal__search" placeholder="アイコン名で検索">

      <!-- アイコン一覧をグリッド形式で登録 -->
      <div class="my-fa-modal__grid">
        <?php foreach ($icons as $class) : ?>
          <button type="button" class="my-fa-modal__icon" data-class="<?php echo esc_attr($class); ?>">
            <i class="<?php echo esc_attr($class); ?>" aria-hidden="true"></i>
            <span><?php echo esc_html(substr($class, strpos($class, 'fa-') + 3)); ?></span>
          </button>
        <?php endforeach; ?>
      </div>
    </div>
  </dialog>

  <script>
  jQuery(function ($) {
    // ダイアログと検索欄、アイコン一覧を取得
    var dialog  = $('#my-fa-modal')[0];
    var $search = $('.my-fa-modal__search');
    var $icons  = $('.my-fa-modal__icon');

    // 編集対象のメニュー項目を初期化
    var $item;

    // アイコンパレットを開く
    $(document).on('click', '.my-fa-open', function () {
      // クリックしたボタンのメニュー項目を取得
      $item = $(this).closest('.menu-item');

      // 検索欄を初期化してアイコン一覧を更新
      $search.val('').trigger('input');

      // ダイアログを表示して検索欄にフォーカスを設定
      dialog.showModal();
      $search.trigger('focus');
    });

    // 閉じるボタンでパレットを閉じる
    $('.my-fa-modal__close').on('click', function () {
      dialog.close();
    });

    // 背景クリックでパレットを閉じる
    $(dialog).on('click', function (event) {
      // ダイアログ本体以外をクリックした場合は処理対象外
      if (event.target === dialog) {
        dialog.close();
      }
    });

    // 検索文字列に一致するアイコンを表示
    $search.on('input', function () {
      // 検索文字列を取得して前後の空白を除去
      var query = this.value.trim().toLowerCase();

      // アイコンのクラス名と検索文字列を照合して表示状態を変更
      $icons.each(function () {
        $(this).toggle(this.dataset.class.indexOf(query) !== -1);
      });
    });

    // 選択したアイコンをメニュー項目に設定
    $('.my-fa-modal__grid').on('click', '.my-fa-modal__icon', function () {
      // 編集対象のメニュー項目がない場合は処理対象外
      if (!$item || !$item.length) {
        dialog.close();
        return;
      }

      // 選択したアイコンのクラスを取得
      var iconClass = this.dataset.class;

      // メニュー項目の CSS クラス入力欄を取得
      var $input = $item.find('input[name^="menu-item-classes"]');

      // CSS クラス入力欄がない場合は処理対象外
      if (!$input.length) {
        dialog.close();
        return;
      }

      // 既存の CSS クラスからアイコン関連クラスを除外
      var classes = $input.val().trim().split(/\s+/).filter(function (name) {
        return name && !/^(?:fas|far|fab)$/.test(name) && !/^fa-[a-z0-9-]+$/.test(name);
      });

      // 選択したアイコンのクラスに置換
      classes.push(iconClass);
      $input.val(classes.join(' ')).trigger('change');

      // 選択したアイコンをボタン横に表示
      $item.find('.my-fa-current').html('<i class="' + iconClass + '" aria-hidden="true"></i>');

      // アイコンパレットを閉じる
      dialog.close();
    });
  });
  </script>
  <?php
});

さいごに

今回紹介した方法を使えば、WordPressのメニュー編集画面からFont Awesome 5のアイコンを検索・選択し、クラス名を手入力する手間を省けます。
選択したアイコンはプレビューで確認でき、アイコンの変更も簡単に行えます。
Cocoonのボックスメニューやヘッダー・フッターのモバイルボタンなど、メニューのアイコン設定を効率化したい場合に活用してください。

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