Gutenberg(ブロックエディタ)メモ: Buttonを使う(MediaUploadとRichTextも含む)

WordPressのGutenberg
WordPressのGutenberg

WordPressのGutenbergハンドブックに載っている記述ではよく分かりませんでしたが、ほぼ完成のコードを見ることができ、それに少し手を入れる形で作った内容です。

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

注意点

  • Gutenbergがコアに入る前の段階の記事です
  • 書き方だけではなく、語句や名称にも問題がある可能性があります
  • 私自身の書き方がまだ固まっていないため、当ブログ内のサンプルコードにはconstやfunction、アロー関数の使用の有無など、表記揺れがあります

実現したいこと

  • ハンドブックにあるButtonを使う

ハンドブックでは以下のページに説明があります。

当初は用途を「何らかのボタンを記事に出力するためのコンポーネント」だと勘違いしてしまい作り方がわかりませんでしたが、いくらか試しながらサンプルは無いかと調べたところ、以下の記事を見つけてようやく役割を理解できました。

あくまで投稿欄内のブロックに使うUI用のコンポーネントであり、記事に出力するボタンを作るということではありませんでした(出力用のボタンもこのコンポーネントを使うのかもしれませんが)。

なお、以下のサンプルは上記記事の質問に記載されたコードをほとんどそのまま使わせてもらっています。

サンプル

Button完成例
Button

以下は「見出しとコンテンツと画像を出力するパーツ」「アップロードボタンを作って画像をアップロードする」という動作を目的としたサンプルです。

なお、本来の動作は「画像の取得に成功するとボタンは消えて画像だけが表示される」なのですが、ボタンを付けたままの方がわかりやすいのではとMediaUploadrender内を改造して試しています。

この形はよろしくないように思いますが、動作自体は期待したものになったようです。


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

const { registerBlockType } = wp.blocks;
const { MediaUpload, RichTexts } = wp.editor;
const { Fragment } = wp.element;
const { Button } = wp.components;

const testImgSetHeadingStyle = { borderLeft: '5px solid #555', paddingLeft: '.5em',fontWeight: 'bold' };
const testImgSetTextareaStyle = { border: '1px solid #ccc' };

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

  attributes: {
    title: {
      type: 'array',
      source: 'children',
      selector: 'h2',
    },
    content: {
      type: 'array',
      source: 'children',
      selector: 'p',
    },
    mediaID: {
      type: 'number',
    },
    mediaURL: {
      type: 'string',
      source: 'attribute',
      selector: 'div',
      attribute: 'data-src',
    }
  },

  edit( { attributes, className, setAttributes } ) {
    const { mediaID, mediaURL, title, content } = attributes;

    const onChangeTitle = value => {
      setAttributes( { title: value } );
    };

    const onSelectImage = media => {
      setAttributes( {
        mediaURL: media.url,
        mediaID: media.id,
      } );
    };

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

    return (
      <div className={ className }>
        <p style={testImgSetHeadingStyle}>Title</p>
        <RichText
          tagName="h2"
          placeholder={ 'Page Header' }
          value={ title }
          onChange={ onChangeTitle }
          style={testImgSetTextareaStyle}
        />
        <hr />
        <p style={testImgSetHeadingStyle}>Content</p>
        <RichText
          tagName="p"
          placeholder={ 'Page Content' }
          value={ content }
          onChange={ onChangeContent }
          style={testImgSetTextareaStyle}
        />
        <hr />
        <MediaUpload
          onSelect={ onSelectImage }
          type="image"
          value={ mediaID }
          render={ ( { open } ) => (
            <Fragment>
              <div>
              { mediaID
                ? <Button className={ 'image-button' } onClick={ open }>
                    <img src={ mediaURL } />
                  </Button>
                : <p>No Image</p>
              }
              </div>
              <Button className={ 'button button-large' } onClick={ open }>
                Upload Image
              </Button>
            </Fragment>
          )}
        />
      </div>
    );
  },
  save( { attributes, className } ) {
    const { mediaID, mediaURL, title, content } = attributes;

    const divStyle = {};
    if( mediaURL ) {
        divStyle.backgroundImage = 'URL('+mediaURL+')';
    }

    return (
      <div className={ className } data-src={ mediaURL } style={ divStyle }>
      <h2>{ title }</h2>
      <p>{ content }</p>
      </div>
    );
  },
});

説明入りサンプル

以下は上記と同じコードにコメントで説明を入れたものです。


//ブロックが作れるようにregisterBlockTypeを読み込む
const { registerBlockType } = wp.blocks;
//editやsave内で<RichText>などが使えるように、 wp.editorから読み込む
const { MediaUpload, RichText } = wp.editor;
//editやsave内で<Fragment>が使えるように、 wp.elementから読み込む
const { Fragment } = wp.element;
//editやsave内で<Button>が使えるように、 wp.componentから読み込む
const { Button } = wp.components;

//装飾用CSSを設定
const testImgSetHeadingStyle = { borderLeft: '5px solid #555', paddingLeft: '.5em',fontWeight: 'bold' };
const testImgSetTextareaStyle = { border: '1px solid #ccc' };

//編集画面用の設定を作成
registerBlockType( 'my-plugin/test-img-set', {
  //iconやcategoryなどの設定(Title以外は参考にしたコードのまま決定)
  title: 'test-img-set',
  icon: 'universal-access-alt',
  category: 'layout',
  //属性とその値を利用できるように設定。初期設定のようなもの。
  attributes: {
    title: {
      type: 'array',
      source: 'children',
      selector: 'h2',
    },
    content: {
      type: 'array',
      source: 'children',
      selector: 'p',
    },
    mediaID: {
      type: 'number',
    },
    //save関数内を見るとこの設定の具体的な理由がわかる。
    mediaURL: {
      type: 'string',
      source: 'attribute',
      selector: 'div',
      attribute: 'data-src',
    }
  },

  //editは投稿画面内の内容を設定
  edit( { attributes, className, setAttributes } ) {
    //attributesでまとめていた各属性を分けて使えるようにする
    //こうしておけば{mediaID}と書けばmediaID: num(数値)のように出力される
    const { mediaID, mediaURL, title, content } = attributes;

    //タイトル用のRichText内の文字列が変化したら、attributesのtitleに都度値をセットする
    const onChangeTitle = value => {
        setAttributes( { title: value } );
    };

    //コンテンツ用のRichText内の文字列が変化したら、attributesのtitleに都度値をセットする
    const onChangeContent = newContent => {
        setAttributes( { content: newContent } );
    };

    //MediaUploadで画像を選ぶとonSelectによってここが呼ばれ、urlとidがそれぞれatribbutesのmediaURLとmediaIDに設定される
    const onSelectImage = media => {
      setAttributes( {
        mediaURL: media.url,
        mediaID: media.id,
      } );
    };

    return (
      <div className={ className }>
        <p style={testImgSetHeadingStyle}>Title</p>
        <RichText
          tagName="h2"
          placeholder={ 'Page Header' }
          value={ title }
          onChange={ onChangeTitle }
          style={testImgSetTextareaStyle}
        />
        {//今回は試しでhrも使用
        }
        <hr />
        <p style={testImgSetHeadingStyle}>Content</p>
        <RichText
          tagName="p"
          placeholder={ 'Page Content' }
          value={ content }
          onChange={ onChangeContent }
          style={testImgSetTextareaStyle}
        />
        <hr />
        <MediaUpload
          {//画像が選択されたらonSelectImageを呼ぶ
          }
          onSelect={ onSelectImage }
          type="image"
          value={ mediaID }
          //MediaUploadの出力内容を設定
          render={ ( { open } ) => (
            <Fragment>
              <div>
              { mediaID
                {//mediaIDが取れればimage-buttonクラスを付けて、画像アップローダーを表示させ、クリックで選択画面を出す
                }
                ? <Button className={ 'image-button' } onClick={ open }>
                    <img src={ mediaURL } />
                  </Button>
                {//mediaIDが取れなければ、No Imageと表示(=初期状態)
                }
                : <p>No Image</p>
              }
              </div>
              {//onClickでopneが呼ばれて画像の値を取得
              }
              <Button className={ 'button button-large' } onClick={ open }>
                Upload Image
              </Button>
            </Fragment>
          )}
        />
      </div>
    );
  },
  //saveは保存時の内容を設定。基本的にはそのまま出力されるので、記事表示時の内容とほぼ同義。
  //最初に設定し、edit内で操作したattributesの値をattributesと書くことでまとめて引数にしている
  save( { attributes, className } ) {
    //attributesでまとめていた各属性を分けて使えるようにする
    const { mediaID, mediaURL, title, content } = attributes;

    //divStyleという定数を新たに定義
    const divStyle = {};
    //mediaURLがあればdivStyleにbackgroundImageという名称でプロパティを設定して値をいれる
    //この状態で{divStyle}と書くとbackgroundImage:'URL(画像のURL)'という形で出力されるので、そのままスタイル属性の値に使える(JSではbackground-imageではなく、backgroundImageのため)
    if( mediaURL ) {
      divStyle.backgroundImage = 'URL('+mediaURL+')';
    }

    return (
      {/*
      //背景画像にdivStyle.backgroundImageの値を出力
      //このサンプルだけではdata-srcは利用されていないが、参考になるのでサンプル通り記載\
      //data-srcは使っていませんが、こういう形もあるという小さな例として一応参考にしたコードのまま記載しています
      */}
      <div className={ className } data-src={ mediaURL } style={ divStyle }>
      {//RichText使用時の入力内容出力には、RichText.Contentを使った方が安全かもしれません
      }
      <h2>{ title }</h2>
      {//RichText使用時の入力内容出力には、RichText.Contentを使った方が安全かもしれません
      }
      <p>{ content }</p>
      </div>
    );
  },
});

補足

MediaUploadの動作はまだ確認できていなので、説明は推測が大半です。

いずれにせよ、コードの全体が示されていればそういう推測も容易になりますし、付随して複数の部分の記述や動作を確認できるので、今回の参考ページには非常に感謝しています。

関連記事

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

2人がこの記事を評価

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

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

コメント欄