トップへ戻る
BLOGS

WordPress アイコンブロックをテーマに組み込む

WordPress アイコンブロックをテーマに組み込む

はい。今回も自身のテーマの機能拡張としてブロック開発を行っていきます
以前の記事にて自身のテーマの中にブロックを構築する環境を構築するところを紹介しました
今回はそのブロック開発環境にて、「アイコンブロック」、、
有料テーマでよくある、ボックスと好きなアイコンを追加出来るブロックを作成していきます

環境構築の記事はこちらです

関連記事

環境が整ったら実際に作成できるブロックの概要をまとめておきます

IconBlockの使用を決める

開発を始める前にまずは設計を行います
どのような機能を持たせるか、何ができるのか、これらを箇条書きでも羅列させることで常に指標を持って開発が進む為、後々変更があった場合や修正などの際に基盤となってくれます

今回実装できたブロックは以下のような例です

必要なもの

こっちにコンテンツが入ります
自由にタイトルアイコンの文字列は変更できるようにします

上記のようにアイコンと、文字列を組み合わせたり、画像やリストなどもインナーコンテンツに含めることができるようにします

  • タイトルなしのサイドアイコンデザイン
  • アイコンの種類を複数の中から選択可能

入れられるコンテンツには制限数はありません

印象としてはこんな感じのイメージで制作していきます
簡単ながらイメージはついたでしょうか? 毎回何かを制作するときは箇条書きでもいいので概要をまとめとくと良いと思います

ディレクトリの設定

今回の作業ディレクトリの作成のためのwebpackの設定です

webpack.config.js

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

module.exports = {
  ...defaultConfig, //既存の設定をここに展開
  entry: {
    exbox: "./src/exbox.js",
    iconbox: "./src/iconbox.js", //今回作成するファイルを監視
  },
  output: {
    path: path.join(__dirname, "/build"),
    filename: "[name].js",
  },
};

これでbuildディレクトリに「iconbox.js」が作成されるはずです

今自動生成されたファイルをテーマファイルのfunctions.phpにて読み込むようにします

functions.php

// カスタムブロック登録用スクリプト
add_action( 'enqueue_block_editor_assets', function () {

  // アイコンブロック登録
  $iconbox_asset_file = include __DIR__ . '/../blocks/build/iconbox.asset.php';
	wp_enqueue_script(
		'iconbox-script',
		get_theme_file_uri( '/blocks/build/iconbox.js' ),
		$iconbox_asset_file['dependencies'],
    $iconbox_asset_file['version'],
		true
	);
  //Font Awesome読み込み
  wp_enqueue_style('fontawesome_css',
'https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.1.1/css/all.min.css');
});

アクションフックのenqueue_block_editor_assetsはブロックが読み込まれるタイミングで実行されるフックです
今回使用するアイコンは全て「Font Awesome」から使用するためこちらの読み込みもここで行っておきます

では早速ブロックの登録を行います

ブロックの登録 regiterBlockType

ブロックの登録スクリプトを記述していきます

src/iconbox.js

import { registerBlockType } from "@wordpress/blocks";
import { InnerBlocks, RichText, useBlockProps } from "@wordpress/block-editor";
import IconBoxEdit from "./modules/IconBoxEdit";

registerBlockType("oja/icon-block", {
  title: "アイコンボックス",
  description: "アイコンデザインを変更して下さい",
  category: "text",
  keywords: ["icon", "box", "oja"],
  icon: "block-default",
  supports: {
    html: false,
    customClassName: false,
  },
  attributes: {
    iconBoxType: {
      typp: "string",
      default: "fa-pen",
    },
    isHeadLine: {
      type: "boolean",
      default: true,
    },
    iconHead: {
      type: "string",
      default: "",
      source: 'html',
      selector: "h3"
    },
  },
  edit: IconBoxEdit,
  save: () => {
      return null;
  },
});

今回の仕様はこんな感じです。のちのちsaveメソッドも記述していきます

属性値について

今回定義した属性値は以下の用途で使用します

iconBoxType
今回設定するウェブアイコンのクラス名です。FontAwesomeのHTML名をそのまま使用しました
isHeadLine
上部アイコンデザインにするかサイドデザインにするかの真偽値です
iconHead
上部アイコンデザインの場合のアイコン横のテキストを保存するために使用します

さて、次はこの属性値を使って管理画面の作り込みを行ってみます

editメソッド

import IconBoxEdit from "./modules/IconBoxEdit"; //このディレクトリを作成する

前項のブロック登録ファイルにてeditメソッドはモジュールとして読み込んでいたので、そのようにディレクトリを作成し作り込んでいきます

./modules/IconBoxEdit.js

import {
  InnerBlocks,
  InspectorControls,
  RichText,
  useBlockProps,
} from "@wordpress/block-editor";
import {
  PanelBody,
  PanelRow,
  SelectControl,
  ToggleControl,
} from "@wordpress/components";

const ALLOWED_BLOCKS = [
  "core/image",
  "core/paragraph",
  "core/list",
  "core/heading",
  "core/table",
  "oja/post-list",
  "oja/oja-related-post-block",
  "ojako/custom-dlblock",
];
const boxType = [
  { label: "電球", value: "fa-lightbulb" },
  { label: "お知らせ", value: "fa-bell" },
  { label: "カート", value: "fa-cart-shopping" },
  { label: "吹き出し", value: "fa-comment-dots" },
  { label: "星", value: "fa-star" },
  { label: "はてなマーク", value: "fa-question" },
  { label: "チェック", value: "fa-circle-check" },
  { label: "ノート", value: "fa-file-pen" },
  { label: "クリップボード", value: "fa-clipboard" },
  { label: "鉛筆", value: "fa-pen" },
  { label: "歯車", value: "fa-gear" },
  { label: "注意", value: "fa-triangle-exclamation" },
  { label: "いいね!", value: "fa-thumbs-up" },
  { label: "低評価", value: "fa-thumbs-down" },
  { label: "ハート", value: "fa-heart" },
  { label: "旗", value: "fa-flag" },
  { label: "ビックリマーク", value: "fa-circle-exclamation" },
];

export default function IconBoxEdit({
  className,
  setAttributes,
  attributes: { iconBoxType, isHeadLine, iconHead },
}) {
  isHeadLine
    ? (className += ` icon-headline ${iconBoxType}`)
    : (className += ` ${iconBoxType}`);
  const spanprops = useBlockProps({
    className: `icon-element ${iconBoxType}`
  });
  const blockwraper = useBlockProps({className: className});

  return [
    <InspectorControls>
      <PanelBody title="ボックスのデザイン" initialOpen={true}>
        <PanelRow>
          <SelectControl
            label=""
            value={iconBoxType}
            options={boxType}
            onChange={(val) => setAttributes({ iconBoxType: val })}
          />
        </PanelRow>
        <PanelRow>
          <ToggleControl
            label={isHeadLine ? "見出しアイコン" : "サイドアイコン"}
            help=""
            checked={isHeadLine}
            onChange={(val) => setAttributes({ isHeadLine: val })}
          />
        </PanelRow>
      </PanelBody>
    </InspectorControls>,
    <div {...blockwraper}>
      <span {...spanprops}>
        {isHeadLine && (
          <RichText
            className="icon-head"
            value={iconHead}
            onChange={(val) => setAttributes({iconHead: val})}
            tagName="h3"
            placeholder="見出しを入力できます"
            keepPlaceholderOnFocus={true}
          />
        )}
      </span>
      <div className="icon-block-inner">
        <InnerBlocks allowedBlocks={ALLOWED_BLOCKS} />
      </div>
    </div>,
  ];
}

一気に記述してしまってややこしいので解説します

SelectControlでアイコンを選ぶ

まずはファイルのトップレベルで SelectControl のオプションにて使用する配列を定義しておきます
boxTypeという名前で今回は定義しました
ここで valueとして設定している値はFont Awesomeのアイコンクラス名でこのクラス名がついた要素の疑似要素に対してアイコンを出力してくれます
後はインスペクターの中でSelectControlを出力しattirbutes.iconBoxTypeに対して値を設定するようにします

<SelectControl
  label=""
  value={iconBoxType}
  options={boxType}
  onChange={(val) => setAttributes({ iconBoxType: val })}
/>

ToggleControl タイトルの有無を制御する

上部アイコンデザインの場合にタイトルが入力できるように真偽値で判定して要素を出し分けます
まずは三項演算子でブロックのラッパー要素であるdivタグに対してクラス名を追加する式を定義しています

   ? (className += ` icon-headline ${iconBoxType}`)
   : (className += ` ${iconBoxType}`);
  、、省略、、、
  // クラス名をラッパー要素に追加する
  const blockwraper = useBlockProps({className: className});

※ ここでラッパー要素にiconBoxTypeをクラス名として追加してしまうとラッパー要素にもアイコンが出力してしまいますが、今回はこれで実装しようと思います。次回更新時は別の方法に切り替えるかもしれません

真偽値はToggleControl にて切り替えるようにして、attributes.isHeadLineへ保存します

<ToggleControl
 label={isHeadLine ? "見出しアイコン" : "サイドアイコン"}
 help=""
 checked={isHeadLine}
 onChange={(val) => setAttributes({ isHeadLine: val })}
/>

最後に真偽値がtrueのときだけ入力要素であるRichTextを出力できるようにします

 {isHeadLine && ( この中の処理は左辺がtrueの時だけ実行される )}

アイコンの要素にクラス名を追加

今回は「 spanタグ 」にアイコンを出力させます

<span {...spanprops}> </span>

アイコンのテキストを入力してもらう

RichTextコンポーネントはisHeadLineの真偽値によって出力します
そして入力してもらった値はattributes.iconHeadに保存するようにし、出力するタグは「 h3 」としました

<RichText
 className="icon-head"
 value={iconHead}
 onChange={(val) => setAttributes({iconHead: val})}
 tagName="h3"
 placeholder="見出しを入力できます"
 keepPlaceholderOnFocus={true}
/>

InnerBlocks 内部ブロックの設定

InnerBlocksコンポーネントでallowedBlocksを定義しこのブロックの中に入れるブロックの種類をスクリプトのトップレベルで定義した変数で設定しています
コアブロックについては適宜調べて下さい

また、InnerBlocksはフロントエンド側では何も出力しない為、ラップしたいクラス名の<div>タグで覆ってあります

<InnerBlocks allowedBlocks={ALLOWED_BLOCKS} />

saveメソッドの定義

それでは上記のeditメソッドで用意した使用をもとに、フロントエンド側の出力を実装します

src/iconbox.js

save: ({ className, attributes: { iconBoxType, isHeadLine, iconHead } }) => {
    isHeadLine
      ? (className += ` icon-headline ${iconBoxType}`)
      : (className += ` ${iconBoxType}`);
    const blockwraper = useBlockProps.save({className: className});
    iconBoxType += ` icon-element`;
    return (
      <div {...blockwraper}>
        <span className={iconBoxType}>
          {isHeadLine && (
            <RichText.Content
              className="icon-head"
              value={iconHead}
              tagName="h3"
            />
          )}
        </span>
        <div className="icon-block-inner">
          <InnerBlocks.Content />
        </div>
      </div>
    );
  },

ほとんどeditと同じなので今回は折りたたんでいますが変更点としては
ブロックパラメータをラッパー要素に設定するuseBlockPropsに対してsaveメソッド版の
.saveとしていること、また同じメソッドに使用制限でもあるのか、出力内容がおかしかった為
spanタグに対して挿入するクラス名の場合はあまり好ましくありませんが、直接代入しております

iconBoxType += icon-element;

これで同じ条件で管理画面とフロントエンド側の実装が完成しました
後はSCSSでスタイリングを行います

scssでクラス名ごとにデザインする

ここではiconBoxTypeに保存された値がブロックのラッパー要素に指定してあるため、これを使用し各々デザインをしていこうと思います

block-style/_iconbox.scss

@charset "utf-8";

@mixin iconColorSet($color) {
  background-color: rgba($color, 0.1);
  border: none;
  .icon-element {
    &::before {
      color: $color;
    }
    h3 {
      color: $color;
    }
  }
  .icon-block-inner {
    border-left: 2px dashed rgba($color, 0.6);
  }
}

.wp-block-oja-icon-block {
  border: 1px solid rgb(228, 108, 97);
  width: 95%;
  min-height: 45px;
  display: flex;
  align-items: center;
  padding: 20px;
  border-radius: 5px;
  margin-bottom: 20px;
  &:before {
    content: none !important;
  }
  .icon-element {
    position: relative;
    font-weight: 900;
    padding-right: 2.5%;
    display: block;
    &::before {
      font-family: "Font Awesome 5 Free";
      font-size: 22px;
    }
    h3 {
      display: inline-block;
      margin: 20px;
      &:before {
        content: none;
      }
    }
  }
  .icon-block-inner {
    border-left: 2px dashed #D5D9F2;
    padding-left: 2%;
    min-height: 32px;
    width: 90%;
    ul {
      border: none;
    }
  }

  &.icon-headline {
    flex-direction: column;
    align-items: flex-start;
    padding-top: 0;
    .icon-element {
      padding-left: 3.5%;
      width: 100%;
      &:before {
        font-size: 30px;
      }
    }
    .icon-block-inner {
      border: none !important;
    }
  }

  // 注意、ハート、?マーク、ビックリマーク
  &.fa-triangle-exclamation,
  &.fa-heart,
  &.fa-question,
  &.fa-circle-exclamation {
    @include iconColorSet(#F13D54);
  }

  // 星 、電球、カート、旗
  &.fa-star,
  &.fa-lightbulb,
  &.fa-cart-shopping,
  &.fa-flag {
    @include iconColorSet(#FFBF0E);
  }

  // クリップボード、歯車、いいね!、低評価
  &.fa-clipboard,
  &.fa-gear,
  &.fa-thumbs-down,
  &.fa-thumbs-up {
    @include iconColorSet(#616dc9);
  }

  // お知らせ、吹き出し、チェック、鉛筆、ノート
  &.fa-bell,
  &.fa-comment-dots,
  &.fa-circle-check,
  &.fa-pen,
  &.fa-file-pen {
    @include iconColorSet(#05B483);
  }
}

スタイリングは個々にお好みで設定して下さい

ラッパー要素の疑似要素だけはキャンセルしなければなりませんが、もっとイメージに合ったカラーリングや、アイコンも簡単に追加できます

今回学んだまとめ事項

useBlockProps()

今回はおもに、今更ながらですがuseBlockProps()の使用を学びました。
まずこのメソッドはブロックの情報を出力するということ、そしてそれをブロックのラッパー要素に出力しなければいけない事。
これは少し不思議でしたが、saveメソッドで重複して使用するとuseBlockProps()を使用した要素に同じようにブロック情報が出力され同じクラス名になってしまう事象が、、
まだ検証が足りませんがuseBlockProps.save()は重複できないのかも、、そしてsaveメソッド内ではsaveを使用しなければいけないため、どうしてもこれに引っかかるのではないか、、

アイコンのバージョンについて

今回実装するのに一番苦労しているポイントですが、このタイミングでFont Awesomeのバージョンが5から6へ上がっており、今までのサイト内とのバージョンの整合性がうまく行えず、表示崩れが各所に見られております

順次修正していきますが、あまりコピペしまくったりするのも考えようでした、、

お疲れさまでした(^^)

コメントをお待ちしております

お気軽にコメントをどうぞ。

CAPTCHA