如何从多个古登堡侧边栏控件控制元素类?

时间:2020-06-27 作者:NextGenThemes

我正在尝试扩展core/table 阻止和复制粘贴、跟踪和出错,使我接近我要做的事情。但我觉得这不是我喜欢的。

我喜欢扩展块,使其具有控制<table> 该块已经对两个基类执行了此操作,但我希望它能够与Bootstrap CSS tables.

原始块代码can be found here.

我喜欢做的是古腾堡也做得很好的事情,而对于桌块来说,就是为了风格。当您将其设置为“条带化”时,它将添加is-style-striped 到街区。它将实时更新该字段,一旦手动删除该类,它将自动将样式切换为“默认”。我喜欢将一些类切换链接到这样的文本字段。我在GB代码中搜索了一点,但找不到它的位置和方式。这个buildTableClasses 使用的函数classnames/dedupe 可能是一种方式,但这里有一些问题。

                        <ToggleControl
                            label={ __( \'Hover\' ) }
                            checked={ !! tableHover }
                            onChange={ () => props.setAttributes( {
                                tableHover: ! tableHover,
                                tableClass: buildTableClasses( props.attributes ),
                            } ) }
                        />
我不太明白这里使用的否定和双重否定。这段代码基于我找到的一些教程。目前,is行为怪异。就像反向切换一样,当切换关闭时会添加类,但当切换最后一个类所坚持的另一个切换时也会添加类,有些奇怪。我试过了,但它完全坏了,所以我就这样贴了出来我目前拥有的完整代码。Ready to try plugin on Github. 这个save 函数是从GB复制粘贴的,并针对表类进行了修改。

import classnames from \'classnames/dedupe\';

const wp = window.wp;
const { __ } = wp.i18n;
const { addFilter } = wp.hooks;
const { assign } = window.lodash;
const {
    createHigherOrderComponent,
} = wp.compose;

const {
    Fragment,
} = wp.element;

const {
    RichText,
    InspectorControls,
    getColorClassName,
} = wp.editor;

const {
    PanelBody,
    TextControl,
    ToggleControl,
} = wp.components;

const filterBlocks = ( settings ) => {
    console.log( settings );

    if ( settings.name !== \'core/table\' ) {
        return settings;
    }

    const newSettings = {
        ...settings,
        attributes: {
            ...settings.attributes, // spread in old attributes so we don\'t lose them!
            tableClass: { // here is our new attribute
                type: \'string\',
                default: \'table \',
            },
            tableBordered: { // here is our new attribute
                type: \'boolean\',
                default: false,
            },
            tableStriped: { // here is our new attribute
                type: \'boolean\',
                default: false,
            },
            tableHover: { // here is our new attribute
                type: \'boolean\',
                default: true,
            },
        },
        save( { attributes } ) {
            const {
                hasFixedLayout,
                head,
                body,
                foot,
                backgroundColor,
                caption,
                tableBordered,
                tableStriped,
                tableHover,
                tableClass,
            } = attributes;
            const isEmpty = ! head.length && ! body.length && ! foot.length;

            if ( isEmpty ) {
                return null;
            }

            const classes = buildTableClasses( attributes );

            const hasCaption = ! RichText.isEmpty( caption );

            const Section = ( { type, rows } ) => {
                if ( ! rows.length ) {
                    return null;
                }

                const Tag = `t${ type }`;

                return (
                    <Tag>
                        { rows.map( ( { cells }, rowIndex ) => (
                            <tr key={ rowIndex }>
                                { cells.map(
                                    ( { content, tag, scope, align }, cellIndex ) => {
                                        const cellClasses = classnames( {
                                            [ `has-text-align-${ align }` ]: align,
                                        } );

                                        return (
                                            <RichText.Content
                                                className={
                                                    cellClasses ?
                                                        cellClasses :
                                                        undefined
                                                }
                                                data-align={ align }
                                                tagName={ tag }
                                                value={ content }
                                                key={ cellIndex }
                                                scope={
                                                    tag === \'th\' ? scope : undefined
                                                }
                                            />
                                        );
                                    }
                                ) }
                            </tr>
                        ) ) }
                    </Tag>
                );
            };

            return (
                <figure>
                    <table className={ classes === \'\' ? undefined : classes }>
                        <Section type="head" rows={ head } />
                        <Section type="body" rows={ body } />
                        <Section type="foot" rows={ foot } />
                    </table>
                    { hasCaption && (
                        <RichText.Content tagName="figcaption" value={ caption } />
                    ) }
                </figure>
            );
        },
    };

    return newSettings;
};

addFilter(
    \'blocks.registerBlockType\',
    \'example/filter-blocks\',
    filterBlocks
);

function buildTableClasses( attributes ) {
    const {
        hasFixedLayout,
        backgroundClass,
        tableStriped,
        tableBordered,
        tableHover,
        tableClass,
    } = attributes;

    const classes = classnames(
        tableClass.split( \' \' ),
        backgroundClass,
        {
            table: true,
            \'has-fixed-layout\': hasFixedLayout,
            \'has-background\': !! backgroundClass,
            \'table-bordered\': tableBordered,
            \'table-striped\': tableStriped,
            \'table-hover\': tableHover,
        }
    );

    return classes;
}

const tableClassControl = createHigherOrderComponent( ( BlockEdit ) => {
    return ( props ) => {
        if ( \'core/table\' !== props.name ) {
            return (
                <BlockEdit { ...props } />
            );
        }

        const {
            tableStriped,
            tableBordered,
            tableHover,
            tableClass,
        } = props.attributes;

        return (
            <Fragment>
                <BlockEdit { ...props } />
                <InspectorControls>
                    <PanelBody
                        title={ __( \'Table classes\' ) }
                        initialOpen={ true }
                    >
                        <ToggleControl
                            label={ __( \'Striped\' ) }
                            checked={ !! tableStriped }
                            onChange={ () => props.setAttributes( {
                                tableStriped: ! tableStriped,
                                tableClass: buildTableClasses( props.attributes ),
                            } ) }
                        />
                        <ToggleControl
                            label={ __( \'Bordered\' ) }
                            checked={ !! tableBordered }
                            onChange={ () => props.setAttributes( {
                                tableBordered: ! tableBordered,
                                tableClass: buildTableClasses( props.attributes ),
                            } ) }
                        />
                        <ToggleControl
                            label={ __( \'Hover\' ) }
                            checked={ !! tableHover }
                            onChange={ () => props.setAttributes( {
                                tableHover: ! tableHover,
                                tableClass: buildTableClasses( props.attributes ),
                            } ) }
                        />
                        <TextControl
                            label={ __( \'<table> classes\' ) }
                            type="text"
                            value={ tableClass }
                            onChange={ ( value ) =>
                                props.setAttributes( { tableClass: value } )
                            }
                        />
                    </PanelBody>
                </InspectorControls>
            </Fragment>
        );
    };
}, \'tableClassControl\' );

addFilter( \'editor.BlockEdit\', \'extend-block-example/with-spacing-control\', tableClassControl );

1 个回复
最合适的回答,由SO网友:Sally CJ 整理而成

双NOT运算符(!!)

这只是一种将非布尔值转换/类型转换为布尔值的方法,并且!! <expression> 给了我们相反的! <expression>.

let foo = \'bar\'; // non-empty string
console.log( ! foo, !! foo ); // false, true

foo = \'\'; // now it\'s an empty string
console.log( ! foo, !! foo ); // true, false
<代码中的问题wp.editor works,它已被弃用,因此您应该使用wp.blockEditor 而是:

const {
    RichText,
    InspectorControls,
    getColorClassName
} = wp.blockEditor; // not wp.editor
  • 英寸buildTableClasses(), backgroundClass 不是块中的属性。所以(第一个)应该是backgroundColor 并定义backgroundClass 像这样:

    const backgroundClass = getColorClassName(
        \'background-color\',
        backgroundColor
    );
    
  • buildTableClasses() 只能与一起使用save 函数,因为从edit 函数,您将得到;倒置的“;问题,因为函数将接收旧属性。

    我将使用专用功能更新;<table> “类别”;字段更新切换控件时,仅添加/删除与该切换关联的类,例如。table-hover 对于;悬停(&Q);切换:

    function onChangeTableHover() {
        props.setAttributes( {
            // Toggle the state.
            tableHover: ! tableHover,
    
            // Then add/remove only the table-hover class.
            tableClass: classnames( tableClass, {
                \'table-hover\': ! tableHover,
            } ),
        } );
    }
    
    /* Then in the JSX:
    <ToggleControl
        label={ __( \'Hover\' ) }
        checked={ !! tableHover }
        onChange={ onChangeTableHover }
    />
    */
    
    您可能正在考虑背景颜色类,但它们应该由颜色选择器开关控制(添加/删除)(在“颜色设置”面板/部分中)。

    当;<table> “类别”;字段已更新,例如您键入table-hover 在此处或将其删除时,应更改切换状态:

    function onChangeTableClass( value ) {
        // User likely typed a whitespace.
        if ( tableClass === value.trim() ) {
            props.setAttributes( { tableClass: value } );
            return;
        }
    
        const list = value.split( / +/ );
    
        props.setAttributes( {
            // Update the value.
            tableClass: classnames( value ),
    
            // Then the toggles.
            tableHover: list.indexOf( \'table-hover\' ) >= 0,
            tableBordered: list.indexOf( \'table-bordered\' ) >= 0,
            tableStriped: list.indexOf( \'table-striped\' ) >= 0,
        } );
    }
    
    /* Then in the JSX:
    <TextControl
        label={ __( \'<table> classes\' ) }
        type="text"
        value={ tableClass }
        onChange={ onChangeTableClass }
    />
    */
    
    tableClass 属性应为table table-hover 因为默认值tableHover 州为true.

    (只是一个建议)我想你应该attribute 作为source 对于tableClass 属性

    是的,古腾堡进行区块验证validate 块的输出:

    在编辑器初始化期间,使用从帖子内容解析的属性重新生成每个块的已保存标记。如果新生成的标记与post内容中已存储的标记不匹配,则该块将标记为无效。这是因为我们假设,除非用户进行编辑,否则标记应该与保存的内容保持一致。

    因此,如果保存的标记是<p class="foo">bar baz</p>, 但新生成的标记是<p class="foo new-class">bar baz</p>, 然后你会得到一个错误。

    因此,因为您正在更改输出,所以最好复制块,自定义它并将其注册为新块…

    创建新块可能不像简单地扩展现有块那么容易,但新块比稍后必须处理块验证错误要好,例如在停用插件/主题之后

  • 如果您只想扩展核心表块,那么您需要copy the original edit component, 编辑代码,并将其用作edit 块的函数,类似于使用save 作用

    原因是您想更新;<table> “类别”;每当选择/取消选择背景颜色时,需要修改PanelColorSettings 要素

    其次,复制还允许您删除/重新排序现有节或向这些节添加新控件。

    试试我的脚本,你可以在GitHub上找到它(source | build), 我用wp-scripts package.

    更新:关于tableHover: ! tableHover<attribute>: ! <attribute>

    这相当于下面第四行在PHP中的作用:

    <?php
    $attrs = [ \'tableHover\' => false ];
    $tableHover = $attrs[\'tableHover\'];            // get the current value
    $attrs[\'tableHover\'] = ! $tableHover;          // toggle/change the value
    var_dump( $tableHover, $attrs[\'tableHover\'] ); // bool(false) bool(true)
    
    所以你(在评论中)问道:

    我还是很困惑tableHover: ! tableHover 在…上onChange. ... 为什么在设置属性时该属性被否定?

    这是因为该属性是布尔类型,所以该值只能是truefalse.

    因此,如果当前值props.attributestrue, 然后tableHover: ! tableHover 将值切换/更改为false.

    // The currently saved block attributes.
    const { tableHover } = props.attributes;
    
    // When updating the tableHover, we could simply do:
    props.setAttributes( { tableHover: ! tableHover } );
    // .. which is equivalent to:
    props.setAttributes( { tableHover: tableHover ? false : true } );
    
    或在onChange 回调时,可以使用作为第一个参数传递给回调的toggle控件元素的当前状态。

    <ToggleControl
        checked={ !! tableHover }
        onChange={ ( checked ) => props.setAttributes( { tableHover: checked } ) }
    />
    
    我想这更容易理解?:)

    但是tableHover: ! tableHover 是一个更简单的版本,无需使用第一个onChange 参数

    因此,只需使用您喜欢的任何方法,但请确保设置正确的值,例如,如果选中了切换控制元素,请设置tableHovertrue.

    顺便说一句,你也可以使用!! 用PHP代替(bool):

    <?php
    $foo = \'bar\';
    var_dump( (bool) $foo, !! $foo ); // bool(true) bool(true)