Gutenberg(ブロックエディタ)メモ: カスタムHTMLを使う

WordPressのGutenberg
WordPressのGutenberg

WordPressのGutenbergハンドブックに記述がみつからず、GitHubに載っている方法での実装を試した内容です。

構築環境
WordPress4.9.8
JavaScriptJSX/ESNext
Gutenberg3.9.0

注意点

  • Gutenbergがコアに入る前の段階の記事です
  • 書き方だけではなく、語句や名称にも問題がある可能性があります
  • バッドノウハウな状態だと思いますので、読む方は参考程度に考えてください
  • 私自身の書き方がまだ固まっていないためと参考にしたサンプルの都合上、当ブログ内のサンプルコードにはアロー関数の使用の有無などに表記揺れがあります

[追記 2018.11.8]
Gutenberg4.2.0よりカスタムHTMLに使われているCodeEditorPlaneTextに変更されたため、当記事もPlaneTextに変更しました。これに伴い入力欄左にあった行数の表示やタグの色変更がなくなりました。

実現したいこと

  • GutenbergでカスタムHTMLを追加する
  • カスタムHTMLに初期値(任意のHTML)を入れる

「カスタムHTML」という名称のブロックに関してはハンドブックでは見当たりませんでしたが、GitHubの以下のページが該当するようです(ブロックのtitleにCustom HTMLという名称があっただけでした)。

  1. gutenberg/block-library/html/index.js
  2. gutenberg/components/code-editor/README.md

コードをみると編集ではPlaneTextコンポーネントを使い、出力時にRawHTMLコンポーネントを使う仕組みの模様です。

以降のサンプルはサンプル1が上記の1を参照し、サンプル2は上記2を参照しつつ上記1で得た要素も取り入れています。

今回の問題点

根本的な問題として、これらのコードをそのまま使ってよいものかの判断が付きませんでした。

当初はRichTextのように簡単に使えるコンポーネントにされていると考えていて、それさえかけばGitHubに載っている記述そのままを記載する必要はないのではと思ったためです。

しかしながら調べても該当のものを出せるコンポーネントがわからず、当記事のような形となっています。

未発見、あるいは今後追加されるかもしれませんので、特に参考程度にご覧下さい。

サンプル 1

カスタムHTML
カスタムHTML(※現在は左に行数はなくタグの色も変化しません)

以下が「プレビュー機能も付いたカスタムHTML(=コードエディタ?)」のサンプルです。

一部使っていないコンポーネントがあるようですが何かの意図があって書かれているのではと思うため、ほぼGitHubのコードと同じです。そのため形を整える以外は、初期値の設置(コード内のコメント部分)を追加した程度です。

ButtonwithStateなどの使い方の実例も見られるコードで参考になる点が多々ありました。


//初期設定
add_action( 'enqueue_block_editor_assets', function() {
  wp_enqueue_script(
    'myplugin-gutenberge',
    plugins_url( 'block.js', __FILE__ ),
    [ 'wp-blocks', 'wp-element', 'wp-components', 'wp-i18n', 'wp-editor', 'wp-compose' ]
  );
} );

const {
  registerBlockType,
  setDefaultBlockName,
  setUnknownTypeHandlerName,
getPhrasingContentSchema
} = wp.blocks;
const { RawHTML } = wp.element
const { __ } = wp.i18n;
const { Disabled, SandBox} = wp.components;
const { BlockControls,PlainText } = wp.editor;
const { withState } = wp.compose;

registerBlockType( 'my-plugin/test-customhtml', {
  title: 'test-customHTML',
  icon: 'universal-access-alt',
  category: 'layout',

  supports: {
    customClassName: false,
    className: false,
    html: false,
  },

  attributes: {
    content: {
      type: 'string',
      source: 'html',
    },
  },

  transforms: {
    from: [
      {
        type: 'raw',
        isMatch: ( node ) => node.nodeName === 'FIGURE' && !! node.querySelector( 'iframe' ),
        schema: {
          figure: {
            require: [ 'iframe' ],
            children: {
              iframe: {
                attributes: [ 'src', 'allowfullscreen', 'height', 'width' ],
              },
              figcaption: {
                children: getPhrasingContentSchema(),
              },
            },
          },
        },
      },
    ],
  },

  edit: withState( {
    isPreview: false,
  } )( ( { attributes, setAttributes, setState, isSelected, toggleSelection, isPreview } ) => (

    //以下で初期値を設定。未入力時点ではattributes.contentがundefinedであるため、それを判定して値を入れる。
    if(attributes.content == undefined){
      setAttributes( { content: '<ul>\n\t<li>default HTML</li>\n</ul>' } );
    }

    <div className="wp-block-html">
      <BlockControls>
        <div className="components-toolbar">
          <button
            className={ `components-tab-button ${ ! isPreview ? 'is-active' : '' }` }
            onClick={ () => setState( { isPreview: false } ) }
          >
            <span>HTML</span>
          </button>
          <button
            className={ `components-tab-button ${ isPreview ? 'is-active' : '' }` }
            onClick={ () => setState( { isPreview: true } ) }
          >
            <span>{ __( 'Preview' ) }</span>
          </button>
        </div>
      </BlockControls>
      <Disabled.Consumer>
        { ( isDisabled ) => (
          ( isPreview || isDisabled ) ? (
            <SandBox html={ attributes.content } />
          ) : (
            <PlainText
              value={ attributes.content }
              focus={ isSelected }
              onFocus={ toggleSelection }
              onChange={ ( content ) => setAttributes( { content } ) }
            />
          )
        ) }
      </Disabled.Consumer>
    </div>
  ) ),

  save( { attributes } ) {
    return <RawHTML>{ attributes.content }</RawHTML>;
  },
} );

まだあやふやな推測しかできない部分が多いのでコメント挿入版はありません。

サンプル 2

カスタムHTML(コードディタ)
カスタムHTML(※現在は左に行数はなくタグの色も変化しません)

サンプル2はプレビュー機能がない簡易版のようなものかもしれません。

サンプル1に比べると機能的に劣る可能性がありますが(iframefigcaptionに対する設定がないなど)、用途がある程度限定されるならば使える可能性はありそうです。

なお、RawHTMLを使わない場合は、そのままCodeEditorとして利用可能です(HTMLが文字列のまま出力される)。


//初期設定
add_action( 'enqueue_block_editor_assets', function() {
  wp_enqueue_script(
    'myplugin-gutenberge',
    plugins_url( 'block.js', __FILE__ ),
    [ 'wp-blocks', 'wp-element', 'wp-editor' ]
  );
} );

const { registerBlockType } = wp.blocks;
const { RawHTML } = wp.element
const { PlainText } = wp.editor;

registerBlockType( 'my-plugin/test-codeeditor', {
  title: 'test-codeeditor',
  icon: 'universal-access-alt',
  category: 'layout',

  attributes: {
    content: {
      type: 'string',
      default: '<ul>\n\t<li>default HTML</li>\n</ul>',
    }
  },
  edit( { attributes, setAttributes } ) {
    const { content } = attributes;

    const onChangeContent = newContent => {
      setAttributes( { content: newContent } );
    };

    return(
      <div className="wp-block-html">
        <PlainText
            value={ attributes.content }
            onChange={ onChangeContent }
          />
      </div>
    );
  },
  save( { attributes, className } ) {
    const { content } = attributes;
    return (
      <div className={ className }>
        <RawHTML>{content}</RawHTML>
      </div>
    );
  },
} );

サンプル1との違い source

大きな記述内容の違い以外にも一点重要な部分があります。


supports: {
  customClassName: false,
  className: false,
  html: false,
},
attributes: {
  content: {
    type: 'string',
    source: 'html',
  }
}

上記はサンプル1の一部ですが、save内でRawHTML(おそらく、渡された文字列ではなくHTMLとして出力させるコンポーネント)を使う場合に以下のような状態でした。

  • attributessourcehtmlで、supportshtmlfalseではない
  • savereturn時にRawHTMLを囲むようにdivなどを記述する
  • 上記2点の場合、保存後の再表示(リロードなど)を行うと二重に外側のdivが出力されてブロックが壊れる

対して、以下がサンプル2の状態です。


attributes: {
  content: {
    type: 'string',
    default: '<ul>\n\t<li>default HTML</li>\n</ul>',
  }
},

sourceを指定しないことでサンプル1の記述がなくとも動作を確認しました。

supportshtml:falseattributessource:htmlの役割と関係性が確認できず、これを書かない事による影響が把握できていませんが、少なくとも単にliaタグなどを使ってもおかしな動作にはなりませんでした。

とはいえ懸念を感じる場合はサンプル1のほうを基本にしたほうが良いのかもしれません。

サンプル1との違い 初期値

サンプル1はwitStateで括られているためすぐにif文が動作する模様ですが、サンプル2は自動保存が走るタイミングで実行されるようで、同じ記述では初期値が入るまでにラグがでてしまいました。

そこで、サンプル2では以下のようにdefaultを使って試したところ、意図どうりの動作になりました。


attributes: {
  content: {
    type: 'string',
    default: '<ul>\n<li>default HTML</li>\n</ul>',
  }
},

関連記事

Gutenberg(ブロックエディタ)に関連する記事一覧。

1人がこの記事を評価

役に立ったよという方は上の「記事を評価する」ボタンをクリックしてもらえると嬉しいです。

連投防止のためにCookie使用。SNSへの投稿など他サービスとの連動は一切ありません。

コメント欄