トップへ戻る
BLOGS

WordPress BOXブロックをテーマに組み込む

WordPress BOXブロックをテーマに組み込む

はい。
前回の記事にて自身のテーマの中にブロックを構築する環境を構築するところを紹介しましたが、
今回はそのブロック開発環境にて、ボックスブロックいわゆる有名なテーマで言うところの「JIN」などにみられるシンプルボックス的な物を作成したいと思います

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

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

EXBOXブロックの概要

まずはブロックを挿入したらどうなるのか?、、、
そのブロックはどういった挙動をするのか?、、、
そのためにどういった内容を実装する必要があるかを設計します

簡単なブロック使用まとめ

任意のデフォルトブロックを挿入できる
ボックスのデザインを変更できる
ボックスの枠線のタイプを変更可能
通常のブロックへの変換も可能
タイトルの有無を選択可能

上記のデザインも今回作成したブロックにて実装しました。

まず概要としては上記の内容を目指します
ここからさらに掘り下げつつコーディングしていきましょう

選べる基本のスタイル

まずは基本のスタイル4つを用意します

CSSで言うところの border プロパティで設定できる「線の種類」です
これはデフォルトのインスペクターのスタイルパネルを使用しホバープレビューにも対応します

  • シンプル
  • 太線
  • 点線
  • 二重線

上記のデザインも今回作成したブロックにて実装しました。

そして、上記のデザインに対してカラー設定を追加します
こちらの内容はイメージしやすくなるようにラジオボタンの実装が良さそうです

  • サイトカラー
  • ブルー
  • レッド
  • イエロー
  • グリーン
  • ピンク
  • グレー
  • ブラック

上記のデザインも今回作成したブロックにて実装しました。

さてあらかた概要も決まったところで、いざ実装していきましょう

テーマに読み込んだファイルの中でブロック登録の関数を宣言します

registerBlockTypeを実装

registerBlockType メソッドを宣言しブロックの登録を行います
この関数は第2引数にパラメータを渡すことでそのブロックの概要をまとめることが出来ます

exbox.js

import { registerBlockType, createBlock } from "@wordpress/blocks";
import { InnerBlocks, RichText } from "@wordpress/block-editor";
import ExEdit from "./modules/ExboxEdit";

registerBlockType("oja/exbox-block", {
  title: "EXボックス",
  description: "用途に合わせてデザインを変更して下さい",
  category: "text",
  keywords: ["box", "ex", "oja"],
  icon: "block-default",
  styles: [
    {
      name: "solid",
      label: "シンプル",
      isDefault: true,
    },
    {
      name: "bold",
      label: "太線",
    },
    {
      name: "dashed",
      label: "点線",
    },
    {
      name: "double",
      label: "二重線",
    },
  ],
  supports: {
    html: false,
    customClassName: false,
  },
  example: {
    attributes: {
      boxTitle: "好きなテキストや画像が入ります",
    },
  },
  transforms: {
    from: [
      {
        type: "block",
        //"段落, 見出し"から
        blocks: ["core/paragraph", , "core/heading"],
        //処理されるブロックの属性を受け取るコールバック
        transform: ({ content }) => {
          return createBlock("oja/exbox-block", {
            // ojaブロックのcontentにPブロックのcontent
            boxTitle: content,
          });
        },
      },
    ],
    to: [
      {
        type: "block",
        //"段落"へ
        blocks: ["core/paragraph"],
        //isMatchでfalseを返すと変換を適用しない
        //boxTitleがあるときだけ変換を選択できる
        isMatch: ({ boxTitle }) => {
          if (boxTitle) return true;
          return false;
        },
        transform: ({ boxTitle }) => {
          return createBlock("core/paragraph", {
            content: boxTitle,
          });
        },
      },
    ],
  },
  attributes: {
    boxTitle: {
      type: "string",
      default: "",
      source: "html",
      selector: "h3",
    },
    boxColor: {
      type: "string",
      default: "blue",
    },
    borderSetting: {
      type: "string",
      default: "nomal-head",
    },
    isHeadLine: {
      type: "boolean",
      default: true,
    },
  },
  edit: ExEdit,
  save: ({
    className,
    attributes: { boxTitle, isHeadLine, boxColor, borderSetting },
  }) => {
    className += ` ${boxColor} ${borderSetting}`;
    return (
      <div className={className}>
        {isHeadLine && (
          <RichText.Content className="ex-head" value={boxTitle} tagName="h3" />
        )}
        <InnerBlocks.Content />
      </div>
    );
  },
});

ちょっと解説

第2引数に設定するのはそれぞれの設定項目のプロパティです
stylesプロパティに設定した labelプロパティはインスペクターに表示され、そのラベルで表示されたボタンをクリックすると、値の中の name プロパティの値がブロックのラッパー要素に対して「.is-style-○○○」という形でクラス名が当たる使用です

したがって、これらのクラス名に対して、CSSを設定しておけばプレビュー画面にも対応してくれる便利なプロパティです

transformsプロパティは「そのブロックが他のどのブロックに変換できるかを示す」プロパティで
fromプロパティは他のブロックからの変換内容を、
toプロパティはこのブロックexboxから他のブロックへ変換する際のルール、値の引き継ぎなどを設定します

attributesの設定

attributes 属性値について少し解説します

boxTitle
ボックスブロックにタイトルを設定する際にその値を保存します
boxColor
ボックス全体のカラー設定を保存するために使用します
borderSetting
ここではボックスのデザイン設定に使用するクラス名を保存します
isHeadLine
これはボックス内でタイトルを表示するかどうかの真偽値です

これらの設定値を使用してブロックの状態を整えます

save メソッド

save メソッドではpropsからclassNameを取得してきて、設定値を追加しています

className += ` ${boxColor} ${borderSetting}`;

また <RichText.content>コンポーネントはタイトル用のコンポーネントになるため、isHeadLineの条件分岐を判定しtrueの場合のみコンポーネントを表示するようにします

そして、好きなブロックを挿入できるように <innerBlocks.Content>を返しておきます

ここから先はimportしておいた、Editメソッドを実装していきます

ExboxEdit.js

Editメソッドとして export しておきます

ExboxEdit.js

import { InnerBlocks, RichText } from "@wordpress/block-editor";
import { ExboxInspector } from "./ExboxInspector";

const ALLOWED_BLOCKS = [
  "core/image",
  "core/paragraph",
  "core/list",
  "core/heading",
  "core/table",
  "oja/post-list",
  "oja/oja-related-post-block",
  "ojako/custom-dlblock",
];

export default function ExEdit({ className, attributes, setAttributes }) {
  const { boxTitle, isHeadLine, boxColor, borderSetting } = attributes;
  className += ` ${boxColor} ${borderSetting}`;
  return [
    <ExboxInspector attributes={attributes} setAttributes={setAttributes} />,
    <div className={className}>
      {isHeadLine && (
        <RichText
          className="ex-head"
          value={boxTitle}
          onChange={(val) => setAttributes({ boxTitle: val })}
          tagName="h3"
          placeholder="タイトルを入力"
          //フォーカスされた際も文字が入力されるまでプレースホルダーテキストを表示
          keepPlaceholderOnFocus={true}
        />
      )}
      <InnerBlocks allowedBlocks={ALLOWED_BLOCKS} />
    </div>,
  ];
}

簡単に解説を

ALLOWED_BLOCKS変数では<innerBlocks>のブロックで許可するブロックを設定しておきます
ここでは画像ブロック、段落ブロック、リストブロック、見出しブロック、テーブルブロック、そして今まで作成してきたおジャコブロックを内包出来るようにしました

return メソッドでは saveメソッドと同じように isHeadLineによる分岐を行う他インスペクターを表示するコンポーネントを設定しています

<ExboxInspector attributes={attributes} setAttributes={setAttributes} />

ここからはこのインスペクターの設定を行っていきます

ExboxInspector.js

先の attributes にて設定しておいた項目を適宜選択出来るようにします

ExboxInspector.js

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

const sinpleBoxContent = [
  { label: "シンプル", value: "simple-bd" },
  { label: "内側線", value: "inline-bd" },
  { label: "角丸 (線の変更は不可)", value: "radius-bd" },
  { label: "チェック柄", value: "check-bd" },
  { label: "水玉模様", value: "polka-dot" },
  { label: "左縦線 (細線選択不可)", value: "left-angle" },
  { label: "大カッコ", value: "brackets" },
  { label: "カギカッコ", value: "angle-brackets" },
  { label: "黒板風 (色、線変更不可)", value: "blackboard" },
];
const titleBoxContent = [
  { label: "シンプル", value: "sinple-head" },
  { label: "ノーマル", value: "nomal-head" },
  { label: "上側", value: "top-head" },
  { label: "外側", value: "outer-head" },
  { label: "内側", value: "inner-head" },
  { label: "角丸", value: "radius-head" },
  { label: "吹き出し", value: "speech-head" },
 
];

//インスペクター表示関数
export const ExboxInspector = ({
  attributes: { boxColor, borderSetting, isHeadLine },
  setAttributes,
}) => {
  const updateHeadStyle = ( value ) => {
    setAttributes({ isHeadLine: value });
    setAttributes({ borderSetting: "nomal-head" });
  }

  return (
    <InspectorControls>
      <PanelBody title="ボックス枠線設定" initialOpen={true}>
        <PanelRow>
          <ToggleControl
            label={isHeadLine ? "タイトルを表示する" : "タイトルは表示しない"}
            help=""
            checked={isHeadLine}
            onChange={updateHeadStyle}
          />
        </PanelRow>
        <PanelRow>
          <SelectControl
            label={isHeadLine ? "タイトル付きスタイル" : "シンプルスタイル"}
            value={borderSetting}
            options={isHeadLine ? titleBoxContent : sinpleBoxContent}
            onChange={(val) => setAttributes({ borderSetting: val })}
          />
        </PanelRow>
      </PanelBody>
      <PanelBody title="ボックスカラー設定" initialOpen={false}>
        <PanelRow>
          <RadioControl
            className=" exbox-color-setting"
            selected={boxColor}
            options={[
              { label: "ブルー", value: "blue" },
              { label: "レッド", value: "red" },
              { label: "グリーン", value: "green" },
              { label: "イエロー", value: "yellow" },
              { label: "ピンク", value: "pink" },
              { label: "グレー", value: "gray" },
              { label: "ブラック", value: "black" },
            ]}
            onChange={(val) => setAttributes({ boxColor: val })}
          />
        </PanelRow>
      </PanelBody>
    </InspectorControls>
  );
};

文頭で宣言しているsinpleBoxContentはタイトルなし設定の場合のデザインをクラス名として設定しています
反対にtitleBoxContentはタイトル付きのデザインでの設定です
これら2つのデザイン設定をisHeadLineの真偽値によって出し分けれるように三項演算子を用いています

最終的にSCSSでデザインを整えて全ての作業が完了です

SCSSでデザインする

_exbox.scss

@charset 'utf-8';

// カラーセッティング
$colorSet:(
  ("blue",#82c8e2),
  ("red", #ee5656),
  ("yellow",#f7cf2e),
  ("green",#39cd75),
  ("pink",#f7b2b2),
  ("gray",#9c9c9c),
  ("black",#313131)
);

// mixin設定 色設定の共通化に利用
@mixin boxColorSet($bdColor) {
  border-color: $bdColor;
  background-color: rgba($bdColor, 0.1);
  .ex-head {
    background: $bdColor;
  }
  &.sinple-head {
    .ex-head {
      color: $bdColor;
    }
  }
  &.speech-head {
    .ex-head {
      &::after {
        border-top: 14px solid $bdColor;
      }
    }
  }
  &.inline-bd {
    box-shadow: 0 0 0 12px rgba($bdColor, 0.4);
  }
  &.radius-bd {
    box-shadow: 0 0 4px 4px rgba($bdColor, 0.8), 0 0 4px 3px rgba($bdColor, 0.4) inset;
    background: rgba($bdColor, 0.1);
  }
  &.check-bd {
    background-image: linear-gradient(45deg,rgba($bdColor, 0.1) 25%,transparent 25%,transparent 50%,rgba($bdColor, 0.1) 50%,rgba($bdColor, 0.1) 75%,transparent 75%,transparent),linear-gradient(-45deg,rgba($bdColor, 0.1) 25%,transparent 25%,transparent 50%,rgba($bdColor, 0.1) 50%,rgba($bdColor, 0.1) 75%,transparent 75%,transparent);
  }
  &.polka-dot {
    background-color: rgba($bdColor, 0.25);
  }
  &.left-angle {
    border-top: none;
    border-right: none;
    border-bottom: none;
    border-left-width: 8px;
    border-left-color: $bdColor;
  }
  &.angle-brackets {
    &::before {
	    border-left: 6px solid $bdColor;
	    border-top: 6px solid $bdColor;
    }
    &::after {
      border-right: 6px solid $bdColor;
      border-bottom: 6px solid $bdColor;
    }
  }
}

.wp-block-oja-exbox-block {
  border: 2px solid #4DA1D4;
  padding: 3%;
  position: relative;
  margin-top: 5%;
  color: #465d65;

  // 枠線設定 (スタイル)
  &.is-style-solid {
    border: 2px solid ;
  }
  &.is-style-bold {
    border: 5px solid ;
  }
  &.is-style-dashed {
    border: 3px dashed ;
  }
  &.is-style-double {
    border: 5px double ;
  }

  // ボックス見出し
  .ex-head {
    color: #fff;
    margin: 0;
    padding: .3em;
    font-weight: bold;
    letter-spacing: 3.5px;
    line-height: 1.8;
    font-size: 1.2rem;
    &::before {
      content: none;
    }
  }

  // カラー設定
  @each $prop,$color in $colorSet {
    &.#{$prop} {
      @include boxColorSet($color);
    }
  }

  /**
  * タイトル付きスタイル
  */
  // シンプル
  &.sinple-head {
    background: none;
    .ex-head {
      position: absolute;
      top: -1em;
      left: 4%;
      background: #fff;
      padding: 0 0.5em;
    }
  }

  // ノーマルスタイル
  &.nomal-head {
    .ex-head {
      position: absolute;
      top: -1em;
      left: 4%;
      font-size: 1.1rem;
      padding: 0 0.5em;
    }
  }

  // 上側スタイル
  &.top-head {
    padding: 0;
    .ex-head {
      font-size: 1.4rem;
      padding: 4px 6px;
      text-align: center;
    }
  }

  // 外側スタイル
  &.outer-head {
    margin-top: 8%;
    padding: 2%;
    .ex-head {
      position: absolute;
      top: -16%;
      left: -2px;
      padding: 4px 10px;
    }
  }

  // 内側スタイル
  &.inner-head {
    padding-top: 6%;
    .ex-head {
      position: absolute;
      left: 0%;
      top: 0%;
      border-radius: 0px 0px 4px 0px;
      padding: 4px 10px;
    }
  }

  // 角丸スタイル
  &.radius-head {
    border-radius: 10px;
    .ex-head {
      border-radius: 8px;
      position: absolute;
      top: -1em;
      left: 4%;
      padding: 0.2em 0.6em;
    }
  }

  // 吹き出しスタイル
  &.speech-head {
    padding: 0;
    .ex-head {
      font-size: 1.4rem;
      text-align: center;
      margin: 0;
      position: relative;
      &::after {
        position: absolute;
        content: '';
        top: 100%;
        left: calc( 50% - 14px );
        border: 14px solid transparent;
        width: 0;
        height: 0;
      }
    }
  }

  /**
  * タイトルなしスタイル
  */
  //内側線スタイル
  &.inline-bd {
    padding: 1em;
    border-radius: 5px;
  }

  // 角丸スタイル
  &.radius-bd {
    margin: 1em 0;
    padding: 1em;
    border-radius: 10px;
    border: none;
  }

  // チェック柄
  &.check-bd {
    background-size: 23px 23px;
    border-radius: 5px;
    background-color: #f9ffff;
  }
  // 水玉模様
  &.polka-dot {
    border: none;
    background-image: radial-gradient(#fff 22%, transparent 0), radial-gradient(#fff 22%, transparent 0);
    background-position: 0 0, 10px 10px;
    background-size: 23px 23px;
    border-radius: 10px;
  }

  // 大カッコ
  &.brackets {
    border-radius: 60px/50%;
    border-width: 0 6px;
    background: none;
  }

  // カギカッコ
  &.angle-brackets {
    position: relative;
    border: none;
    background: none;
    &::before, &::after {
	    content:'';
	    width: 50px;
	    height: 50px;
	    position: absolute;
    }
    &::before {
	    top: 0;
	    left: 0;
    }
    &::after {
      bottom: 0;
      right: 0;
    }
  }

  // 黒板風
  &.blackboard {
    background: #104300;
    margin: 1em 0;
    padding: 1em 1em 0 1em;
    border: 8px solid #a60;
    box-shadow: 2px 2px 4px #999, 2px 2px 2px #020 inset;
    color: #fff;
    text-shadow: 0px 0px 2px #fff;
    position: relative;
    &::before, &::after {
	    content:'';
      position: absolute;
      bottom: 0;
      border-radius: 3px 2px 0 2px;
    }
    &::before {
      right: 20%;
      border: solid 3px #fff;
      width: 15px;
    }
    &::after {
      right: 30%;
      border: solid 3px #ffee58;
      width: 20px;
    }
  }


  // インナーブロックのスタイル
  .block-editor-inner-blocks {
    min-height: 32px;
    ul {
      border: none;
    }
  }
}

// インスペクターのスタイル
.exbox-color-setting {
  display: block;
  .components-base-control__field {
    display: flex;
    flex-wrap: wrap;
  }
}

今回はこんな感じの一例にしました
説明するまでもないですが、クラス名に対してセレクターごとにスタイルを変更しており、共通のカラー設定などはmixinで指定しています

今回学んだ事

今更ながら、、registerBlockTypeメソッドのパラメータについて一通り確認することになりました
特にtransformsによってブロックを変換出来る仕様は初めて実装したこともあり新鮮でしたがややこしく、惑わされました 苦笑

またALLOWED_BLOCKSによってInnerBlocksのパラメータを指定し、許可するブロックを設定したことも初めてだったと思います。

今回学んだことは他にもすぐに活かせる基本的な内容であるため、今までのブロックも見直ししようと思います。

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

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

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

CAPTCHA