トップへ戻る
BLOGS

WordPress スライダーブロックの制作 (管理画面編)

WordPress スライダーブロックの制作 (管理画面編)

今回もWordPressのブロック制作です

今回は画像を並べてスライダーを挿入するプラグインを作成していきます

最終的にスライダーの実装にはJavaScriptライブラリの「Swiper」を使用していきます

Swiper は<div>タグなどに指定のクラス名を設定することでスライダーのスタイルや仕様を変更することが可能なので最終的には管理画面のインスペクターの設定などを使用しユーザーの好みのスライダーをブロックとして追加できるようにしたいと思います

※本記事はMacを使用したWordPressのローカル環境であり、Node.jsのインストール環境での開発を想定した記事です

ブロック制作の環境構築

まずはブロック制作の環境構築として、create-blocksを使用します

create-block

「 create-block 」はWordPressがサポートする @wordpress/create-block を使ってビルド環境を構築する方法です。

create-block  @wordpress/scripts を使用し、簡単なコマンドでブロック作成に必要なひな形のファイルを自動生成して環境を構築してくれます。

まずはWordPress環境のプラグインディレクトリにターミナルで移動します

ターミナル

npx @wordpress/create-block   //create-block を対話モードで実行

これでコンピュータとの対話モードが始まるので、それぞれ設定していきます

今回は以下のように設定しました

package.json (抜粋)

{
	"name": "custom-slider",
	"version": "0.1.0",
	"description": "おジャコのスライダーブロックです",
	"author": "ojako",
	"license": "GPL-2.0-or-later",
	"main": "build/index.js",
}

ここからは開発モードをスタートさせてからプログラムを書いていきます

ターミナル

cd custom-slider //作成されたプラグインのディレクトリに移動
        
npm start  //開発モード

そしてWordPressの管理画面に行って「プラグイン」に今作成したプラグインがあるはずなので有効化して簡単なブロックが追加できるはずなので確認してみます

管理画面でブロックを追加すると
「プラグイン名 – hello from the editor!」なる文字列が確認できます

画像を並べられるブロックを登録

まずは複数の画像をアップロードできるブロックを作成します

index.jsを編集する

index.js

import { registerBlockType } from '@wordpress/blocks';
import './style.scss';
import Edit from './edit';

registerBlockType("oja/custom-slider", {
	title: "Oja Custom Slider",
	description: "スライダーを追加します,こちらのパネルからスライダーの各種設定が可能です。",
	category: "common",
	icon: "smiley",
	supports: {
		html: false,
	},
	edit: Edit,
	//save 関数で null を返す
	save: () => {
		return null;
	},
});

今回はPHPでレンダリングを行うダイナミックブロックでの作成となりますそのため、
save.js は削除して saveメソッドではnullを返すようにします

次はPHPです

ブロック登録のPHPファイルを編集

custom-slider.php

<?php
/**
 * Plugin Name:       Oja Custom Slider
 * Description:       おジャコのスライダーブロックです
 * Requires at least: 5.8
 * Requires PHP:      7.0
 * Version:           0.1.0
 * Author:            ojako
 * License:           GPL-2.0-or-later
 * License URI:       https://www.gnu.org/licenses/gpl-2.0.html
 * Text Domain:       custom-slider
 *
 * @package           oja
 */
function oja_slider_block_init() {
  $dir = dirname( __FILE__ );

  $script_asset_path = "$dir/build/index.asset.php";
  if ( ! file_exists( $script_asset_path ) ) {
    throw new Error(
      'You need to run `npm start` or `npm run build` for the "wdl/my-slider" block first.'
    );
  }
  $index_js     = 'build/index.js';
  $script_asset = require( $script_asset_path );
  wp_register_script(
    'oja-slider-script',
    plugins_url( $index_js, __FILE__ ),
    $script_asset['dependencies'],
    $script_asset['version']
  );
 
  $editor_css = 'build/index.css';
  wp_register_style(
    'oja-slider-editor-style',
    plugins_url( $editor_css, __FILE__ ),
    array(),
    filemtime( "$dir/$editor_css" )
  );
 
  $style_css = 'build/style-index.css';
  wp_register_style(
    'oja-slider-style',
    plugins_url( $style_css, __FILE__ ),
    array(),
    filemtime( "$dir/$style_css" )
  );
 
  register_block_type( 'oja/custom-slider', array(
    'editor_script' => 'oja-slider-script',
    'editor_style'  => 'oja-slider-editor-style',
    'style'         => 'oja-slider-style',
    'render_callback' => 'oja_slider_render_func',
    //属性を追加
    'attributes' => [
      //属性 mediaID(メディア ID の配列)
      'mediaID' => [
        'type' => 'array',
        'default' => []
      ],
      //属性 imageUrl(URL の配列)
      'imageUrl' => [
        'type' => 'array',
        'default' => []
      ],
      //属性 imageAlt(alt 属性の配列)
      'imageAlt' => [
        'type' => 'array',
        'default' => []
      ],
      //属性 imageCaption(キャプションの配列)
      'imageCaption' => [
        'type' => 'array',
        'default' => []
      ],
      //ナビゲーションボタンの表示・非表示
      'showNavigationButton' => [
        'type' => 'boolean',
        'default' => true
      ],
      //ページネーションの表示・非表示
      'showPagination' => [
        'type' => 'boolean',
        'default' => true
      ],
      //スクロールバーンの表示・非表示
      'showScrollbar' => [
        'type' => 'boolean',
        'default' => true
      ],
      //キャプションの表示・非表示
      'showCaption' => [
        'type' => 'boolean',
        'default' => true
      ],
    ]
  ) );
}
add_action( 'init', 'oja_slider_block_init' );

register_block_type()でブロックとしての登録を行っています

attributes にたくさん属性を追加してますがスライダーには複数の画像を表示するので、

複数の画像をアップロードして、画像の追加や削除、順番なども入れ替えられるようにする必要があります。

そのため、属性値として以下を定義しておきます

mediaID
mediaのIDとして使用できるため画像の一意の値として使用できます
imageAlt , imageUrl
HTMLの<img>タグでのsrc属性とalt属性になります
imageCaption
画像のキャプション(説明)を挿入する際に使用します
showNavigationButton, showPagination, showScrollbar, showCaption
それぞれInspectorの設定値を保存するために使用します

さて、以上の属性値を使用して次はeditメソッドで管理画面の実装をしたいと思います

edit.js で管理画面の実装

以下のサイトがすごく参考になりました

WordPress Logo WordPress カスタムブロック MediaUpload で複数の画像を挿入

MediaUploadというコンポーネントを使うことで画像をアップロードできるボタンを実装できます

multipleプロパティをtrueにして画像データを配列として扱えるので、その想定で書いていきます

プロパティ説明
allowedTypesメディアライブラリからアップロード・選択できるメディアのタイプを配列で指定。それぞれのメディアタイプは ‘image’, ‘audio’, ‘text’ のような mime type または ‘audio/mpeg’, ‘image/gif’ のような完全な mime type を指定。
multiple複数選択を許可するかどうかの真偽値。デフォルトは false
value選択したメディアの Media ID (数値)。multiple が true の場合は media ID の配列
onCloseメディアモーダルが閉じられたときに呼び出されるコールバック関数。メディアが選択されたときと、ユーザーが選択せずにモーダルを閉じたときに呼び出されます。
onSelectメディアが選択されメディアモーダルが閉じられたとき呼び出されるコールバック関数(onClose の後に呼び出されます)。選択したメディア(オブジェクト)が引数として渡されます。
titleメディアモーダルに表示するタイトル。デフォルトは「Select or Upload Media」
modalClassメディアモーダルのフレームに追加される CSS クラス
addToGallerytrue の場合、ギャラリーメディアモーダルはユーザーが画像を追加できるメディアライブラリで直接開きます。false の場合、ギャラリーメディアモーダルは編集モードで開き、ユーザーは既存の画像を並べ替えたり、削除したり、属性を変更したりして、既存の画像を編集できます。 gallery === trueの場合にのみ適用されます。デフォルトは false
gallerytrue の場合、コンポーネントはギャラリーとして開始します。 デフォルトでは、メディアモーダルはギャラリー編集モードで開きますが、addToGallery を使用して変更できます。デフォルトは false
renderメディアライブラリを開くボタンをレンダリングするために呼び出されるコールバック関数。コールバック関数の引数には、呼び出されるとメディアモーダルを開く open という関数が渡されます。

この中で設定するプロパティの関数を定義していきます

onSelectプロパティ

このコールバック関数は選択した画像(オブジェクト)が引数として渡されるので、
その引数から alt と url 及び id プロパティを抜き出して配列として格納しておきます

そして setAttributes() メソッドを使って設定属性の値を取得した選択値で更新します

edit.js

//選択された画像の情報を更新する関数
const onSelectImage = (media) => {
  // media から map で id プロパティの配列を生成
  const media_ID = media.map((image) => image.id);
  // media から map で url プロパティの配列を生成
  const imageUrl = media.map((image) => image.url);
  // media から map で alt プロパティの配列を生成
  const imageAlt = media.map((image) => image.alt);
  // media から map で caption プロパティの配列を生成
  const imageCaption = media.map((image) => image.caption);

  setAttributes({
    mediaID: media_ID, //メディア ID の配列
    imageUrl: imageUrl, // URL の配列
    imageAlt: imageAlt, // alt 属性の配列
    imageCaption: imageCaption, // キャプションの配列
  });
};

渡されるメディアは配列になっているため map メソッドで一つづつ順に処理してそれぞれの値の配列に組み替えます

render プロパティ

ここはメディアライブラリを開くボタンをレンダリングするコールバック関数で、引数には呼び出されるとメディアモーダルを開く open という関数が渡されます。open はコンポーネント側で定義されている関数です

つまり画像のURLが選択されているか判定し、選択されていれば選択されている画像をレンダリング
その際キャプションが設定されているかどうかも分岐して判定しレンダリングする

そして何も選択されていなければ、画像をアップロードしてもらうためにボタンをレンダリング

どちらにしても引数としてもらったコールバック関数 open をonClickに設定しクリックした際に画像を選択するモーダルを出現するようにする

edit.js

//メディアライブラリを開くボタンをレンダリングする関数(上記関数を使って画像をレンダリング)
const getImageButton = (open) => {
  if (attributes.imageUrl.length > 0) {
    return (
      <div onClick={open} className="slider-block-container">
        {attributes.showCaption ? getImagesWithCaption(attributes.imageUrl, attributes.imageCaption) : getImages(attributes.imageUrl)}
      </div>
    );
  } else {
    return (
      <div className="button-container">
        <Button onClick={open} className="button button-large">
          画像をアップロード
        </Button>
      </div>
    );
  }
};

コードで表すには上記の用に if 文で imageUrl が存在している場合に、<img>タグをレンダリング

imageUrl が存在していない場合にはボタンをレンダリングしてそれぞれクリックした際にコールバック関数openを実行します

また showCaption の値を確認しキャプションが設定されているか判定し三項演算子で新たな関数を出し分けます

getImagesWithCaption(attributes.imageUrl, attributes.imageCaption)
<img>タグと<figcaption>タグでレンダリングします
getImages(attributes.imageUrl)
画像のURLから<img>タグでレンダリングします

<img>と<figcaption>をレンダリング

edit.js

//URL とキャプションの配列から画像をキャプション付きで生成(for 文に変更)
const getImagesWithCaption = (url, caption) => {
  let imagesArray = [];
  for (let i = 0; i < url.length; i++) {
    imagesArray.push(
      <figure>
        <img src={url[i]} className="image" alt="アップロード画像" />
        <figcaption className="block-image-caption">
          {caption[i] ? caption[i] : ""}
        </figcaption>
      </figure>
    );
  }
  return imagesArray;
};

こちらはシンプルに url と caption を受け取って for 文で回しHTMLタグでマークアップしてreturnする関数です

<img>タグをレンダリング

edit.js

//URL の配列から画像を生成
const getImages = (urls) => {
  let imagesArray = urls.map((url) => {
    return <img src={url} className="image" alt="アップロード画像" />;
  });
  return imagesArray;
};

こちらは url だけでよいので map メソッドで url だけを抜き出してマークアップをreturnするようにしています

Inspectorを定義

前回の記事同様にInspectorを出力する関数を定義してreturnメソッド内で実行するようにしたいと思います

edit.js

//インスペクターを追加する関数
import { InspectorControls } from "@wordpress/block-editor";
import { PanelBody, PanelRow, ToggleControl } from "@wordpress/components";
const getInspectorControls = () => {
  return (
    <InspectorControls>
      <PanelBody title="Slider Settings" initialOpen={true}>
        <PanelRow>
          <ToggleControl
            label="ナビゲーションボタン"
            checked={attributes.showNavigationButton}
            onChange={(val) => setAttributes({ showNavigationButton: val })}
          />
        </PanelRow>
        <PanelRow>
          <ToggleControl
            label="ページネーション"
            checked={attributes.showPagination}
            onChange={(val) => setAttributes({ showPagination: val })}
          />
        </PanelRow>
        <PanelRow>
          <ToggleControl
            label="スクロールバー"
            checked={attributes.showScrollbar}
            onChange={(val) => setAttributes({ showScrollbar: val })}
          />
        </PanelRow>
        <PanelRow>
          <ToggleControl
            label="キャプション"
            checked={attributes.showCaption}
            onChange={(val) => setAttributes({ showCaption: val })}
          />
        </PanelRow>
      </PanelBody>
    </InspectorControls>
  );
};

特にむずかしいことは行っておらず、トグルの値をsetAttributesで変更するだけのインスペクターを定義しておきます

画像削除の関数を定義

画像を選択した際に、選択した画像を削除する関数を定義しておきます

edit.js

//画像を削除する(メディアをリセットする)関数
const removeMedia = () => {
  setAttributes({
    mediaID: [],
    imageUrl: [],
    imageAlt: [],
    imageCaption: [],
  });
};

難しいことはしていません
すべての値を空にするだけです

returnメソッドを組み立てる

これまで用意してきた関数などを使用してreturnメソッドを書いていきます

まずは文頭で必要なコンポーネントをインポートします

edit.js

import { MediaUpload, MediaUploadCheck } from "@wordpress/block-editor";
import { Button } from "@wordpress/components";

edit.js

return [
  getInspectorControls(), //インスペクター
  <div className={className}>
    <MediaUploadCheck>
      <MediaUpload
        multiple={true}
        gallery={true}
        onSelect={onSelectImage}
        allowedTypes={["image"]}
        value={attributes.mediaID}
        render={({ open }) => getImageButton(open)}
      />
    </MediaUploadCheck>
    {attributes.imageUrl.length != 0 && ( // imageUrl(配列の長さ)で判定
      <MediaUploadCheck>
        <Button
          onClick={removeMedia}
          isLink
          isDestructive
          className="removeImage"
        >
          画像を削除
        </Button>
      </MediaUploadCheck>
    )}
  </div>,
];

こちらは複数の画像をアップロードすることができるように、MediaUpload コンポーネントの multiple と gallery プロパティを true にします
そうすることで単一の値ではなく配列として画像データを扱うことになります

MediaUpload コンポーネントを使用する際は MediaUploadCheck でラップするのが望ましいようです(権限チェックのため)

もし画像が選択されている場合は<Button>コンポーネントを表示させ、プロパティ isDestructive と isLink を設定して赤い文字色のリンクのように見せています

最後に編集画面のUIとして簡単にスタイルを指定して見栄えを良くしておきます

editor.scss

@charset 'utf-8';

.wp-block-oja-custom-slider .slider-block-container {
  display: flex;
  flex-wrap: wrap;
  margin: 20px;
}
 
.wp-block-oja-custom-slider .slider-block-container img {
  width: 100%;
  max-width: 160px;
  margin: 10px;
}
 
.image {
  cursor: pointer;
}
 
.wp-block-oja-custom-slider figcaption.block-image-caption {
  text-align: center;
  margin-top: 0;
}

長くなってきたため今回は管理画面の完成をもって一区切りとします

続きの記事もすぐにアップする予定です

お疲れさまでした

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

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

CAPTCHA