admin管理员组

文章数量:1122846

I've built the following Gutenberg block. It's a carousel of images:

/**
 * WordPress dependencies
 */
import { __ } from '@wordpress/i18n';
import {
    G,
    Path,
    SVG,
    ToolbarGroup,
    ToolbarButton,
    PanelBody,
    __experimentalUnitControl as UnitControl,
    __experimentalUseCustomUnits as useCustomUnits,
} from '@wordpress/components';
import {
    InspectorControls,
    BlockControls,
    MediaUploadCheck,
    MediaUpload,
    MediaPlaceholder,
    RichText,
    useBlockProps,
    useSettings
} from '@wordpress/block-editor';
import {
    useRefEffect
} from '@wordpress/compose';
import { registerBlockType } from '@wordpress/blocks';
import apiFetch from '@wordpress/api-fetch';

/**
 * Internal dependencies
 */
import metadata from './block.json';
import { Carousel } from './carousel.js';

import './editor.scss';
import './style.scss';

const { name } = metadata;

export const settings = {
    icon: {
        src: <SVG viewBox="0 0 24 24" xmlns=";>
            <Path fill="none" d="M0 0h24v24H0V0z" />
            <G><Path d="M20 4v12H8V4h12m0-2H8c-1.1 0-2 .9-2 2v12c0 1.1.9 2 2 2h12c1.1 0 2-.9 2-2V4c0-1.1-.9-2-2-2zm-8.5 9.67l1.69 2.26 2.48-3.1L19 15H9zM2 6v14c0 1.1.9 2 2 2h14v-2H4V6H2z" /></G>
        </SVG>,
        foreground: '#ff8a00'
    },

    edit: (props => {
        const blockProps = useBlockProps();
        const {
            attributes: { images, caption, max_height },
            setAttributes,
        } = props;

        const units = useCustomUnits({
            availableUnits: useSettings('spacing.units') || [
                'px',
                'em',
                'rem',
                'vw',
                'vh',
            ],
            defaultValues: { px: 100, em: 10, rem: 10, vw: 10, vh: 25 },
        });

        const displayImages = (images) => {
            return (
                images.map((image, index) => {
                    return (
                        <div className="carousel-item" key={index}>
                            <figure>
                                <img src={image.url} style={{ maxHeight: max_height }} alt={image.alt} key={image.id} />
                                {image.caption && (
                                    <figcaption>{image.caption}</figcaption>
                                )}
                            </figure>
                        </div>
                    )
                })
            )
        };

        function setImages(media) {
            setAttributes({ images: media })
        }

        function setCaption(value) {
            setAttributes({ caption: value })
        }

        function setMaxHeight(value) {
            setAttributes({ max_height: value })
        }

        const ref = useRefEffect((element) => {
            Carousel(element, { speed: 10 });
        }, []);

        return (
            <>
                <InspectorControls>
                    <PanelBody
                        title={__('Settings', 'rather-simple-carousel')}
                    >
                        <UnitControl
                            label={__('Max height of elements', 'rather-simple-carousel')}
                            min="1"
                            onChange={setMaxHeight}
                            value={max_height}
                            units={units}
                        />
                    </PanelBody>
                </InspectorControls>
                <BlockControls>
                    {images.length > 0 && (
                        <ToolbarGroup>
                            <MediaUploadCheck>
                                <MediaUpload
                                    allowedTypes={['image']}
                                    multiple={true}
                                    gallery={true}
                                    value={images.map((image) => image.id)}
                                    onSelect={setImages}
                                    render={({ open }) => (
                                        <ToolbarButton onClick={open}>
                                            {__('Edit images', 'rather-simple-carousel')}
                                        </ToolbarButton>)}
                                />
                            </MediaUploadCheck>
                        </ToolbarGroup>
                    )}
                </BlockControls>
                <MediaUploadCheck>
                    {images.length > 0 &&
                        <figure {...blockProps}>
                            <div className="carousel-wrapper" ref={ref}>
                                <div className="carousel-frame">
                                    <div className="carousel-items">
                                        {displayImages(images)}
                                    </div>
                                </div>
                                <div className="carousel-arrow left"><span className="icon"><svg version="1.1" viewBox="0 0 24 24" xmlns=";><path d="m17.5 5v14l-11-7z" /></svg></span></div>
                                <div className="carousel-arrow right"><span className="icon"><svg version="1.1" viewBox="0 0 24 24" xmlns=";><path d="m6.5 5v14l11-7z" /></svg></span></div>
                            </div>
                            <RichText
                                className="carousel-caption"
                                tagName="figcaption"
                                placeholder={__('Enter a caption', 'rather-simple-carousel')}
                                value={caption}
                                onChange={setCaption}
                            />
                        </figure>
                    }
                    {images.length === 0 &&
                        <MediaPlaceholder
                            accept="image/*"
                            allowedTypes={['image']}
                            onSelect={setImages}
                            multiple={true}
                            gallery={true}
                            addToGallery={true}
                            handleUpload={true}
                            labels={
                                { title: __('Rather Simple Carousel', 'rather-simple-carousel') }
                            }
                        />
                    }
                </MediaUploadCheck>
            </>
        );

    }),

};

registerBlockType(name, settings);

This is the corresponding block.json file:

{
    "$schema": ".json",
    "apiVersion": 3,
    "name": "my/rather-simple-carousel",
    "title": "Rather Simple Carousel",
    "description": "Display a carousel.",
    "textdomain": "rather-simple-carousel",
    "category": "media",
    "attributes": {
        "images": {
            "type": "array",
            "default": []
        },
        "caption": {
            "type": "string"
        },
        "max_height": {
            "type": "string",
            "default": "300px"
        }
    },
    "supports": {
        "spacing": {
            "margin": true,
            "padding": true
        }
    },
    "editorScript": "file:./index.js",
    "viewScript": "file:./view.js",
    "style": "file:./style-index.css",
    "editorStyle": "file:./index.css"
}

The block works as expected with no issues. I need to add a transform to migrate a shortcode with the following syntax: [carousel id="n"], where id is a post id representing a custom post type with the following meta fields:

_rsc_carousel_items contains a string with attachment ids separated with commas (ex: 2220,2241,3256)

_rsc_carousel_max_height contains a string with a height value in pixels (ex: 200px)

_rsc_carousel_caption contains a string with a caption text

I added the following transform function to the block, but it's not working:

transforms: {
    from: [
        {
            type: 'shortcode',
            tag: 'carousel',
            attributes: {
                images: {
                    type: 'array',
                    source: async ({ id }) => {
                        const response = await apiFetch({ path: `/wp/v2/posts/${id}` });
                        const post = await response.json();
                        const imageIdsString = post.meta._rsc_carousel_items;
                        const imageIds = imageIdsString.split(',');

                        const images = await Promise.all(
                            imageIds.map(async (imageId) => {
                                const imageResponse = await apiFetch({
                                    path: `/wp/v2/media/${imageId}`,
                                });
                                const image = await imageResponse.json();
                                return {
                                    id: image.id,
                                    url: image.source_url,
                                    alt: image.alt_text,
                                    caption: image.caption.rendered,
                                };
                            })
                        );

                        return images;
                    },
                    query: {
                        id: {
                            type: 'string',
                            shortcode: (attrs) => attrs.id,
                        },
                    },
                },
                caption: {
                    type: 'string',
                    source: async ({ id }) => {
                        const response = await apiFetch({ path: `/wp/v2/posts/${id}` });
                        const post = await response.json();
                        return post.meta._rsc_carousel_caption;
                    },
                    query: {
                        id: {
                            type: 'string',
                            shortcode: (attrs) => attrs.id,
                        },
                    },
                },
                max_height: {
                    type: 'string',
                    source: async ({ id }) => {
                        const response = await apiFetch({ path: `/wp/v2/posts/${id}` });
                        const post = await response.json();
                        return post.meta._rsc_carousel_max_height;
                    },
                    query: {
                        id: {
                            type: 'string',
                            shortcode: (attrs) => attrs.id,
                    },
                },
            }
        },
    },
],
},

Can anyone help me troubleshoot this? Thanks

I've built the following Gutenberg block. It's a carousel of images:

/**
 * WordPress dependencies
 */
import { __ } from '@wordpress/i18n';
import {
    G,
    Path,
    SVG,
    ToolbarGroup,
    ToolbarButton,
    PanelBody,
    __experimentalUnitControl as UnitControl,
    __experimentalUseCustomUnits as useCustomUnits,
} from '@wordpress/components';
import {
    InspectorControls,
    BlockControls,
    MediaUploadCheck,
    MediaUpload,
    MediaPlaceholder,
    RichText,
    useBlockProps,
    useSettings
} from '@wordpress/block-editor';
import {
    useRefEffect
} from '@wordpress/compose';
import { registerBlockType } from '@wordpress/blocks';
import apiFetch from '@wordpress/api-fetch';

/**
 * Internal dependencies
 */
import metadata from './block.json';
import { Carousel } from './carousel.js';

import './editor.scss';
import './style.scss';

const { name } = metadata;

export const settings = {
    icon: {
        src: <SVG viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg">
            <Path fill="none" d="M0 0h24v24H0V0z" />
            <G><Path d="M20 4v12H8V4h12m0-2H8c-1.1 0-2 .9-2 2v12c0 1.1.9 2 2 2h12c1.1 0 2-.9 2-2V4c0-1.1-.9-2-2-2zm-8.5 9.67l1.69 2.26 2.48-3.1L19 15H9zM2 6v14c0 1.1.9 2 2 2h14v-2H4V6H2z" /></G>
        </SVG>,
        foreground: '#ff8a00'
    },

    edit: (props => {
        const blockProps = useBlockProps();
        const {
            attributes: { images, caption, max_height },
            setAttributes,
        } = props;

        const units = useCustomUnits({
            availableUnits: useSettings('spacing.units') || [
                'px',
                'em',
                'rem',
                'vw',
                'vh',
            ],
            defaultValues: { px: 100, em: 10, rem: 10, vw: 10, vh: 25 },
        });

        const displayImages = (images) => {
            return (
                images.map((image, index) => {
                    return (
                        <div className="carousel-item" key={index}>
                            <figure>
                                <img src={image.url} style={{ maxHeight: max_height }} alt={image.alt} key={image.id} />
                                {image.caption && (
                                    <figcaption>{image.caption}</figcaption>
                                )}
                            </figure>
                        </div>
                    )
                })
            )
        };

        function setImages(media) {
            setAttributes({ images: media })
        }

        function setCaption(value) {
            setAttributes({ caption: value })
        }

        function setMaxHeight(value) {
            setAttributes({ max_height: value })
        }

        const ref = useRefEffect((element) => {
            Carousel(element, { speed: 10 });
        }, []);

        return (
            <>
                <InspectorControls>
                    <PanelBody
                        title={__('Settings', 'rather-simple-carousel')}
                    >
                        <UnitControl
                            label={__('Max height of elements', 'rather-simple-carousel')}
                            min="1"
                            onChange={setMaxHeight}
                            value={max_height}
                            units={units}
                        />
                    </PanelBody>
                </InspectorControls>
                <BlockControls>
                    {images.length > 0 && (
                        <ToolbarGroup>
                            <MediaUploadCheck>
                                <MediaUpload
                                    allowedTypes={['image']}
                                    multiple={true}
                                    gallery={true}
                                    value={images.map((image) => image.id)}
                                    onSelect={setImages}
                                    render={({ open }) => (
                                        <ToolbarButton onClick={open}>
                                            {__('Edit images', 'rather-simple-carousel')}
                                        </ToolbarButton>)}
                                />
                            </MediaUploadCheck>
                        </ToolbarGroup>
                    )}
                </BlockControls>
                <MediaUploadCheck>
                    {images.length > 0 &&
                        <figure {...blockProps}>
                            <div className="carousel-wrapper" ref={ref}>
                                <div className="carousel-frame">
                                    <div className="carousel-items">
                                        {displayImages(images)}
                                    </div>
                                </div>
                                <div className="carousel-arrow left"><span className="icon"><svg version="1.1" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg"><path d="m17.5 5v14l-11-7z" /></svg></span></div>
                                <div className="carousel-arrow right"><span className="icon"><svg version="1.1" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg"><path d="m6.5 5v14l11-7z" /></svg></span></div>
                            </div>
                            <RichText
                                className="carousel-caption"
                                tagName="figcaption"
                                placeholder={__('Enter a caption', 'rather-simple-carousel')}
                                value={caption}
                                onChange={setCaption}
                            />
                        </figure>
                    }
                    {images.length === 0 &&
                        <MediaPlaceholder
                            accept="image/*"
                            allowedTypes={['image']}
                            onSelect={setImages}
                            multiple={true}
                            gallery={true}
                            addToGallery={true}
                            handleUpload={true}
                            labels={
                                { title: __('Rather Simple Carousel', 'rather-simple-carousel') }
                            }
                        />
                    }
                </MediaUploadCheck>
            </>
        );

    }),

};

registerBlockType(name, settings);

This is the corresponding block.json file:

{
    "$schema": "https://schemas.wp.org/trunk/block.json",
    "apiVersion": 3,
    "name": "my/rather-simple-carousel",
    "title": "Rather Simple Carousel",
    "description": "Display a carousel.",
    "textdomain": "rather-simple-carousel",
    "category": "media",
    "attributes": {
        "images": {
            "type": "array",
            "default": []
        },
        "caption": {
            "type": "string"
        },
        "max_height": {
            "type": "string",
            "default": "300px"
        }
    },
    "supports": {
        "spacing": {
            "margin": true,
            "padding": true
        }
    },
    "editorScript": "file:./index.js",
    "viewScript": "file:./view.js",
    "style": "file:./style-index.css",
    "editorStyle": "file:./index.css"
}

The block works as expected with no issues. I need to add a transform to migrate a shortcode with the following syntax: [carousel id="n"], where id is a post id representing a custom post type with the following meta fields:

_rsc_carousel_items contains a string with attachment ids separated with commas (ex: 2220,2241,3256)

_rsc_carousel_max_height contains a string with a height value in pixels (ex: 200px)

_rsc_carousel_caption contains a string with a caption text

I added the following transform function to the block, but it's not working:

transforms: {
    from: [
        {
            type: 'shortcode',
            tag: 'carousel',
            attributes: {
                images: {
                    type: 'array',
                    source: async ({ id }) => {
                        const response = await apiFetch({ path: `/wp/v2/posts/${id}` });
                        const post = await response.json();
                        const imageIdsString = post.meta._rsc_carousel_items;
                        const imageIds = imageIdsString.split(',');

                        const images = await Promise.all(
                            imageIds.map(async (imageId) => {
                                const imageResponse = await apiFetch({
                                    path: `/wp/v2/media/${imageId}`,
                                });
                                const image = await imageResponse.json();
                                return {
                                    id: image.id,
                                    url: image.source_url,
                                    alt: image.alt_text,
                                    caption: image.caption.rendered,
                                };
                            })
                        );

                        return images;
                    },
                    query: {
                        id: {
                            type: 'string',
                            shortcode: (attrs) => attrs.id,
                        },
                    },
                },
                caption: {
                    type: 'string',
                    source: async ({ id }) => {
                        const response = await apiFetch({ path: `/wp/v2/posts/${id}` });
                        const post = await response.json();
                        return post.meta._rsc_carousel_caption;
                    },
                    query: {
                        id: {
                            type: 'string',
                            shortcode: (attrs) => attrs.id,
                        },
                    },
                },
                max_height: {
                    type: 'string',
                    source: async ({ id }) => {
                        const response = await apiFetch({ path: `/wp/v2/posts/${id}` });
                        const post = await response.json();
                        return post.meta._rsc_carousel_max_height;
                    },
                    query: {
                        id: {
                            type: 'string',
                            shortcode: (attrs) => attrs.id,
                    },
                },
            }
        },
    },
],
},

Can anyone help me troubleshoot this? Thanks

Share Improve this question edited Jun 3, 2024 at 23:51 leemon asked Jun 2, 2024 at 18:00 leemonleemon 2,0024 gold badges22 silver badges51 bronze badges 10
  • 2 note that when you use apiFetch it's going to make a brand new request even if the entity you need has already been fetched and stored in the editors data store, causing slowdowns and possibly other problems. It would be much safer and faster to query the WP Data store for this information, and if it isn't available it would fetch it for you. – Tom J Nowell Commented Jun 2, 2024 at 18:13
  • I also see your images attribute is an array and it stores raw URLs, this could be problematic! The core gallery block used to work this way but switched instead to being a container block that held image blocks, don't reinvent block controls, now you can reorder gallery items using standard block controls, they show up in the block outliner, and you get a lot more flexibility for less code. – Tom J Nowell Commented Jun 2, 2024 at 18:14
  • I tried using wp.data.select('core').getEntityRecord() to get the information but apparently one can't use hooks such as useSelect inside a transform. It throws a Invalid hook call. Hooks can only be called inside of the body of a function component error. – leemon Commented Jun 3, 2024 at 12:47
  • useSelect isn't the only way to interact with WP Data, and is actually a somewhat recent introduction meant to replace withSelect, neither of which should be needed here, but if you refer to my other suggestions then there would be no need at all to fetch the posts, only the IDs would be necessary which you already have. Similarly you already have a function component to call useSelect from, the edit component – Tom J Nowell Commented Jun 3, 2024 at 14:45
  • 1 if the meta isn't registered it won't appear in the REST API, so the values are invisible to the editor and inaccessible. You don't have to set all the attributes in the transform function, you can check if they're present in the edit component and fill them in after the fact inside a useEffect if they're missing. Though storing the URL at all is a mistake that complicates things, better to store attachment IDs then generate the associated URLs on render instead of storing the URLs. Those apiFetch calls are also going to yield bad performance – Tom J Nowell Commented Jun 9, 2024 at 20:31
 |  Show 5 more comments

1 Answer 1

Reset to default 1

I'm sharing my solution here in case it can help someone, as I believe this type of shortcode transform is a common use case.

As fetching data inside a transform is not possible right now, I'm creating the block inside the transform by passing just the carousel id attribute from the carousel shortcode. I then fill the rest of the attributes afterwards inside the edit function by querying the WP data store there. Finally, I set the temporary id block attribute to undefined so it's not stored in the block markup as this attribute is useless after the transform.

I know this is a bit convoluted and cumbersome, but it seems to be the only way to do this right now. If anyone finds a better way to do this, I'm all ears.

Thanks to Tom J Nowell for the help.

block.json:

{
    "$schema": "https://schemas.wp.org/trunk/block.json",
    "apiVersion": 3,
    "name": "my/rather-simple-carousel",
    "title": "Rather Simple Carousel",
    "description": "Display a carousel.",
    "textdomain": "rather-simple-carousel",
    "category": "media",
    "attributes": {
        "id": {
            "type": "integer"
        },
        "images": {
            "type": "array",
            "items": {
                "type": "integer"
            },
            "default": []
        },
        "caption": {
            "type": "string",
            "default": ""
        },
        "max_height": {
            "type": "string",
            "default": "300px"
        }
    },
    "supports": {
        "spacing": {
            "margin": true,
            "padding": true
        }
    },
    "editorScript": "file:./index.js",
    "viewScript": "file:./view.js",
    "style": "file:./style-index.css",
    "editorStyle": "file:./index.css"
}

index.js:

/**
 * WordPress dependencies
 */
import {
    G,
    Path,
    SVG,
} from '@wordpress/components';
import {
    registerBlockType
} from '@wordpress/blocks';

/**
 * Internal dependencies
 */
import metadata from './block.json';
import Edit from './edit';
import transforms from './transforms';

import './editor.scss';
import './style.scss';

const { name } = metadata;

export const settings = {
    icon: {
        src: <SVG viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg">
            <Path fill="none" d="M0 0h24v24H0V0z" />
            <G><Path d="M20 4v12H8V4h12m0-2H8c-1.1 0-2 .9-2 2v12c0 1.1.9 2 2 2h12c1.1 0 2-.9 2-2V4c0-1.1-.9-2-2-2zm-8.5 9.67l1.69 2.26 2.48-3.1L19 15H9zM2 6v14c0 1.1.9 2 2 2h14v-2H4V6H2z" /></G>
        </SVG>,
        foreground: '#ff8a00'
    },

    edit: Edit,
    transforms,
};

registerBlockType(name, settings);

edit.js:

/**
 * WordPress dependencies
 */
import { __ } from '@wordpress/i18n';
import {
    ToolbarGroup,
    ToolbarButton,
    PanelBody,
    __experimentalUnitControl as UnitControl,
    __experimentalUseCustomUnits as useCustomUnits,
} from '@wordpress/components';
import {
    InspectorControls,
    BlockControls,
    MediaUploadCheck,
    MediaUpload,
    MediaPlaceholder,
    RichText,
    useBlockProps,
    useSettings
} from '@wordpress/block-editor';
import { useEffect } from '@wordpress/element';
import { useRefEffect } from '@wordpress/compose';
import { useSelect } from '@wordpress/data';

/**
 * Internal dependencies
 */
import { Carousel } from './carousel.js';

const Edit = (props) => {

    const blockProps = useBlockProps();
    const {
        attributes: { id, images, caption, max_height },
        setAttributes,
    } = props;

    const units = useCustomUnits({
        availableUnits: useSettings('spacing.units') || [
            'px',
            'em',
            'rem',
            'vw',
            'vh',
        ],
        defaultValues: { px: 100, em: 10, rem: 10, vw: 10, vh: 25 },
    });

    // Get the carousel post meta data using the temporary id attribute.
    const { post, hasPostResolved } = useSelect(select => {
        return {
            post: select('core').getEntityRecord('postType', 'carousel', id, {
                context: 'edit',
                _fields: ['id', 'meta']
            }),
            hasPostResolved: select('core').hasFinishedResolution('getEntityRecord', ['postType', 'carousel', id, {
                context: 'edit',
                _fields: ['id', 'meta']
            }]),
        }
    }, [id]);

    // Fill the missing attributes after a shortcode-to-block transform.
    useEffect(() => {
        if (hasPostResolved && post) {
            if (images.length === 0) {
                const postImages = post.meta._rsc_carousel_items.split(',');
                setAttributes({ images: postImages });
            }
            if (!caption) {
                const postCaption = post.meta._rsc_carousel_caption;
                setAttributes({ caption: postCaption });
            }
            if (max_height === '300px') {
                const postMaxHeight = post.meta._rsc_carousel_max_height;
                setAttributes({ max_height: postMaxHeight + 'px' });
            }
            // Unset the id attribute as it's no longer necessary.
            setAttributes({ id: undefined });
        }
    }, [hasPostResolved, post]);

    const { attachments } = useSelect(select => {
        const query = {
            include: images.join(','),
            per_page: images.length,
            orderby: 'include'
        };
        return {
            attachments: select('core').getMediaItems(query, { context: 'view' }),
        }
    }, [images]);

    const displayImages = (images) => {
        return (
            images.map((image, index) => {
                return (
                    <div className="carousel-item" key={index}>
                        <figure>
                            <img src={image.source_url} style={{ maxHeight: max_height }} alt={image.alt_text} key={image.id} />
                            {image.caption && (
                                <figcaption>{image.caption.rendered}</figcaption>
                            )}
                        </figure>
                    </div>
                )
            })
        )
    };

    function setImages(media) {
        const imageIDs = media.map(image => image.id);
        setAttributes({ images: imageIDs })
    }

    function setCaption(value) {
        setAttributes({ caption: value })
    }

    function setMaxHeight(value) {
        setAttributes({ max_height: value })
    }

    const ref = useRefEffect((element) => {
        Carousel(element, { speed: 10 });
    }, []);

    return (
        <>
            <InspectorControls>
                <PanelBody
                    title={__('Settings', 'rather-simple-carousel')}
                >
                    <UnitControl
                        label={__('Max height of elements', 'rather-simple-carousel')}
                        min="1"
                        onChange={setMaxHeight}
                        value={max_height}
                        units={units}
                    />
                </PanelBody>
            </InspectorControls>
            <BlockControls>
                {images.length > 0 && (
                    <ToolbarGroup>
                        <MediaUploadCheck>
                            <MediaUpload
                                allowedTypes={['image']}
                                multiple={true}
                                gallery={true}
                                value={images}
                                onSelect={setImages}
                                render={({ open }) => (
                                    <ToolbarButton onClick={open}>
                                        {__('Edit images', 'rather-simple-carousel')}
                                    </ToolbarButton>)}
                            />
                        </MediaUploadCheck>
                    </ToolbarGroup>
                )}
            </BlockControls>
            <MediaUploadCheck>
                {attachments && attachments.length > 0 ?
                    <figure {...blockProps}>
                        <div className="carousel-wrapper" ref={ref}>
                            <div className="carousel-frame">
                                <div className="carousel-items">
                                    {
                                        displayImages(attachments)
                                    }
                                </div>
                            </div>
                            <div className="carousel-arrow left"><span className="icon"><svg version="1.1" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg"><path d="m17.5 5v14l-11-7z" /></svg></span></div>
                            <div className="carousel-arrow right"><span className="icon"><svg version="1.1" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg"><path d="m6.5 5v14l11-7z" /></svg></span></div>
                        </div>
                        <RichText
                            className="carousel-caption"
                            tagName="figcaption"
                            placeholder={__('Enter a caption', 'rather-simple-carousel')}
                            value={caption}
                            onChange={setCaption}
                        />
                    </figure>
                    :
                    <MediaPlaceholder
                        accept="image/*"
                        allowedTypes={['image']}
                        onSelect={setImages}
                        multiple={true}
                        gallery={true}
                        addToGallery={true}
                        handleUpload={true}
                        labels={
                            { title: __('Rather Simple Carousel', 'rather-simple-carousel') }
                        }
                    />
                }
            </MediaUploadCheck>
        </>
    );

}

export default Edit;

transforms.js

/**
 * WordPress dependencies
 */
import { createBlock } from '@wordpress/blocks';

const transforms = {
    from: [
        {
            type: 'shortcode',
            tag: 'carousel',
            transform({ named: { id } }) {
                return createBlock('my/rather-simple-carousel', {
                    id: id
                });
            }
        },
    ],
}

export default transforms;

本文标签: javascriptHow to transform a shortcode into a block