Wonderful! WordPress

Gutenberg Block Editor 自作その2 - id指定

Page No.2

background image を設定できる グループボックスなんてのは?

実際のところ、いまや画像を表示するのに img タグよりも背景画像として表示させる方が多くなっている。画像をいくつも並べる場合、それぞれ元画像のアスペクトレシオが違っていても、背景画像なら表示する大きさを統一しても( 画像によってはアスペクトレシオが維持されない )、Lighthouse のチェックで点数が下がることもない。

と、いうことで、インラインスタイル で背景画像をブロックに設定することは可能なのだろうか。たしか、カバー?ブロックでそれができたはずだという記憶があって試しにやってみたが、background ではなく img タグにての表示だった。あれっ?

しかたがない。それでは、やはり自作ブロックにてインラインスタイルの設定ができるように試みるとする。
インラインスタイルといってそんなに違ったものでもなく、インスペクターコントロールで取得した値を適応すればいいだけのことなのではなかろうか、と、安易に思いつつとりかかる。
と、すっかり忘れてしまっていたけれど、実はこれはすでにやっていることだった。下に出てくる data 属性もまたしかり。・・・>「Gutenberg Block Editor だとさ page3」 のプラグインブロックにて。

あと、もうひとつ。div と決まったタグではなく、標準のグループブロック同様に、sectionarticlediv とその都度選択できる汎用のグループブロックであれば、それぞれのブロックを用意する必要もなくなるなぁと。と、できたものが下。 React のものは、どうにも自信がなく、どこか不安がついてまわるけれど、エラーも出ず、期待通りに動いてはいる。

( function ( blocks, element, blockEditor, components ) {
	const el = element.createElement,
        Fragment = element.Fragment,
        InspectorControls = blockEditor.InspectorControls,
        TextControl = components.TextControl,
        SelectControl = components.SelectControl,
        useBlockProps = blockEditor.useBlockProps,
        InnerBlocks = blockEditor.InnerBlocks;

    // 文字列としてのスタイル設定をばらして連想配列にする関数
	// useBlockProps の style は連想配列の形式なため
    function treat_style( val ){
        let tmpstyle = {};
        if ( val ) {
            let vary = val.split( ';' );

            for ( let i = 0, vcnt = vary.length; i < vcnt; ++i ) {
                let tmp = [];
                
                if ( vary[ i ] ) {
                    tmp[0] = vary[ i ].substring( 0, vary[ i ].indexOf( ':' ) );
                    tmp[1] = vary[ i ].substring( vary[ i ].indexOf( ':' ) + 1 );

                    // ハイフンのはいるプロパティはそのままのケバブだとエラーになって怒られるのでキャメルケースに変更する
                    tmp[2] = tmp[0].split( '-' );

                    if ( tmp[2][1] ) {
                        let tmpstr = '';
                        for ( let j = 1, tar_len = tmp[2].length; j < tar_len; ++j  ) {
                            tmpstr += tmp[2][ j ].slice( 0, 1 ).toUpperCase() + tmp[2][j].slice( 1 );
                        }
                        tmp[2][0] += tmpstr;
                    }

                    if ( tmp[1] ) {
                        tmpstyle[ tmp[2][0] ] = tmp[1];
                    }
                }
            }
        }
        return tmpstyle;
    }

    blocks.registerBlockType( 'stx-id-style-wrapper/id-style-wrapper', {
        title: 'STX id-style-wrapper',
        icon: 'smiley',
        category: 'design',
        description: 'id 及びインラインスタイルを設定できsection、article、div のタグを選択できるラッパー',

        attributes: {
            anchor: { type: 'string' },
            wrapper: { type: 'string', default: 'div' },
            styles: { type: 'string', default: '' },
        },

        supports: {
            anchor: true,
            // className: false, 
        },

        edit: function( props ) {
            var blockProps = useBlockProps();
            let styles = props.attributes.styles,
                wrapper = props.attributes.wrapper,
                elem = { className: props.className, style: treat_style( props.attributes.styles ) };

            return(
                el(
                    Fragment,
                    null,
                    el(
                        InspectorControls,
                        null,
                        el( 'div', { id: 'stx_iddiv_sdbr', style: { fontSize: '1.3em' } },
                            el(
                                SelectControl,
                                {
                                    label: 'Select Tag',
                                    value: wrapper,
                                    options: [
                                        {
                                            value: 'section',
                                            label: 'section'
                                        },
                                        {
                                            value: 'article',
                                            label: 'article'
                                        },
                                        {
                                            value: 'div',
                                            label: 'div'
                                        }
                                    ],
                                    onChange: function( newValue ){ props.setAttributes( { wrapper: newValue } ); }
                                }
                            ),
                            el(
                                TextControl,
                                {
                                    label: 'styles',
                                    help: 'div に設定するインラインスタイル。通常のcssに記述するスタイルで。eq. margin-left:10px;color:red;font-size:1.1em;',
                                    style: { color: 'red' },// このスタイルは有効ではあるけれど、label に対してのもの 
                                    value: styles,
                                    onChange: function( newValue ){ props.setAttributes( { styles: newValue } ); }
                                }
                            ),
                        ),
                    ),
                    el(
                        props.attributes.wrapper,
                        Object.assign( blockProps, elem ),
                        el( InnerBlocks )
                    )
                )
            );
        },

        save: function( props ) {

            var blockProps = useBlockProps.save();

            if ( props.attributes.anchor ) {
                blockProps.id = props.attributes.anchor;
            }
            if ( props.attributes.styles ) {
                blockProps.style = treat_style ( props.attributes.styles );
            }

            return el(
                props.attributes.wrapper,
                blockProps,
                el( InnerBlocks.Content )
            );
        },
    });
}(
	window.wp.blocks,
	window.wp.element,
	window.wp.blockEditor,
    window.wp.components,
) );
JavaScript
CopyExpand

useBlockProps を使ってる。エディターでそのスタイルやら id やらを反映させるのにちょっと苦労してしまった。スタイルにおいて、useBlockPropsstyle の値は、オブジェクト( 連想配列 )の形式なのでそれにあわせないといけないので。もっといい方法、正道な方法があるのかもしれないけれど、一応、これでちゃんと動いている。ただし、上の状態だと edit (編集画面)においては、id属性が反映されない。それは下で反映されるように改善してる。

そして、随分と後になって気が付いたことだけれど、apiVersion 2 を指定した場合、class も反映されなくなる。それは、edituseBlockProps() に指定する部分の className: props.classNameclassName: proprs.attributes.className に変更すれば class は反映されるようになった。apiVersion 1 においても、これで反映されるので、そもそもこちらで書いていればよかったのかも。

id、style、data属性を設定できるグループボックス

ちょっと考えてみると、スタイルを設定できるのなら、data属性も設定できるはず。そう、data属性を設定しているボックスにおいては、カスタムHTML ボックスでごまかしていたのであった。
と、いうことで上のブロックをちょっと進化させて data属性も設定できるようにしたのが下のもの。それに、edit (編集画面)において id属性が反映されるようにしてる。自的には、上と下のブロックの registerBlockType 関数は引数も使う関数も同じなので、同じ一つの即時関数の中に入れている。

( function ( blocks, element, blockEditor, components ) {
	const el = element.createElement,
        Fragment = element.Fragment,
        InspectorControls = blockEditor.InspectorControls,
        TextControl = components.TextControl,
        SelectControl = components.SelectControl,
        useBlockProps = blockEditor.useBlockProps,
        InnerBlocks = blockEditor.InnerBlocks;

    function treat_style( val ){
        let tmpstyle = {};
        if ( val ) {
            let vary = val.split( ';' );

            for ( let i = 0, vcnt = vary.length; i < vcnt; ++i ) {
                let tmp = [];
                
                if ( vary[ i ] ) {
                    tmp[0] = vary[ i ].substring( 0, vary[ i ].indexOf( ':' ) );
                    tmp[1] = vary[ i ].substring( vary[ i ].indexOf( ':' ) + 1 );

                   tmp[2] = tmp[0].split( '-' );

                    if ( tmp[2][1] ) {
                        let tmpstr = '';
                        for ( let j = 1, tar_len = tmp[2].length; j < tar_len; ++j  ) {
                            tmpstr += tmp[2][ j ].slice( 0, 1 ).toUpperCase() + tmp[2][j].slice( 1 );
                        }
                        tmp[2][0] += tmpstr;
                    }
                    if ( tmp[1] ) {
                        tmpstyle[ tmp[2][0] ] = tmp[1];
                    }
                }
            }
        }
        return tmpstyle;
    }

    blocks.registerBlockType( 'stx-data-wrapper/data-wrapper', {
        title: 'STX data-wrapper',
        icon: 'smiley',
        category: 'design',
        description: 'id 及びインラインスタイル、data属性を設定できsection、article、div のタグを選択できるラッパー',

        attributes: {
            anchor: { type: 'string' },
            wrapper: { type: 'string', default: 'div' },
            datastr: { type: 'string', default: '' },
            styles: { type: 'string', default: '' },
        },

        supports: {
            anchor: true,
            // className: false, 
        },

        edit: function( props ) {
            var blockProps = useBlockProps();
            let datastr = props.attributes.datastr,
                styles = props.attributes.styles,
                wrapper = props.attributes.wrapper,
                anchor = props.attributes.anchor,
                elem = {
                    className: props.className,
                    style: treat_style( props.attributes.styles ),
                    id: anchor ? anchor : blockProps.id,
                };

            if ( props.attributes.datastr ) {
                const tmpdata = props.attributes.datastr,
                    datas = tmpdata.split( ',' ),
                    dataslen = datas.length;
                
                for ( let i = 0; i < dataslen; ++i ) {
                    dataid = 'data-' + datas[ i ].substring( 0, datas[ i ].indexOf( ':' ) ),
                    datascr = -1 !== datas[ i ].indexOf( ':' ) ? datas[ i ].substring( datas[ i ].indexOf( ':' ) + 1 ) : '';
                
                    elem[ dataid ] = datascr;
                }
            }

            return(
                el(
                    Fragment,
                    null,
                    el(
                        InspectorControls,
                        null,
                        el( 'div', { id: 'stx_iddiv_sdbr', style: { fontSize: '1.3em' } },
                            el(
                                SelectControl,
                                {
                                    label: 'Select Tag',
                                    value: wrapper,
                                    options: [
                                        {
                                            value: 'section',
                                            label: 'section'
                                        },
                                        {
                                            value: 'article',
                                            label: 'article'
                                        },
                                        {
                                            value: 'div',
                                            label: 'div'
                                        }
                                    ],
                                    onChange: function( newValue ){ props.setAttributes( { wrapper: newValue } ); }
                                }
                            ),
                            el(
                                TextControl,
                                {
                                    label: 'data',
                                    help: 'block に設定する data 属性文字列。dataid : datastr の形式。複数のdata属性を設定する場合は","区切りで。',
                                    value: datastr,
                                    onChange: function( newValue ){ props.setAttributes( { datastr: newValue } ); }
                                }
                            ),
                            el(
                                TextControl,
                                {
                                    label: 'styles',
                                    help: 'div に設定するインラインスタイル。通常のcssに記述するスタイルで。eq. margin-left:10px;color:red;font-size:1.1em;',
                                    value: styles,
                                    onChange: function( newValue ){ props.setAttributes( { styles: newValue } ); }
                                }
                            ),
                        ),
                    ),
                    el(
                        props.attributes.wrapper,
                        Object.assign( blockProps, elem ),
                        el( InnerBlocks )
                    )
                )
            );
        },

        save: function( props ) {

            var blockProps = useBlockProps.save();

            if ( props.attributes.anchor ) {
                blockProps.id = props.attributes.anchor;
            }
            if ( props.attributes.styles ) {
                blockProps.style = treat_style ( props.attributes.styles );
            }

            if ( props.attributes.datastr ) {
                const tmpdata = props.attributes.datastr,
                    datas = tmpdata.split( ',' ),
                    dataslen = datas.length;
                
                for ( let i = 0; i < dataslen; ++i ) {
                    dataid = 'data-' + datas[ i ].substring( 0, datas[ i ].indexOf( ':' ) ),
                    datascr = -1 !== datas[ i ].indexOf( ':' ) ? datas[ i ].substring( datas[ i ].indexOf( ':' ) + 1 ) : '';
                
                    blockProps[ dataid ] = datascr;
                }
            }

            return el(
                props.attributes.wrapper,
                blockProps,
                el( InnerBlocks.Content )
            );
        },
    });
}(
	window.wp.blocks,
	window.wp.element,
	window.wp.blockEditor,
    window.wp.components,
) );
JavaScript
CopyExpand

と、いうことで以上。
supports classNamefalse 指定すると class がダブってしまう問題は、いまだ解決できていない。もうちょっとじたばたする必要があるよう。とりあえず、これはこれにて。
そして、ブロックづくりはまだまだつづくのである・・・「Gutenberg Block Editor 自作その3 – form編」
このページの前編はといえば・・・>「Gutenberg Block Editor だとさ

Sanbanse Funabashi
2011.01.01 sunrise

Top

スクロールさせるか画像をクリックすると元に戻ります。