WordPress・カスタムブロック作成の基本(その1)

できる限りカスタムブロックは作らずに、コアブロックで対応するのがベストだと思います
あくまでも ・・・・・

WordPress5.8のリリースから、ブロックタイプを登録する標準の方法として、block.json メタデータファイルの使用が推奨されています

wordpress.org

らしいのでw 、備忘録としてまとめようと思います

  • スクリプトにブロックの登録は書かなくてよい
  • ブロック用ファイルの読み込みがほぼ自動(@wordpress/scripts パッケージを利用した場合)
目次
  1. 「ブロック」の仕組みをざっくりと
  2. 開発環境の準備
    1. phpファイル
    2. block.jsonファイル
    3. JavaScriptファイル
  3. 最初のカスタムブロック
  4. ブロックツールバーとサイドバー
    1. RichText
    2. BlockControls
    3. InspectorControls
    4. PanelBodyとRangeControl
  5. スタイルの追加について
  6. 関連投稿

「ブロック」の仕組みをざっくりと

「ブロックエディタ」の「ブロック」は「Block API規定のプロパティ集合をもったJSONオブジェクト」です
「JSONオブジェクト」として格納された「ブロックタイプ」を処理することで「PHPとJavaScript」でコードの共有が可能になります

なんとなくのイメージ図💦

ブロックの仕組み
const block = {
    clientId,   // ユニークな文字列識別子。
    type,       // ブロックタイプ (paragraph, image...)
    attributes, // (キー, 値) 現行ブロックの直接のプロパティやコンテンツを表す属性の集合
    innerBlocks // 子ブロックやインナーブロックの集合
}

開発環境の準備

「カスタムブロック」は「プラグイン」として作成してサイトに追加します

最低限必要なファイルは

  • PHPファイル(プラグインヘッダとブロック用のスクリプトやスタイルの読み込み用)
  • JavaScriptファイル(ブロックの実装用)
  • block.jsonファイル(ブロックタイプを登録用のメタデータファイル)

「ブロックエディタ(Gutenberg)」は「React」で開発されていて、JSX構文をつかうならコンパイルが必要です
通常は「webpackのようなモジュールバンドラー」を使って開発します

「@wordpress/create-block」というブロック開発用のパッケージが用意されています
ビルド環境はnpx @wordpress/create-block ファイルの保管場所 を実行すればすぐに準備できます
*通常はプラグインディレクトリ wp-content/pluginsに移動して実行します

phpファイル

プラグインヘッダとブロック用のファイル(js css)依存関係を解決して読み込む設定です

init(プラグインの初期化)のタイミングで「register_block_type関数」を実行
*@wordpress/scriptsパッケージを利用するとシンプルになります
これだけて登録完了!!

<?php
/*
Plugin Name: test
*/
// *直接ファイルにアクセスされた場合exitするように追記
if ( !defined( 'ABSPATH' ) ) {
 exit; 
}

//@wordpress/scriptsパッケージをつかうとシンプルになります
function create_block_test_block_init() {
	register_block_type( __DIR__ );
}
add_action( 'init', 'create_block_test_block_init' );

register_block_type関数
第1引数は「 block.jsonファイル」のあるフォルダへのパス(名前が異なるときはフルパス)
第2引数は配列でブロックをレンダーする際に使用されるコールバック

ちなみに、register_block_type関数の第1引数の__DIR__(ファイルがあるフォルダへのパス)で「 block.jsonファイル」が同じ階層にある前提でブロックタイプを登録しています

block.jsonファイル

*例としてドキュメントからコピーしてきたもの

{
    "$schema": "https://schemas.wp.org/trunk/block.json",
    "apiVersion": 2,
    "name": "my-plugin/notice",
    "title": "Notice",
    "category": "text",
    "parent": [ "core/group" ],
    "icon": "star",
    "description": "Shows warning, error or success notices…",
    "keywords": [ "alert", "message" ],
    "version": "1.0.3",
    "textdomain": "my-plugin",
    "attributes": {
        "message": {
            "type": "string",
            "source": "html",
            "selector": ".message"
        }
    },
    "providesContext": {
        "my-plugin/message": "message"
    },
    "usesContext": [ "groupId" ],
    "supports": {
        "align": true
    },
    "styles": [
        { "name": "default", "label": "Default", "isDefault": true },
        { "name": "other", "label": "Other" }
    ],
    "example": {
        "attributes": {
            "message": "This is a notice!"
        }
    },
    "editorScript": "file:./build/index.js",
    "script": "file:./build/script.js",
    "viewScript": "file:./build/view.js",
    "editorStyle": "file:./build/index.css",
    "style": "file:./build/style.css"
}

「editorScript・script・viewScript・editorStyle・style」は、「wp_register_script」 と 「wp_register_style 」でスクリプトとスタイルを登録するときに必要なパラメータです
「block.jsonファイル」からの相対パス(接頭辞 file:をつける)を書くと、ビルド時にbuild/index.asset.phpが作成され依存関係が定義されます
*stringのサブタイプ(WPDefinedAsset)で指定するそうですw

要するに
こんな感じで「register_block_typeの第二引数のキー」で「wp_register_script」の識別名を指定し「wp_register_script」で依存関係のあるJSファイルを登録していたけど「wp_register_script」や「wp_register_style」が不要ってこと
*(注意)@wordpress/scripts パッケージを利用した場合は不要です

function hoge_init() {
  wp_register_script(
    'hogehoge',
     plugins_url( 'index.js', __FILE__ ),
     array( 'wp-blocks', 'wp-element' ) 
  );
  register_block_type( 
    'namespace/block-name, 
     array(   
      'editor_script' => 'hogehoge', 
    ) 
  );
}
add_action( 'init', 'hoge_init' );

おもなブロックオブジェクトのプロパティ
*「block.json」のキーとして書いています

「block.json」のキー意味
apiVersionブロックが使用するBlockAPIのバージョン(WordPress 5.6からバージョンは2)
*デフォルトは1
number
name(必須)
namespace/block-name で、namespaceはプレフィックス(プラグインやテーマの名前)
string
title(必須)ブロックの表示タイトルstring
categoryコアのカテゴリ
text
media
design
widgets
theme
embed
*追加可能です
string
iconブロックのアイコンstring
descriptionブロックの簡潔な説明string
keywordsブロックを検索するときのキーワードstring[]
version現在のバージョンstring
textdomain翻訳するときは必要string
supportsエディターで使用される機能を宣言します
"html": true にするとHTML編集を確認できます
object
stylesブロックのラッパーにクラス名を追加array
parent親のブロックがあるとき(ネストされた場合)のみ利用string[]
exampleプレビュー用object
attributesブロックによって保存されるデータについての情報object
editorScriptエディタ内でのみエンキューされるスクリプトの定義*string
scriptエディタ内と閲覧ページが表示される際の両方でエンキューされるスクリプトの定義*string
viewScript閲覧ページが表示されるときのみエンキューされるスクリプトの定義*string
editorStyleエディター内でのみエンキューされるスタイルの定義*string
styleエディタ内と閲覧ページが表示される際の両方でエンキューされるスタイルの定義*string

「attributesプロパティ」について
パースやシリアライズは「attributesプロパティ」のデータをもとに処理されます
「attributesプロパティ」は「属性名をキー」「属性定義を値」にもつオブジェクトです
属性定義には最低「 typeまたはenum」が必要です
デフォルト値(default)をキーにできます

属性定義説明
typeデータの種類で下記のいずれか
null
boolean
object
array
string
integer
number (integer と同じ)
enum固定された値のセット
例 size: enum: [ ‘large’, ‘small’, ‘tiny’ ]
selectorHTMLタグ・classやid 属性など(querySelectorに指定できるもの)
sourceどのように属性値を取り出すか(通常はselectorと組み合わせて使用し、selector が指定されていないときはブロックのルートノードに対して実行されます)
なし :こんな感じで<!– wp:paragraph {“key”: “value”} –>コメントに保存
attribute :HTML要素の属性に保存
text :HTMLのテキスに保存。
html : HTMLに保存(この典型的な使用例は RichText)
query :オブジェクトの配列に保存
meta :投稿メタに保存(非推奨)

JavaScriptファイル

ブロックの実装です

registerBlockType関数でブロックの登録が不要になりました

registerBlockType('create-block/test', {
//ここが不要
   title: 'test',
    icon: '',
    category: 'design',
    attributes: {
        content: {
            type: 'array',
            source: 'children',
            selector: 'p',
     },
//
    },
} );

「registerBlockType関数」の第1引数はブロック名です
*サーバーでブロックを登録したときはクライアントでも同じブロック名( block.jsonの”name”の値)で登録します
第2引数はオブジェクトです(通常「editとsave」を含みます)
「edit」と「save」でブロックがどのように「レンダー・操作・保存」されるかのインターフェイスが提供されます
editの戻り値は、エディタ側でのレンダリング結果
saveの戻り値は、閲覧側での見え方で、データベース(post_contentフィールド)に挿入するブロックの形(wp.elementのインスタンス)です

import { registerBlockType } from '@wordpress/blocks';
//または const { registerBlockType } = wp.blocks; 

registerBlockType( 'create-block/test', {
	edit: function () {
		return "";
	},
	save: function () {
		return "";
	}
} );

「WordPress環境 (Block API)」でのグローバルオブジェクトは「wp」です
パッケージは WordPress内で「wp.」でアクセスできます

クライアントサイドだけでブロックを登録するときは「block.jsonファイル」からデータを読み込んで登録します

import { registerBlockType } from '@wordpress/blocks';
//「block.jsonファイル」からデータを読み込んで登録
import metadata from './block.json';
 
registerBlockType( metadata, {

} );

最初のカスタムブロック

こちらを試してみます

npx @wordpress/create-block test を実行(かなり時間がかかる〜w)
WordPress管理画面で「testプラグイン」を有効化

「edit.jsとsave.js」は削除して「index.js」にすべて書きますw

block.jsonに追加

"attributes": {
    "message": {
        "type": "string",
        "source": "text",
        "selector": "div",
        "default": ""
    }
},

試しに、「index.jsファイル」でpropsを確認してみます
import { _ } from ‘@wordpress/i18n’;は翻訳に必要なので、今回は不要です しかしコピーしたコードに{ _( ‘テキスト’, ‘textdomain’ ) }がつかわれているとエラーになるので、念のため「index.jsファイル」に追加しました
「index.jsファイル」を編集し、npm startします

import { __ } from '@wordpress/i18n';
import { registerBlockType } from '@wordpress/blocks';

registerBlockType('create-block/test', {
	edit( props ) {
		console.log(props)
   }
} );

これだけで、アイコンが表示されます
*「edit.jsとsave.js」を削除しているのでエラーになりますがw

デベロッパーコンソールで確認
コンテンツは props .attributes.messageで参照されます

propsをコンソールで確認

「edit」の引数には通常は「setAttributes関数とattributes」を分割代入でわたします(これで、例えばコンテンツはattributes.messageで参照されます)
「attributesは各属性の値」「setAttributesは属性を更新する関数」です
その他に「isSelected(ブロックが現在選択されているか)」などがあります
{ isSelected && (trueの処理) }

registerBlockType('create-block/test', {
	edit: function ({ attributes, setAttributes }) {
		
	},
} );

「TextControlコンポーネント(input)」をつかいます
属性
label:ラベル
type : デフォルトはtext
value : 入力の現在の値
onChange : 入力の値を受け取る関数(「setAttributes」をつかい messageの値を更新)
「useBlockProps」で「Reactフック」がつかえます

import { __ } from '@wordpress/i18n';
import { registerBlockType } from '@wordpress/blocks';
import { useBlockProps } from '@wordpress/block-editor';
import { TextControl } from '@wordpress/components';

registerBlockType('create-block/test', {
//edit: function()は省略記法にしました
 edit({ attributes, setAttributes }) {
		const blockProps = useBlockProps();
		return (
			<div { ...blockProps }>
				<TextControl
					label={ 'メッセージ' }
					value={ attributes.message }
					onChange={ ( val ) => setAttributes( { message: val } ) }
				/>
			</div>
		);
 },
} );

「edit」での「useBlockProps」について
*APIのVersion2から「useBlockProps」が必要だそうです
ブロックのラッパー要素に「className」を展開します(ブロックラッパー要素に 「useBlockPropsから取得したprops」を適用します)
<div { …useBlockProps() }>という感じ

*「className」を追加したいときは「useBlockProps」の引数にわたします
useBlockProps( { className: 'hoge'} );

「’create-block/test’で登録したブロック」のクラス名は「wp-block-create-block-test」です

style.scssファイル」は閲覧側とエディタ内両方に反映するスタイル
editor.scssファイル」はエディタ内でのみ反映するスタイル

「save」はブロックのオブジェクトをシリアライズしてデータベース( post_contentフィールド) に保存します
引数には、基本的に「attributes」を分割代入でわたします

import { __ } from '@wordpress/i18n';
import { registerBlockType } from '@wordpress/blocks';
import { useBlockProps } from '@wordpress/block-editor';
import { TextControl } from '@wordpress/components';
import './style.scss';
import './editor.scss';

registerBlockType('create-block/test', {
	 edit( { attributes, setAttributes } ) {
     const blockProps = useBlockProps();
     return (
      <div { ...blockProps }>
        <TextControl
          label={ 'メッセージ' }
          value={ attributes.message }
          onChange={ ( val ) => setAttributes( { message: val } ) }
         />
      </div>
     );
	},
//ここから
	 save( { attributes } ) {
     const blockProps = useBlockProps.save();
     return <div { ...blockProps }>{ attributes.message }</div>;
    }
//ここまで
} );

「save」での「useBlockProps」について
静的ブロックをレンダーするときは、ブロックのラッパー要素 useBlockProps.save() から返されるpropsを適用します
*これでHTML属性やブロックのクラス名が正しくレンダーされます

最初のカスタムブロック

反映しないときは
npm startをやり直します
ブラウザのキャッシュをクリア
buildディレクトを削除する
サーバー側のキャッシュ設定を確認する
バージョンをあげてみるなど…w
パソコンのスペックの問題なのか😂 エラーなのか反映していないのかわからないことがしばしば💦
(@wordpress/create-blockはかなりの重さです)

ブロックツールバーとサイドバー

ブロックエディタで使えるさまざまな「Reactのコンポーネント」があります

WordPressd Reactのコンポーネント

ブロックツールバー

サイドバー

@wordpress/block-editorパッケージ(wp.editor
「ブロックの値の読み込みと保存のメカニズム」を投稿とそのコンテンツに関連付けます
投稿オブジェクトの操作に関連するさまざまなコンポーネント(投稿タイトル入力コンポーネントなど)を提供
RichText・BlockControlsなど、おもにブロックツールバーでつかうコンポーネントが含まれています
@wordpress/componentsパッケージ (wp.components
汎用的なコンポーネント(画面間で共有される共通のUI要素とWordPressダッシュボードの機能を作成するために使用される)
スタイルを追加するためのCSSが含まれてるときはCSSを追加します
おもにサイドバーでつかいます

RichText

リッチテキストは、任意のブロックレベル要素(div・h2・p など)に編集可能なコンテナを設定でき、style.cssだけでフロントエンドとエディタにスタイルが適用できます(editor.css が不要)

「block.jsonファイルのattributes」を変更しています

{
    "$schema": "https://schemas.wp.org/trunk/block.json",
    "apiVersion": 2,
    "name": "create-block/test",
    "version": "0.1.0",
    "title": "Test",
    "category": "widgets",
    "icon": "smiley",
    "description": "test",
    "attributes": {
        "content": {
            "type": "string",
            "source": "html",
            "selector": "p"
        }
    },
    "supports": {
        "html": true
    },
    "textdomain": "test",
    "editorScript": "file:./build/index.js",
    "editorStyle": "file:./build/index.css",
    "style": "file:./build/style-index.css"
}

リファレンスの例をほぼそのままコピーしてindex.jsに
onChange={ ( content ) => setAttributes( { content } ) }
*{ content : content }なら{ content }に省略できるので、あわせるようにしますw

import { registerBlockType } from '@wordpress/blocks';
import { useBlockProps, RichText } from '@wordpress/block-editor';
import './style.scss';
import './editor.scss';

registerBlockType( 'create-block/test', {

edit( { attributes, setAttributes } ) {
const blockProps = useBlockProps();
return (
    <RichText
    { ...blockProps }
    tagName="p"
    value={ attributes.content }
    //コンテンツは太字、斜体、リンクはできるが、他のフォーマットオプションは許可されない
    allowedFormats={ [ 'core/bold', 'core/italic', 'core/link' ] }
    onChange={ ( content ) => setAttributes( { content } ) }
    placeholder= '...'
    />
);
},

save( { attributes } ) {
const blockProps = useBlockProps.save();
    return <RichText.Content { ...blockProps }
    tagName="p"
    value={ attributes.content } />;
    }
} );
太字、斜体、リンクが表示されています

BlockControls

editの戻り値にBlockControlsを加えるとツールバーにそのcontrolsが表示されます

*「controls」はボックスシャドウをつける際につかいます(最後)

<BlockControls
    //controlsでオリジナルを追加できます
   controls={[
     {
      icon: "...",
      title: "・・・",
      onClick: 関数
      isActive: Boolean
     }
   ]}
 >

「BlockControlsとAlignmentToolbar」のつかい方
「block.jsonファイルのattributes」に「alignment」を追加します

    "attributes": {
        "content": {
            "type": "string",
            "source": "html",
            "selector": "p"
        },
        "alignment": {
            "type": "string"
        }
    },

「index.js」ファイルを編集します
AlignmentToolbarは選択したアイコンで値として「right・center・left」が取得できます

import { __ } from '@wordpress/i18n';
import { registerBlockType } from '@wordpress/blocks';
//AlignmentToolbar, BlockControlsを追加
import { useBlockProps, RichText, AlignmentToolbar, BlockControls } from '@wordpress/block-editor';
import './style.scss';
import './editor.scss';

registerBlockType('create-block/test', {
	edit( { attributes, setAttributes } ) {
	//attributesのキーは分割代入
	const { alignment, content, } = attributes;
	//値を更新する関数
	const onChangeAlignment = ( newAlignment ) => {
	setAttributes( {alignment: newAlignment === undefined ? 'none' : newAlignment, } );
	};
return (
	<>
{/* //AlignmentToolbar, BlockControlsコンポーネントを追加 */}
		<BlockControls>
			<AlignmentToolbar
			value={ alignment }
			onChange={ onChangeAlignment }
			/>
		</BlockControls>
		<RichText  { ...useBlockProps() }
			allowedFormats={ [ 'core/bold', 'core/italic', 'core/link' ] }
			tagName="p"
			onChange={ ( content ) => setAttributes( { content } ) }
			value={content}
//style属性を追加します
			style={{textAlign: alignment}}
		/>
	</>
	);
},

save( {attributes} ){
//attributesのキーは分割代入
	const { alignment, content, } = attributes;
return (
	<>
	<RichText.Content { ...useBlockProps.save() }
		tagName="p"
		value={content}
//style属性を追加します
		style={{textAlign: alignment}}
	/>
	</>
	);
},

} );
AlignmentToolbar

InspectorControls

サイドバーに「InspectorControlsとPanelColorSettings」をつかいカラーパレットを追加します
「block.jsonファイルのattributes」に「textColorとbackgroundColor」を追加します

    "attributes": {
        "content": {
            "type": "string",
            "source": "html",
            "selector": "p"
        },
        "alignment": {
            "type": "string"
        },
        "textColor": {
            "type": "string"
        },
        "backgroundColor": {
            "type": "string"
        }
    },

「InspectorControls」はサイドバーのコンポーネントです
その中に「PanelColorSettings」をセットします

import { __ } from '@wordpress/i18n';
import { registerBlockType } from '@wordpress/blocks';
//InspectorControls,PanelColorSettings,ContrastCheckerを追加
import {
	useBlockProps,
	RichText,
	AlignmentToolbar,
	BlockControls,
	InspectorControls,
	PanelColorSettings,
	ContrastChecker
} from '@wordpress/block-editor';
import './style.scss';
import './editor.scss';

registerBlockType('create-block/test', {
	edit( { attributes, setAttributes } ) {
//attributesのキーは分割代入
	const { alignment, content, backgroundColor, textColor} = attributes;
	const onChangeAlignment = ( newAlignment ) => {
		setAttributes( {
		alignment: newAlignment === undefined ? 'none' : newAlignment,
		} );
	};
 return (
	<>
{/* //InspectorControls,PanelColorSettings,ContrastCheckerを追加 */}
	<InspectorControls>
		<PanelColorSettings
			title={"color"}
			colorSettings={[
				{
					value: backgroundColor,
					onChange: (backgroundColor)=>{setAttributes( { backgroundColor } )},
					label: "BackgorundColor"
				},
				{
					value: textColor,
					onChange: (textColor)=>{setAttributes( {textColor } )},
					label: "TextColor"
				}
			]}
		>
{/* //ContrastCheckerは色の組み合わせをチェックをします */}
			<ContrastChecker
				textColor={textColor}
				backgroundColor={backgroundColor}
			/>
		</PanelColorSettings>
	</InspectorControls>
	<BlockControls>
		<AlignmentToolbar
			value={ alignment }
			onChange={ onChangeAlignment }
		/>
	</BlockControls>
	<RichText  {...useBlockProps()}
		allowedFormats={ [ 'core/bold', 'core/italic', 'core/link' ] }
		tagName="p"
		onChange={ ( content ) => setAttributes( { content } ) }
		value={content}
//スタイルを追加
		style={{textAlign: alignment, backgroundColor: backgroundColor ,color: textColor}}
	/>
	</>
	);
	},

	save({ attributes }) {
//attributesのキーは分割代入
	const { alignment, content, backgroundColor, textColor} = attributes;
return (
	<>
	<RichText.Content { ...useBlockProps.save() }
		tagName="p"
		value={content}
//スタイルを追加
		style={{textAlign: alignment, backgroundColor: backgroundColor ,color: textColor}}
	/>
	</>
	);
	},

} );
PanelColor

PanelBodyとRangeControl

「PanelBodyとRangeControl」をつかいボックスにシャドウをつけます
「block.jsonファイルのattributes」に「shadowとshadowOpactiy」を追加します

{
	"$schema": "https://schemas.wp.org/trunk/block.json",
	"apiVersion": 2,
	"name": "create-block/test",
	"version": "0.1.0",
	"title": "Test",
	"category": "widgets",
	"icon": "smiley",
	"description": "test",
	"attributes": {
		"content": {
			"type": "string",
			"source": "html",
			"selector": "p"
		},
		"alignment": {
			"type": "string"
		},
		"shadow":{
			"type": "boolean",
			"default": false
		},
		"shadowOpactiy": {
			"type": "number",
			"default": 0.3
		},
		"textColor": {
			"type": "string"
		},
		"backgroundColor": {
			"type": "string"
		}
	},
	"supports": {
		"html": true
	},
	"textdomain": "test",
	"editorScript": "file:./build/index.js",
	"editorStyle": "file:./build/index.css",
	"style": "file:./build/style-index.css"
}

1:「BlockControlsのcontrols」でシャドウの「ON・OFF」を切り替えます
2:サイドバーは「PanelBody」でtitleを指定すると折りたたみできます
シャドウがオンのときだけ「RangeControl」を表示、値を調整します
3:今までインラインスタイルで対応していましたが💦 classを追加してスタイルをかきます
「useBlockPropsで展開しているclassName」に「classnames」をつかって追加します
const blockProps = useBlockProps( {
className: someClassName,
style: { color: 'blue' },
} );

*インラインスタイルもこのようにつかうようですw

import { __ } from '@wordpress/i18n';
import { registerBlockType } from '@wordpress/blocks';
import {
    useBlockProps,
    RichText,
    AlignmentToolbar,
    BlockControls,
    InspectorControls,
    PanelColorSettings,
    ContrastChecker
} from '@wordpress/block-editor';
//Classnamesをつかって複数のクラスを指定します
import classnames from "classnames";
// RangeControl, PanelBodyを追加
import { RangeControl, PanelBody } from "@wordpress/components";
import './style.scss';
import './editor.scss';

registerBlockType('create-block/test', {
    edit( { attributes, setAttributes } ) {
//attributesのキーは分割代入
    const { alignment, content, backgroundColor, textColor, shadow, shadowOpacity} = attributes;
    const onChangeAlignment = ( newAlignment ) => {
        setAttributes( {
        alignment: newAlignment === undefined ? 'none' : newAlignment,
        } );
    };
//useBlockPropsにclassnamesをわたします
//has-shadowクラスはshadowがtureのとき
//shadow-opacity-[数値✖️100]クラスはRangeControlの数値により変化
    const blockProps =  useBlockProps( {
        className: classnames( {
            "has-shadow": shadow,
            [`shadow-opacity-${shadowOpacity * 100}`]: shadowOpacity
        } )
    });
 return (
    <>
{/* //InspectorControls,PanelColorSettings,ContrastCheckerを追加 */}
    <InspectorControls>
{/* //PanelBodyにRangeControlを追加0.1~0,4の値で0.1刻み */}
        <PanelBody title={"Setting"}>
{/* //shadowがtrueのときにRangeControlを表示 */}
        {shadow && (
            <RangeControl
                label={"Shadow Opacity"}
                value={shadowOpacity}
                onChange={( shadowOpacity ) => setAttributes( {shadowOpacity } )}
                min={0.1}
                max={0.4}
                step={0.1}
            />
        )}
        </PanelBody>
        <PanelColorSettings
            title={"color"}
            colorSettings={[
                {
                    value: backgroundColor,
                    onChange: (backgroundColor)=>{setAttributes( { backgroundColor } )},
                    label: "BackgorundColor"
                },
                {
                    value: textColor,
                    onChange: (textColor)=>{setAttributes( {textColor } )},
                    label: "TextColor"
                }
            ]}
        >
{/* //ContrastCheckerは色の組み合わせをチェックをします */}
            <ContrastChecker
                textColor={textColor}
                backgroundColor={backgroundColor}
            />
        </PanelColorSettings>
    </InspectorControls>
{/* //シャドウの「ON・OFF」を切り替え*/}
    <BlockControls
        controls={[
        {
            icon: "chart-bar",
            title: "Shadow",
            onClick: () => {setAttributes({shadow: !shadow})},
            isActive: shadow
        }
        ]}
    >
        <AlignmentToolbar
            value={ alignment }
            onChange={ onChangeAlignment }
        />
    </BlockControls>
{/* blockPropsを展開 */}
    <RichText  {...blockProps}
        allowedFormats={ [ 'core/bold', 'core/italic', 'core/link' ] }
        tagName="p"
        onChange={ ( content ) => setAttributes( { content } ) }
        value={content}
        style={{textAlign: alignment, backgroundColor: backgroundColor ,color: textColor}}
    />
    </>
    );
    },

    save({ attributes }) {
//attributesのキーは分割代入
    const { alignment, content, backgroundColor, textColor, shadow, shadowOpacity} = attributes;
//useBlockProps.saveにclassnamesをわたします
    const blockProps =  useBlockProps.save( {
        className: classnames( {
            "has-shadow": shadow,
            [`shadow-opacity-${shadowOpacity * 100}`]: shadowOpacity
        } )
    } );

return (
    <>
{/* blockPropsを展開 */}
    <RichText.Content { ...blockProps }
        tagName="p"
        value={content}
        style={{textAlign: alignment, backgroundColor: backgroundColor ,color: textColor}}
    />
    </>
    );
    },

} );

style.scssの例です

.wp-block-create-block-test {
    padding: 1rem;
  &.has-shadow{
    box-shadow: 5px 5px 10px rgba(0,0,0,0.2);
    &.shadow-opacity-10 {
        box-shadow: 5px 5px 10px rgba(0, 0, 0, 0.1);
        }
    &.shadow-opacity-20 {
        box-shadow: 5px 5px 10px rgba(0, 0, 0, 0.2);
        }
    &.shadow-opacity-30 {
        box-shadow:5px 5px 10px rgba(0, 0, 0, 0.3);
        }
     &.shadow-opacity-40 {
        box-shadow: 5px 5px 10px rgba(0, 0, 0, 0.4);
        }
    }
}

editor.scssの例

.wp-block-create-block-test{
    border: solid 1px #eee;
}
RangeControlとBlockControls

スタイルの追加について

ラッパー要素にクラスを追加するのは簡単で「block.jsonファイル」に「styles」を追加します
*”isDefault”: trueはスタイルを初期値にするとき

"styles": [
		{
			"name": "rounded",
			"label": "Rounded",
			"isDefault": true
		},
		{
			"name": "outline",
			"label": "Outline"
		},
		{
			"name": "squared",
			"label": "Squared"
		}
	],

選択したクラスが追加されます(p class="wp-block-create-block-test is-style-rounded"
is-style-[選択したname]クラスが追加されるので「style.scssファイルに」それぞれのスタイルを追加します
*反映しないときはキャッシュクリア・npm startなどw

ブロックエディタ

ちなみに、カスタムブロックと関係ありませんが
register_block_styleでコアブロックにスタイルを適応できます
下記コードで「Pブロック」に「is-style-rounded」を追加して、インラインスタイル適応しています

register_block_style(
//ブロックの名前
 'core/paragraph',
     array(
    'name'  => 'rounded',
    'label' => 'Rounded',
//インラインスタイルまたは
//エンキューしているスタイルシートのハンドルを追加
    'inline_style' => '.is-style-rounded {
        border-radius: 6px;
    }',
    )
);

ブラウザのコンソールでwp.blocks.getBlockTypes()と入力するとブロック名の完全なリストが表示されます

余談複数のブロックを同じ環境で作れないかとググったものの・・・わからないことが多すぎw
深みにハマりそうなので諦めます💦

,