admin管理员组

文章数量:1124811

I have a plugin that registers four different Gutenberg blocks. I build them using the build script from the wp-scripts package. As you all surely know, this script automatically generates an index.asset.php file with an array with all the script dependencies for each block. For example:

<?php return array('dependencies' => array('react', 'wp-block-editor', 'wp-blocks', 'wp-components', 'wp-compose', 'wp-element', 'wp-i18n', 'wp-primitives'), 'version' => 'a955a4059265b7ad1ce6');

I need to add the masonry script (included in WP) to this dependency array in just ONE (not ALL) of the blocks, like this:

<?php return array('dependencies' => array('react', 'wp-block-editor', 'wp-blocks', 'wp-components', 'wp-compose', 'wp-element', 'wp-i18n', 'wp-primitives', 'masonry'), 'version' => 'a955a4059265b7ad1ce6');

I know that I can add this dependency manually after I build the blocks, but I'm wondering if there's a way to do this automatically via the block block.json file, a custom webpack.config.js file or anything else.

The contents of the block.json file is:

{
    "$schema": ".json",
    "apiVersion": 3,
    "name": "my/masonry-block",
    "title": "My Masonry Block",
    "description": "Display a Masonry gallery.",
    "textdomain": "my-domain",
    "category": "media",
    "attributes": {
        "items": {
            "type": "array",
            "default": []
        },
        "columns": {
            "type": "number",
            "minimum": 1,
            "maximum": 8,
            "default": 3
        },
        "gap": {
            "type": "string",
            "default": "10px"
        },
        "captions": {
            "type": "boolean",
            "default": false
        }
    },
    "supports": {
        "spacing": {
            "margin": true,
            "padding": true
        },
        "align": [ "center" ]
    },
    "editorScript": [
        "file:./index.js",
        "imagesloaded",
        "masonry"
    ],
    "viewScript": [
        "file:./view.js",
        "imagesloaded",
        "masonry"
    ],
    "style": [
        "file:./style-index.css",
        "file:./view.css"
    ],
    "editorStyle": "file:./index.css"
}

I'm registering the block with:

register_block_type(
    __DIR__ . '/build/blocks/masonry',
    array(
        'render_callback' => array( $this, 'render_block_masonry' ),
    )
);

This is the block edit function (some code omitted for brevity):

const displayItems = (items) => {
    return (
        items.map((item, index) => {
            return (
                <div className="gallery-item" key={index}>
                    <figure>
                        <a data-rel="collection" href={item.url} data-sub-html={item.caption}>
                            <img className={`wp-image-${item.id}`} src={item.url} alt={item.alt} key={item.id} />
                        </a>
                        {captions && item.caption &&
                            <figcaption className="wp-element-caption">
                                {item.caption}
                            </figcaption>
                        }
                    </figure>
                </div>
            )
        })
    )
};

const containerMasonryRef = useRefEffect((element) => {
    var msnry;

    const { ownerDocument } = element;
    const { defaultView } = ownerDocument;

    if (!defaultView.imagesLoaded || !defaultView.Masonry) {
        console.log('scripts not loaded');
        return;
    }
    
    imagesLoaded(element, function () {
        console.log('images loaded');
        msnry = new defaultView.Masonry(element, {
            itemSelector: '.gallery-item',
            columnWidth: '.grid-sizer',
            percentPosition: true,
            gutter: parseInt(gap),
        });
    });

    return () => {
        msnry?.destroy();
    }

}, [items, columns, gap, captions]);

const containerLightboxRef = useRefEffect((element) => {
    lightGallery(element, {
        selector: 'a[data-rel^=collection]',
        mode: 'lg-fade',
        preload: 4,
        counter: false,
        download: false,
        youtubePlayerParams: {
            autoplay: 0
        },
        vimeoPlayerParams: {
            autoplay: 0
        }
    });

    return () => {
        window?.lgData[element?.getAttribute('lg-uid')]?.destroy(true);
    }

}, []);

const mergedRefs = useMergeRefs([
    containerMasonryRef,
    containerLightboxRef,
]);

return (
    <>
        <InspectorControls>
            <PanelBody
                title={__('Settings')}
            >
                <RangeControl
                    label={__('Columns')}
                    value={columns}
                    onChange={setColumns}
                    min={1}
                    max={8}
                />
                <UnitControl
                    label={__('Gap between items')}
                    min="0"
                    onChange={setGap}
                    value={gap}
                    units={units}
                />
            </PanelBody>
        </InspectorControls>
        <BlockControls group="block">
            <ToolbarButton
                onClick={toggleCaptions}
                icon={captionIcon}
                isPressed={captions}
                label={
                    captions
                        ? __('Hide captions')
                        : __('Show captions')
                }
            />
        </BlockControls>
        <BlockControls group="other">
            {items.length > 0 && (
                <ToolbarGroup>
                    <MediaUploadCheck>
                        <MediaUpload
                            allowedTypes={['image']}
                            multiple={true}
                            gallery={true}
                            value={items.map((item) => item.id)}
                            onSelect={setItems}
                            render={({ open }) => (
                                <ToolbarButton onClick={open}>
                                    {__('Edit items')}
                                </ToolbarButton>)}
                        />
                    </MediaUploadCheck>
                </ToolbarGroup>
            )}
        </BlockControls>
        <MediaUploadCheck>
            {items.length > 0 ?
                <div {...blockProps}>
                    <div className="gallery-items" style={{ '--gap': gap }} ref={mergedRefs}>
                        <div className="grid-sizer"></div>
                        {displayItems(items)}
                    </div>
                </div>
                :
                <MediaPlaceholder
                    accept="image/*"
                    allowedTypes={['image']}
                    onSelect={setItems}
                    multiple={true}
                    gallery={true}
                    addToGallery={true}
                    handleUpload={true}
                    labels={
                        { title: __('My Masonry Block') }
                    }
                />
            }
        </MediaUploadCheck>
    </>
);

I have a plugin that registers four different Gutenberg blocks. I build them using the build script from the wp-scripts package. As you all surely know, this script automatically generates an index.asset.php file with an array with all the script dependencies for each block. For example:

<?php return array('dependencies' => array('react', 'wp-block-editor', 'wp-blocks', 'wp-components', 'wp-compose', 'wp-element', 'wp-i18n', 'wp-primitives'), 'version' => 'a955a4059265b7ad1ce6');

I need to add the masonry script (included in WP) to this dependency array in just ONE (not ALL) of the blocks, like this:

<?php return array('dependencies' => array('react', 'wp-block-editor', 'wp-blocks', 'wp-components', 'wp-compose', 'wp-element', 'wp-i18n', 'wp-primitives', 'masonry'), 'version' => 'a955a4059265b7ad1ce6');

I know that I can add this dependency manually after I build the blocks, but I'm wondering if there's a way to do this automatically via the block block.json file, a custom webpack.config.js file or anything else.

The contents of the block.json file is:

{
    "$schema": "https://schemas.wp.org/trunk/block.json",
    "apiVersion": 3,
    "name": "my/masonry-block",
    "title": "My Masonry Block",
    "description": "Display a Masonry gallery.",
    "textdomain": "my-domain",
    "category": "media",
    "attributes": {
        "items": {
            "type": "array",
            "default": []
        },
        "columns": {
            "type": "number",
            "minimum": 1,
            "maximum": 8,
            "default": 3
        },
        "gap": {
            "type": "string",
            "default": "10px"
        },
        "captions": {
            "type": "boolean",
            "default": false
        }
    },
    "supports": {
        "spacing": {
            "margin": true,
            "padding": true
        },
        "align": [ "center" ]
    },
    "editorScript": [
        "file:./index.js",
        "imagesloaded",
        "masonry"
    ],
    "viewScript": [
        "file:./view.js",
        "imagesloaded",
        "masonry"
    ],
    "style": [
        "file:./style-index.css",
        "file:./view.css"
    ],
    "editorStyle": "file:./index.css"
}

I'm registering the block with:

register_block_type(
    __DIR__ . '/build/blocks/masonry',
    array(
        'render_callback' => array( $this, 'render_block_masonry' ),
    )
);

This is the block edit function (some code omitted for brevity):

const displayItems = (items) => {
    return (
        items.map((item, index) => {
            return (
                <div className="gallery-item" key={index}>
                    <figure>
                        <a data-rel="collection" href={item.url} data-sub-html={item.caption}>
                            <img className={`wp-image-${item.id}`} src={item.url} alt={item.alt} key={item.id} />
                        </a>
                        {captions && item.caption &&
                            <figcaption className="wp-element-caption">
                                {item.caption}
                            </figcaption>
                        }
                    </figure>
                </div>
            )
        })
    )
};

const containerMasonryRef = useRefEffect((element) => {
    var msnry;

    const { ownerDocument } = element;
    const { defaultView } = ownerDocument;

    if (!defaultView.imagesLoaded || !defaultView.Masonry) {
        console.log('scripts not loaded');
        return;
    }
    
    imagesLoaded(element, function () {
        console.log('images loaded');
        msnry = new defaultView.Masonry(element, {
            itemSelector: '.gallery-item',
            columnWidth: '.grid-sizer',
            percentPosition: true,
            gutter: parseInt(gap),
        });
    });

    return () => {
        msnry?.destroy();
    }

}, [items, columns, gap, captions]);

const containerLightboxRef = useRefEffect((element) => {
    lightGallery(element, {
        selector: 'a[data-rel^=collection]',
        mode: 'lg-fade',
        preload: 4,
        counter: false,
        download: false,
        youtubePlayerParams: {
            autoplay: 0
        },
        vimeoPlayerParams: {
            autoplay: 0
        }
    });

    return () => {
        window?.lgData[element?.getAttribute('lg-uid')]?.destroy(true);
    }

}, []);

const mergedRefs = useMergeRefs([
    containerMasonryRef,
    containerLightboxRef,
]);

return (
    <>
        <InspectorControls>
            <PanelBody
                title={__('Settings')}
            >
                <RangeControl
                    label={__('Columns')}
                    value={columns}
                    onChange={setColumns}
                    min={1}
                    max={8}
                />
                <UnitControl
                    label={__('Gap between items')}
                    min="0"
                    onChange={setGap}
                    value={gap}
                    units={units}
                />
            </PanelBody>
        </InspectorControls>
        <BlockControls group="block">
            <ToolbarButton
                onClick={toggleCaptions}
                icon={captionIcon}
                isPressed={captions}
                label={
                    captions
                        ? __('Hide captions')
                        : __('Show captions')
                }
            />
        </BlockControls>
        <BlockControls group="other">
            {items.length > 0 && (
                <ToolbarGroup>
                    <MediaUploadCheck>
                        <MediaUpload
                            allowedTypes={['image']}
                            multiple={true}
                            gallery={true}
                            value={items.map((item) => item.id)}
                            onSelect={setItems}
                            render={({ open }) => (
                                <ToolbarButton onClick={open}>
                                    {__('Edit items')}
                                </ToolbarButton>)}
                        />
                    </MediaUploadCheck>
                </ToolbarGroup>
            )}
        </BlockControls>
        <MediaUploadCheck>
            {items.length > 0 ?
                <div {...blockProps}>
                    <div className="gallery-items" style={{ '--gap': gap }} ref={mergedRefs}>
                        <div className="grid-sizer"></div>
                        {displayItems(items)}
                    </div>
                </div>
                :
                <MediaPlaceholder
                    accept="image/*"
                    allowedTypes={['image']}
                    onSelect={setItems}
                    multiple={true}
                    gallery={true}
                    addToGallery={true}
                    handleUpload={true}
                    labels={
                        { title: __('My Masonry Block') }
                    }
                />
            }
        </MediaUploadCheck>
    </>
);
Share Improve this question edited Feb 13, 2024 at 14:52 leemon asked Feb 12, 2024 at 20:15 leemonleemon 2,0124 gold badges22 silver badges51 bronze badges
Add a comment  | 

1 Answer 1

Reset to default 1

Actually, you do not need to worry about adding the Masonry's script handle to the dependencies array in the asset file (index.asset.php).

All you needed to do to make your Masonry code work properly is by using the script property to add the masonry (and imagesloaded) as dependencies for your editor and view/front-end scripts.

"editorScript": "file:./index.js",
"script": [
    "imagesloaded",
    "masonry"
],
"viewScript": "file:./view.js",

I don't know and haven't checked further why adding masonry to the editorScript and viewScript did not work, but the above (trick) worked well for me.

  • Using the script property to add the dependency, is also the reason why the Gutenberg Test Iframed Masonry Block worked. I.e. The plugin manually registered the shared editor and front-end script, and the plugin also sets "script": "iframed-masonry-block-script" in the block metadata.

For completeness though, I created a simple Webpack plugin which basically modifies the asset source before it is written to the asset file, but (hopefully) after the Dependency Extraction Webpack Plugin added the asset to the compilation queue.

Here's the plugin which I placed it in the root project folder.

And here's my custom Webpack config file, i.e. webpack.config.js.

BTW, thanks for sharing about the Gutenberg Test Iframed Masonry Block.

本文标签: javascriptHow to add an additional dependency to a block indexassetphp file