WordPress スライダーを表示するブロックの作成
WordPress のブロックエディタ Gutenberg でスライダーのプラグイン Swiper を使ってスライダーを表示するカスタムブロックの作り方についての覚書です。
以下は(旧)ブロックの作成 チュートリアル(削除予定)などの古い情報を参考にしているため部分的に古い情報になっています。
最新のブロック開発入門(2024/12):WordPress 初めてのブロック開発
更新日:2024年12月13日
作成日:2020年10月11日
ブロックの基本的な作成方法や環境の構築方法については以下を御覧ください。
- 関連ページ
Node.js がインストールされていて、WordPress のローカル環境があることを前提にしています。
- [2020年10月18日] 属性の設定など内容を一部を変更しました。
- [2020年10月21日] プレビューボタンの追加での useEffect フックで第2パラメータを指定するように変更し、自動再生オプションなどを設定する方法を追加しました。
概要
スライダープラグイン Swiper を使って、MediaUpload コンポーネントで選択した複数の画像をスライダーで表示するカスタムブロックを作成します。
create-block を使って環境構築及び初期ファイルを作成し、まず MediaUpload コンポーネントを使って複数の画像を挿入できるブロックを作成します。
そして Swiper のファイルを読み込み、save 関数でレンダリングをスライダー用に変更してスライダーで表示できるようにします。
ファイルのセットアップ
ブロックの作成に必要なファイルとスライダープラグイン Swiper のファイルを用意します。
ブロックの作成では JSX を使用するので JSX をコンパイルする環境が必要ですが、create-block を使用すると簡単に環境の構築と初期ファイルが作成できます。
関連ページ:Gutenberg ブロック開発の環境構築
この例ではプラグインとしてブロックを作成するので、ターミナルでプラグインディレクトリ wp-content/plugins に移動します。パスは環境に合わせて指定します。
$ cd /path/to/wp-content/plugins return //プラグインディレクトリへ移動
続いて、create-block のコマンドを実行して環境の構築と初期ファイルを生成します。
以下は、--namespace オプションで名前空間(namespace)を wdl に指定し、slug(作成するプラグインのフォルダ名)に my-slider を指定して create-block のコマンドを実行しています。
この例では slug に my-slider を指定してコマンドを実行したので、my-slider というフォルダがプラグインディレクトリ(wp-content/plugins )に作成され、その中に以下のようなファイルが出力されます。
my-slider //作成されたプラグインのディレクトリ
├── block.json
├── build //ビルドで出力されるファイル(本番環境で使用するファイル)のディレクトリ
│ ├── index.asset.php
│ ├── index.css
│ ├── index.js
│ └── style-index.css
├── node_modules
├── my-slider.php //PHP 側でブロックを登録するプラグインファイル
├── package-lock.json
├── package.json
├── readme.txt
└── src //開発用ディレクトリ(この中のファイルを編集)
├── edit.js //edit 関数を記述するファイル
├── editor.scss //エディター用スタイル
├── index.js //ブロック用スクリプト(エントリーポイント)
├── save.js //save 関数を記述するファイル
└── style.scss //フロントエンド及びエディターに適用するスタイル
開発モード
ターミナルで作成されたプラグインのディレクトリ(この例では my-slider)に移動して npm start を実行して開発モードで作業をしていきます。
開発モードの場合、ファイルの変更が自動的に検知され、ビルドが実行されるので、毎回ビルドコマンドを実行する必要がありません。何らかの理由でビルドが失敗するとターミナル及びコンソールにエラーが表示されます。
開発モードを終了するには control + c を押します。
$ cd my-slider //作成されたプラグインのディレクトリに移動 $ npm start //開発モード
開発が完了して procuction ビルドするには npm run build を実行します。
MediaUpload を使ってブロックを作成
生成されたファイルを使って複数の画像をアップロードできるブロックを作成します。
スライダーには複数の画像を表示するので、複数の画像をアップロードして、画像の追加や削除、順番なども入れ替えられるようにする必要があります。
そのためには MediaUpload コンポーネントの multiple と gallery プロパティを true にします。
MediaUpload コンポーネントを使って複数の画像を挿入する方法の詳細については以下を御覧ください。
以下がブロックのコードです。コードの詳細は上記リンクを参照ください。
以下はエントリーポイントのファイルで、edit 関数と save 関数はインポートしています。
import { registerBlockType } from '@wordpress/blocks';
import './style.scss';
import Edit from './edit';
import save from './save';
registerBlockType( 'wdl/my-slider', {
title: 'My Slider',
description: 'Example block written with ESNext standard and JSX support',
category: 'widgets',
icon: 'smiley',
supports: {
html: false,
},
//属性を設定
attributes: {
//属性 mediaID(メディア ID の配列)
mediaID: {
type: 'array',
default: []
},
//img の src に指定する URL
imageUrl: {
type: 'array',
default: []
},
//img の alt 属性の値
imageAlt: {
type: 'array',
default: []
},
},
edit: Edit,
save,
} );
以下は edit 関数が記述された Edit コンポーネントの JavaScript ファイルです。
import { MediaUpload, MediaUploadCheck } from '@wordpress/block-editor';
import { Button } from '@wordpress/components';
import './editor.scss';
export default function Edit( props ) {
//分割代入を使って props 経由でプロパティを変数に代入
const { className, attributes, setAttributes} = props;
//選択された画像の情報を更新する関数(media は画像オブジェクトの配列)
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);
setAttributes( {
mediaID: media_ID, //メディア ID の配列
imageUrl: imageUrl, // URL の配列
imageAlt: imageAlt, // alt 属性の配列
} );
};
//URL の配列から画像を生成
const getImages = ( urls ) => {
let imagesArray = urls.map(( url ) => {
return (
<img
src={ url }
className="image"
alt="アップロード画像"
/>
);
});
return imagesArray;
}
//メディアライブラリを開くボタンをレンダリングする関数(上記関数を使って画像をレンダリング)
const getImageButton = (open) => {
if(attributes.imageUrl.length > 0 ) {
return (
<div onClick={ open } className="block-container">
{ getImages( attributes.imageUrl ) }
</div>
)
}
else {
return (
<div className="button-container">
<Button
onClick={ open }
className="button button-large"
>
画像をアップロード
</Button>
</div>
);
}
}
//画像を削除する(メディアをリセットする)関数
const removeMedia = () => {
setAttributes({
mediaID: [],
imageUrl: [],
imageAlt: [],
});
}
return (
<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>
);
}
以下は save 関数が記述された JavaScript ファイルです。
import { Fragment } from '@wordpress/element';
export default function save( { attributes } ) {
//画像をレンダリングする関数
const getImagesSave = ( url, alt ) => {
let image_elem;
let imagesArray = [];
for( let i = 0 ; i < url.length; i ++ ) {
if( url.length === 0 ) {
image_elem = null;
}else{
if( alt[i] ) {
image_elem = (
<img
className="card_image"
src={ url[i] }
alt={ alt[i] }
/>
);
}else{
image_elem = (
<img
className="card_image"
src={ url[i] }
alt=""
aria-hidden="true"
/>
);
}
}
imagesArray.push( image_elem ) ;
}
return imagesArray;
}
return (
<div className="block-container">
{ getImagesSave( attributes.imageUrl, attributes.imageAlt ) }
</div>
);
}
以下はフロントエンド及びエディターのスタイルを設定する Sass ファイルです。この時点では挿入した画像は最大幅を設定し Flexbox で表示しています(後で変更)。
.wp-block-wdl-my-slider {
background-color: #fefefe;
color: #666;
padding: 10px;
border: 1px solid #ccc;
}
.block-container {
display: flex;
justify-content: center;
flex-wrap: wrap;
}
.block-container img {
width: 100%;
max-width: 160px;
margin: 10px;
}
以下はエディターのスタイルを設定する Sass ファイルです。
.image {
cursor: pointer;
}
プラグインの有効化(確認)
プラグインページでブロックのプラグインを有効化して確認します。
投稿にブロックを挿入するとエディタ側では以下のように表示されます。
「画像をアップロード」をクリックするとモーダルが表示され、画像を選択することができます。
「ギャラリーを編集」では画像を追加したり、順番を並べ替えることができます。選択した画像をブロックに挿入するには「ギャラリーを挿入」をクリックします。
編集画面に挿入された画像は画像をクリックすれば追加や編集ができます。「画像を削除」をクリックすると全ての画像が削除されます。
投稿を保存すると、フロントエンド側では以下のような表示になります。
スライダープラグイン Swiper
この例では、スライダーの表示には Swiper という JavaScript のプラグインを使用します。
Swiper の使い方の詳細は以下を御覧ください。
この例では Swiper のダウンロードページの swiper-bundle.min.js と swiper-bundle.min.css をコピーして使用しています。この時点の最新版は 6.3.2 ですが、右上のセレクトメニューでバージョンを指定することもできます。
| ファイル | ソースコード | タイプ | この例でのファイル名 |
|---|---|---|---|
| swiper-bundle.min.js | RAW | JavaScript | swiper.js |
| swiper-bundle.min.css | RAW | CSS | swiper.css |
ブロックのプラグインのフォルダ(my-slider)の中に assets というフォルダを作成し、その中にコピーしたファイルとスライダーを初期化するための JavaScript ファイル(init-swiper.js)を保存します。
ファイル名は上記表に記載されている「この例でのファイル名」に変更しています。
Swiper を初期化するためのファイル
以下は Swiper を初期化するためのファイル init-swiper.js の例です。
let mySwiper = new Swiper ('.swiper-container', {
//スライダーを自動再生する場合は以下のコメントを外す
/*autoplay: {
delay: 4000, //4秒間隔でスライドを自動的に実行
},*/
//最後に達したら先頭に戻る
loop: true,
//ページネーション表示の設定
pagination: {
el: '.swiper-pagination', //ページネーションの要素
type: 'bullets', //ページネーションの種類
clickable: true, //クリックに反応させる
},
//ナビゲーションボタン(矢印)表示の設定
navigation: {
nextEl: '.swiper-button-next', //「次へボタン」要素の指定
prevEl: '.swiper-button-prev', //「前へボタン」要素の指定
},
//スクロールバー表示の設定
scrollbar: {
el: '.swiper-scrollbar', //要素の指定
},
})
以下は Swiper のサイトからコピーした JavaScript と CSS ファイルの冒頭部分です。
/**
* Swiper 6.3.2
* Most modern mobile touch slider and framework with hardware accelerated transitions
* http://swiperjs.com
*
* Copyright 2014-2020 Vladimir Kharlampidi
*
* Released under the MIT License
*
* Released on: September 28, 2020
*/
!function(e,t){"object"==typeof ・・・以下省略・・・
@charset "UTF-8"; /* 追加 */
/**
* Swiper 6.3.2
* Most modern mobile touch slider and framework with hardware accelerated transitions
* http://swiperjs.com
*
* Copyright 2014-2020 Vladimir Kharlampidi
*
* Released under the MIT License
*
* Released on: September 28, 2020
*/
@font-face{font-family:swiper-icons; ・・・以下省略・・・
スライダーのスクリプトの読み込み
assets フォルダに保存したスライダーのスクリプトとスタイルを PHP でブロックを登録するファイル(my-slider.php)で wp_enqueue_script と wp_enqueue_style を使って、enqueue_block_assets アクションで読み込みます。
以下を my-slider.php に追加します。
この例の場合、エディター側ではスライダーは表示しないので ! is_admin() で判定してフロントエンド側でのみ読み込むようにします。
function add_my_slider_scripts_and_styles() {
$dir = dirname( __FILE__ );
//管理画面以外(フロントエンド側でのみ読み込む)
if(! is_admin()) {
//Swiper の JavaScript ファイルの読み込み(エンキュー)
wp_enqueue_script(
'swiper-slider',
plugins_url( '/assets/swiper.js', __FILE__ ),
array(),
filemtime( "$dir/assets/swiper.js" ),
true
);
//Swiper を初期化するためのファイルの読み込み(エンキュー)
wp_enqueue_script(
'swiper-slider-init',
plugins_url( '/assets/init-swiper.js', __FILE__ ),
//依存ファイルに上記 Swiper の JavaScript を指定
array('swiper-slider'),
filemtime( "$dir/assets/init-swiper.js" ),
true
);
//Swiper の CSS ファイルの読み込み(エンキュー)
wp_enqueue_style(
'swipe-style',
plugins_url( '/assets/swiper.css', __FILE__ ),
array(),
filemtime( "$dir/assets/swiper.css" )
);
}
}
add_action('enqueue_block_assets', 'add_my_slider_scripts_and_styles');
<?php
/**
* Plugin Name: My Slider
* Description: Example block written with ESNext standard and JSX support – build step required.
* Version: 0.1.0
* Author: The WordPress Contributors
* License: GPL-2.0-or-later
* License URI: https://www.gnu.org/licenses/gpl-2.0.html
* Text Domain: my-slider
*
* @package wdl
*/
function wdl_my_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(
'wdl-my-slider-block-editor',
plugins_url( $index_js, __FILE__ ),
$script_asset['dependencies'],
$script_asset['version']
);
$editor_css = 'build/index.css';
wp_register_style(
'wdl-my-slider-block-editor',
plugins_url( $editor_css, __FILE__ ),
array(),
filemtime( "$dir/$editor_css" )
);
$style_css = 'build/style-index.css';
wp_register_style(
'wdl-my-slider-block',
plugins_url( $style_css, __FILE__ ),
array(),
filemtime( "$dir/$style_css" )
);
register_block_type( 'wdl/my-slider', array(
'editor_script' => 'wdl-my-slider-block-editor',
'editor_style' => 'wdl-my-slider-block-editor',
'style' => 'wdl-my-slider-block',
) );
}
add_action( 'init', 'wdl_my_slider_block_init' );
function add_my_slider_scripts_and_styles() {
$dir = dirname( __FILE__ );
//Swiper の JavaScript ファイルの読み込み(エンキュー)
wp_enqueue_script(
'swiper-slider',
plugins_url( '/assets/swiper.js', __FILE__ ),
array(),
filemtime( "$dir/assets/swiper.js" ),
true
);
//Swiper を初期化するためのファイルの読み込み(エンキュー)
wp_enqueue_script(
'swiper-slider-init',
plugins_url( '/assets/init-swiper.js', __FILE__ ),
//依存ファイルに上記 Swiper の JavaScript を指定
array('swiper-slider'),
filemtime( "$dir/assets/init-swiper.js" ),
true
);
//Swiper の CSS ファイルの読み込み(エンキュー)
wp_enqueue_style(
'swipe-style',
plugins_url( '/assets/swiper.css', __FILE__ ),
array(),
filemtime( "$dir/assets/swiper.css" )
);
}
add_action('enqueue_block_assets', 'add_my_slider_scripts_and_styles');
Swiper のマークアップ
以下は Swiper でスライダーを表示するための基本的なマークアップの例です。
save 関数でレンダリングする際に、以下の構造を記述すればスライダーが表示されます。
<!-- スライダーのメインのコンテナー -->
<div class="swiper-container">
<!-- スライダーのラッパー -->
<div class="swiper-wrapper">
<!-- スライド .swiper-slide の中に画像を配置 -->
<div class="swiper-slide"><img src="images/sample_01.jpg" alt=""></div>
<div class="swiper-slide"><img src="images/sample_02.jpg" alt=""></div>
<div class="swiper-slide"><img src="images/sample_03.jpg" alt=""></div>
</div>
<!-- ページネーションの表示 -->
<div class="swiper-pagination"></div>
<!-- ナビゲーションボタンの表示 -->
<div class="swiper-button-prev"></div>
<div class="swiper-button-next"></div>
<!-- スクロールバーの表示 -->
<div class="swiper-scrollbar"></div>
</div>
save 関数
save 関数(save.js)を Swiper のマークアップで書き換えます。
画像をレンダリングする関数 getImagesSave() では Swiper のマークアップに合わせて img をレンダリングする際に、swiper-slide クラスを指定した div でラップします。
そして return ステートメントでは Swiper の構造でマークアップします。但し、class は className とする必要があります。
export default function save( { attributes } ) {
//画像をレンダリングする関数
const getImagesSave = ( url, alt ) => {
let image_elem;
let imagesArray = [];
for( let i = 0 ; i < url.length; i ++ ) {
if( url.length === 0 ) {
image_elem = null;
}else{
if( alt[i] ) {
image_elem = (
<div className="swiper-slide">
<img
className="card_image"
src={ url[i] }
alt={ alt[i] }
/>
</div>
);
}else{
image_elem = (
<div className="swiper-slide">
<img
className="card_image"
src={ url[i] }
alt=""
aria-hidden="true"
/>
</div>
);
}
}
imagesArray.push( image_elem ) ;
}
return imagesArray;
}
return (
<div className="slider-container">
<div className="swiper-container">
<div className="swiper-wrapper">
{ getImagesSave( attributes.imageUrl, attributes.imageAlt ) }
</div>
<div className="swiper-pagination"></div>
<div className="swiper-button-prev"></div>
<div className="swiper-button-next"></div>
<div className="swiper-scrollbar"></div>
</div>
</div>
);
}
スタイルを設定
今まで設定していたスタイルは、複数の画像を Flexbox で表示するためのものだったので、フロントエンド側ではスライダーを表示するように変更します。
style.scss は以下のように Flexbox の設定を削除します。.wp-block-wdl-my-slider は 自動的にブロックに付与されるクラス です。
.wp-block-wdl-my-slider {
background-color: #fefefe;
color: #666;
padding: 10px;
border: 1px solid #ccc;
}
また、ナビゲーションボタンやページネーションの色を設定するには以下を追加します。以下はナビゲーションボタンとページネーションの色を白(#fff)にする例です。
:root {
--swiper-navigation-color: #fff;
--swiper-pagination-color: #fff;
}
editor.scss ではエディタ側では画像を Flexbox で表示するようにします。
.wp-block-wdl-my-slider .slider-block-container {
display: flex;
flex-wrap: wrap;
}
.wp-block-wdl-my-slider .slider-block-container img {
width: 100%;
max-width: 160px;
margin: 10px;
}
.image {
cursor: pointer;
}
これでフロントエンド側ではスライダーが表示されるようになります。
妥当性検証プロセス
save 関数を書き換えてビルドして、投稿のページで再読込すると「このブロックには、想定されていないか無効なコンテンツが含まれています」と表示され、コンソールにはエラーが表示されます。
これは、エディターが現在定義しているものとは異なる save 関数の出力を検出するために発生しますが、表示されるボタンをクリックして「ブロックを削除」を選択し、現在のブロックを一度削除してから再度ブロックを挿入すれば問題ありません。
再度ブロックを挿入して、画像をブロックに挿入して投稿を保存するとフロントエンド側ではスライダーが表示されます。以下は style.scss でビゲーションボタンとページネーションの色を変更してあります。
エディター側の表示は変更はありません。
画像をクリックして、スライダーに表示する画像をギャラリー機能を使って追加・削除したり、順番を並べ替えたりすることができます。
スライダーのオプションの追加
インスペクターにトグルボタンなどを配置して、スライダーのナビゲーションボタンやページネーションなどの表示・非表示を制御することができます。
以下は、スライダー Swiper のナビゲーションボタン、ページネーション、スクロールバーの表示・非表示を切り替えるトグルボタンをインスペクターに表示する例です。
attributes の追加
ナビゲーションボタン、ページネーション、スクロールバーの表示・非表示の状態を保持する属性を registerBlockType に追加します。
registerBlockType( 'wdl/my-slider', {
title: 'My Slider',
・・・中略・・・
attributes: {
・・・中略・・・
//ナビゲーションボタンの表示・非表示
showNavigationButton: {
type: 'boolean',
default: true
},
//ページネーションの表示・非表示
showPagination: {
type: 'boolean',
default: true
},
//ページネーションの表示・非表示
showScrollbar: {
type: 'boolean',
default: true
}
},
edit: Edit,
save,
} );
インスペクターの追加
Edit コンポーネント(edit.js)にインスペクターの記述を追加します。
インスペクターの設定の詳細は「インスペクター」を御覧ください。
この例ではインスペクターを追加する関数を作成してナビゲーションボタン、ページネーション、スクロールバーの表示・非表示を選択するトグルボタンを設置します。
InspectorControls、PanelBody、PanelRow、ToggleControl コンポーネントをインポートします。
import { InspectorControls } from '@wordpress/block-editor';
import { PanelBody, PanelRow, ToggleControl } from '@wordpress/components';
ToggleControl コンポーネントの onChange プロパティのコールバック関数は checked の値(真偽値)を引数に取るので、選択状態が変更されたら setAttributes を使って属性の値を更新します。
//インスペクターを追加する関数
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>
</PanelBody>
</InspectorControls>
);
}
return ステートメントの書き換え
return ステートメントでは配列を使ってインスペクターと MediaUpload コンポーネントをレンダリングします。
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>
]
);
import { MediaUpload, MediaUploadCheck } from '@wordpress/block-editor';
import { Button } from '@wordpress/components';
import { InspectorControls } from '@wordpress/block-editor';
import { PanelBody, PanelRow, ToggleControl } from '@wordpress/components';
import './editor.scss';
export default function Edit( props ) {
//分割代入を使って props 経由でプロパティを変数に代入
const { className, attributes, setAttributes} = props;
//選択された画像の情報を更新する関数
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);
setAttributes( {
mediaID: media_ID, //メディア ID の配列
imageUrl: imageUrl, // URL の配列
imageAlt: imageAlt, // alt 属性の配列
} );
};
//URL の配列から画像を生成
const getImages = ( urls ) => {
let imagesArray = urls.map(( url ) => {
return (
<img
src={ url }
className="image"
alt="アップロード画像"
/>
);
});
return imagesArray;
}
//メディアライブラリを開くボタンをレンダリングする関数(上記関数を使って画像をレンダリング)
const getImageButton = (open) => {
if(attributes.imageUrl.length > 0 ) {
return (
<div onClick={ open } className="block-container">
{ getImages( attributes.imageUrl ) }
</div>
)
}
else {
return (
<div className="button-container">
<Button
onClick={ open }
className="button button-large"
>
画像をアップロード
</Button>
</div>
);
}
}
//画像を削除する(メディアをリセットする)関数
const removeMedia = () => {
setAttributes({
mediaID: [],
imageUrl: [],
imageAlt: [],
});
}
//インスペクターを追加する関数
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>
</PanelBody>
</InspectorControls>
);
}
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>
]
);
}
save 関数の書き換え
save 関数ではトグルボタンの状態(属性の値)により、return ステートメントでナビゲーションボタンなどをレンダリングするかどうかを判定します。
それぞれの属性の値は真偽値なので、以下のようにその値が true の場合にレンダリングするようにします。
return (
<div className="slider-container">
<div className="swiper-container">
<div className="swiper-wrapper">
{ getImagesSave( attributes.imageUrl, attributes.imageAlt ) }
</div>
{ attributes.showPagination &&
<div className="swiper-pagination"></div>
}
{ attributes.showNavigationButton &&
<Fragment>
<div className="swiper-button-prev"></div>
<div className="swiper-button-next"></div>
</Fragment>
}
{ attributes.showScrollbar &&
<div className="swiper-scrollbar"></div>
}
</div>
</div>
);
上記 12〜13行目の2つの div 要素は Fragment でラップする必要があるので、このファイルの先頭でインポートします。
import { Fragment } from '@wordpress/element';
import { Fragment } from '@wordpress/element';
export default function save( { attributes } ) {
//画像をレンダリングする関数
const getImagesSave = ( url, alt ) => {
let image_elem;
let imagesArray = [];
for( let i = 0 ; i < url.length; i ++ ) {
if( url.length === 0 ) {
image_elem = null;
}else{
if( alt[i] ) {
image_elem = (
<div className="swiper-slide">
<img
className="card_image"
src={ url[i] }
alt={ alt[i] }
/>
</div>
);
}else{
image_elem = (
<div className="swiper-slide">
<img
className="card_image"
src={ url[i] }
alt=""
aria-hidden="true"
/>
</div>
);
}
}
imagesArray.push( image_elem ) ;
}
return imagesArray;
}
return (
<div className="slider-container">
<div className="swiper-container">
<div className="swiper-wrapper">
{ getImagesSave( attributes.imageUrl, attributes.imageAlt ) }
</div>
{ attributes.showPagination &&
<div className="swiper-pagination"></div>
}
{ attributes.showNavigationButton &&
<Fragment>
<div className="swiper-button-prev"></div>
<div className="swiper-button-next"></div>
</Fragment>
}
{ attributes.showScrollbar &&
<div className="swiper-scrollbar"></div>
}
</div>
</div>
);
}
これでエディター画面のインスペクターのグルボタンでナビゲーションボタンなどの表示・非表示を切り替えることができます。
キャプションの追加
画像にキャプションが設定されていれば、スライダーにキャプションを表示する例です。
画像のキャプションの値を保存する属性 imageCaption を registerBlockType に追加します。また、キャプションの表示・非表示もインスペクターのトグルボタンで切り替えられるようにするので属性 showCaption も追加します。
//img の キャプションの値
imageCaption: {
type: 'array',
default: []
},
//キャプションの表示・非表示
showCaption: {
type: 'boolean',
default: true
},
import { registerBlockType } from '@wordpress/blocks';
import './style.scss';
import Edit from './edit';
import save from './save';
registerBlockType( 'wdl/my-slider', {
title: 'My Slider',
description: 'Example block written with ESNext standard and JSX support',
category: 'widgets',
icon: 'smiley',
supports: {
html: false,
},
//属性を設定
attributes: {
//属性 mediaID(メディア ID の配列)
mediaID: {
type: 'array',
default: []
},
//img の src に指定する URL
imageUrl: {
type: 'array',
default: []
},
//img の alt 属性の値
imageAlt: {
type: 'array',
default: []
},
//ナビゲーションボタンの表示・非表示
showNavigationButton: {
type: 'boolean',
default: true
},
//ページネーションの表示・非表示
showPagination: {
type: 'boolean',
default: true
},
//ページネーションの表示・非表示
showScrollbar: {
type: 'boolean',
default: true
},
//img の キャプションの値
imageCaption: {
type: 'array',
default: []
},
//キャプションの表示・非表示
showCaption: {
type: 'boolean',
default: true
},
},
edit: Edit,
save,
} );
Edit コンポーネント(edit.js)で属性 imageCaption の値を更新する記述を追加します。
//選択された画像の情報を更新する関数
const onSelectImage = ( media ) => {
const media_ID = media.map((image) => image.id);
const imageUrl = media.map((image) => image.url);
const imageAlt = media.map((image) => image.alt);
// media から map で caption プロパティの配列を生成
const imageCaption = media.map((image) => image.caption);
setAttributes( {
mediaID: media_ID,
imageUrl: imageUrl,
imageAlt: imageAlt,
imageCaption: imageCaption, // キャプションの配列
} );
};
インスペクターをレンダリングする関数にキャプションのトグルボタンを追加します。
//インスペクターを追加する関数
const getInspectorControls = () => {
return (
<InspectorControls>
<PanelBody
title='Slider Settings'
initialOpen={true}
>
・・・中略・・・
<PanelRow>
<ToggleControl
label="キャプション"
checked={attributes.showCaption}
onChange={(val) => setAttributes({ showCaption: val })}
/>
</PanelRow>
</PanelBody>
</InspectorControls>
);
}
figure と figcaption を使って画像とキャプションを生成する関数 getImagesWithCaption() を追加します。
//URL とキャプションの配列から画像をキャプション付きで生成
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;
}
メディアライブラリを開くボタンをレンダリングする関数 getImageButton() の画像をレンダリングする部分を、画像のみかキャプション付きの画像をレンダリングするかを属性 showCaption の値で判定して切り替えます。
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>
);
}
};
画像を削除する(メディアをリセットする)関数にキャプションの属性 imageCaption をリセットする記述を追加します。
//画像を削除する(メディアをリセットする)関数
const removeMedia = () => {
setAttributes({
mediaID: [],
imageUrl: [],
imageAlt: [],
imageCaption: [],
});
}
import { MediaUpload, MediaUploadCheck } from '@wordpress/block-editor';
import { Button } from '@wordpress/components';
import { InspectorControls } from '@wordpress/block-editor';
import { PanelBody, PanelRow, ToggleControl } from '@wordpress/components';
import './editor.scss';
export default function Edit( props ) {
//分割代入を使って props 経由でプロパティを変数に代入
const { className, attributes, setAttributes} = props;
//選択された画像の情報を更新する関数
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, // キャプションの配列
} );
};
//URL の配列から画像を生成
const getImages = ( urls ) => {
let imagesArray = urls.map(( url ) => {
return (
<img
src={ url }
className="image"
alt="アップロード画像"
/>
);
});
return imagesArray;
}
//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;
}
//メディアライブラリを開くボタンをレンダリングする関数(上記関数を使って画像をレンダリング)
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>
);
}
};
//画像を削除する(メディアをリセットする)関数
const removeMedia = () => {
setAttributes({
mediaID: [],
imageUrl: [],
imageAlt: [],
imageCaption: [],
});
}
//インスペクターを追加する関数
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>
);
}
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>
]
);
}
save 関数の画像をレンダリングする関数 getImagesSave() で属性 showCaption の値が true ならキャプションのレンダリングを追加します。
return ステートメントの getImagesSave() の引数にキャプションの属性(attributes.imageCaption)を追加します。
import { Fragment } from '@wordpress/element';
export default function save( { attributes } ) {
//画像をレンダリングする関数
const getImagesSave = ( url, alt, caption ) => {
let image_elem;
let imagesArray = [];
for( let i = 0 ; i < url.length; i ++ ) {
if( url.length === 0 ) {
image_elem = null;
}else{
if( alt[i] ) {
image_elem = (
<div className="swiper-slide">
<img
className="card_image"
src={ url[i] }
alt={ alt[i] }
/>
{ attributes.showCaption &&
<div class="caption">{ caption[i] ? caption[i] : "" }</div>
}
</div>
);
}else{
image_elem = (
<div className="swiper-slide">
<img
className="card_image"
src={ url[i] }
alt=""
aria-hidden="true"
/>
{ attributes.showCaption &&
<div class="caption">{ caption[i] ? caption[i] : "" }</div>
}
</div>
);
}
}
imagesArray.push( image_elem ) ;
}
return imagesArray;
}
return (
<div className="slider-container">
<div className="swiper-container">
<div className="swiper-wrapper">
{ getImagesSave( attributes.imageUrl, attributes.imageAlt, attributes.imageCaption ) }
</div>
{ attributes.showPagination &&
<div className="swiper-pagination"></div>
}
{ attributes.showNavigationButton &&
<Fragment>
<div className="swiper-button-prev"></div>
<div className="swiper-button-next"></div>
</Fragment>
}
{ attributes.showScrollbar &&
<div className="swiper-scrollbar"></div>
}
</div>
</div>
);
}
キャプションをオンにした場合の表示
キャプションをオフにした場合の表示
エディター画面のインスペクターでキャプションをオンにするとフロントエンド側ではキャプションを表示します。キャプションの位置などのスタイルは style.scss で設定することができます。
PHP でレンダリング
以下はブロックをダイナミックブロックに変更して PHP でレンダリングする例です。
PHP でレンダリングする場合、PHP 側の出力(マークアップ)を変更しても妥当性検証プロセスは行われないので、マークアップを変更する際に発生するブロックを一度削除してから再度ブロックを挿入しなければならない問題を回避できます。
そのため、例えばブロックにタイトルを追加すれば、既存のブロック全てにタイトルが追加されます。また、その際、JavaScript 側の再コンパイルは不要です。
edit.js には変更はありません。
関連ページ:ダイナミックブロックの作り方
save 関数で null を返す
ダイナミックブロックでは save 関数が null を返すようにします。
registerBlockType( 'wdl/my-slider', {
title: 'My Slider',
description: 'Example block written with ESNext standard and JSX support',
category: 'widgets',
icon: 'smiley',
supports: {
html: false,
},
attributes: {
・・・中略・・・
},
edit: Edit,
//save 関数で null を返す
save: () => { return null }
} );
import { registerBlockType } from '@wordpress/blocks';
import './style.scss';
import Edit from './edit';
import save from './save';
registerBlockType( 'wdl/my-slider', {
title: 'My Slider',
description: 'Example block written with ESNext standard and JSX support',
category: 'widgets',
icon: 'smiley',
supports: {
html: false,
},
//属性を設定
attributes: {
//属性 mediaID(メディア ID の配列)
mediaID: {
type: 'array',
default: []
},
//img の src に指定する URL
imageUrl: {
type: 'array',
default: []
},
//img の alt 属性の値
imageAlt: {
type: 'array',
default: []
},
//ナビゲーションボタンの表示・非表示
showNavigationButton: {
type: 'boolean',
default: true
},
//ページネーションの表示・非表示
showPagination: {
type: 'boolean',
default: true
},
//ページネーションの表示・非表示
showScrollbar: {
type: 'boolean',
default: true
},
//img の キャプションの値
imageCaption: {
type: 'array',
default: []
},
//キャプションの表示・非表示
showCaption: {
type: 'boolean',
default: true
},
},
edit: Edit,
//save 関数で null を返す
save: () => { return null }
} );
register_block_type に attributes を追加
register_block_type 関数の第2パラメータの配列に attributes キーを追加して、ブロックから抽出したいすべての属性を設定することができます。register_block_type に attributes を設定する場合は、オブジェクトではなく連想配列で設定する必要があります。
register_block_type( 'wdl/my-slider', array(
'editor_script' => 'wdl-my-slider-block-editor',
'editor_style' => 'wdl-my-slider-block-editor',
'style' => 'wdl-my-slider-block',
//属性を追加
'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
],
]
) );
render_callback を設定
ブロックの出力を PHP でレンダリングするには、register_block_type 関数の第2パラメータの配列に render_callback キーを追加し、値にコールバック関数(PHP でレンダリングする関数)を指定します。
register_block_type( 'wdl/my-slider', array(
'editor_script' => 'wdl-my-slider-block-editor',
'editor_style' => 'wdl-my-slider-block-editor',
'style' => 'wdl-my-slider-block',
//render_callback を追加
'render_callback' => 'my_slider_render',
'attributes' => [
'mediaID' => [
'type' => 'array',
'default' => []
],
・・・中略・・・
]
) );
PHP でスライダーをレンダリングする関数を定義します。
以下はスライダーを PHP でレンダリングするコールバック関数の例です。属性の showCaption が true でキャプションが設定されていればキャプションも表示するようにしています。
render_callback の関数はブロックの出力を return する必要があるので、文字列(HTML)を作成して最後に return します。
外側の div 要素には save 関数では自動的にブロックに付与されるクラス(この例の場合は wp-block-wdl-my-slider)を指定しています。
属性 imageUrl(画像の URL の配列)に含まれる URL を元に for 文でスライダーのマークアップを生成しています。URL、キャプション、alt 属性の値はそれぞれエスケープ処理しています。
function my_slider_render($attributes, $content) {
//属性 imageUrl が空なら何も表示しない
if (empty($attributes['imageUrl'])) {
return '';
}
//属性 imageUrl が空でなければスライダーのマークアップを組み立てる
$output = '<div class="wp-block-wdl-my-slider">';
$output .= '<div class="swiper-container" '. $slider_options .'>';
$output .= '<div class="swiper-wrapper">';
$imageUrl = $attributes['imageUrl'];
$imageCaption = $attributes['imageCaption'];
$imageAlt = $attributes['imageAlt'];
for($i = 0; $i < count($imageUrl); $i ++) {
$img_url = esc_url($imageUrl[$i]);
$img_caption = $imageCaption[$i] ? esc_html($imageCaption[$i]): '';
$img_alt = $imageAlt[$i] ? esc_attr($imageAlt[$i]): '';
if($img_alt !=="") {
$output .= '<div class="swiper-slide"><img className="card_image" src="'
. $img_url . '" alt="' . $img_alt . '" />';
}else {
$output .= '<div class="swiper-slide"><img className="card_image" src="'
. $img_url . '" alt="" aria-hidden="true" />';
}
//キャプションを表示する場合
if($attributes['showCaption']) {
if($img_caption) {
$output .= '<div class="caption">'. $img_caption . '</div>';
}
}
$output .= '</div>';
}
$output .= '</div>';
//ページネーションを表示する場合
if($attributes['showPagination']) {
$output .= '<div class="swiper-pagination"></div>';
}
//ナビゲーションボタンを表示する場合
if($attributes['showNavigationButton']) {
$output .= '<div class="swiper-button-prev"></div><div class="swiper-button-next"></div>';
}
//スクロールバーを表示する場合
if($attributes['showScrollbar']) {
$output .= '<div class="swiper-scrollbar"></div>';
}
$output .= '</div></div>';
return $output;
}
<?php
/**
* Plugin Name: My Slider
* Description: Example block written with ESNext standard and JSX support – build step required.
* Version: 0.1.0
* Author: The WordPress Contributors
* License: GPL-2.0-or-later
* License URI: https://www.gnu.org/licenses/gpl-2.0.html
* Text Domain: my-slider
*
* @package wdl
*/
function wdl_my_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(
'wdl-my-slider-block-editor',
plugins_url( $index_js, __FILE__ ),
$script_asset['dependencies'],
$script_asset['version']
);
$editor_css = 'build/index.css';
wp_register_style(
'wdl-my-slider-block-editor',
plugins_url( $editor_css, __FILE__ ),
array(),
filemtime( "$dir/$editor_css" )
);
$style_css = 'build/style-index.css';
wp_register_style(
'wdl-my-slider-block',
plugins_url( $style_css, __FILE__ ),
array(),
filemtime( "$dir/$style_css" )
);
register_block_type( 'wdl/my-slider', array(
'editor_script' => 'wdl-my-slider-block-editor',
'editor_style' => 'wdl-my-slider-block-editor',
'style' => 'wdl-my-slider-block',
'render_callback' => 'my_slider_render',
//属性を追加
'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', 'wdl_my_slider_block_init' );
function my_slider_render($attributes, $content) {
//属性 imageUrl が空なら何も表示しない
if (empty($attributes['imageUrl'])) {
return '';
}
//属性 imageUrl が空でなければスライダーのマークアップを組み立てる
$output = '<div class="wp-block-wdl-my-slider">';
$output .= '<div class="swiper-container" '. $slider_options .'>';
$output .= '<div class="swiper-wrapper">';
$imageUrl = $attributes['imageUrl'];
$imageCaption = $attributes['imageCaption'];
$imageAlt = $attributes['imageAlt'];
for($i = 0; $i < count($imageUrl); $i ++) {
$img_url = esc_url($imageUrl[$i]);
$img_caption = $imageCaption[$i] ? esc_html($imageCaption[$i]): '';
$img_alt = $imageAlt[$i] ? esc_attr($imageAlt[$i]): '';
if($img_alt !=="") {
$output .= '<div class="swiper-slide"><img className="card_image" src="'
. $img_url . '" alt="' . $img_alt . '" />';
}else {
$output .= '<div class="swiper-slide"><img className="card_image" src="'
. $img_url . '" alt="" aria-hidden="true" />';
}
//キャプションを表示する場合
if($attributes['showCaption']) {
if($img_caption) {
$output .= '<div class="caption">'. $img_caption . '</div>';
}
}
$output .= '</div>';
}
$output .= '</div>';
//ページネーションを表示する場合
if($attributes['showPagination']) {
$output .= '<div class="swiper-pagination"></div>';
}
//ナビゲーションボタンを表示する場合
if($attributes['showNavigationButton']) {
$output .= '<div class="swiper-button-prev"></div><div class="swiper-button-next"></div>';
}
//スクロールバーを表示する場合
if($attributes['showScrollbar']) {
$output .= '<div class="swiper-scrollbar"></div>';
}
$output .= '</div></div>';
return $output;
}
function add_my_slider_scripts_and_styles() {
$dir = dirname( __FILE__ );
//Swiper の JavaScript ファイルの読み込み(エンキュー)
wp_enqueue_script(
'swiper-slider',
plugins_url( '/assets/swiper.js', __FILE__ ),
array(),
filemtime( "$dir/assets/swiper.js" ),
true
);
//Swiper を初期化するためのファイルの読み込み(エンキュー)
wp_enqueue_script(
'swiper-slider-init',
plugins_url( '/assets/init-swiper.js', __FILE__ ),
//依存ファイルに上記 Swiper の JavaScript を指定
array('swiper-slider'),
filemtime( "$dir/assets/init-swiper.js" ),
true
);
//Swiper の CSS ファイルの読み込み(エンキュー)
wp_enqueue_style(
'swipe-style',
plugins_url( '/assets/swiper.css', __FILE__ ),
array(),
filemtime( "$dir/assets/swiper.css" )
);
}
add_action('enqueue_block_assets', 'add_my_slider_scripts_and_styles');
自動再生オプションなどの追加
スライダーの自動再生やエフェクトなどのオプションをインスペクターに追加する例です。この例では初期状態ではこれらの設定は非表示にしてあります。
パネルのボタンをクリックすると設定を表示します。選択した画像が多くなると、ブロックが大きくなってしまいますが、ブロックに max-height を指定してスクロールバーなどを表示するようにスタイルで設定することが可能です。
複数のスライダーを設定する際に、異なるパラメータを設定することができます。但し、エディター画面でのプレビューは1つずつ確認する必要があります(プレビューボタンをクリックするとそのブロックの設定が他のプレビューモードで表示しているスライダーにも反映されてしまいます)。
スライダーの初期化ファイルの変更
スライダーの自動再生やエフェクトなどのオプションのパラメータは Swiper を初期化するためのファイルに記述しているので動的に変更するのは難しいです(と思います)。
それらのパラメータを動的に変更できるようにするにはスライダーのマークアップに data 属性を設定してそれらの値を attributes を使って変更することができます。
例えば、スライダーのループを有効にして効果(エフェクト)を cube で表示し、スライドのスピードを1秒にするには以下のように data-xxxx 属性を使ってマークアップします。
<div class="swiper-container" data-loop="true" data-effect="cube" data-speed="1000">
<div class="swiper-wrapper">
<div class="swiper-slide"><img src="images/swiper_img_01.jpg" alt=""></div>
<div class="swiper-slide"><img src="images/swiper_img_02.jpg" alt=""></div>
<div class="swiper-slide"><img src="images/swiper_img_03.jpg" alt=""></div>
</div>
<div class="swiper-pagination"></div>
<div class="swiper-button-prev"></div>
<div class="swiper-button-next"></div>
<div class="swiper-scrollbar"></div>
</div>
上記のようなマークアップでパラメータを変更できるように、今まで使用していた Swiper を初期化するファイル(フロントエンド側で読み込むファイル)を以下のように変更し、ファイル名も my-swiper-init.js に変更します。
内容は、フロントエンド側で表示されるスライダーの要素を取得して、それらのマークアップの data 属性からパラメータの値を取得し、それらの値を使ってスライダーを初期化しています。
このブロック以外でも Swiper を使っている可能性を考慮して、querySelectorAll() でブロックに自動的に付与されるクラス wp-block-wdl-my-slider 内の swiper-container クラスの要素を取得します。
詳細は「スライダープラグイン Swiper(v5)の使い方」を参照ください。
//そのページにあるスライダーの要素を変数 sliderElems に取得
const sliderElems = document.querySelectorAll('.wp-block-wdl-my-slider .swiper-container');
for ( let element of sliderElems ) {
//data 属性の値をパラメータの値として使用する変数に格納
let elementSpeed = element.getAttribute('data-speed'),
elementDirection = element.getAttribute('data-direction'),
elementAutoPlay = element.getAttribute('data-autoplay'),
elementLoop = element.getAttribute('data-loop'),
elementEffect = element.getAttribute('data-effect'),
elementSlidesPerView = element.getAttribute('data-slidesPerView'),
elementCenteredSlides = element.getAttribute('data-centeredSlides');
//data 属性が設定されていない場合は初期値(デフォルト)を設定及び型を変換
if (!elementSpeed) {
elementSpeed = 300;
}
if (!elementDirection) {
elementDirection = 'horizontal';
}
//data-autoplay が設定されていれば値を数値に変換し、設定されていなければ大きな値を設定
if (elementAutoPlay) {
elementAutoPlay = parseInt(elementAutoPlay);
} else {
elementAutoPlay = 999999999;
}
//真偽値の場合は文字列から真偽値に変換
if (elementLoop == 'true') {
elementLoop = true;
} else {
elementLoop = false;
}
if (!elementEffect) {
elementEffect = 'slide';
}
if (!elementSlidesPerView) {
elementSlidesPerView = 1;
}
if (elementCenteredSlides == 'true') {
elementCenteredSlides = true;
} else {
elementCenteredSlides = false;
}
//上記パラメータを使って Swiper を初期化
let swiperSlider = new Swiper(element, {
direction: elementDirection,
speed: parseInt(elementSpeed),
autoplay: {
delay: elementAutoPlay
},
loop: elementLoop,
effect: elementEffect,
slidesPerView: parseInt(elementSlidesPerView),
centeredSlides: elementCenteredSlides,
pagination: {
el: '.swiper-pagination',
type: 'bullets',
clickable: true,
},
navigation: {
nextEl: '.swiper-button-next',
prevEl: '.swiper-button-prev',
},
scrollbar: {
el: '.swiper-scrollbar',
},
});
}
ファイル名を変更したので、ファイルの読み込みの記述を変更します。
function add_my_slider_scripts_and_styles() {
$dir = dirname( __FILE__ );
・・・中略・・・
if(! is_admin()) {
//Swiper を初期化するためのファイルの読み込みを変更
wp_enqueue_script(
'swiper-slider-init',
plugins_url( '/assets/my-swiper-init.js', __FILE__ ),
array('swiper-slider'),
filemtime( "$dir/assets/my-swiper-init.js" ),
true
);
}
・・・中略・・・
}
add_action('enqueue_block_assets', 'add_my_slider_scripts_and_styles');
パラメータの attributes を追加
この例では PHP でレンダリングしていますが、JavaScript 側の registerBlockType() の attributes にもスライダーのパラメータに使用する属性を追加します(何故か設定しないとインスペクターに初期値が反映されないため。どこかがおかしい?)。
attributes: {
mediaID: {
type: 'array',
default: []
},
・・・中略・・・
//スライダー自動再生
slideAutoPlay: {
type: 'number',
default: 0
},
//スライダースピード
slideSpeed: {
type: 'number',
default: 300
},
//スライダーのループ設定
slideLoopEnable: {
type: 'boolean',
default: true
},
//スライダーのエフェクト
slideEffect: {
type: 'string',
default: 'slide'
},
//スライダーの画像表示枚数
slidesPerView: {
type: 'number',
default: 1
},
//スライダー画像の中央寄せ
slideCentered: {
type: 'boolean',
default: false
},
},
同様に PHP 側の register_block_type() の attributes にもスライダーのパラメータに使用する属性を追加します。
'attributes' => [
'mediaID' => [
'type' => 'array',
'default' => []
],
・・・中略・・・
//スライダー自動再生
'slideAutoPlay' => [
'type' => 'number',
'default' => 0
],
//スライダースピード
'slideSpeed' => [
'type' => 'number',
'default' => 300
],
//スライダーのループ設定
'slideLoopEnable' => [
'type' => 'boolean',
'default' => true
],
//スライダーのエフェクト
'slideEffect' => [
'type' => 'string',
'default' => 'slide'
],
//スライダーの画像表示枚数
'slidesPerView' => [
'type' => 'number',
'default' => 1
],
//スライダー画像の中央寄せ
'slideCentered' => [
'type' => 'boolean',
'default' => false
],
]
PHP でのレンダリングの変更
ブロックの出力を PHP でレンダリングする render_callback 関数を以下のように変更します。
8〜28行目でスライダーのパラメータに使用する data 属性を作成します。 data 属性の値には attributes に設定した属性を指定します。作成したオプション(data 属性)を .swiper-container の div 要素に追加します。
function my_slider_render($attributes, $content) {
if (empty($attributes['imageUrl'])) {
return '';
}
//スライダーのオプション(attributes の値により data 属性を追加)
$slider_options = '';
//自動再生
if($attributes['slideAutoPlay'] !== 0) {
$slider_options .= 'data-autoplay="' .$attributes['slideAutoPlay'] . '"';
}
//スライドスピード
$slider_options .= ' data-speed="' .$attributes['slideSpeed'] . '"';
//ループ
if($attributes['slideLoopEnable']) {
$slider_options .= ' data-loop="true"';
}
//スライドエフェクト
if($attributes['slideEffect'] !== 'slide') {
$slider_options .= 'data-effect="' .$attributes['slideEffect'] . '"';
}
//スライダーに一度に表示する画像の数
$slider_options .= ' data-slidesPerView="' .$attributes['slidesPerView'] . '"';
//画像の中央配置
if($attributes['slideCentered']) {
$slider_options .= ' data-centeredSlides="true"';
}
$output = '<div class="wp-block-wdl-my-slider">';
//上記で作成したオプション(data 属性)を追加
$output .= '<div class="swiper-container" '. $slider_options .'>';
$output .= '<div class="swiper-wrapper">';
$imageUrl = $attributes['imageUrl'];
$imageCaption = $attributes['imageCaption'];
$imageAlt = $attributes['imageAlt'];
for($i = 0; $i < count($imageUrl); $i ++) {
$img_url = esc_url($imageUrl[$i]);
$img_caption = $imageCaption[$i] ? esc_html($imageCaption[$i]): '';
$img_alt = $imageAlt[$i] ? esc_attr($imageAlt[$i]): '';
if($img_alt !=="") {
$output .= '<div class="swiper-slide" ><img className="card_image" src="'
. $img_url . '" alt="' . $img_alt . '" />';
}else {
$output .= '<div class="swiper-slide" ><img className="card_image" src="'
. $img_url . '" alt="" aria-hidden="true" />';
}
//キャプションを表示する場合
if($attributes['showCaption']) {
if($img_caption) {
$output .= '<div class="caption">'. $img_caption . '</div>';
}
}
$output .= '</div>';
}
$output .= '</div>';
if($attributes['showPagination']) {
$output .= '<div class="swiper-pagination"></div>';
}
if($attributes['showNavigationButton']) {
$output .= '<div class="swiper-button-prev"></div><div class="swiper-button-next"></div>';
}
if($attributes['showScrollbar']) {
$output .= '<div class="swiper-scrollbar"></div>';
}
$output .= '</div></div>';
return $output;
}
オプションをインスペクターに追加
スライダーのパラメータを設定するオプション(項目)をインスペクターに追加します。
すでにあるインスペクターを表示する関数 getInspectorControls に PanelBody コンポーネントを追加してその中に RangeControl や SelectControl、CheckboxControl コンポーネントを配置します。追加する PanelBody の initialOpen プロパティには false を指定して初期状態では閉じた状態で表示します。
自動再生では0を指定すると自動生成しないことにして、最大8000ミリ秒まで100ミリ秒単位で設定できるようにしています。
const getInspectorControls = () => {
return (
<InspectorControls>
<PanelBody
title='スライダー表示設定'
initialOpen={true}
>
・・・中略・・・
</PanelBody>
<PanelBody
title='スライダー詳細設定'
initialOpen={false}
>
<PanelRow>
<RangeControl
label='自動再生'
value={attributes.slideAutoPlay}
onChange={(val) => setAttributes({ slideAutoPlay: val })}
min={0}
max={8000}
step={100}
help="自動生成しない場合は0を指定"
/>
</PanelRow>
<PanelRow>
<RangeControl
label='スライドスピード'
value={attributes.slideSpeed}
onChange={(val) => setAttributes({ slideSpeed: val })}
min={100}
max={1000}
step={100}
/>
</PanelRow>
<PanelRow>
<SelectControl
label="エフェクト"
value={attributes.slideEffect}
options={[
{label: "Slide", value: 'slide'},
{label: "Fade", value: 'fade'},
{label: "Cube", value: 'cube'},
{label: "Coverflow", value: 'coverflow'},
{label: "Flip", value: 'flip'},
]}
onChange={(val) => setAttributes({ slideEffect: val })}
/>
</PanelRow>
<PanelRow>
<CheckboxControl
label="ループ"
checked={attributes.slideLoopEnable}
onChange={(val) => setAttributes({ slideLoopEnable: val })}
/>
</PanelRow>
<PanelRow>
<RangeControl
label='表示枚数'
value={attributes.slidesPerView}
onChange={(val) => setAttributes({ slidesPerView: val })}
min={1}
max={5}
/>
</PanelRow>
<PanelRow>
<CheckboxControl
label="中央配置"
checked={attributes.slideCentered}
onChange={(val) => setAttributes({ slideCentered: val })}
/>
</PanelRow>
</PanelBody>
</InspectorControls>
);
}
プレビューでのスライダーの初期化の変更
プレビューで表示するスライダーに設定されたパラメータが反映されるように、スライダーを初期化する関数で属性の値をパラメータに指定して動的にオプションを設定するように変更します。
この関数は、プレビューモードがレンダリングされる際(属性 isEditMode が変更された際)に、useEffect フックで実行されます。
Swiper では自動生成のパラメータ autoplay の delay に0を指定するとスライドが自動生成されてしまうので、ユーザがレンジコントロールで0を指定した場合は、delay の値を 999999999 にして実質的に自動再生されないようにしています。
また、この例のスライダーのパラメータの値を保存する属性はそのブロックの値を保存していますが、複数のブロックがプレビューモードで表示されている場合、個々のブロックとその属性の関連付けがないため、対象のブロックのプレビューボタンを押すと、その他のブロックのプレビューモードになっているスライダーにもそのブロックの設定が反映されてしまいます。保存されている設定やフロントエンド側には影響はありませんが、ブロックのプレビューは1つずつ行わないとおかしなことになってしまいます。
const initSwiper = () => {
let elementSpeed = attributes.slideSpeed,
elementAutoPlay = attributes.slideAutoPlay,
elementLoop = attributes.slideLoopEnable,
elementEffect = attributes.slideEffect,
elementSlidesPerView = attributes.slidesPerView,
elementCenteredSlides = attributes.slideCentered;
//自動再生はインスペクターで 0 の場合は、大きな値を指定して実質的に自動再生しないようにする
if (elementAutoPlay != 0) {
elementAutoPlay = parseInt(elementAutoPlay);
} else {
elementAutoPlay = 999999999;
}
let swiperSlider = new Swiper('.swiper-container', {
speed: parseInt(elementSpeed),
autoplay: {
delay: elementAutoPlay
},
loop: elementLoop,
effect: elementEffect,
slidesPerView: parseInt(elementSlidesPerView),
centeredSlides: elementCenteredSlides,
pagination: {
el: '.swiper-pagination', //ページネーションの要素
type: 'bullets', //ページネーションの種類
clickable: true, //クリックに反応させる
},
navigation: {
nextEl: '.swiper-button-next', //「次へボタン」要素の指定
prevEl: '.swiper-button-prev', //「前へボタン」要素の指定
},
scrollbar: {
el: '.swiper-scrollbar', //要素の指定
},
});
}
サンプルファイル
以下は上記で作成したスライダーを表示するブロックのサンプルです(実用的なものではありません)。
実際には Smart Slider 3 や WordPress Slider Block Gutenslider のようなスライダーのプラグインが色々あるのでそれらを利用するのが簡単です。
編集モード
プレビューモード
以下はサンプルのファイル構成とソースコードです。実際にブロックを試すには src フォルダのファイルをコンパイルする必要があります。
my-slider
├── assets
│ ├── my-swiper-init.js //ファイル変更
│ ├── swiper.css
│ └── swiper.js
├── block.json
├── build
│ ├── index.asset.php
│ ├── index.css
│ ├── index.js
│ └── style-index.css
├── node_modules
├── my-slider.php
├── package-lock.json
├── package.json
├── readme.txt
└── src
├── edit.js
├── editor.scss
├── index.js
├── save.js //PHP でレンダリングする場合は使用しない
└── style.scss
const sliderElems = document.querySelectorAll('.wp-block-wdl-my-slider .swiper-container');
for ( let element of sliderElems ) {
let elementSpeed = element.getAttribute('data-speed'),
elementDirection = element.getAttribute('data-direction'),
elementAutoPlay = element.getAttribute('data-autoplay'),
elementLoop = element.getAttribute('data-loop'),
elementEffect = element.getAttribute('data-effect'),
elementSlidesPerView = element.getAttribute('data-slidesPerView'),
elementCenteredSlides = element.getAttribute('data-centeredSlides');
if (!elementSpeed) {
elementSpeed = 300;
}
if (!elementDirection) {
elementDirection = 'horizontal';
}
if (elementAutoPlay) {
elementAutoPlay = parseInt(elementAutoPlay);
} else {
elementAutoPlay = 999999999;
}
if (elementLoop == 'true') {
elementLoop = true;
} else {
elementLoop = false;
}
if (!elementEffect) {
elementEffect = 'slide';
}
if (!elementSlidesPerView) {
elementSlidesPerView = 1;
}
if (elementCenteredSlides == 'true') {
elementCenteredSlides = true;
} else {
elementCenteredSlides = false;
}
let swiperSlider = new Swiper(element, {
direction: elementDirection,
speed: parseInt(elementSpeed),
autoplay: {
delay: elementAutoPlay
},
loop: elementLoop,
effect: elementEffect,
slidesPerView: parseInt(elementSlidesPerView),
centeredSlides: elementCenteredSlides,
pagination: {
el: '.swiper-pagination',
type: 'bullets',
clickable: true,
},
navigation: {
nextEl: '.swiper-button-next',
prevEl: '.swiper-button-prev',
},
scrollbar: {
el: '.swiper-scrollbar',
},
});
}
swiper.css(https://unpkg.com/swiper@6.3.2/swiper-bundle.min.css)
swiper.js(https://unpkg.com/swiper@6.3.2/swiper-bundle.min.js)
<?php
/**
* Plugin Name: My Slider
* Description: Example block written with ESNext standard and JSX support – build step required.
* Version: 0.1.0
* Author: The WordPress Contributors
* License: GPL-2.0-or-later
* License URI: https://www.gnu.org/licenses/gpl-2.0.html
* Text Domain: my-slider
*
* @package wdl
*/
function wdl_my_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(
'wdl-my-slider-block-editor',
plugins_url( $index_js, __FILE__ ),
$script_asset['dependencies'],
$script_asset['version']
);
$editor_css = 'build/index.css';
wp_register_style(
'wdl-my-slider-block-editor',
plugins_url( $editor_css, __FILE__ ),
array(),
filemtime( "$dir/$editor_css" )
);
$style_css = 'build/style-index.css';
wp_register_style(
'wdl-my-slider-block',
plugins_url( $style_css, __FILE__ ),
array(),
filemtime( "$dir/$style_css" )
);
register_block_type( 'wdl/my-slider', array(
'editor_script' => 'wdl-my-slider-block-editor',
'editor_style' => 'wdl-my-slider-block-editor',
'style' => 'wdl-my-slider-block',
'render_callback' => 'my_slider_render',
//属性を追加
'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
],
//スライダー自動再生
'slideAutoPlay' => [
'type' => 'number',
'default' => 0
],
//スライダースピード
'slideSpeed' => [
'type' => 'number',
'default' => 300
],
//スライダーのループ設定
'slideLoopEnable' => [
'type' => 'boolean',
'default' => true
],
//スライダーのエフェクト
'slideEffect' => [
'type' => 'string',
'default' => 'slide'
],
//スライダーの画像表示枚数
'slidesPerView' => [
'type' => 'number',
'default' => 1
],
//スライダー画像の中央寄せ
'slideCentered' => [
'type' => 'boolean',
'default' => false
],
]
) );
}
add_action( 'init', 'wdl_my_slider_block_init' );
function my_slider_render($attributes, $content) {
//属性 imageUrl が空なら何も表示しない
if (empty($attributes['imageUrl'])) {
return '';
}
//スライダーのオプション(attributes の値により data 属性を追加)
$slider_options = '';
if($attributes['slideAutoPlay'] !== 0) {
$slider_options .= 'data-autoplay="' .$attributes['slideAutoPlay'] . '"';
}
$slider_options .= ' data-speed="' .$attributes['slideSpeed'] . '"';
if($attributes['slideLoopEnable']) {
$slider_options .= ' data-loop="true"';
}
if($attributes['slideEffect'] !== 'slide') {
$slider_options .= 'data-effect="' .$attributes['slideEffect'] . '"';
}
$slider_options .= ' data-slidesPerView="' .$attributes['slidesPerView'] . '"';
if($attributes['slideCentered']) {
$slider_options .= ' data-centeredSlides="true"';
}
//属性 imageUrl が空でなければスライダーのマークアップを組み立てる
$output = '<div class="wp-block-wdl-my-slider">';
$output .= '<div class="swiper-container" '. $slider_options .'>';
$output .= '<div class="swiper-wrapper">';
$imageUrl = $attributes['imageUrl'];
$imageCaption = $attributes['imageCaption'];
$imageAlt = $attributes['imageAlt'];
for($i = 0; $i < count($imageUrl); $i ++) {
$img_url = esc_url($imageUrl[$i]);
$img_caption = $imageCaption[$i] ? esc_html($imageCaption[$i]): '';
$img_alt = $imageAlt[$i] ? esc_attr($imageAlt[$i]): '';
if($img_alt !=="") {
$output .= '<div class="swiper-slide" ><img className="card_image" src="'
. $img_url . '" alt="' . $img_alt . '" />';
}else {
$output .= '<div class="swiper-slide" ><img className="card_image" src="'
. $img_url . '" alt="" aria-hidden="true" />';
}
//キャプションを表示する場合
if($attributes['showCaption']) {
if($img_caption) {
$output .= '<div class="caption">'. $img_caption . '</div>';
}
}
$output .= '</div>';
}
$output .= '</div>';
//ページネーションを表示する場合
if($attributes['showPagination']) {
$output .= '<div class="swiper-pagination"></div>';
}
//ナビゲーションボタンを表示する場合
if($attributes['showNavigationButton']) {
$output .= '<div class="swiper-button-prev"></div><div class="swiper-button-next"></div>';
}
//スクロールバーを表示する場合
if($attributes['showScrollbar']) {
$output .= '<div class="swiper-scrollbar"></div>';
}
$output .= '</div></div>';
return $output;
}
function add_my_slider_scripts_and_styles() {
$dir = dirname( __FILE__ );
//Swiper の JavaScript ファイルの読み込み(エンキュー)
wp_enqueue_script(
'swiper-slider',
plugins_url( '/assets/swiper.js', __FILE__ ),
array(),
filemtime( "$dir/assets/swiper.js" ),
true
);
if(! is_admin()) {
//Swiper を初期化するためのファイルの読み込み(エンキュー)
wp_enqueue_script(
'swiper-slider-init',
plugins_url( '/assets/my-swiper-init.js', __FILE__ ),
//依存ファイルに上記 Swiper の JavaScript を指定
array('swiper-slider'),
filemtime( "$dir/assets/my-swiper-init.js" ),
true
);
}
//Swiper の CSS ファイルの読み込み(エンキュー)
wp_enqueue_style(
'swipe-style',
plugins_url( '/assets/swiper.css', __FILE__ ),
array(),
filemtime( "$dir/assets/swiper.css" )
);
}
add_action('enqueue_block_assets', 'add_my_slider_scripts_and_styles');
import { BlockControls, MediaUpload, MediaUploadCheck } from '@wordpress/block-editor';
import { Button } from '@wordpress/components';
import { InspectorControls } from '@wordpress/block-editor';
import { Toolbar, PanelBody, PanelRow, ToggleControl, RangeControl, SelectControl, CheckboxControl } from '@wordpress/components';
import { Fragment } from '@wordpress/element';
import { useEffect } from '@wordpress/element';
import './editor.scss';
export default function Edit( props ) {
//分割代入を使って props 経由でプロパティを変数に代入
const { className, attributes, setAttributes} = props;
//選択された画像の情報を更新する関数
const onSelectImage = ( media ) => {
const media_ID = media.map((image) => image.id);
const imageUrl = media.map((image) => image.url);
const imageAlt = media.map((image) => image.alt);
const imageCaption = media.map((image) => image.caption);
setAttributes( {
mediaID: media_ID, //メディア ID の配列
imageUrl: imageUrl, // URL の配列
imageAlt: imageAlt, // alt 属性の配列
imageCaption: imageCaption, // キャプションの配列
} );
};
//URL の配列から画像を生成
const getImages = ( urls ) => {
let imagesArray = urls.map(( url ) => {
return (
<img
src={ url }
className="image"
alt="アップロード画像"
/>
);
});
return imagesArray;
}
//URL とキャプションの配列から画像をキャプション付きで生成
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 の配列から画像を生成(スライダー表示用)
const getSliderImages = ( url, caption ) => {
let imagesArray = [];
for(let i = 0 ; i < url.length; i ++) {
let image = '';
if(attributes.showCaption) {
image = (
<div className="swiper-slide">
<img
src={ url[i] }
className="image"
alt="アップロード画像"
/>
<div class="caption">
{ caption[i] ? caption[i] : '' }
</div>
</div>
)
}else{
image = (
<div className="swiper-slide">
<img
src={ url[i] }
className="image"
alt="アップロード画像"
/>
</div>
)
}
imagesArray.push ( image );
}
return imagesArray;
}
//スライダーを初期化する関数(属性の値をパラメータに指定して動的にオプションを設定)
const initSwiper = () => {
let elementSpeed = attributes.slideSpeed,
elementAutoPlay = attributes.slideAutoPlay,
elementLoop = attributes.slideLoopEnable,
elementEffect = attributes.slideEffect,
elementSlidesPerView = attributes.slidesPerView,
elementCenteredSlides = attributes.slideCentered;
//自動再生はインスペクターで 0 の場合は、大きな値を指定して実質的に自動再生しないようにする
if (elementAutoPlay != 0) {
elementAutoPlay = parseInt(elementAutoPlay);
} else {
elementAutoPlay = 999999999;
}
let swiperSlider = new Swiper('.swiper-container', {
speed: parseInt(elementSpeed),
autoplay: {
delay: elementAutoPlay
},
loop: elementLoop,
effect: elementEffect,
slidesPerView: parseInt(elementSlidesPerView),
centeredSlides: elementCenteredSlides,
pagination: {
el: '.swiper-pagination', //ページネーションの要素
type: 'bullets', //ページネーションの種類
clickable: true, //クリックに反応させる
},
navigation: {
nextEl: '.swiper-button-next', //「次へボタン」要素の指定
prevEl: '.swiper-button-prev', //「前へボタン」要素の指定
},
scrollbar: {
el: '.swiper-scrollbar', //要素の指定
},
});
}
const getImageButton = (open) => {
if(attributes.imageUrl.length > 0 ) {
if(attributes.isEditMode) {
return (
<div onClick={ open } className="slider-block-container">
{ attributes.showCaption ? getImagesWithCaption( attributes.imageUrl, attributes.imageCaption ) : getImages( attributes.imageUrl ) }
</div>
)
}else{
//isEditMode が false ならスライダーを表示
return (
<div className="slider-container">
<div className="swiper-container">
<div className="swiper-wrapper">
{ getSliderImages(attributes.imageUrl, attributes.imageCaption) }
</div>
{ attributes.showPagination &&
<div className="swiper-pagination"></div>
}
{ attributes.showNavigationButton &&
<Fragment>
<div className="swiper-button-prev"></div>
<div className="swiper-button-next"></div>
</Fragment>
}
{ attributes.showScrollbar &&
<div className="swiper-scrollbar"></div>
}
</div>
</div>
)
}
}
else {
return (
<div className="button-container">
<Button
onClick={ open }
className="button button-large"
>
画像をアップロード
</Button>
</div>
);
}
};
//画像を削除する(メディアをリセットする)関数
const removeMedia = () => {
setAttributes({
mediaID: [],
imageUrl: [],
imageAlt: [],
imageCaption: [],
});
}
//インスペクターを追加する関数
const getInspectorControls = () => {
return (
<InspectorControls>
<PanelBody
title='スライダー表示設定'
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>
<PanelBody
title='スライダー詳細設定'
initialOpen={false}
>
<PanelRow>
<RangeControl
label='自動再生'
value={attributes.slideAutoPlay}
onChange={(val) => setAttributes({ slideAutoPlay: val })}
min={0}
max={8000}
step={100}
help="自動生成しない場合は0を指定"
/>
</PanelRow>
<PanelRow>
<RangeControl
label='スライドスピード'
value={attributes.slideSpeed}
onChange={(val) => setAttributes({ slideSpeed: val })}
min={100}
max={1000}
step={100}
/>
</PanelRow>
<PanelRow>
<SelectControl
label="エフェクト"
value={attributes.slideEffect}
options={[
{label: "Slide", value: 'slide'},
{label: "Fade", value: 'fade'},
{label: "Cube", value: 'cube'},
{label: "Coverflow", value: 'coverflow'},
{label: "Flip", value: 'flip'},
]}
onChange={(val) => setAttributes({ slideEffect: val })}
/>
</PanelRow>
<PanelRow>
<CheckboxControl
label="ループ"
checked={attributes.slideLoopEnable}
onChange={(val) => setAttributes({ slideLoopEnable: val })}
/>
</PanelRow>
<PanelRow>
<RangeControl
label='表示枚数'
value={attributes.slidesPerView}
onChange={(val) => setAttributes({ slidesPerView: val })}
min={1}
max={5}
/>
</PanelRow>
<PanelRow>
<CheckboxControl
label="中央配置"
checked={attributes.slideCentered}
onChange={(val) => setAttributes({ slideCentered: val })}
/>
</PanelRow>
</PanelBody>
</InspectorControls>
);
}
const getBlockControls = () => {
return (
<BlockControls>
<Toolbar>
<Button
//属性 isEditMode の値により表示するラベルを切り替え
label={ attributes.isEditMode ? "Preview" : "Edit" }
//属性 isEditMode の値により表示するアイコンを切り替え
icon={ attributes.isEditMode ? "format-image" : "edit" }
className="my-custom-button"
//setAttributes を使って属性の値を更新(真偽値を反転)
onClick={() => setAttributes({ isEditMode: !attributes.isEditMode })}
/>
</Toolbar>
</BlockControls>
);
}
//useEffect フックを使ってスライダーを初期化
useEffect(() => {
initSwiper();
}, [attributes.isEditMode] );
return (
[
getBlockControls(), //プレビューボタン
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 && attributes.isEditMode &&
<MediaUploadCheck>
<Button
onClick={removeMedia}
isLink
isDestructive
className="removeImage">画像を削除
</Button>
<Button
onClick={() => setAttributes({ isEditMode: !attributes.isEditMode })}
isLink
className="removeImage">スライダーをプレビュー
</Button>
</MediaUploadCheck>
}
</div>
]
);
}
import { registerBlockType } from '@wordpress/blocks';
import './style.scss';
import Edit from './edit';
import save from './save';
registerBlockType( 'wdl/my-slider', {
title: 'My Slider',
description: 'Example block written with ESNext standard and JSX support',
category: 'widgets',
icon: 'smiley',
supports: {
html: false,
},
//属性を設定
attributes: {
//属性 mediaID(メディア ID の配列)
mediaID: {
type: 'array',
default: []
},
//img の src に指定する URL
imageUrl: {
type: 'array',
default: []
},
//img の alt 属性の値
imageAlt: {
type: 'array',
default: []
},
//ナビゲーションボタンの表示・非表示
showNavigationButton: {
type: 'boolean',
default: true
},
//ページネーションの表示・非表示
showPagination: {
type: 'boolean',
default: true
},
//ページネーションの表示・非表示
showScrollbar: {
type: 'boolean',
default: true
},
//img の キャプションの値
imageCaption: {
type: 'array',
default: []
},
//キャプションの表示・非表示
showCaption: {
type: 'boolean',
default: true
},
//isEditMode を属性として追加
isEditMode: {
type: 'boolean',
default: true
},
//スライダー自動再生
slideAutoPlay: {
type: 'number',
default: 0
},
//スライダースピード
slideSpeed: {
type: 'number',
default: 300
},
//スライダーのループ設定
slideLoopEnable: {
type: 'boolean',
default: true
},
//スライダーのエフェクト
slideEffect: {
type: 'string',
default: 'slide'
},
//スライダーの画像表示枚数
slidesPerView: {
type: 'number',
default: 1
},
//スライダー画像の中央寄せ
slideCentered: {
type: 'boolean',
default: false
},
},
edit: Edit,
//save 関数で null を返す
save: () => { return null }
} );
.wp-block-wdl-my-slider .slider-block-container {
display: flex;
flex-wrap: wrap;
margin: 20px;
}
.wp-block-wdl-my-slider .slider-block-container img {
width: 100%;
max-width: 160px;
margin: 10px;
}
.image {
cursor: pointer;
}
.wp-block-wdl-my-slider figcaption.block-image-caption {
text-align: center;
margin-top: 0;
}
$gray: #cccccc;
$off-white: #f1f1f1;
.wp-block-wdl-my-slider {
background-color: #fefefe;
color: #666;
padding: 10px;
border: 1px solid #ccc;
}
.button-container {
text-align: center;
padding: 22% 0;
background: $off-white;
border: 1px solid $gray;
border-radius: 2px;
margin: 0 0 1.2rem 0;
}
:root {
--swiper-navigation-color: #B8DCF5;
--swiper-pagination-color: #B8DCF5;
}
.swiper-container .caption {
background-color: rgba(0,0,0,0.7);
color: #fefefe;
text-align: center;
position: absolute;
bottom: 0px;
width: 100%;
}
.swiper-container .swiper-pagination.swiper-pagination-bullets {
bottom: 30px;
}
{
"name": "my-slider",
"version": "0.1.0",
"description": "Example block written with ESNext standard and JSX support – build step required.",
"author": "The WordPress Contributors",
"license": "GPL-2.0-or-later",
"main": "build/index.js",
"scripts": {
"build": "wp-scripts build",
"format:js": "wp-scripts format-js",
"lint:css": "wp-scripts lint-style",
"lint:js": "wp-scripts lint-js",
"start": "wp-scripts start",
"packages-update": "wp-scripts packages-update"
},
"devDependencies": {
"@wordpress/scripts": "^12.3.0"
}
}
save 関数を使ってレンダリングする場合
以下は PHP でのレンダリングではなく、save 関数を使ってレンダリングする場合のサンプルです。
index.js では save 関数の記述を null から変更します。
edit: Edit,
save,
//上記を追加して、以下を削除
//save: () => { return null }
また、attributes の source プロパティは PHP でレンダリングされるブロックではサポートされていませんが、save 関数を使ってレンダリングする場合は使用することができます。
source プロパティを指定しない場合、属性はブロックのコメントデリミタに JSON 形式で保存されます。
以下は source プロパティを指定しない場合のコードエディターでの表示です。
この例の場合、自動再生などのパラメータの値は data 属性に設定しているので、save 関数を使ってレンダリングする場合は source プロパティに attribute を指定して attribute プロパティで data 属性を指定することができます。関連:attributes
slideAutoPlay: {
type: 'number',
default: 0,
source: 'attribute',
selector: '.swiper-container',
attribute: 'data-autoplay',
},
以下は source プロパティを指定した場合のコードエディターでの表示で、自動再生のオプションの slideAutoPlay などはコメントデリミタからなくなっているのが確認できます。
この例の場合、source プロパティを指定する必要性はありませんが、以下は attributes の source プロパティを指定した場合の例です(PHP でレンダリングする場合は、source プロパティを削除する必要があります)。
import { registerBlockType } from '@wordpress/blocks';
import './style.scss';
import Edit from './edit';
import save from './save';
registerBlockType( 'wdl/my-slider', {
title: 'My Slider',
description: 'Example block written with ESNext standard and JSX support',
category: 'widgets',
icon: 'smiley',
supports: {
html: false,
},
//属性を設定
attributes: {
//属性 mediaID(メディア ID の配列)
mediaID: {
type: 'array',
default: []
},
//img の src に指定する URL
imageUrl: {
type: 'array',
default: []
},
//img の alt 属性の値
imageAlt: {
type: 'array',
default: []
},
//ナビゲーションボタンの表示・非表示
showNavigationButton: {
type: 'boolean',
default: true
},
//ページネーションの表示・非表示
showPagination: {
type: 'boolean',
default: true
},
//ページネーションの表示・非表示
showScrollbar: {
type: 'boolean',
default: true
},
//img の キャプションの値
imageCaption: {
type: 'array',
default: []
},
//キャプションの表示・非表示
showCaption: {
type: 'boolean',
default: true
},
//isEditMode を属性として追加
isEditMode: {
type: 'boolean',
default: true
},
//スライダー自動再生
slideAutoPlay: {
type: 'number',
default: 0,
source: 'attribute',
selector: '.swiper-container',
attribute: 'data-autoplay',
},
//スライダースピード
slideSpeed: {
type: 'number',
default: 300,
source: 'attribute',
selector: '.swiper-container',
attribute: 'data-speed',
},
//スライダーのループ設定
slideLoopEnable: {
type: 'boolean',
default: true,
source: 'attribute',
selector: '.swiper-container',
attribute: 'data-loop',
},
//スライダーのエフェクト
slideEffect: {
type: 'string',
default: 'slide',
source: 'attribute',
selector: '.swiper-effect',
attribute: 'data-autoplay',
},
//スライダーの画像表示枚数
slidesPerView: {
type: 'number',
default: 1,
source: 'attribute',
selector: '.swiper-container',
attribute: 'data-slidesPerView',
},
//スライダー画像の中央寄せ
slideCentered: {
type: 'boolean',
default: false,
source: 'attribute',
selector: '.swiper-container',
attribute: 'data-centeredSlides',
},
/* PHP でレンダリングする場合は source プロパティは使えない
//スライダー自動再生
slideAutoPlay: {
type: 'number',
default: 0
},
//スライダースピード
slideSpeed: {
type: 'number',
default: 300
},
//スライダーのループ設定
slideLoopEnable: {
type: 'boolean',
default: true
},
//スライダーのエフェクト
slideEffect: {
type: 'string',
default: 'slide'
},
//スライダーの画像表示枚数
slidesPerView: {
type: 'number',
default: 1
},
//スライダー画像の中央寄せ
slideCentered: {
type: 'boolean',
default: false
}, */
},
edit: Edit,
save,
} );
my-slider.php では attributes と render_callback キーを削除します。
<?php
/**
* Plugin Name: My Slider
* Description: Example block written with ESNext standard and JSX support – build step required.
* Version: 0.1.0
* Author: The WordPress Contributors
* License: GPL-2.0-or-later
* License URI: https://www.gnu.org/licenses/gpl-2.0.html
* Text Domain: my-slider
*
* @package wdl
*/
function wdl_my_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(
'wdl-my-slider-block-editor',
plugins_url( $index_js, __FILE__ ),
$script_asset['dependencies'],
$script_asset['version']
);
$editor_css = 'build/index.css';
wp_register_style(
'wdl-my-slider-block-editor',
plugins_url( $editor_css, __FILE__ ),
array(),
filemtime( "$dir/$editor_css" )
);
$style_css = 'build/style-index.css';
wp_register_style(
'wdl-my-slider-block',
plugins_url( $style_css, __FILE__ ),
array(),
filemtime( "$dir/$style_css" )
);
register_block_type( 'wdl/my-slider', array(
'editor_script' => 'wdl-my-slider-block-editor',
'editor_style' => 'wdl-my-slider-block-editor',
'style' => 'wdl-my-slider-block',
) );
}
add_action( 'init', 'wdl_my_slider_block_init' );
function add_my_slider_scripts_and_styles() {
$dir = dirname( __FILE__ );
//Swiper の JavaScript ファイルの読み込み(エンキュー)
wp_enqueue_script(
'swiper-slider',
plugins_url( '/assets/swiper.js', __FILE__ ),
array(),
filemtime( "$dir/assets/swiper.js" ),
true
);
if(! is_admin()) {
//Swiper を初期化するためのファイルの読み込み(エンキュー)
wp_enqueue_script(
'swiper-slider-init',
plugins_url( '/assets/my-swiper-init.js', __FILE__ ),
//依存ファイルに上記 Swiper の JavaScript を指定
array('swiper-slider'),
filemtime( "$dir/assets/my-swiper-init.js" ),
true
);
}
//Swiper の CSS ファイルの読み込み(エンキュー)
wp_enqueue_style(
'swipe-style',
plugins_url( '/assets/swiper.css', __FILE__ ),
array(),
filemtime( "$dir/assets/swiper.css" )
);
}
add_action('enqueue_block_assets', 'add_my_slider_scripts_and_styles');
import { Fragment } from '@wordpress/element';
export default function save( { attributes } ) {
//画像をレンダリングする関数
const getImagesSave = ( url, alt, caption ) => {
let image_elem;
let imagesArray = [];
for( let i = 0 ; i < url.length; i ++ ) {
if( url.length === 0 ) {
image_elem = null;
}else{
if( alt[i] ) {
image_elem = (
<div className="swiper-slide">
<img
className="card_image"
src={ url[i] }
alt={ alt[i] }
/>
{ attributes.showCaption &&
<div class="caption">{ caption[i] ? caption[i] : "" }</div>
}
</div>
);
}else{
image_elem = (
<div className="swiper-slide">
<img
className="card_image"
src={ url[i] }
alt=""
aria-hidden="true"
/>
{ attributes.showCaption &&
<div class="caption">{ caption[i] ? caption[i] : "" }</div>
}
</div>
);
}
}
imagesArray.push( image_elem ) ;
}
return imagesArray;
}
//attributes から data 属性に指定する値を取得
let elementSpeed = parseInt(attributes.slideSpeed),
elementAutoPlay = parseInt(attributes.slideAutoPlay),
elementLoop = attributes.slideLoopEnable ? "true" : "false",
elementEffect = attributes.slideEffect,
elementSlidesPerView = parseInt(attributes.slidesPerView),
elementCenteredSlides = attributes.slideCentered ? "true" : "false";
//自動再生はインスペクターで 0 の場合は、大きな値を指定して実質的に自動再生しないようにする
if (elementAutoPlay === 0) {
elementAutoPlay = 999999999;
}
return (
<div className="slider-container">
<div
className="swiper-container"
data-speed={elementSpeed}
data-autoplay={elementAutoPlay}
data-loop={elementLoop}
data-effect={elementEffect}
data-slidesPerView={elementSlidesPerView}
data-centeredSlides={elementCenteredSlides}
>
<div className="swiper-wrapper">
{ getImagesSave( attributes.imageUrl, attributes.imageAlt, attributes.imageCaption ) }
</div>
{ attributes.showPagination &&
<div className="swiper-pagination"></div>
}
{ attributes.showNavigationButton &&
<Fragment>
<div className="swiper-button-prev"></div>
<div className="swiper-button-next"></div>
</Fragment>
}
{ attributes.showScrollbar &&
<div className="swiper-scrollbar"></div>
}
</div>
</div>
);
}
save 関数を書き換えて投稿のページで再読込すると「このブロックには、想定されていないか無効なコンテンツが含まれています」と表示されるので、表示されるボタンをクリックして「ブロックを削除」を選択し、現在のブロックを一度削除してから再度ブロックを挿入する必要があります。





























