creating elements/innerblocks via rangecontrol

I’m attempting to put together a variable/mixed content carousel block. I’m having issues trying to figure out how to create/delete individual slides in the carousel (using something like rangecontrol).

So far I have this:

const ALLOWED_BLOCKS = [ 'core/paragraph' ];
const BLOCKS_TEMPLATE = [
    [ 'core/columns', {}, [] ],
];

registerBlockType( 'blocks/carousel', {
    title: __( 'Carousel' ),
    icon: 'layout',
    attributes: {
        count: {
            type: 'number',
        },

    },

    edit( { attributes, setAttributes, className } ) {

        const onChangeCount = value => {
            setAttributes( { count: value } );
        };

        return [
            <InspectorControls key="controls">
                <PanelBody>
                    <RangeControl
                        label={ __( 'Slides' ) }
                        value={ attributes.count }
                        onChange={ onChangeCount }
                        min={ 2 }
                        max={ 6 }
                    />
                </PanelBody>
            </InspectorControls>,
            <div className={ className } key="content">
                <InnerBlocks
                    template={ BLOCKS_TEMPLATE }
                    allowedBlocks={ ALLOWED_BLOCKS }
                    templateLock="all"
                />
            </div>,
        ];
    },

    save( { attributes } ) {
        return (
            <div>
                <InnerBlocks.Content />
            </div>
        );
    },
} );

So I’m not entirely sure what I need to allow for creating/deleting the slides via the range. If someone could offer me a rough solution and/or point me at some documentation I’d appreciate it.

2 Answers
2

The code from core/columns and core/column blocks are a good example of how to achieve this behaviour. Basically we register 2 block types: Carousel and Slide.

  • Slide block type does not appear in the inserter.
  • Carousel block type is the root block which appears in the inserter. Instead of keeping the count of slides in an attribute we listen to getBlocks selector. We use the replaceInnerBlocks action to update the number of slides.

If you need further explanation please let me know.

const { times } = lodash;
const { __ } = wp.i18n;
const { registerBlockType, createBlock } = wp.blocks;
const { PanelBody, RangeControl } = wp.components;
const { InnerBlocks, InspectorControls } = wp.blockEditor;
const { useDispatch, useSelect } = wp.data;
const { Fragment } = wp.element;

registerBlockType("my-plugin/slide", {
    title: __("Slide"),
    icon: "carrot",
    category: "common",
    attributes: {},
    supports: { inserter: false },
    edit(props) {
        const { className } = props;

        return (
            <div className={className}>
                <InnerBlocks
                    allowedBlocks={["core/quote", "core/gallery"]}
                    templateLock={false}
                />
            </div>
        );
    },
    save(props) {
        return (
            <div>
                <InnerBlocks.Content />
            </div>
        );
    }
});

registerBlockType("my-plugin/carousel", {
    title: __("Carousel"),
    icon: "layout",
    category: "common",
    attributes: {},
    edit(props) {
        const { className, clientId } = props;
        const { replaceInnerBlocks } = useDispatch("core/block-editor");
        const { slides } = useSelect(select => ({
            slides: select("core/block-editor").getBlocks(clientId)
        }));

        return (
            <div className={className}>
                <InspectorControls>
                    <PanelBody>
                        <RangeControl
                            label={__("Slides")}
                            value={slides.length}
                            min={2}
                            max={6}
                            onChange={count => {
                                let inner_blocks = slides;

                                if (slides.length < count) {
                                    inner_blocks = [
                                        ...inner_blocks,
                                        ...times(count - slides.length, () =>
                                            createBlock("my-plugin/slide")
                                        )
                                    ];
                                } else if (slides.length > count) {
                                    inner_blocks = inner_blocks.slice(0, count);
                                }

                                replaceInnerBlocks(clientId, inner_blocks, false);
                            }}
                        />
                    </PanelBody>
                </InspectorControls>
                <InnerBlocks
                    template={[["my-plugin/slide"], ["my-plugin/slide"]]}
                    allowedBlocks={["my-plugin/slide"]}
                    templateLock="all"
                />
            </div>
        );
    },
    save(props) {
        return (
            <div>
                <InnerBlocks.Content />
            </div>
        );
    }
});

Leave a Comment