Code-Prettify でシンタックスハイライトするブロックの作成
Highlight.js を使ってシンタックスハイライトするカスタムブロックの作成方法を新しく掲載しましたので、よろしければ御覧ください。
WordPress Highlight.js カスタムブロックの作成
最新のブロック開発入門(2024/12):WordPress 初めてのブロック開発
以下は(旧)ブロックの作成 チュートリアル(削除予定)などの古い情報を参考にしているため部分的に古い情報になっています。
WordPress のブロックエディタ Gutenberg で記述したコードを Code-Prettify を使ってシンタックスハイライトするブロックを作成する方法の覚書です。
更新日:2024年12月13日
作成日:2020年11月02日
ブロックの基本的な作成方法や環境の構築方法については以下を御覧ください。
- 関連ページ
また、Node.js がインストールされていて、WordPress のローカル環境があることを前提にしています。
概要
TextareaControl コンポーネントを使ってコードを入力するエリアを表示し、入力されたコードをフロントエンドではエスケープ処理して Code-Prettify を使ってシンタックスハイライトで表示するブロックを作成する例です。
ブロックの編集画面では、サイドバーの設定項目(インスペクター)でスキンや行番号の表示・非表示、max-width などをブロックごとに設定できるようにします。
以下は上記入力時のフロントエンド側の表示例です。
環境構築とファイルの作成
以下は wp-scripts を使って JSX をコンパイルする環境を構築する例です。create-block を使うともっと簡単に環境を構築することができます。
ターミナルでプラグインのディレクトリ(wp-content/plugins/)に移動してブロックのディレクトリを作成します。この例では my-code-block というディレクトリを作成します。
作成したディレクトリに移動します。
$ cd /Applications/MAMP/htdocs/blocks/wp-content/plugins return $ mkdir my-code-block return $ cd my-code-block return
npm init -y を実行して package.json というファイルを生成します。
npm init コマンドに -y オプションを指定するとデフォルトの設定値で package.json というファイルが生成されます。
$ npm init -y return //package.json を生成
//生成される package.json の場所と内容が表示される
Wrote to /Applications/MAMP/htdocs/blocks/wp-content/plugins/my-code-block/package.json:
{
"name": "my-code-block",
"version": "1.0.0",
"description": "",
"main": "index.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
},
"keywords": [],
"author": "",
"license": "ISC"
}
npm install コマンドで @wordpress/scripts をインストールします。
--save-dev は開発環境で使う(開発時に依存する)パッケージに指定するオプションで、--save-exact は正確なバージョンのみを依存の対象とするオプションです(npm install)。
$ npm install --save-dev --save-exact @wordpress/scripts return //インストールを実行
インストールが完了すると、node_modules ディレクトリが作成されその中に関連パッケージがコピーされ、作業用のディレクトリは以下のような構成になります。
my-code-block ├── node_modules //関連パッケージ(モジュール)が入っているディレクトリ ├── package-lock.json //自動的に生成されるファイル └── package.json //パッケージの設定ファイル
本番用のビルド(production ビルド)や開発モードをコマンドラインから実行できるように package.json の scripts フィールドにコマンド(npm script)を追加します(6〜9行目)。
{
"name": "my-code-block",
"version": "1.0.0",
"description": "",
"main": "build/index.js",
"scripts": {
"start": "wp-scripts start",
"build": "wp-scripts build"
},
"keywords": [],
"author": "",
"license": "ISC",
"devDependencies": {
"@wordpress/scripts": "^12.3.0"
}
}
必要に応じて description や author を設定できます。また、start や build 以外にも format:js や lint:js、packages-update などのコマンドを追加することができます。
{
"name": "my-code-block",
"version": "0.1.0",
"description": "My Code Prettify Block",
"author": "WDL",
"license": "GPL-2.0-or-later",
"main": "build/index.js",
"scripts": {
"start": "wp-scripts start",
"build": "wp-scripts build",
"format:js": "wp-scripts format-js",
"lint:css": "wp-scripts lint-style",
"lint:js": "wp-scripts lint-js",
"packages-update": "wp-scripts packages-update"
},
"devDependencies": {
"@wordpress/scripts": "^12.3.0"
}
}
src フォルダを作成し、以下のファイルを追加します。
my-code-block
├── node_modules
├── package-lock.json
├── package.json
└── src //開発用ディレクトリ(追加)
├── edit.js // edit 関数を記述したファイル(追加)
├── editor.scss //エディタ用のスタイル(追加)
├── index.js // ブロック用スクリプト(追加)
└── style.scss //フロントエンド及びエディタ用のスタイル(追加)
index.js では registerBlockType 関数でブロックを登録します。
この例では edit プロパティは Edit コンポーネントとして別ファイル(src/edit.js)に記述してインポートします。save プロパティでは PHP でレンダリングするので null を返します(ダイナミックブロック)。
import { registerBlockType } from '@wordpress/blocks';
import Edit from './edit';
import './style.scss';
registerBlockType( 'wdl/my-code-block', {
title: 'My Code Block',
description: 'Code Prettify Block (Syntax Highlighter) ', //説明(オプション)
icon: 'smiley', //アイコン(オプション)
category: 'common',
edit: Edit,
save: () => { return null },
});
edit.js は edit プロパティに指定する Edit コンポーネント(edit 関数)を記述します。
import './editor.scss';
export default function Edit( props ) {
const { className } = props;
return (
<div className={ className }>
Hello From Edit.
</div>
);
}
style.scss と editor.scss はこの時点では取り敢えず、自動的に付与されるクラス名を使って適当なスタイルを指定しておきます。
.wp-block-wdl-my-code-block {
color: #999;
border: 1px solid #ccc;
background-color: #EFEFEF;
padding: 10px 20px;
}
.wp-block-wdl-my-code-block {
text-decoration: underline;
}
npm run build を実行してビルドします。
ビルドを実行すると、build ディレクトリが作成されてその中にコンパイルされたファイルが出力されます。また、同時に index.asset.php という依存ファイルとバージョンの情報が記述されたアセットファイルも自動的に生成されます。
my-code-block
├── block.json
├── build //ビルドで出力されるファイルのディレクトリ
│ ├── index.asset.php //依存情報とファイルバージョンが記載されるファイル(自動生成)
│ ├── index.css //editor.scss がビルドで変換された CSS
│ ├── index.js //ビルドされたブロック用のスクリプト
│ └── style-index.css // style.scss がビルドで変換された CSS
├── node_modules
├── package-lock.json
├── package.json
└── src //開発用ディレクトリ(この中のファイルを編集)
├── edit.js
├── editor.scss
├── index.js
└── style.scss
PHP でブロックを登録するファイル my-code-block.php を作成します。
my-code-block
├── block.json
├── build
│ ├── index.asset.php
│ ├── index.css
│ ├── index.js
│ └── style-index.css
├── node_modules
├── my-code-block.php //追加
├── package-lock.json
├── package.json
└── src
├── edit.js
├── editor.scss
├── index.js
└── style.scss
my-code-block.php にはプラグインヘッダを記述し、register_block_type 関数を使って PHP 側でブロックを登録します。この例ではブロックを PHP 側でレンダリングするので、render_callback を指定して定義します。
<?php
/**
* Plugin Name: My Code Block
* Description: Code Prettify Block (Syntax Highlighter) – build step required.
* Version: 0.1.0
* Author: WebDesignLeaves
*
* @package wdl
*/
function wdl_my_code_block_init() {
$dir = dirname( __FILE__ );
//依存スクリプトの配列とバージョンが記述されたアセットファイルの読み込み
$script_asset = require( "$dir/build/index.asset.php" );
//ブロック用のスクリプトの登録
wp_register_script(
//スクリプトのハンドル名
'wdl-my-code-block-editor',
//スクリプトの URL
plugins_url( 'build/index.js', __FILE__ ),
//依存するスクリプト
$script_asset['dependencies'],
//スクリプトのバージョン
$script_asset['version']
);
//エディタ用のスタイルの登録
wp_register_style(
'wdl-my-code-block-editor-style',
plugins_url( 'build/index.css', __FILE__ ),
array(),
filemtime( "$dir/build/index.css" )
);
//フロントエンド及びエディタ用のスタイルの登録
wp_register_style(
'wdl-my-code-block-style',
plugins_url( 'build/style-index.css', __FILE__ ),
array(),
filemtime( "$dir/build/style-index.css" )
);
//ブロックを登録
register_block_type(
//名前空間/ブロック名
'wdl/my-code-block',
//スクリプトやスタイルをブロックに関連付け
array(
'editor_script' => 'wdl-my-code-block-editor',
'editor_style' => 'wdl-my-code-block-editor-style',
'style' => 'wdl-my-code-block-style',
//PHP でレンダリングするコールバック関数を指定
'render_callback' => 'my_code_block_render',
)
);
}
add_action( 'init', 'wdl_my_code_block_init' );
// render_callback 関数の定義(取り敢えず div 要素で Hello from ... と出力)
function my_code_block_render($attributes, $content) {
return '<div class="wp-block-wdl-my-code-block">Hello from PHP for Code Prettify !</div>';
}
プラグインの管理ページを開いて有効化します。
投稿にブロックを挿入すると、エディター画面では以下のように表示されます。
以下はフロントエンド側での表示です。
npm start を実行して開発モードにして作業をします。
開発モードを終了するには control + c を押します。
Code-Prettify
Google Code-Prettify を使うには CDN 経由で読み込みオートローダーを使うのが簡単ですが、このブロックでは Google Code-Prettify をダウンロードして使用します。
関連ページ:Google Code-Prettify の基本的な使い方
ダウンロードのリンク(https://github.com/google/code-prettify/raw/master/distrib/prettify-small.zip)から Code-Prettify をダウンロードして解凍します。
この例のブロックでは prettify.css、prettify.js(run_prettify.js)、lang-css.js(CSS 用言語ハンドラー)を使用します。また、オプションでスキンを指定できるように skin フォルダ内の CSS ファイルに記述されている内容をブロックのスタイルにコピーします(後述)。
解凍したフォルダをブロックのフォルダにコピーします。この例ではフォルダ名を code-prettify としています。
また、この例では prettify.js を読み込んで使用するので PR.prettyPrint() という JavaScript の関数を実行する必要があるため、以下のような JavaScript ファイル(init-prettify.js)を作成して code-prettify フォルダに保存します
※ prettify.js の代わりに run_prettify.js を読み込めば PR.prettyPrint() を実行する必要はありませんが、プレビュー表示で useEffect を使って PR.prettyPrint() を別途実行するためこのようにしています。
window.addEventListener("load", function() {
PR.prettyPrint();
});
この時点では以下のような構成になっています。その他の言語ハンドラーは不要であれば削除しても問題ありませんが、将来的に特定の言語のハンドラーを追加することを考慮して残しています。
my-code-block
├── block.json
├── build
│ ├── index.asset.php
│ ├── index.css
│ ├── index.js
│ └── style-index.css
├── code-prettify //追加
│ ├── init-prettify.js //作成
│ ├── lang-css.js
│ ・・・中略(その他の言語ハンドラー)・・・
│ ├── prettify.css
│ ├── prettify.js
│ └── run_prettify.js
├── node_modules
├── my-code-block.php
├── package-lock.json
├── package.json
└── src
├── edit.js
├── editor.scss
├── index.js
└── style.scss
スクリプトやスタイルの読み込み
code-prettify フォルダに保存した Code-Prettify のスクリプトとスタイルを PHP でブロックを登録するファイル(my-code-block.php)で wp_enqueue_script と wp_enqueue_style を使って、enqueue_block_assets アクションで読み込みます。
以下を my-code-block.php に追加します。この例の場合、エディター側ではシンタックスハイライトは表示しないので ! is_admin() で判定してフロントエンド側でのみ読み込むようにします。
function add_my_code_block_scripts_and_styles() {
$dir = dirname( __FILE__ );
//管理画面以外(フロントエンド側でのみ読み込む)
if(! is_admin()) {
//Code-Prettify の JavaScript ファイルの読み込み(エンキュー)
wp_enqueue_script(
'code-prettify',
plugins_url( '/code-prettify/prettify.js', __FILE__ ),
array(),
filemtime( "$dir/code-prettify/prettify.js" ),
true
);
//CSS 用言語ハンドラーの JavaScript ファイルの読み込み(エンキュー)
wp_enqueue_script(
'code-prettify-css-lang',
plugins_url( '/code-prettify/lang-css.js', __FILE__ ),
array('code-prettify'),
filemtime( "$dir/code-prettify/lang-css.js" ),
true
);
//PR.prettyPrint() を実行する JavaScript ファイルの読み込み(エンキュー)
wp_enqueue_script(
'code-prettify-init',
plugins_url( '/code-prettify/init-prettify.js', __FILE__ ),
array('code-prettify'),
filemtime( "$dir/code-prettify/init-prettify.js" ),
true
);
//Code-Prettify の基本スタイルの読み込み(エンキュー)
wp_enqueue_style(
'code-prettify-style',
plugins_url( '/code-prettify/prettify.css', __FILE__ ),
array(),
filemtime( "$dir/code-prettify/prettify.css" )
);
}
}
add_action('enqueue_block_assets', 'add_my_code_block_scripts_and_styles');
スタイルのカスタマイズ
必要に応じてサイトや好みに合わせて Code-Prettify のスタイルをカスタマイズします。
このブロックではオプションでスキンを変更できるようにするため、クラスセレクタを使って skin フォルダの CSS ファイルの内容をコピーしています。スタイルは style.scss に SASS で記述できるのでスキンのスタイルはネストしています。
以下は一例です。指定するスタイルはテーマなどの設定により調整する必要があります。
スキンのスタイルは code-prettify/styles/ にミニファイされていないものがあるので、それを元にカスタマイズして使用することができます。また、ビルドする際に CSS もミニファイされるので、style.scss にはミニファイされていないものをコピーした方が作業しやすいです。
また、この例では基本のスタイル prettify.css の最後の部分(li の背景色)をコメントアウトしています。
.pln{color:#000}@media screen{.str{color:#080}.kwd{color:#008}.com{color:#800}.typ{color:#606}.lit{color:#066}.clo,.opn,.pun{color:#660}.tag{color:#008}.atn{color:#606}.atv{color:#080}.dec,.var{color:#606}.fun{color:red}}@media print,projection{.kwd,.tag,.typ{font-weight:700}.str{color:#060}.kwd{color:#006}.com{color:#600;font-style:italic}.typ{color:#404}.lit{color:#044}.clo,.opn,.pun{color:#440}.tag{color:#006}.atn{color:#404}.atv{color:#060}}pre.prettyprint{padding:2px;border:1px solid #888}ol.linenums{margin-top:0;margin-bottom:0}li.L0,li.L1,li.L2,li.L3,li.L5,li.L6,li.L7,li.L8{list-style-type:none}
/* コメントアウト li.L1,li.L3,li.L5,li.L7,li.L9{background:#eee} */
基本的な表示
エディター画面では TextareaControl コンポーネントを使ってユーザがテキストエリアにコードのテキストを入力できるようにします。
入力された値は属性に保存します。この例のブロックでは PHP でレンダリングするので、属性は register_block_type 関数に設定します。
attributes を設定
テキストエリアに入力された値を保持するために attributes プロパティを追加し属性を設定します。
my-code-block.php の register_block_type 関数に attributes プロパティを追加し、属性 codeArea を設定します。入力される値は文字列なので type を string に、default は空文字列に設定します。
register_block_type(
'wdl/my-code-block',
array(
'editor_script' => 'wdl-my-code-block-editor',
'editor_style' => 'wdl-my-code-block-editor-style',
'style' => 'wdl-my-code-block-style',
'render_callback' => 'my_code_block_render',
//属性を追加
'attributes' => [
//属性 codeArea を設定
'codeArea' => [
'type' => 'string',
'default' => ''
],
],
)
);
TextareaControl
TextareaControl はユーザがテキストを入力することができる textarea 要素を使ったコンポーネントです。以下のようなプロパティを設定することができます。
| プロパティ | 説明 |
|---|---|
| label | このプロパティを指定すると、指定された値の文字列を使って label 要素が出力されます。 |
| help | このプロパティに文字列を指定するとヘルプテキストを出力します。 |
| rows | テキストエリアの行数を指定します。デフォルトは4です。 |
| value | この要素の値(表示される文字列) |
| onChange | 入力の値が変更されたら呼び出される関数(イベントハンドラ) |
edit 関数(edit.js)では TextareaControl コンポーネントをインポートしてコードを入力できるテキストエリアをレンダリングします。
props からクラス名(className)、属性(attributes)、属性を更新する関数(setAttributes)を分割代入で変数に受け取ります。
テキストエリアに入力される値が変更されると onChange プロパティの setAttributes メソッドで値(value)を更新します。
また、TextareaControl コンポーネントのデフォルトの行数は4なので、テキストエリアに入力されている値(attributes.codeArea)を改行文字で分割し、その数を行数に設定するようにしています。以下の場合、何も入力されていない状態では3行分の高さのテキストエリアを表示します。
import { TextareaControl } from '@wordpress/components';
import './editor.scss';
export default function Edit( props ) {
const { className, attributes, setAttributes } = props;
//テキストエリア(TextareaControl)の行数
let codeAreaRows = attributes.codeArea.split(/\r|\r\n|\n/).length > 3 ? attributes.codeArea.split(/\r|\r\n|\n/).length : 3;
return (
<div className={ className }>
<TextareaControl
label="Code:"
value={ attributes.codeArea }
onChange={ (code) => setAttributes({ codeArea: code }) }
rows={ codeAreaRows }
/>
</div>
);
}
render_callback を変更
この時点の render_callback 関数では div 要素で「Hello from ...」と出力するようになっていますが、入力された値を出力するように変更します。
Code-Prettify でシンタックスハイライトして表示するには、シンタックスハイライトで表示したい部分を以下のように prettyprint クラスを指定した <pre> タグで囲みます。
行番号を表示する場合は、linenums というクラスを指定します。関連項目:Code-Prettify /コードの記述
<pre class="prettyprint"> //コードを記述 </pre>
以下が変更後の render_callback 関数です。
入力された値は属性 codeArea に保存されているので、PHP 側では $attributes['codeArea'] でアクセスできます。
テキストエリアに何も入力されていない場合(属性 codeArea が空文字列)は、何も表示しないので return します。
入力された値は esc_html() でエスケープ処理して出力する必要があります。
また、この例では prettyprint クラスを指定した pre 要素を、自動的に付与されるブロックのクラス wp-block-wdl-my-code-block で囲んでいます。このクラスは edit 関数では props 経由で className プロパティとして取得できますが、PHP 側では自分で記述します。
function my_code_block_render($attributes, $content) {
//属性 codeArea が空なら何も表示しない
if (empty($attributes['codeArea'])) {
return '';
}
//入力された値を esc_html() でエスケープ処理して出力
return '<div class="wp-block-wdl-my-code-block"><pre class="prettyprint linenums">'.esc_html($attributes['codeArea']).'</pre></div>';
}
以下はエディター画面でテキストエリアにコードを入力した場合の例です。
以下はフロントエンド側の表示例です。
my-code-block.php のスタイルの読み込みで、例えば prettify.css の代わりに skin ディレクトリの sunburst.css を読み込めば以下のような表示になります。
wp_enqueue_style( 'code-prettify-style-sunburst', plugins_url( '/code-prettify/skins/sunburst.css', __FILE__ ), array(), filemtime( "$dir/code-prettify/skins/sunburst.css" ) );
<?php
/**
* Plugin Name: My Code Block
* Description: Code Prettify Block (Syntax Highlighter) – build step required.
* Version: 0.1.0
* Author: WebDesignLeaves
*
* @package wdl
*/
function wdl_my_code_block_init() {
$dir = dirname( __FILE__ );
//アセットファイルの読み込み
$script_asset = require( "$dir/build/index.asset.php" );
//ブロック用のスクリプトの登録
wp_register_script(
//スクリプトのハンドル名
'wdl-my-code-block-editor',
//スクリプトの URL
plugins_url( 'build/index.js', __FILE__ ),
//依存するスクリプト
$script_asset['dependencies'],
//スクリプトのバージョン
$script_asset['version']
);
//エディタ用のスタイルの登録
wp_register_style(
'wdl-my-code-block-editor-style',
plugins_url( 'build/index.css', __FILE__ ),
array(),
filemtime( "$dir/build/index.css" )
);
//フロントエンド及びエディタ用のスタイルの登録
wp_register_style(
'wdl-my-code-block-style',
plugins_url( 'build/style-index.css', __FILE__ ),
array(),
filemtime( "$dir/build/style-index.css" )
);
//ブロックを登録
register_block_type(
//名前空間/ブロック名
'wdl/my-code-block',
//スクリプトやスタイルをブロックに関連付け
array(
'editor_script' => 'wdl-my-code-block-editor',
'editor_style' => 'wdl-my-code-block-editor-style',
'style' => 'wdl-my-code-block-style',
//PHP でレンダリングするコールバック関数を指定
'render_callback' => 'my_code_block_render',
//
'attributes' => [
'codeArea' => [
'type' => 'string',
'default' => ''
],
],
)
);
}
add_action( 'init', 'wdl_my_code_block_init' );
// render_callback 関数の定義
function my_code_block_render($attributes, $content) {
//属性 codeArea が空なら何も表示しない
if (empty($attributes['codeArea'])) {
return '';
}
return '<div class="wp-block-wdl-my-code-block"><pre class="prettyprint linenums">'.esc_html($attributes['codeArea']).'</pre></div>';
}
//Code-Prettify のスクリプトやスタイルの読み込み
function add_my_code_block_scripts_and_styles() {
$dir = dirname( __FILE__ );
//管理画面以外(フロントエンド側でのみ読み込む)
if(! is_admin()) {
//Code-Prettify の JavaScript ファイルの読み込み(エンキュー)
wp_enqueue_script(
'code-prettify',
plugins_url( '/code-prettify/prettify.js', __FILE__ ),
array(),
filemtime( "$dir/code-prettify/prettify.js" ),
true
);
//CSS 用言語ハンドラーの JavaScript ファイルの読み込み(エンキュー)
wp_enqueue_script(
'code-prettify-css-lang',
plugins_url( '/code-prettify/lang-css.js', __FILE__ ),
array('code-prettify'),
filemtime( "$dir/code-prettify/lang-css.js" ),
true
);
//PR.prettyPrint() を実行する JavaScript ファイルの読み込み(エンキュー)
wp_enqueue_script(
'code-prettify-init',
plugins_url( '/code-prettify/init-prettify.js', __FILE__ ),
array('code-prettify'),
filemtime( "$dir/code-prettify/init-prettify.js" ),
true
);
//Code-Prettify の基本スタイルの読み込み(エンキュー)
wp_enqueue_style(
'code-prettify-style',
plugins_url( '/code-prettify/prettify.css', __FILE__ ),
array(),
filemtime( "$dir/code-prettify/prettify.css" )
);
}
}
add_action('enqueue_block_assets', 'add_my_code_block_scripts_and_styles');
インスペクターの追加
行番号の表示・非表示や言語の指定、スキンの指定などをブロックごとにユーザが設定できるようにするためエディター画面にインスペクターを追加します。
属性を追加
インスペクターでユーザが設定した値を保持するために register_block_type で属性を追加します。
register_block_type(
'wdl/my-code-block',
array(
'editor_script' => 'wdl-my-code-block-editor',
'editor_style' => 'wdl-my-code-block-editor-style',
'style' => 'wdl-my-code-block-style',
'render_callback' => 'my_code_block_render',
'attributes' => [
'codeArea' => [
'type' => 'string',
'default' => ''
],
//以下の属性を追加
//行番号の表示・非表示
'linenums' => [
'type' => 'boolean',
'default' => true
],
//行番号の開始番号
'linenumsStart' => [
'type' => 'number',
'default' => 1
],
//言語の指定
'lang' => [
'type' => 'string',
'default' => ''
],
//配置の指定
'align' => [
'type' => 'string',
'default' => ''
],
//max-width を指定するかどうか
'maxWidthEnable' => [
'type' => 'boolean',
'default' => false
],
//max-width の値
'maxWidth' => [
'type' => 'number',
'default' => 0
],
//スキン(一番良く使うものをデフォルトに指定)
'skin' => [
'type' => 'string',
'default' => 'desert'
],
],
)
);
インスペクターの表示
edit.js ではインスペクターに表示する必要なコンポーネントをインポートします。
関連項目:インスペクター
import { InspectorControls } from '@wordpress/block-editor';
import { TextareaControl, PanelBody, PanelRow, ToggleControl, SelectControl, TextControl, RangeControl, CheckboxControl } from '@wordpress/components';
インスペクターの表示は getInspectorControls という関数を定義して表示するようにします(return ステートメント内に直接記述することもできます)。
開始する行番号を指定する TextControl コンポーネントは行番号を表示する場合(attributes.linenums が true の場合)に表示するようにしています。同様に max-width の値を設定する RangeControl は、attributes.maxWidthEnable が true の場合に表示するようにしています。
また、ブロックの配置は save 関数でレンダリングする場合は、registerBlockType で supports プロパティを指定して簡単にツールバーに表示できますが、この例の場合、PHP でレンダリングするのでインスペクターに項目を追加しています(もっと良い方法があるかも知れません)。
const getInspectorControls = () => {
return (
<InspectorControls>
<PanelBody
title='シンタックスハイライト設定'
initialOpen={true}
>
<PanelRow>
<ToggleControl
label={ attributes.linenums ? "行番号(表示)" : "行番号(非表示)" }
checked={attributes.linenums}
onChange={(val) => setAttributes({ linenums: val })}
/>
</PanelRow>
{ attributes.linenums && //上記が true の場合に表示
<PanelRow>
<TextControl
label="開始する行番号"
type="number"
value={ attributes.linenumsStart }
onChange={ (val) => setAttributes({ linenumsStart: parseInt(val) }) }
/>
</PanelRow>
}
<PanelRow>
<SelectControl
label="ブロックの配置"
value={attributes.align}
options={[
{label: "なし", value: ''},
{label: "左寄せ", value: 'left'},
{label: "中央揃え", value: 'center'},
{label: "右寄せ", value: 'right'},
{label: "幅広", value: 'wide'},
]}
onChange={(val) => setAttributes({ align: val })}
/>
</PanelRow>
<PanelRow>
<SelectControl
label="lang"
value={attributes.lang}
options={[
{label: "Default", value: ''},
{label: "CSS", value: 'css'},
]}
onChange={(val) => setAttributes({ lang: val })}
/>
</PanelRow>
<PanelRow>
<CheckboxControl
label="max-width を指定"
checked={attributes.maxWidthEnable}
onChange={(val) => setAttributes({ maxWidthEnable: val })}
help="※ インラインスタイルで設定します"
/>
</PanelRow>
{ attributes.maxWidthEnable && //上記が true の場合に表示
<PanelRow>
<RangeControl
label='max-width'
value={attributes.maxWidth}
onChange={(val) => setAttributes({ maxWidth: parseInt(val) })}
min={300}
max={1800}
step={10}
help="max-width を px で指定"
/>
</PanelRow>
}
<PanelRow>
<SelectControl
label="skin"
value={attributes.skin}
options={[
{label: "Basic", value: ''},
{label: "Desert", value: 'desert'},
{label: "Doxy", value: 'doxy'},
{label: "Sons-of-obsidian", value: 'sons-of-obsidian'},
{label: "Sunburst", value: 'sunburst'},
]}
onChange={(val) => setAttributes({ skin: val })}
/>
</PanelRow>
</PanelBody>
</InspectorControls>
);
}
InputControl
開始する行番号の入力には TextControl を使用していますが、入力欄が大きい(横幅が広い)ので別のコンポーネントを探したところ InputControl というコンポーネントがあります。
InputControl コンポーネントはサイズも指定できますが、現時点では「This is an experimental component intended to (in time) merge with or replace TextControl.」とされていて将来的に TextControl に統合される可能性があります。
以下は TextControl の代わりに InspectorControls を使用する場合の例です。但し、experimental なため、インポートも通常のインポートではエラーになるので __experimentalInputControl as を指定する必要があります。
import { __experimentalInputControl as InputControl } from '@wordpress/components';
・・・中略・・・
const getInspectorControls = () => {
return (
<InspectorControls>
<PanelBody
title='シンタックスハイライト設定'
initialOpen={true}
>
<PanelRow>
・・・中略・・・
</PanelRow>
{ attributes.linenums &&
<PanelRow>
<InputControl
label="開始する行番号"
type="number"
value={ attributes.linenumsStart }
onChange={ (val) => setAttributes({ linenumsStart: parseInt(val) }) }
/>
</PanelRow>
}
・・・中略・・・
return ステートメント
return ステートメントでは、上記関数 getInspectorControls を使ってインスペクターを出力するので、配列でコンポーネントを指定します。配列内では記述順にレンダリングされ、return ステートメント内で関数を直接呼び出すことができます。
return (
//配列を指定
[
getInspectorControls(), //インスペクター
<div className={ className }>
<TextareaControl
label="Code:"
value={ attributes.codeArea }
onChange={ (code) => setAttributes({ codeArea: code }) }
rows={ codeAreaRows }
/>
</div>
]
);
render_callback を更新
ユーザがインスペクターで設定した値を反映してブロックをレンダリングするように render_callback 関数を更新します。
my-code-block.php のブロックをレンダリングする関数 my_code_block_render() を以下のように変更します。
属性 align には配置の値が保持されているので、その値を使ってブロックの外側の div 要素にクラスを追加します。例えば、中央揃えが選択されている場合、$attributes['align'] の値は center なので、align をその前に付けて aligncenter というクラスを追加します。WordPress のデフォルトのテーマではこれらのクラスのスタイルが設定されていますが、テーマによっては対応するスタイルを設定する必要があります。
スキンも属性に保持されている値をブロックの外側の div 要素にクラスとして追加します。例えば desert というスキンが選択されていれば、style.scss で設定している .desert が適用されます。
max-width はインラインスタイルで外側の div 要素に指定しています。
行番号の linenums と言語の指定 lang-xxxx は prettyprint クラスが指定されている pre 要素に追加します。この例では言語の指定は CSS のみにしていますが、追加の言語を指定できるようにするには、その言語ハンドラーを別途 my-code-block.php で読み込む必要があります。
例えば配置を中央揃え、スキンを desert を選択し、max-width を 920px に設定し、行番号を7から開始して表示する場合は以下のようなマークアップを出力するようにします。
<div class="wp-block-wdl-my-code-block aligncenter desert" style="max-width:920px"> <pre class="prettyprint linenums:7">コード</pre> </div>
以下が変更後の render_callback 関数 my_code_block_render です。
function my_code_block_render($attributes, $content) {
//属性 codeArea が空なら何も表示しない
if (empty($attributes['codeArea'])) {
return '';
}
//ブロックに追加するクラス
$add_block_class = '';
//配置
if($attributes['align']) {
$add_block_class .= ' align' . $attributes['align'];
}
//スキン
if($attributes['skin']) {
$add_block_class .= ' '.$attributes['skin'];
}
//ブロックに指定するインラインスタイル
$add_style = '';
// maxWidthEnable が true なら max-width をインラインスタイルで設定
if($attributes['maxWidthEnable']) {
$add_style = ' style="max-width:' . $attributes['maxWidth'] . 'px" ';
}
// ブロックの div 要素に追加のクラスとスタイルを指定
$output = '<div class="wp-block-wdl-my-code-block' . $add_block_class . '" ' . $add_style . '>';
// pre 要素に追加するクラス
$add_pre_class = '';
// linenums が true なら行番号を表示するために linenums クラスを追加
if($attributes['linenums']) {
$add_pre_class = ' linenums';
//行の開始番号が指定されていればその値を設定
if($attributes['linenumsStart'] !== 1) {
$add_pre_class .= ':' . $attributes['linenumsStart'];
}
}
// 言語が指定されていればそのクラス(lang-xxxx)を設定
if($attributes['lang']) {
$add_pre_class .= ' lang-' . ($attributes['lang']);
}
//$output = '<div class="wp-block-wdl-my-code-block"' . $add_style . '>';
$output .= '<pre class="prettyprint' . $add_pre_class . '">';
//入力された値をエスケープ処理
$output .= esc_html($attributes['codeArea']).'</pre></div>';
// 最終的なマークアップを返す
return $output;
}
以下はここまでの時点での各ファイルです。
import { registerBlockType } from '@wordpress/blocks';
import Edit from './edit';
import './style.scss';
registerBlockType( 'wdl/my-code-block', {
title: 'My Code Block',
description: 'Code Prettify Block (Syntax Highlighter) ',
icon: 'smiley',
category: 'common',
edit: Edit,
save: () => { return null },
});
import { InspectorControls } from '@wordpress/block-editor';
import { TextareaControl, PanelBody, PanelRow, ToggleControl, SelectControl, TextControl, RangeControl, CheckboxControl } from '@wordpress/components';
import './editor.scss';
export default function Edit( props ) {
const { className, attributes, setAttributes } = props;
//インスペクターを表示する関数
const getInspectorControls = () => {
return (
<InspectorControls>
<PanelBody
title='シンタックスハイライト設定'
initialOpen={true}
>
<PanelRow>
<ToggleControl
label={ attributes.linenums ? "行番号(表示)" : "行番号(非表示)" }
checked={attributes.linenums}
onChange={(val) => setAttributes({ linenums: val })}
/>
</PanelRow>
{ attributes.linenums && //上記が true の場合に表示
<PanelRow>
<TextControl
label="開始する行番号"
type="number"
value={ attributes.linenumsStart }
onChange={ (val) => setAttributes({ linenumsStart: parseInt(val) }) }
/>
</PanelRow>
}
<PanelRow>
<SelectControl
label="ブロックの配置"
value={attributes.align}
options={[
{label: "なし", value: ''},
{label: "左寄せ", value: 'left'},
{label: "中央揃え", value: 'center'},
{label: "右寄せ", value: 'right'},
{label: "幅広", value: 'wide'},
]}
onChange={(val) => setAttributes({ align: val })}
/>
</PanelRow>
<PanelRow>
<SelectControl
label="lang"
value={attributes.lang}
options={[
{label: "Default", value: ''},
{label: "CSS", value: 'css'},
]}
onChange={(val) => setAttributes({ lang: val })}
/>
</PanelRow>
<PanelRow>
<CheckboxControl
label="max-width を指定"
checked={attributes.maxWidthEnable}
onChange={(val) => setAttributes({ maxWidthEnable: val })}
help="※ インラインスタイルで設定します"
/>
</PanelRow>
{ attributes.maxWidthEnable && //上記が true の場合に表示
<PanelRow>
<RangeControl
label='max-width'
value={attributes.maxWidth}
onChange={(val) => setAttributes({ maxWidth: parseInt(val) })}
min={300}
max={1800}
step={10}
help="max-width を px で指定"
/>
</PanelRow>
}
<PanelRow>
<SelectControl
label="skin"
value={attributes.skin}
options={[
{label: "Basic", value: ''},
{label: "Desert", value: 'desert'},
{label: "Doxy", value: 'doxy'},
{label: "Sons-of-obsidian", value: 'sons-of-obsidian'},
{label: "Sunburst", value: 'sunburst'},
]}
onChange={(val) => setAttributes({ skin: val })}
/>
</PanelRow>
</PanelBody>
</InspectorControls>
);
}
let codeAreaRows = attributes.codeArea.split(/\r|\r\n|\n/).length > 3 ? attributes.codeArea.split(/\r|\r\n|\n/).length : 3;
return (
//配列を指定
[
getInspectorControls(), //インスペクター
<div className={ className }>
<TextareaControl
label="Code:"
value={ attributes.codeArea }
onChange={ (code) => setAttributes({ codeArea: code }) }
rows={ codeAreaRows }
/>
</div>
]
);
}
import { InspectorControls } from '@wordpress/block-editor';
import { TextareaControl, PanelBody, PanelRow, ToggleControl, SelectControl, TextControl, RangeControl, CheckboxControl } from '@wordpress/components';
import './editor.scss';
export default function Edit( props ) {
const { className, attributes, setAttributes } = props;
//インスペクターを表示する関数
const getInspectorControls = () => {
return (
<InspectorControls>
<PanelBody
title='シンタックスハイライト設定'
initialOpen={true}
>
<PanelRow>
<ToggleControl
label={ attributes.linenums ? "行番号(表示)" : "行番号(非表示)" }
checked={attributes.linenums}
onChange={(val) => setAttributes({ linenums: val })}
/>
</PanelRow>
{ attributes.linenums && //上記が true の場合に表示
<PanelRow>
<TextControl
label="開始する行番号"
type="number"
value={ attributes.linenumsStart }
onChange={ (val) => setAttributes({ linenumsStart: parseInt(val) }) }
/>
</PanelRow>
}
<PanelRow>
<SelectControl
label="ブロックの配置"
value={attributes.align}
options={[
{label: "なし", value: ''},
{label: "左寄せ", value: 'left'},
{label: "中央揃え", value: 'center'},
{label: "右寄せ", value: 'right'},
{label: "幅広", value: 'wide'},
]}
onChange={(val) => setAttributes({ align: val })}
/>
</PanelRow>
<PanelRow>
<SelectControl
label="lang"
value={attributes.lang}
options={[
{label: "Default", value: ''},
{label: "CSS", value: 'css'},
]}
onChange={(val) => setAttributes({ lang: val })}
/>
</PanelRow>
<PanelRow>
<CheckboxControl
label="max-width を指定"
checked={attributes.maxWidthEnable}
onChange={(val) => setAttributes({ maxWidthEnable: val })}
help="※ インラインスタイルで設定します"
/>
</PanelRow>
{ attributes.maxWidthEnable && //上記が true の場合に表示
<PanelRow>
<RangeControl
label='max-width'
value={attributes.maxWidth}
onChange={(val) => setAttributes({ maxWidth: parseInt(val) })}
min={300}
max={1800}
step={10}
help="max-width を px で指定"
/>
</PanelRow>
}
<PanelRow>
<SelectControl
label="skin"
value={attributes.skin}
options={[
{label: "Basic", value: ''},
{label: "Desert", value: 'desert'},
{label: "Doxy", value: 'doxy'},
{label: "Sons-of-obsidian", value: 'sons-of-obsidian'},
{label: "Sunburst", value: 'sunburst'},
]}
onChange={(val) => setAttributes({ skin: val })}
/>
</PanelRow>
</PanelBody>
</InspectorControls>
);
}
let codeAreaRows = attributes.codeArea.split(/\r|\r\n|\n/).length > 3 ? attributes.codeArea.split(/\r|\r\n|\n/).length : 3;
return (
[
getInspectorControls(),
<div className={ className }>
<TextareaControl
label="Code:"
value={ attributes.codeArea }
onChange={ (code) => setAttributes({ codeArea: code }) }
rows={ codeAreaRows }
/>
</div>
]
);
}
.entry-content .wp-block-wdl-my-code-block {
width: 100%;
}
.wp-block-wdl-my-code-block pre.prettyprint {
font-family: Monaco, Menlo, Consolas, 'Courier New', Courier, monospace, sans-serif;
font-size: 16px;
padding: 10px;
}
.wp-block-wdl-my-code-block .prettyprint ol{
margin-left: 10px;
white-space: pre-wrap; /* 折り返し */
}
.wp-block-wdl-my-code-block .prettyprint ol.linenums > li {
list-style-type: decimal; /* 行番号を1行ずつ表示 */
border-left:solid 1px #EBB15E; /* 行番号との区切り線 */
padding-left: 1rem; /* 間隔の調整 */
margin: 0 2rem; /* 間隔の調整 */
}
.wp-block-wdl-my-code-block .prettyprint ol.linenums > li::marker {
color: #aaa; /* 行番号の色 */
}
.desert {
/* desert.css をコピー */
pre .atn,pre .kwd,pre .tag{font-weight:700}pre.prettyprint{display:block;background-color:#333}pre .nocode{background-color:none;color:#000}pre .str{color:#ffa0a0}pre .kwd{color:khaki}pre .com{color:#87ceeb}pre .typ{color:#98fb98}pre .lit{color:#cd5c5c}pre .pln,pre .pun{color:#fff}pre .tag{color:khaki}pre .atn{color:#bdb76b}pre .atv{color:#ffa0a0}pre .dec{color:#98fb98}ol.linenums{margin-top:0;margin-bottom:0;color:#AEAEAE}li.L0,li.L1,li.L2,li.L3,li.L5,li.L6,li.L7,li.L8{list-style-type:none}@media print{pre.prettyprint{background-color:none}code .str,pre .str{color:#060}code .kwd,pre .kwd{color:#006;font-weight:700}code .com,pre .com{color:#600;font-style:italic}code .typ,pre .typ{color:#404;font-weight:700}code .lit,pre .lit{color:#044}code .pun,pre .pun{color:#440}code .pln,pre .pln{color:#000}code .tag,pre .tag{color:#006;font-weight:700}code .atn,pre .atn{color:#404}code .atv,pre .atv{color:#060}}
}
.sunburst {
/* sunburst.css をコピー */
code .str,pre .str{color:#65B042}code .kwd,pre .kwd{color:#E28964}code .com,pre .com{color:#AEAEAE;font-style:italic}code .typ,pre .typ{color:#89bdff}code .lit,pre .lit{color:#3387CC}code .pln,code .pun,pre .pln,pre .pun{color:#fff}code .tag,pre .tag{color:#89bdff}code .atn,pre .atn{color:#bdb76b}code .atv,pre .atv{color:#65B042}code .dec,pre .dec{color:#3387CC}code.prettyprint,pre.prettyprint{background-color:#000;border-radius:8px}pre.prettyprint{width:95%;margin:1em auto;padding:1em;white-space:pre-wrap}ol.linenums{margin-top:0;margin-bottom:0;color:#AEAEAE}li.L0,li.L1,li.L2,li.L3,li.L5,li.L6,li.L7,li.L8{list-style-type:none}@media print{code .str,pre .str{color:#060}code .kwd,pre .kwd{color:#006;font-weight:700}code .com,pre .com{color:#600;font-style:italic}code .typ,pre .typ{color:#404;font-weight:700}code .lit,pre .lit{color:#044}code .pun,pre .pun{color:#440}code .pln,pre .pln{color:#000}code .tag,pre .tag{color:#006;font-weight:700}code .atn,pre .atn{color:#404}code .atv,pre .atv{color:#060}}
}
.doxy {
/* doxy.css をコピー */
a,code.prettyprint a,pre.prettyprint a{text-decoration:none}code .str,pre .str{color:#fec243}code .kwd,pre .kwd{color:#8470FF}code .com,pre .com{color:#32cd32;font-style:italic}code .typ,pre .typ{color:#6ecbcc}code .lit,pre .lit{color:#d06}code .pun,pre .pun{color:#8B8970}code .pln,pre .pln{color:#f0f0f0}code .tag,pre .tag{color:#9c9cff}code .htm,pre .htm{color:plum}code .xsl,pre .xsl{color:#d0a0d0}code .atn,pre .atn{color:#46eeee;font-weight:400}code .atv,pre .atv{color:#EEB4B4}code .dec,pre .dec{color:#3387CC}code.prettyprint,pre.prettyprint{font-family:'Droid Sans Mono','CPMono_v07 Bold','Droid Sans';font-weight:700;font-size:16px;background-color:#0f0f0f;-moz-border-radius:8px;-webkit-border-radius:8px;-o-border-radius:8px;-ms-border-radius:8px;-khtml-border-radius:8px;border-radius:8px}pre.prettyprint{width:95%;margin:1em auto;padding:1em;white-space:pre-wrap}ol.linenums{margin-top:0;margin-bottom:0;color:#8B8970}li.L0,li.L1,li.L2,li.L3,li.L5,li.L6,li.L7,li.L8{list-style-type:none}@media print{code.prettyprint,pre.prettyprint{background-color:#fff}code .str,pre .str{color:#088}code .kwd,pre .kwd{color:#006;font-weight:700}code .com,pre .com{color:#oc3;font-style:italic}code .typ,pre .typ{color:#404;font-weight:700}code .lit,pre .lit{color:#044}code .pun,pre .pun{color:#440}code .pln,pre .pln{color:#000}code .tag,pre .tag{color:#b66ff7;font-weight:700}code .htm,code .xsl,pre .htm,pre .xsl{color:#606;font-weight:700}code .atn,pre .atn{color:#c71585;font-weight:400}code .atv,pre .atv{color:#088;font-weight:400}}
}
.sons-of-obsidian {
/* sons-of-obsidian.css をコピー */
.str{color:#EC7600}.kwd{color:#93C763}.com{color:#66747B}.typ{color:#678CB1}.lit{color:#FACD22}.pln,.pun{color:#F1F2F3}.tag{color:#8AC763}.atn{color:#E0E2E4}.atv{color:#EC7600}.dec{color:purple}pre.prettyprint{border:0 solid #888}ol.linenums{margin-top:0;margin-bottom:0}.prettyprint{background:#000}li.L0,li.L1,li.L2,li.L3,li.L4,li.L5,li.L6,li.L7,li.L8,li.L9{color:#555;list-style-type:decimal}li.L1,li.L3,li.L5,li.L7,li.L9{background:#111}@media print{.kwd,.tag,.typ{font-weight:700}.str{color:#060}.kwd{color:#006}.com{color:#600;font-style:italic}.typ{color:#404}.lit{color:#044}.pun{color:#440}.pln{color:#000}.tag{color:#006}.atn{color:#404}.atv{color:#060}}
}
.components-textarea-control__input {
font-size: 16px;
padding: 10px;
background: #efefef;
}
ファイル名を表示
TextControl コンポーネントを使ってファイル名を入力できるようにする例です。
適宜スタイルを設定すれば以下のようにタブのような表示にすることもできます。
register_block_type にファイル名を保存する属性 fileName を追加します。
//ファイル名を保存する属性を追加 'fileName' => [ 'type' => 'string', 'default' => '' ],
edit 関数の return ステートメントで TextControl コンポーネントを使ってファイル名を入力する input 要素をレンダリングします。その際にスタイルを適用しやすいようにクラス名(filename)を設定します。
return (
//配列を指定
[
getInspectorControls(),
<div className={ className }>
<TextControl
label="File Name"
type="string"
className="filename"
value={ attributes.fileName }
onChange={ (val) => setAttributes({ fileName: val }) }
/>
<TextareaControl
label="Code"
value={ attributes.codeArea }
onChange={ (code) => setAttributes({ codeArea: code }) }
rows={ codeAreaRows }
/>
</div>
]
);
editor.scss でエディター画面での input 要素のスタイル(最大幅や文字サイズなど)を指定します。
.filename input {
max-width: 300px;
margin-left: 10px;
font-size: 16px;
}
render_callback 関数を更新します。
入力されたファイル名は $attributes['fileName'] に入っているので、その値が空でなければ(ファイル名が指定されていれば)外側の div 要素に filename_wrapper クラスを追加します。
そして、入力されたファイル名($attributes['fileName'])をエスケープ処理して file_name クラスを付与した p 要素で出力します。
例えば以下のようなマークアップを出力するようにします。
<div class="wp-block-wdl-my-code-block aligncenter desert filename_wrapper" style="max-width:920px"> <p class="file_name">ファイル名</p> <pre class="prettyprint linenums:7">コード</pre> </div>
以下が変更後の render_callback 関数 my_code_block_render です。
function my_code_block_render($attributes, $content) {
if (empty($attributes['codeArea'])) {
return '';
}
$add_block_class = '';
if($attributes['align']) {
$add_block_class .= ' align' . $attributes['align'];
}
if($attributes['skin']) {
$add_block_class .= ' '.$attributes['skin'];
}
//ファイル名が指定されていれば filename_wrapper クラスを追加
if($attributes['fileName']) {
$add_block_class .= ' filename_wrapper';
}
$add_style = '';
if($attributes['maxWidthEnable']) {
$add_style = ' style="max-width:' . $attributes['maxWidth'] . 'px" ';
}
$output = '<div class="wp-block-wdl-my-code-block' . $add_block_class . '" ' . $add_style . '>';
//ファイル名の要素を格納する変数
$file_name = '';
//ファイル名が指定されていればファイル名を p 要素で出力
if($attributes['fileName']) {
$file_name = '<p class="file_name">' . esc_html($attributes['fileName']) . '</p>';
$output .= $file_name;
}
$add_pre_class = '';
if($attributes['linenums']) {
$add_pre_class = ' linenums';
if($attributes['linenumsStart'] !== 1) {
$add_pre_class .= ':' . $attributes['linenumsStart'];
}
}
if($attributes['lang']) {
$add_pre_class .= ' lang-' . ($attributes['lang']);
}
$output .= '<pre class="prettyprint' . $add_pre_class . '">';
$output .= esc_html($attributes['codeArea']).'</pre></div>';
return $output;
}
必要に応じてスタイルを調整します。以下はファイル名をタブのように表示するスタイルの例です。
ファイル名を表示する p 要素(p.file_name)を top で位置を調整していますが環境(テーマのスタイル)に合わせて調整する必要がるかと思います。
また、スキンの各スタイルに合わせて調整も必要です。以下では角丸などの設定を追加しています。スキンによっては pre 要素の幅を 95% に設定してあるものがあるので以下では削除しています。
import { registerBlockType } from '@wordpress/blocks';
import Edit from './edit';
import './style.scss';
registerBlockType( 'wdl/my-code-block', {
title: 'My Code Block',
description: 'Code Prettify Block (Syntax Highlighter) ',
icon: 'smiley',
category: 'common',
edit: Edit,
save: () => { return null },
});
import { InspectorControls } from '@wordpress/block-editor';
import { TextareaControl, PanelBody, PanelRow, ToggleControl, SelectControl, TextControl, RangeControl, CheckboxControl } from '@wordpress/components';
import './editor.scss';
export default function Edit( props ) {
const { className, attributes, setAttributes } = props;
//インスペクターを表示する関数
const getInspectorControls = () => {
return (
<InspectorControls>
<PanelBody
title='シンタックスハイライト設定'
initialOpen={true}
>
<PanelRow>
<ToggleControl
label={ attributes.linenums ? "行番号(表示)" : "行番号(非表示)" }
checked={attributes.linenums}
onChange={(val) => setAttributes({ linenums: val })}
/>
</PanelRow>
{ attributes.linenums && //上記が true の場合に表示
<PanelRow>
<TextControl
label="開始する行番号"
type="number"
value={ attributes.linenumsStart }
onChange={ (val) => setAttributes({ linenumsStart: parseInt(val) }) }
/>
</PanelRow>
}
<PanelRow>
<SelectControl
label="ブロックの配置"
value={attributes.align}
options={[
{label: "なし", value: ''},
{label: "左寄せ", value: 'left'},
{label: "中央揃え", value: 'center'},
{label: "右寄せ", value: 'right'},
{label: "幅広", value: 'wide'},
]}
onChange={(val) => setAttributes({ align: val })}
/>
</PanelRow>
<PanelRow>
<SelectControl
label="lang"
value={attributes.lang}
options={[
{label: "Default", value: ''},
{label: "CSS", value: 'css'},
]}
onChange={(val) => setAttributes({ lang: val })}
/>
</PanelRow>
<PanelRow>
<CheckboxControl
label="max-width を指定"
checked={attributes.maxWidthEnable}
onChange={(val) => setAttributes({ maxWidthEnable: val })}
help="※ インラインスタイルで設定します"
/>
</PanelRow>
{ attributes.maxWidthEnable && //上記が true の場合に表示
<PanelRow>
<RangeControl
label='max-width'
value={attributes.maxWidth}
onChange={(val) => setAttributes({ maxWidth: parseInt(val) })}
min={300}
max={1800}
step={10}
help="max-width を px で指定"
/>
</PanelRow>
}
<PanelRow>
<SelectControl
label="skin"
value={attributes.skin}
options={[
{label: "Basic", value: ''},
{label: "Desert", value: 'desert'},
{label: "Doxy", value: 'doxy'},
{label: "Sons-of-obsidian", value: 'sons-of-obsidian'},
{label: "Sunburst", value: 'sunburst'},
]}
onChange={(val) => setAttributes({ skin: val })}
/>
</PanelRow>
</PanelBody>
</InspectorControls>
);
}
let codeAreaRows = attributes.codeArea.split(/\r|\r\n|\n/).length > 3 ? attributes.codeArea.split(/\r|\r\n|\n/).length : 3;
return (
//配列を指定
[
getInspectorControls(),
<div className={ className }>
<TextControl
label="File Name"
type="string"
className="filename"
value={ attributes.fileName }
onChange={ (val) => setAttributes({ fileName: val }) }
/>
<TextareaControl
label="Code"
value={ attributes.codeArea }
onChange={ (code) => setAttributes({ codeArea: code }) }
rows={ codeAreaRows }
/>
</div>
]
);
}
<?php
/**
* Plugin Name: My Code Block
* Description: Code Prettify Block (Syntax Highlighter) – build step required.
* Version: 0.1.0
* Author: WebDesignLeaves
*
* @package wdl
*/
function wdl_my_code_block_init() {
$dir = dirname( __FILE__ );
//アセットファイルの読み込み
$script_asset = require( "$dir/build/index.asset.php" );
//ブロック用のスクリプトの登録
wp_register_script(
//スクリプトのハンドル名
'wdl-my-code-block-editor',
//スクリプトの URL
plugins_url( 'build/index.js', __FILE__ ),
//依存するスクリプト
$script_asset['dependencies'],
//スクリプトのバージョン
$script_asset['version']
);
//エディタ用のスタイルの登録
wp_register_style(
'wdl-my-code-block-editor-style',
plugins_url( 'build/index.css', __FILE__ ),
array(),
filemtime( "$dir/build/index.css" )
);
//フロントエンド及びエディタ用のスタイルの登録
wp_register_style(
'wdl-my-code-block-style',
plugins_url( 'build/style-index.css', __FILE__ ),
array(),
filemtime( "$dir/build/style-index.css" )
);
//ブロックを登録
register_block_type(
//名前空間/ブロック名
'wdl/my-code-block',
//スクリプトやスタイルをブロックに関連付け
array(
'editor_script' => 'wdl-my-code-block-editor',
'editor_style' => 'wdl-my-code-block-editor-style',
'style' => 'wdl-my-code-block-style',
//PHP でレンダリングするコールバック関数を指定
'render_callback' => 'my_code_block_render',
//属性を設定
'attributes' => [
// 入力された値
'codeArea' => [
'type' => 'string',
'default' => ''
],
//行番号の表示・非表示
'linenums' => [
'type' => 'boolean',
'default' => true
],
//行番号の開始番号
'linenumsStart' => [
'type' => 'number',
'default' => 1
],
//言語の指定
'lang' => [
'type' => 'string',
'default' => ''
],
//配置の指定
'align' => [
'type' => 'string',
'default' => ''
],
//max-width を指定するかどうか
'maxWidthEnable' => [
'type' => 'boolean',
'default' => false
],
//max-width の値
'maxWidth' => [
'type' => 'number',
'default' => 0
],
//スキン
'skin' => [
'type' => 'string',
'default' => 'desert'
],
//ファイル名
'fileName' => [
'type' => 'string',
'default' => ''
],
],
)
);
}
add_action( 'init', 'wdl_my_code_block_init' );
// render_callback 関数の定義
function my_code_block_render($attributes, $content) {
//属性 codeArea が空なら何も表示しない
if (empty($attributes['codeArea'])) {
return '';
}
//ブロックに追加するクラス
$add_block_class = '';
//配置
if($attributes['align']) {
$add_block_class .= ' align' . $attributes['align'];
}
//スキン
if($attributes['skin']) {
$add_block_class .= ' '.$attributes['skin'];
}
//ファイル名が指定されていれば filename_wrapper クラスを追加
if($attributes['fileName']) {
$add_block_class .= ' filename_wrapper';
}
//ブロックに指定するインラインスタイル
$add_style = '';
// maxWidthEnable が true なら max-width をインラインスタイルで設定
if($attributes['maxWidthEnable']) {
$add_style = ' style="max-width:' . $attributes['maxWidth'] . 'px" ';
}
// ブロックの div 要素に追加のクラスとスタイルを指定
$output = '<div class="wp-block-wdl-my-code-block' . $add_block_class . '" ' . $add_style . '>';
//ファイル名の要素を格納する変数
$file_name = '';
//ファイル名が指定されていればファイル名を p 要素で出力
if($attributes['fileName']) {
$file_name = '<p class="file_name">' . esc_html($attributes['fileName']) . '</p>';
$output .= $file_name;
}
// pre 要素に追加するクラス
$add_pre_class = '';
// linenums が true なら行番号を表示するために linenums クラスを追加
if($attributes['linenums']) {
$add_pre_class = ' linenums';
//行の開始番号が指定されていればその値を設定
if($attributes['linenumsStart'] !== 1) {
$add_pre_class .= ':' . $attributes['linenumsStart'];
}
}
// 言語が指定されていればそのクラス(lang-xxxx)を設定
if($attributes['lang']) {
$add_pre_class .= ' lang-' . ($attributes['lang']);
}
//$output = '<div class="wp-block-wdl-my-code-block"' . $add_style . '>';
$output .= '<pre class="prettyprint' . $add_pre_class . '">';
//入力された値をエスケープ処理
$output .= esc_html($attributes['codeArea']).'</pre></div>';
// 最終的なマークアップを返す
return $output;
}
//Code-Prettify のスクリプトやスタイルの読み込み
function add_my_code_block_scripts_and_styles() {
$dir = dirname( __FILE__ );
//管理画面以外(フロントエンド側でのみ読み込む)
if(! is_admin()) {
//Code-Prettify の JavaScript ファイルの読み込み(エンキュー)
wp_enqueue_script(
'code-prettify',
plugins_url( '/code-prettify/prettify.js', __FILE__ ),
array(),
filemtime( "$dir/code-prettify/prettify.js" ),
true
);
//CSS 用言語ハンドラーの JavaScript ファイルの読み込み(エンキュー)
wp_enqueue_script(
'code-prettify-css-lang',
plugins_url( '/code-prettify/lang-css.js', __FILE__ ),
array('code-prettify'),
filemtime( "$dir/code-prettify/lang-css.js" ),
true
);
//PR.prettyPrint() を実行する JavaScript ファイルの読み込み(エンキュー)
wp_enqueue_script(
'code-prettify-init',
plugins_url( '/code-prettify/init-prettify.js', __FILE__ ),
array('code-prettify'),
filemtime( "$dir/code-prettify/init-prettify.js" ),
true
);
//Code-Prettify の基本スタイルの読み込み(エンキュー)
wp_enqueue_style(
'code-prettify-style',
plugins_url( '/code-prettify/prettify.css', __FILE__ ),
array(),
filemtime( "$dir/code-prettify/prettify.css" )
);
}
}
add_action('enqueue_block_assets', 'add_my_code_block_scripts_and_styles');
/* textarea 要素 */
.components-textarea-control__input {
font-size: 16px;
padding: 10px;
}
.filename input {
max-width: 300px;
margin-left: 10px;
font-size: 16px;
}
.wp-block-wdl-my-code-block {
position: relative;
}
.wp-block-wdl-my-code-block p.file_name {
margin: 0;
display: inline-block;
font-size: 15px;
position: relative;
top: 2px; /* 要調整 */
padding: 2px 5px !important; /* 追加 */
}
.wp-block-wdl-my-code-block pre.prettyprint {
font-family: Monaco, Menlo, Consolas, 'Courier New', Courier, monospace, sans-serif;
font-size: 16px;
padding: 10px;
margin-top: 0 !important; /* 追加 */
}
.wp-block-wdl-my-code-block .prettyprint ol{
margin-left: 10px;
white-space: pre-wrap; /* 折り返し */
}
.wp-block-wdl-my-code-block .prettyprint ol.linenums > li {
list-style-type: decimal; /* 行番号を1行ずつ表示 */
border-left:solid 1px #EBB15E; /* 行番号との区切り線 */
padding-left: 1rem; /* 間隔の調整 */
margin: 0 2rem; /* 間隔の調整 */
}
.wp-block-wdl-my-code-block .prettyprint ol.linenums > li::marker {
color: #aaa; /* 行番号の色 */
font-size: 13px;
}
.desert {
/* desert.css をコピー */
pre.prettyprint { display: block; background-color: #333 }
pre .nocode { background-color: none; color: #000 }
pre .str { color: #ffa0a0 } /* string - pink */
pre .kwd { color: #f0e68c; font-weight: bold }
pre .com { color: #87ceeb } /* comment - skyblue */
pre .typ { color: #98fb98 } /* type - lightgreen */
pre .lit { color: #cd5c5c } /* literal - darkred */
pre .pun { color: #fff } /* punctuation */
pre .pln { color: #fff } /* plaintext */
pre .tag { color: #f0e68c; font-weight: bold } /* html/xml tag - lightyellow */
pre .atn { color: #bdb76b; font-weight: bold } /* attribute name - khaki */
pre .atv { color: #ffa0a0 } /* attribute value - pink */
pre .dec { color: #98fb98 } /* decimal - lightgreen */
/* Specify class=linenums on a pre to get line numbering */
ol.linenums { margin-top: 0; margin-bottom: 0; color: #AEAEAE } /* IE indents via margin-left */
li.L0,li.L1,li.L2,li.L3,li.L5,li.L6,li.L7,li.L8 { list-style-type: none }
/* Alternate shading for lines */
li.L1,li.L3,li.L5,li.L7,li.L9 { }
@media print {
pre.prettyprint { background-color: none }
pre .str, code .str { color: #060 }
pre .kwd, code .kwd { color: #006; font-weight: bold }
pre .com, code .com { color: #600; font-style: italic }
pre .typ, code .typ { color: #404; font-weight: bold }
pre .lit, code .lit { color: #044 }
pre .pun, code .pun { color: #440 }
pre .pln, code .pln { color: #000 }
pre .tag, code .tag { color: #006; font-weight: bold }
pre .atn, code .atn { color: #404 }
pre .atv, code .atv { color: #060 }
}
/* 追加 */
p.file_name {background-color:#333; color: #eee; border: 1px solid #888; border-bottom: none;}
}
.sunburst {
/* sunburst.css をコピー */
pre .str, code .str { color: #65B042; } /* string - green */
pre .kwd, code .kwd { color: #E28964; } /* keyword - dark pink */
pre .com, code .com { color: #AEAEAE; font-style: italic; } /* comment - gray */
pre .typ, code .typ { color: #89bdff; } /* type - light blue */
pre .lit, code .lit { color: #3387CC; } /* literal - blue */
pre .pun, code .pun { color: #fff; } /* punctuation - white */
pre .pln, code .pln { color: #fff; } /* plaintext - white */
pre .tag, code .tag { color: #89bdff; } /* html/xml tag - light blue */
pre .atn, code .atn { color: #bdb76b; } /* html/xml attribute name - khaki */
pre .atv, code .atv { color: #65B042; } /* html/xml attribute value - green */
pre .dec, code .dec { color: #3387CC; } /* decimal - blue */
pre.prettyprint, code.prettyprint {
background-color: #000;
border-radius: 8px;
border-top-left-radius:0; /* 追加 */
}
pre.prettyprint {
/* 削除 width:95%;*/
margin: 1em auto;
padding: 1em;
white-space: pre-wrap;
}
/* Specify class=linenums on a pre to get line numbering */
ol.linenums { margin-top: 0; margin-bottom: 0; color: #AEAEAE; } /* IE indents via margin-left */
li.L0,li.L1,li.L2,li.L3,li.L5,li.L6,li.L7,li.L8 { list-style-type: none }
/* Alternate shading for lines */
li.L1,li.L3,li.L5,li.L7,li.L9 { }
@media print {
pre .str, code .str { color: #060; }
pre .kwd, code .kwd { color: #006; font-weight: bold; }
pre .com, code .com { color: #600; font-style: italic; }
pre .typ, code .typ { color: #404; font-weight: bold; }
pre .lit, code .lit { color: #044; }
pre .pun, code .pun { color: #440; }
pre .pln, code .pln { color: #000; }
pre .tag, code .tag { color: #006; font-weight: bold; }
pre .atn, code .atn { color: #404; }
pre .atv, code .atv { color: #060; }
}
/* 追加 */
p.file_name {background-color:#000; color: #eee; border: 1px solid #888; border-bottom: none; border-top-left-radius:8px; border-top-right-radius:8px;}
}
.doxy {
/* doxy.css をコピー */
pre .str, code .str { color: #fec243; } /* string - eggyolk gold */
pre .kwd, code .kwd { color: #8470FF; } /* keyword - light slate blue */
pre .com, code .com { color: #32cd32; font-style: italic; } /* comment - green */
pre .typ, code .typ { color: #6ecbcc; } /* type - turq green */
pre .lit, code .lit { color: #d06; } /* literal - cherry red */
pre .pun, code .pun { color: #8B8970; } /* punctuation - lemon chiffon4 */
pre .pln, code .pln { color: #f0f0f0; } /* plaintext - white */
pre .tag, code .tag { color: #9c9cff; } /* html/xml tag (bluey) */
pre .htm, code .htm { color: #dda0dd; } /* html tag light purply*/
pre .xsl, code .xsl { color: #d0a0d0; } /* xslt tag light purply*/
pre .atn, code .atn { color: #46eeee; font-weight: normal;} /* html/xml attribute name - lt turquoise */
pre .atv, code .atv { color: #EEB4B4; } /* html/xml attribute value - rosy brown2 */
pre .dec, code .dec { color: #3387CC; } /* decimal - blue */
a {
text-decoration: none;
}
pre.prettyprint, code.prettyprint {
font-family:'Droid Sans Mono','CPMono_v07 Bold','Droid Sans';
font-weight: bold;
font-size: 16px; /* 変更 */
background-color: #0f0f0f;
-moz-border-radius: 8px;
-webkit-border-radius: 8px;
-o-border-radius: 8px;
-ms-border-radius: 8px;
-khtml-border-radius: 8px;
border-radius: 8px;
border-top-left-radius:0; /* 追加 */
} /* background is black (well, just a tad less dark ) */
pre.prettyprint {
/* 削除 width:95%;*/
margin: 1em auto;
padding: 1em;
white-space: pre-wrap;
}
pre.prettyprint a, code.prettyprint a {
text-decoration:none;
}
/* Specify class=linenums on a pre to get line numbering; line numbers themselves are the same color as punctuation */
ol.linenums { margin-top: 0; margin-bottom: 0; color: #8B8970; } /* IE indents via margin-left */
li.L0,li.L1,li.L2,li.L3,li.L5,li.L6,li.L7,li.L8 { list-style-type: none }
/* Alternate shading for lines */
li.L1,li.L3,li.L5,li.L7,li.L9 { }
/* print is mostly unchanged from default at present */
@media print {
pre.prettyprint, code.prettyprint { background-color: #fff; }
pre .str, code .str { color: #088; }
pre .kwd, code .kwd { color: #006; font-weight: bold; }
pre .com, code .com { color: #0c3; font-style: italic; }
pre .typ, code .typ { color: #404; font-weight: bold; }
pre .lit, code .lit { color: #044; }
pre .pun, code .pun { color: #440; }
pre .pln, code .pln { color: #000; }
pre .tag, code .tag { color: #b66ff7; font-weight: bold; }
pre .htm, code .htm { color: #606; font-weight: bold; }
pre .xsl, code .xsl { color: #606; font-weight: bold; }
pre .atn, code .atn { color: #c71585; font-weight: normal; }
pre .atv, code .atv { color: #088; font-weight: normal; }
}
/* 追加 */
p.file_name {background-color:#0f0f0f; color: #eee; border: 1px solid #888; border-bottom: none; border-top-left-radius:8px; border-top-right-radius:8px; }
}
.sons-of-obsidian {
/* sons-of-obsidian.css をコピー */
.str{
color: #EC7600;
}
.kwd{
color: #93C763;
}
.com{
color: #66747B;
}
.typ{
color: #678CB1;
}
.lit{
color: #FACD22;
}
.pun{
color: #F1F2F3;
}
.pln{
color: #F1F2F3;
}
.tag{
color: #8AC763;
}
.atn{
color: #E0E2E4;
}
.atv{
color: #EC7600;
}
.dec{
color: purple;
}
pre.prettyprint{
border: 0px solid #888;
}
ol.linenums{
margin-top: 0;
margin-bottom: 0;
}
.prettyprint {
background: #000;
}
li.L0, li.L1, li.L2, li.L3, li.L4, li.L5, li.L6, li.L7, li.L8, li.L9{
color: #555;
list-style-type: decimal;
}
li.L1, li.L3, li.L5, li.L7, li.L9 {
background: #111;
}
@media print
{
.str{
color: #060;
}
.kwd{
color: #006;
font-weight: bold;
}
.com{
color: #600;
font-style: italic;
}
.typ{
color: #404;
font-weight: bold;
}
.lit{
color: #044;
}
.pun{
color: #440;
}
.pln{
color: #000;
}
.tag{
color: #006;
font-weight: bold;
}
.atn{
color: #404;
}
.atv{
color: #060;
}
}
/* 追加 */
p.file_name {background-color:#000; color: #eee;}
}














