概要
Wordpressにはルビを振る機能がないので書式ツールバー - Format APIにてルビを挿入できるようなカスタムフォーマットを作成
要件定義
- 選択したテキスト部分にルビを入れるようにする
- ルビを入力するフォームはPopoverにて選択テキストの下に表示されるようにする
仕様
-
RichTextToolbarButton
にカスタムボタン - “ルビを追加”ボタンを追加しそれをクリックするとPopoverにてルビの入力欄が表示する - Popoverには入力欄と”ルビを追加”ボタンのみとする
- 一度ルビを設定した箇所の編集機能は追加せずに、該当箇所を選択した場合には
RichTextToolbarButton
の表示は”ルビを削除”ボタンと表示を変えて、一旦削除させるようにする - 入力されるテキストはサニタイズをしておく
2024.08.13修正
WordPresss6.6になり、新しい React JSX トランスフォーム(React 17以降)になったりで@wordpress/scriptでのbuildも@wordpress/scriptバージョン28.x.xで行わないとエラーになります。
参考URL
https://make.wordpress.org/core/2024/06/06/jsx-in-wordpress-6-6/
また、エディタもiframe化などが行われてきているので、その辺りの修正を追加しました。
実装
実装は@wordpress/scriptを使用して作成します。
コード全体
import { useState, useEffect, useRef } from "@wordpress/element";
import { Popover, Button,FontSizePicker } from "@wordpress/components";
import { registerFormatType, applyFormat, removeFormat, getActiveFormat, useAnchor} from '@wordpress/rich-text';
import { RichTextToolbarButton, RichTextShortcut } from '@wordpress/block-editor';
import { __ } from "@wordpress/i18n";
import './editor.scss';
import './style.scss';
const font_size_icon = (<svg id="b" data-name="layer" xmlns="http://www.w3.org/2000/svg" width="293" height="219" viewBox="0 0 293 219">
<g id="c" data-name="container">
<g id="d" data-name="group">
<path id="e" data-name="path" d="m240.95,55.63h-40.35l-22.48,70.54,17.29,54.26h52l10.99,38.56h34.6l-52.05-163.37Zm-38.92,96.45s15.4-55.6,18.75-69.07c3.34,13.47,18.75,69.07,18.75,69.07h-37.49Z" stroke-width="0"/>
<path id="f" data-name="path" d="m123.87,0h-54.1L0,219h46.39l14.73-51.7h71.42l14.73,51.7h46.39L123.87,0Zm-52.18,129.29s20.65-74.53,25.13-92.59c4.48,18.05,25.13,92.59,25.13,92.59h-50.26Z" stroke-width="0"/>
</g>
</g>
</svg>);
const MyFontSizeChange = (props) => {
const { isActive, value, onChange, contentRef } = props;
/**
* Popover の設定
*/
//Popoverの開閉状態をuseStateで管理するため
const [isPopoverOpen, setIsPopoverOpen] = useState(false);
//選択したテキストの情報ObjectをuseStateで管理するため
//useAnchorを使用するため不必要に
//const [popoverPosition, setPopoverPositon] = useState(null);
//フォントサイズを格納する変数をuseStateで管理
const [fontSize, setFontSizeState] = useState(null);
const [currentFontSize, setCurrentFontSize] = useState(null);
const [currentValueStart, setCurrentValueStart] = useState(value.start);
const useRefFont = useRef(null);
const useRefSize = useRef('1em');
const fontSizes = [
{
name: __('極小','efc-block'),
slug: 'xsmall',
size: "0.65em",
},
{
name: __('小','efc-block'),
slug: 'small',
size: "0.75em",
},
{
name: __('中','efc-block'),
slug: 'middule',
size: "1.25em",
},
{
name: __('大','efc-block'),
slug: "large",
size: "1.5em"
},
{
name: __('特大','efc-block'),
slug: "xtralarge",
size: "1.75em"
},
];
/**
*
* @returns
* 選択テキストのObjectを取得して存在したら返す
* 存在しなかったら選択しているブロックの要素(段落)などを返す
*
* useAnchorをしようするので不必要に
const getSelectedRange = () => {
const selection = window.getSelection();
const range = selection.getRangeAt(0);
return range ? range : contentRef.current
}
*/
/**
* useAnchorの設定
* useAnchorのsettingsにFormatTypeの情報+isActiveを渡すことで
* 正確な位置を取得してくれる
*/
const settings = {
name:'efc-format/fzs',
title: __('font size select', 'efc-block'),
tagName: 'span',
className: 'has-font-size-picker',
attributes: {
style: '',
},
edit: MyFontSizeChange,
};
const popoverAnchor = useAnchor({
editableContentElement: contentRef.current,
settings: { ...settings,isActive }
});
/**
* RichTextToolbarButtonで追加したボタンをクリックした時の処理
* Popoverを開く処理
*/
const openPopover = () => {
if (!isActive) {
applyFontSizeFormat("1em");//初回のみ最初に設定
}
/**
* useAnchorを使用するので不必要に
if (contentRef.current) {
//Popoverの表示位置のため選択テキストのObjectをセットしておく
//setPopoverPositon(getSelectedRange());
}
*/
setIsPopoverOpen(true);
};
/** Popoverを閉じる関数 */
const closePopover = () => {
setIsPopoverOpen(false);
}
/**
* Font Size の変更するための設定
* フォントサイズを反映させる
*/
const applyFontSizeFormat = (fontSize) => {
setCurrentFontSize(fontSize);
useRefFont.current = `--custom-font-size:${fontSize}`;
const myId = `cfz-${crypto.randomUUID()}`;
onChange(
applyFormat(value, {
type: 'efc-format/fzs',
attributes: {
style: useRefFont.current,
id: myId,
}
})
)
};
//設定を削除
const removeFontSizeFormat = () => {
setFontSizeState(null);
setCurrentFontSize(null);
closePopover();
return (
onChange(removeFormat(value,'efc-format/fzs'))
);
};
//前回のフォントサイズと違う場合のみappleFormatを実行させる
useEffect(() => {
if (currentFontSize !== fontSize) {
applyFontSizeFormat(fontSize);
closePopover();
}
}, [fontSize]);
//Confirm Font Size
const conformFontSize = (newFontSize) => {
setFontSizeState(newFontSize);
}
/**
* カーソル位置が同じブロック内だとPopoverが閉じない対策
* startが変わると閉じるように設定
*/
if (currentValueStart !== value.start) {
setCurrentValueStart(value.start);
setFontSizeState(null);
setCurrentFontSize(null);
setIsPopoverOpen(false);
}
/**------- Copmonentの設定---------- */
/**
* Popoverコンポーネントの設定
*/
const MyFontSizePoporver = () => {
useEffect(() => {
if (isActive) {
const activeFormat = getActiveFormat(value, 'efc-format/fzs');
const chkAttributes = activeFormat.unregisteredAttributes ? activeFormat.unregisteredAttributes : activeFormat.attributes;
const styledFontsize = chkAttributes.style.replace('--custom-font-size:', '');
setFontSizeState(styledFontsize);
setCurrentFontSize(styledFontsize);
useRefSize.current = styledFontsize;
}
},[]);
/**
* FontSicePickerオーバーライド
* FontSizePickerにはonClickイベントがないので、divでラップしてそれにonClickを設置
*
* FontSicePickerのonChangeがcurrentの場合でも一旦ほかに変更したら動作することを確認
const popoverClick = (target) => {
//Popover のどこでもクリックしたら反応するのでターゲットを絞る処理を追加
const parent = target.closest('button');
const chkIDReg = /toggle-group-control-as-radio-group[-\d]+/;
if (parent !== null && parent.id && parent.getAttribute("id").match(chkIDReg)) {
const newFontSize = parent.getAttribute("data-value");
useRefSize.current = newFontSize;
const activeFormat = getActiveFormat(value, 'efc-format/fzs');
const chkAttributes = activeFormat.unregisteredAttributes ? activeFormat.unregisteredAttributes : activeFormat.attributes;
const fontSizeSpanDOM = document.getElementById(chkAttributes.id);
//選択したテキストの見た目の大きさのみ変更 confirmButtonクリックでuseState走らせ値をキチンと保存
fontSizeSpanDOM.style.setProperty('--custom-font-size', newFontSize);
}
}
*/
const fontSizeChange = (newFontSize) => {
useRefSize.current = newFontSize;
const activeFormat = getActiveFormat(value, 'efc-format/fzs');
const chkAttributes = activeFormat.unregisteredAttributes ? activeFormat.unregisteredAttributes : activeFormat.attributes;
if (chkAttributes) {
const fontSizeSpanDOM = contentRef.current.querySelector(`#${chkAttributes.id}`);
//選択したテキストの見た目の大きさのみ変更 confirmButtonクリックでuseState走らせ値をキチンと保存
fontSizeSpanDOM.style.setProperty('--custom-font-size', newFontSize);
}
}
return (
<Popover
anchor={popoverAnchor}
placement="bottom-start"
focusOnMount={false}
>
<div className="efc-fontSize-popover-content">
<FontSizePicker
fontSizes={fontSizes}
value={ useRefSize.current }
disableCustomFontSizes={true}
units={["em"]}
onChange={(newFontSize) => {
fontSizeChange(newFontSize);
} }
/>
<p className="popoverInfo"><span className="attention">サイズ選択後「決定」クリックで変更が反映</span></p>
<div className="buttonWrapper">
<Button
className="clearButton"
onClick={removeFontSizeFormat}
>{ __('クリア','efc-block') }</Button>
<Button
variant="secondary"
className="confirmButton"
onClick={() => {
const activeFormat = getActiveFormat(value, 'efc-format/fzs');
//選択して反映させたFormatの情報(attributes)を取得
const chkAttributes = activeFormat.unregisteredAttributes ? activeFormat.unregisteredAttributes : activeFormat.attributes;
//const fontSizeSpanDOM = document.getElementById(chkAttributes.id);
//contentRef.currentにはFormatを反映させたテキストのritch-textのObjectが入っているのでそこからFormatを適応させたElementを取得
const fontSizeSpanDOM = contentRef.current.querySelector(`#${chkAttributes.id}`);
const styledFontsize = chkAttributes.style.replace('--custom-font-size:', '');
fontSizeSpanDOM.style.setProperty('--custom-font-size', styledFontsize);
if (styledFontsize === '1em') {
removeFontSizeFormat();
} else {
closePopover();
}
}}
>{ __('キャンセル','efc-block') }</Button>
<Button
variant='primary'
className="confirmButton"
onClick={() => {
if (useRefSize.current === '1em') {
alert("フォントサイズを選択してください!");
}
else if (useRefSize.current === fontSize) {
closePopover();
} else {
conformFontSize(useRefSize.current);
}
}}
>{ __('決定','efc-block') }</Button>
</div>
</div>
</Popover>
);
};
/**
* toolbar buttonの設定
* RichTextToolbarButtonの設定
*/
//ショートカット用の設定
const shortcutType = 'primaryAlt';
const shortcutCharacter = 'f';
//ボタンのテキストを設定
const buttonText = __('フォントサイズ変更', 'efc-block');
//RichTextToolbarButtonの設定
return (
<div>
<RichTextShortcut type={shortcutType} character={shortcutCharacter} onUse={openPopover} />
<RichTextToolbarButton
icon={font_size_icon}
className="rubySetBtn"
title={buttonText}
onClick={ () => {
openPopover();
}}
isActive={ isActive }
shorcutType={shortcutType}
shorcutCharacter={shortcutCharacter}
/>
{
//Popover
isPopoverOpen &&
(<MyFontSizePoporver></MyFontSizePoporver>)
}
</div>
);
}
registerFormatType('efc-format/fzs', {
title: __('font size select', 'efc-block'),
tagName: 'span',
className:'has-font-size-picker',
attributes: {
'style': '',
'id':''
},
edit: MyFontSizeChange,
});
各コードの解説
使用するコンポーネントの読み込み
import { Fragment, useState} from "@wordpress/element";
import { Popover, Button, TextControl } from "@wordpress/components";
import { registerFormatType, applyFormat, removeFormat, getActiveFormat, insert, replace,
+ useAnchor
} from '@wordpress/rich-text';
import { RichTextToolbarButton, RichTextShortcut} from '@wordpress/block-editor';
import { __, sprintf, _n } from "@wordpress/i18n";
/**
* HTMLサニタイズをしてくれるnode moduleを読み込む
*
*/
import DOMPurify from 'dompurify';
import './editor.scss';
ルビ用のテキストを挿入したりするので、@wordpress/rich-text
のinsert
やreplace
を使用します。
ルビテキストをユーザーが入力するので、入力の値をサニタイズなどを行なってから保存したいため上記のモジュールをインストールしています。
FormatTypeの登録
registerFormatType
でFormatType
を登録します。今回はルビなので、rubyタグとrtタグの二つのタグを使うので、それぞれ別のFormatType
として登録します。
/**
* rtタグ用のFormat Typeを登録
*/
registerFormatType( 'efc-format/rt', {
title: __( 'Ruby Character', 'efc-block' ),
tagName: 'rt',
className: null,
edit( {isActive, value, onChange} ) {
return <Fragment></Fragment>
}
} );
/**
* rubyタグ用のFormat Typeを登録
*/
registerFormatType( 'efc-format/ruby', {
title: __( 'Ruby Wrapper', 'efc-block' ),
tagName: 'ruby',
className: null,
attributes: {
'data-rt': '',//ルビのテキストをdate-rt属性として保存しておく
},
edit: MyCustomButton,//テキストにFotmatを適応するためのボタン設定
} );
ruby
タグ用のFormatType
にはedit
プロパティにボタンやPopoverなどを設定したコンポーネントMyCustomButton
を登録。
attributes
には’data-rt’
としてrt
タグに入れるルビテキストを格納するように設定しておく。
RichTextToolbarButton
はruby
タグ用のFormatType
にのみあれば良いので、rt
タグ用のFormatType
のeditプロパティには空のものを入れておきます。
MyCustomButtonコンポーネントの設定
MyCustomButton
コンポーネントには、RichTextToolbarButton
コンポーネントやPopover
での入力欄、データの反映/保存などを設定します。
MyCustomButton
コンポーネントに渡させるpropsの内isActive
, value
, onChange
, contentRef
を使用します。
const MyCustomButton = (props) => {
/** propsからisActive、value、onChange を取り出して使う */
const { isActive, value, onChange, contentRef } = props;
....
}
- isActive : カスタムフォーマットが該当テキストに反映されているか
- value : 設定の値
- onChange : onChange関数は、変更された入力値を処理するために使用
- contentRef : ブロックのコンテンツが入った React 要素
- activeAttributes : 設定されたattributes
※ただし場合により上手く値を取得できない場合ありなので今回は未使用
変数/useStateなどの設定
const MyCustomButton = (props) => {
/** propsからisActive、value、onChange を取り出して使う */
const { isActive, value, onChange, contentRef } = props;
//入力されたルビテキストを一時的に格納する変数
let rubyText;
//Popoverの開閉状態をuseStateで管理するため
const [isPopoverOpen, setIsPopoverOpen] = useState(false);
//これはPopoverのanchorに使用するためのもので
//選択したテキストの情報ObjectをuseStateで管理するため
const [popoverPosition, setPopoverPositon] = useState(null);
....
}
rubyText
変数は一時的に使用するため通常の変数として宣言しています。useRef
の設定の方がReact的に良いのですが、useRef
での管理では更新のタイミングなどで問題が出るケースがあったので通常の変数にしています。
popoverPosition
はPopover
の表示位置を設定するanchor
プロパティに入れるために選択したテキストのObject情報を入れておくようにします。
RichTextToolbarButtonコンポーネントの設定
//ショートカット用の設定
const shortcutType = 'primaryAlt';
const shortcutCharacter = 'r';
//ボタンのテキストを設定
const buttonText = isActive ? __('ルビを削除', 'efc-block') : __('ルビを追加', 'efc-block');
//RichTextToolbarButtonの設定
return (
<div>
<RichTextShortcut type={shortcutType} character={shortcutCharacter} onUse={openPopover} />
<RichTextToolbarButton
icon="edit"
className="rubySetBtn"
title={buttonText}
onClick={ async () => {
openPopover();
}}
shorcutType={shortcutType}
shorcutCharacter={shortcutCharacter}
/>
{
//Popover
isPopoverOpen &&
(<MyPoporver></MyPoporver>)
}
</div>
);
RichTextToolbarButton
コンポーネントとショートカット用のRichTextShortcut
コンポーネントでruby用のツールバーボタンの設定をします。
isActive
でテキスト文字の表示を振り分けています。
onClick
プロパティにopenPopver
関数を渡してクリックするとPopover
が表示されます。
openPopover関数
RichTextToolbarButton
のonClick
プロパティに登録している関数で、ルビ登録用のPopover
を表示したり、ルビを削除したりする機能を入れています。
//RichTextToolbarButtonで追加したボタンクリックした時の処理
const openPopover = () => {
if (isActive) {
//すでにルビが振ったあるテキストが選択されていたら削除関数を実行
removeRuby()
} else {
if (contentRef.current) {
//選択テキストのObjectをセットしておく
setPopoverPositon(getSelectedRange());
}
//Popoverを開らかせるフラグをtrueに
setIsPopoverOpen(true);
}
}
removeRuby関数
ルビを削除する関数です。
すでにルビが振ったあるテキストが選択されていたら削除関数を実行する様にしています。
ルビの場合ルビテキスト(ひらがな/カタカナ)の文字分ルビがない場合より長くなっています。その分をMyCustomButtonコンポーネントに渡されるvalueに格納されているテキストから削除しなければなりません。しかしvalueは読み取り専用となるので、一旦別の変数に格納してそれを使いテキストのルビ部分をreplace
メソッドで削除する処理を行なっています。
const removeRuby = () => {
//valueが読み取り専用となるので一旦currentValueへ入れて加工していく
let currentValue = value;
//状態を取得して設定しているdata-rubyからルビテキストを取得しておく
const activeFormat = getActiveFormat(currentValue, 'efc-format/ruby');
/**
* attributesはformatTypeを設定直後の状態ではatteributesで取得となるが
* ページをリロード or 遷移した後ではunregisteredAttributesでの取得となるのでその振りわけ
*/
const chkAttributes = activeFormat.unregisteredAttributes ? activeFormat.unregisteredAttributes : activeFormat.attributes;
const rubyText = chkAttributes['data-ruby'];
//選択テキストの開始/終了の位置を変数に入れておく
const rubyEnd = currentValue.end;
const rubyStart = currentValue.start;
//まずはrubyタグのformatTypeの適応を解除
currentValue = removeFormat(currentValue, 'efc-format/ruby', rubyStart, rubyEnd);
//data-rubyから取得したテキストの開始位置を調べてrtStart変数に格納
const rtStart = currentValue.text.indexOf(rubyText);
//テキスト文字長から終端を調べてrtEndに格納
const rtEnd = rtStart + rubyText.length;
//上記設定の開始/終端を使用してrtタグのformatTypeの適応を解除
currentValue = removeFormat(currentValue, 'efc-format/rt', rtStart, rtEnd);
//replaceを使ってrubyTextで削除
currentValue = replace(currentValue, rubyText, '');
//整形した値をonChangeで返す
return onChange( currentValue );
}
MyPoporverコンポーネントの設定
Popover | Block Editor Handbook | WordPress Developer Resources
Popoverは上記のドキュメントを参考に設定します。
表示する位置に関してはanchor
プロパティとplacement
プロパティで設定します。
/**
*
* @returns Component
* Popoverコンポーネント
*/
const MyPoporver = () => {
return (
<Popover
onClose={closePopover}
anchor={popoverPosition}
placement="bottom-start"
>
<div className="efc-addRuby-popover-content">
<TextControl
label="ルビテキストを入力してください"
className="input-rubyText"
value={rubyText}
onChange={onInputChange} // テキスト入力の変更時にrubyText変数を更新
/>
<Button variant="primary" className="button-addRuby" onClick={onConfirm}>{__('ルビを追加', 'efc-block')}</Button>
</div>
</Popover>)
}
anchor
:Element | VirtualElement | null
ポップオーバーがアンカーとして使用する要素。これはElement
か、あるいはVirtualElement
-getBoundingClientRect()
とownerDocument
プロパティを定義したオブジェクト - になります。要素が変更されたときに確実にリアクティブに更新されるように、要素はプレーンなrefではなくstateに格納する必要があります。
とあるのでanchor
プロパティにはgetBoundingClientRect()が
定義されているObject
を登録する。window.getSelection()
にて選択したテキスト部分に関しての同等のObject
を取得できるので、前述のopenPopover
関数内で、後述のgetSelectedRange
関数を使用してpopoverPosition
に代入します。
TextControlコンポーネント
Popover
内にはTextControl
コンポーネントを配置して、こちらにルビてキストを入力できるようにしています。
onChange
プロパティにてonInputChange
関数を走らせてルビの内容をrubeText
変数に格納させています。
Buttonコンポーネント
onClick
プロパティにて入力したルビを適応するためのonConfirm
関数を実行します。
onInputChange関数
DOMPurifyモジュールを使ってサイニタイズしたものを変数に入れています。
/**
* @param {text} newRubyText ルビテキスト
* 処理でサニタイズなどをしている
*/
const onInputChange = (newRubyText) => {
//翻訳機能を使ってサニタイズでは漏れてしまう悪意のある文字列を変換するなど
rubyText = sprintf(_n('%s', 'efc-blocks'), newRubyText);
//HTMLサニタイズ
rubyText = DOMPurify.sanitize(rubyText);
};
rubyText = sprintf(_n('%s', 'efc-blocks'), newRubyText);
の箇所は翻訳機能を使いサニタイズでは漏れてしまう悪意のある文字列を置き換えるなどができます。
※ここの箇所は必要なければ省いてしまっても動作自体は問題ありません。
翻訳ファイルの作成/登録の方法などは手前味噌になりますが、
こちらなどが参考になるかと思います。
getSelectedRange関数
2024.08.13修正
こちらは、公式のuseAnchorを使用する形で対応できるので、不必要になります。
代わりに次項のuseAnchorを使用したpopoverAnchorを追加しました。
Popove
rのanchor
プロパティに選択テキストのObject情報を渡すためにObject情報を取得する関数。
window.getSelection()
にて選択したテキストの位置情報が取得できる。取得できなかった場合は、現在選択されているブロックの情報を渡しておく。
//選択テキストのObjectを取得して存在したら返す
//存在しなかったら選択しているブロックの要素(段落)などを返す
const getSelectedRange = () => {
const selection = window.getSelection();
const range = selection.getRangeAt(0);
return range ? range : contentRef.current
}
useAnchorモジュールを使用して選択したテキストの位置を取得する
/**
* useAnchorの設定
* useAnchorのsettingsにFormatTypeの情報+isActiveを渡すことで
* 正確な位置を取得してくれる
*/
const settings = {
name: 'efc-format/ruby',
title: 'ruby',
className: null,
attributes: {
'data-rt': ''
},
edit: MyCustomButton
};
const popoverAnchor = useAnchor({
editableContentElement: contentRef.current,
settings: { ...settings, isActive }
});
onConfirm関数
onConfirm
関数にて後述のルビのFormatType
を登録するonChangeCallback
関数とPopoverを閉じる関数を実行する。
/**
* 入力完了の処理
*/
const onConfirm = () => {
//Popoveを閉じる
closePopover();
//ルビを適応させる関数を実行
onChangeCallback();
}
onChangeCallback関数
const onChangeCallback = () => {
let ruby,currentValue;
ruby = rubyText;
/**
* window.promptなどで入力欄を設定している時は選択したテキストのfocusや選択状況は解除されないが
* Popoverで入力欄を作った場合にfocusがそちらに移ってしまう。
* そうするとvalueが読み取り専用になってしまうのでcurrentValue変数に入れて加工する
*/
currentValue = value;
if (ruby === '') return;
// ルビの親文字の位置を調べる変数に入れておく
const rubyEnd = currentValue.end;
const rubyStart = currentValue.start;
// currentValue(value)Objectのtextにrubyテキストを追加する
currentValue = insert(currentValue, ruby, rubyEnd);
//insertでテキストを追加するとstart/endが更新されるので再度代入する
currentValue.start = rubyStart;
currentValue.end = rubyEnd + ruby.length;
// rubyタグを適用。
currentValue = applyFormat(currentValue,
{
type: 'efc-format/ruby',
attributes: {
"data-ruby": ruby,//data-ruby属性にルビテキストを保存しておく / 削除する際に必要
}
},
rubyStart,
rubyEnd + ruby.length
);
//rtタグを適応
currentValue = applyFormat( currentValue, {
type: 'efc-format/rt'
}, rubyEnd, rubyEnd + ruby.length );
//整形した値をonChangeで返す
return onChange(currentValue);
}
closePopover関数
//Popoverを閉じる関数
const closePopover = () => {
setIsPopoverOpen(false);
}
WordPress6.6以前の環境に合わせるためのPolyfillの追加
WordPress6.6での使用のためにBuildは、@wordpress/scriptsのバージョン28.x.xで行う必要がありました。
単純のアップデートしてbuildを仕直しすれば6.6でのOKなのですが、そうすると6.5などそれ以前のWordPress環境でエラーになります。
対処法としては、下記のURLに記載されているPolyfillを読み込ませておくと大丈夫でした。
まずは 任意のディレクトリを作成してそこに以下の内容でpackage.jsonを作成します。
{
"name": "runtime",
"version": "1.0.0",
"main": "webpack.conf.js",
"scripts": {
"build:runtime": "webpack --config webpack.config.js"
},
"keywords": [],
"author": "",
"license": "ISC",
"description": "",
"devDependencies": {
"react": "^18.3.1",
"webpack": "^5.93.0",
"webpack-cli": "^5.1.4"
}
}
そしてインストールします。
$ npm i
同じ階層にwebpack.config.jsファイルを以下の記述で配置
const path = require('path');
module.exports = {
entry: {
'react-jsx-runtime': 'react/jsx-runtime',
},
output: {
path: path.resolve(__dirname, '../assets/js'),
filename: 'react-jsx-runtime.js',
library: {
name: 'ReactJSXRuntime',
type: 'window',
},
},
externals: {
react: 'React',
},
};
そして buildを実行
$ npm run build:runtime
そうするとassets/js
ディレクトリにreact-jsx-runtime.js
ファイルが作成されます。
PHPにて作成したreact-jsx-runtime.js
ファイルを読み込みます。
/**
* WordPress6.6以前用のPolyfill読み込み
*/
function my_plugin_block_editor_scripts() {
// JSXランタイムのポリフィルスクリプトを登録
wp_register_script(
'react-jsx-runtime',
plugins_url('assets/js/react-jsx-runtime.js', __FILE__),
[ 'react' ],
'18.3.0',
true
);
wp_enqueue_script('react-jsx-runtime');
}
add_action('enqueue_block_editor_assets', 'my_plugin_block_editor_scripts');
これで6.6以前のバージョンでも大丈夫になります。
参考URL