🔃

【WordPress】リピート可能なカスタムブロックを作る

2024/03/17に公開

実装内容

こんな感じのリピートができるカスタムブロックを作る。

フォルダ構成

themeフォルダ内に下記を作成
---block
|       block_custom.js
|       block_style.css
|       functions-include.php

作成したファイルをカスタムブロックとして登録

functions-include.php
<?php
function add_my_assets_to_block_editor() {
    wp_enqueue_style( 'block-style', get_stylesheet_directory_uri() . '/block/block_style.css' );
    wp_enqueue_script( 'block-custom', get_stylesheet_directory_uri() . '/block/block_custom.js',array( 'wp-blocks', 'wp-dom' ), "", true);
}
add_action( 'enqueue_block_editor_assets', 'add_my_assets_to_block_editor' );

カスタムブロックを作成

HTMLを書いて、ChatGPTに変換してもらうと時短。

<!-- 編集画面に出力したいHTML(editのコード) -->
<div class="faq_list">
  <dl>
    <dt><input type="text" placeholder="質問を入力してください"></dt>
    <dd><textarea placeholder="回答を入力してください"></textarea></dd>
    <button class="remove-faq components-button" style="background-color: rgb(0, 0, 0); color: rgb(255, 255, 255);">削除</button>
  </dl>
  <button class="add-faq components-button" style="background-color: rgb(0, 0, 0); color: rgb(255, 255, 255); margin: auto;">FAQを増やす
    <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" width="24" height="24" ariaHidden="true" focusable="false"><path d="M18 11.2h-5.2V6h-1.6v5.2H6v1.6h5.2V18h1.6v-5.2H18z"></path></svg>
  </button>
</div>

<!-- 投稿に出力したいHTML(saveのコード) -->
<div class="faq_list">
  <dl>
    <dt>質問1</dt>
    <dd>解答1</dd>
  </dl>
</div>

block_custom.jsの中身

長いから3つに分けて記載。

1.宣言部分

1.宣言部分
block_custom.js
const { registerBlockType } = wp.blocks;

registerBlockType("custom-block/faq-section", {
  title: "よくある質問セクション",
  icon: "editor-help",
  category: "design",
  attributes: {
    faqs: {
      type: "array",
      source: "query",
      selector: ".faq_list dl",
      default: [{ question: "", answer: "" }],
      query: {
        question: {
          type: "string",
          source: "html",
          selector: "dl dt",
        },
        answer: {
          type: "string",
          source: "html",
          selector: "dl dd",
        },
      },
    },
  },
  // 2.edit部分
  // 3.save部分
});

公式ドキュメント
https://ja.wordpress.org/team/handbook/block-editor/reference-guides/block-api/block-registration/

icon
ここのやつが使える。
https://developer.wordpress.org/resource/dashicons/#editor-help

category
[ text | media | design | widgets | theme | embed ]が使える。

attributes

  attributes: {
    [任意のattributes名]: {
      type: "array",
      source: "query",// 下記のqueryを参照してバリデーションチェックを行う*
      selector: ".faq_list dl",// 展開する要素名
      default: [{ question: "", answer: "" }],
      query: {
        [任意のquery名]: {
          type: "string",
          source: "html",// htmlを参照してバリデーションチェックを行う*
          selector: "dl dt",// 展開する要素名
        },
      },
    },
  },

保存後にリロードするとエラーになる(コンソールにバリデーションエラーが出る)場合は、ここの設定が原因。

type
[null | boolean | object | array | string | integer | number (integer と同じ)]が使える。
source
[attribute | text | html | query | meta]が使える。
*バリデーションの理解が合っているかわからないから公式ドキュメントをどうぞ。
https://ja.wordpress.org/team/handbook/block-editor/reference-guides/block-api/block-attributes/#値のソース

2.edit部分

2.edit部分
block_custom.js
registerBlockType("custom-block/faq-section", {
  // 1.宣言部分
  edit: function (props) {
    let faqs = props.attributes.faqs;

    function onAddFAQ() {
      let newFAQs = faqs.slice(); // シャローコピー
      newFAQs.push({
        question: "",
        answer: "",
      });
      props.setAttributes({ faqs: newFAQs });
    }

    function onRemoveFAQ(index) {
      let newFAQs = faqs.slice();
      newFAQs.splice(index, 1);
      props.setAttributes({ faqs: newFAQs });
    }

    function onChangeQuestion(newValue, index) {
      let newFAQs = faqs.slice();
      newFAQs[index].question = newValue;
      props.setAttributes({ faqs: newFAQs });
    }

    function onChangeAnswer(newValue, index) {
      let newFAQs = faqs.slice();
      newFAQs[index].answer = newValue;
      props.setAttributes({ faqs: newFAQs });
    }

    return wp.element.createElement(
      "div",
      { className: "faq_list" },
      faqs.map(function (faq, index) {
        return wp.element.createElement(
          "dl",
          { key: index },
          wp.element.createElement(
            "dt",
            null,
            wp.element.createElement("input", {
              type: "text",
              placeholder: "質問を入力してください",
              value: faq.question,
              onChange: function (event) {
                onChangeQuestion(event.target.value, index);
              },
            })
          ),
          wp.element.createElement(
            "dd",
            null,
            wp.element.createElement("textarea", {
              placeholder: "回答を入力してください",
              value: faq.answer,
              onChange: function (event) {
                onChangeAnswer(event.target.value, index);
              },
            })
          ),
          wp.element.createElement(
            "button",
            {
              className: "remove-faq components-button",
              style: {
                backgroundColor: "#000",
                color: "#fff",
              },
              onClick: function () {
                onRemoveFAQ(index);
              },
            },
            "削除"
          )
        );
      }),
      wp.element.createElement(
        "button",
        {
          className: "add-faq components-button",
          style: {
            backgroundColor: "#000",
            color: "#fff",
            margin: "auto",
          },
          onClick: onAddFAQ,
        },
        "FAQを増やす",
        wp.element.createElement(
          "svg",
          {
            xmlns: "http://www.w3.org/2000/svg",
            viewBox: "0 0 24 24",
            width: "24",
            height: "24",
            ariaHidden: "true",
            focusable: "false",
          },
          wp.element.createElement("path", {
            d: "M18 11.2h-5.2V6h-1.6v5.2H6v1.6h5.2V18h1.6v-5.2H18z",
          })
        )
      )
    );
  },
  // 3.save部分
});

(ほとんどGPTが出してくれたから説明できる部分が無い。)
JSXみたいにタグをそのまま書けないのが不便。
SVGがとんでもなく冗長になった。

3.save部分

3.save部分
block_custom.js
registerBlockType("custom-block/faq-section", {
  // 1.宣言部分
  // 2.edit部分
  save: function (props) {
    return wp.element.createElement(
      "div",
      { className: "faq_list" },
      props.attributes.faqs.map(function (faq, index) {
        return wp.element.createElement(
          "dl",
          { key: index},
          wp.element.createElement("dt", null, faq.question),
          wp.element.createElement("dd", null, faq.answer)
        );
      })
    );
  },
});

ここのpropsに値が入ってこないことがある。
エラーが出たら要チェック。

block_style.css

お好みでCSSを書くと編集画面に反映される。
※投稿には反映されない。

今回使用しているCSS
.faq_list {
  display: flex;
  flex-direction: column;
}

.faq_list dl {
  background-color: #fff;
  border-radius: 15px;
}

.faq_list dt,
.faq_list dd {
  display: flex;
  column-gap: min(4vw, 20px);
}

.faq_list dt::before,
.faq_list dd::before {
  margin-top: -0.35em;
  font-size: 24px;
  font-weight: 600;
}

.faq_list dt {
  font-weight: bold;
  color: #db5e1c;
}

.faq_list dt::before {
  content: "Q";
}

.faq_list dd {
  padding-top: 15px;
  margin-top: 15px;
  background-image: repeating-linear-gradient(
      -60deg,
      #393426,
      #393426 5px,
      transparent 5px,
      transparent 10px,
      #393426 10px
    ),
    repeating-linear-gradient(
      90deg,
      #393426,
      #393426 5px,
      transparent 5px,
      transparent 10px,
      #393426 10px
    ),
    repeating-linear-gradient(
      120deg,
      #393426,
      #393426 5px,
      transparent 5px,
      transparent 10px,
      #393426 10px
    ),
    repeating-linear-gradient(
      -90deg,
      #393426,
      #393426 5px,
      transparent 5px,
      transparent 10px,
      #393426 10px
    );
  background-repeat: no-repeat;
  background-position: 0 0, 0 0, 100% 0, 0 100%;
  background-size: 0 100%, 100% 1px, 0 100%, 100% 0;
}

.faq_list dd::before {
  content: "A";
}

.faq_list input,
.faq_list textarea {
  width: 100%;
  min-height: 2rem;
}

カスタムブロックを出力する

投稿の一部であるため、いつもの関数で出力できる。

<?php the_content(); ?>

特定のカスタムブロックのみを出力したい

https://zenn.dev/210neon/articles/603e07bf0a0d1f

Discussion