• Resolved Rhand

    (@rhand)


    I am using a block filter to add options such as loop, autoplay and video cover to the core/cover block. I use Bud.js filters and added cover.filter.js with

    // Import necessary functions from @wordpress/hooks
    import { addFilter } from '@wordpress/hooks';
    import { createHigherOrderComponent } from '@wordpress/compose';
    import { InspectorControls } from '@wordpress/block-editor';
    import { PanelBody, ToggleControl, TextControl } from '@wordpress/components';
    import { Fragment } from '@wordpress/element';
    import React from 'react'; // Ensure React is imported for JSX and cloneElement

    /**
    * @see {@link https://developer.www.ads-software.com/block-editor/reference-guides/filters/block-filters/#blocks-registerblocktype}
    */
    export const hook = 'blocks.registerBlockType';

    /**
    * Filter handle
    */
    export const name = 'sage/cover';

    /**
    * Filter callback
    *
    * @param {Object} settings Block settings.
    * @param {string} name
    * @returns modified settings
    */
    export function callback(settings, name) {
    if (name !== 'core/cover') return settings;

    return {
    ...settings,
    attributes: {
    ...settings.attributes,
    loopVideo: {
    type: 'boolean',
    default: false,
    },
    autoplayVideo: {
    type: 'boolean',
    default: false,
    },
    showPlayButton: {
    type: 'boolean',
    default: true,
    },
    coverImage: {
    type: 'string',
    default: '',
    },
    },
    };
    }

    /**
    * Additional cover block modifications
    */
    export const editHook = 'editor.BlockEdit';
    export const editCallback = createHigherOrderComponent((BlockEdit) => {
    return (props) => {
    const { attributes, setAttributes, name } = props;

    if (name !== 'core/cover') {
    return <BlockEdit {...props} />;
    }

    const { loopVideo, autoplayVideo, showPlayButton, coverImage } = attributes;

    return (
    <Fragment>
    <BlockEdit {...props} />
    <InspectorControls>
    <PanelBody title="Video Options" initialOpen={true}>
    <ToggleControl
    label="Loop Video"
    checked={loopVideo}
    onChange={(value) => setAttributes({ loopVideo: value })}
    />
    <ToggleControl
    label="Autoplay Video"
    checked={autoplayVideo}
    onChange={(value) => setAttributes({ autoplayVideo: value })}
    />
    <ToggleControl
    label="Show Play Button"
    checked={showPlayButton}
    onChange={(value) => setAttributes({ showPlayButton: value })}
    />
    <TextControl
    label="Cover Image URL"
    value={coverImage}
    onChange={(value) => setAttributes({ coverImage: value })}
    help="This image will be used as the cover image for the video."
    />
    </PanelBody>
    </InspectorControls>
    </Fragment>
    );
    };
    }, 'withCoverControls');

    /**
    * Save element modification for the cover block
    */
    export const saveHook = 'blocks.getSaveElement';
    export const saveCallback = (element, blockType, attributes) => {
    if (blockType.name !== 'core/cover') return element;

    const { loopVideo, autoplayVideo, showPlayButton, coverImage } = attributes;

    const children = Array.isArray(element.props.children) ? element.props.children : [element.props.children];

    const modifiedChildren = children.map((child) => {
    if (child && child.type === 'video') {
    return React.cloneElement(child, {
    loop: loopVideo,
    autoPlay: autoplayVideo,
    poster: coverImage || child.props.poster,
    });
    }
    return child;
    });

    return (
    <div {...element.props}>
    {modifiedChildren}
    {showPlayButton && (
    <div className="wp-block-cover__play-button-overlay" style={{ position: 'absolute', zIndex: 10 }}>
    <button className="wp-block-cover__play-button" aria-label="Play Video" />
    </div>
    )}
    </div>
    );
    };

    // Register the filters
    addFilter(hook, name, callback); // Adds custom attributes to the block
    addFilter(editHook, name, editCallback); // Adds the inspector controls in the editor
    addFilter(saveHook, name, saveCallback); // Modifies the save element

    But when I use a pattern

    <!-- wp:cover {"url":"https://via.placeholder.com/1200x600","id":293,"dimRatio":0,"overlayColor":"black","isUserOverlayColor":true,"metadata":{"categories":["fandb"],"patternName":"cafejp/drinks","name":"Drinks Menu"},"align":"full","className":"cafejp-drinks-menu-pattern"} -->
    <div class="wp-block-cover alignfull cafejp-drinks-menu-pattern"><span aria-hidden="true" class="wp-block-cover__background has-black-background-color has-background-dim-0 has-background-dim"></span><img class="wp-block-cover__image-background wp-image-293" alt="" src="https://via.placeholder.com/1200x600" data-object-fit="cover"/><div class="wp-block-cover__inner-container"><!-- wp:group {"layout":{"type":"flex","flexWrap":"nowrap","justifyContent":"right"}} -->
    <div class="wp-block-group"><!-- wp:group {"style":{"spacing":{"padding":{"right":"var:preset|spacing|60","left":"var:preset|spacing|60"}}},"layout":{"type":"flex","orientation":"vertical"}} -->
    <div class="wp-block-group" style="padding-right:var(--wp--preset--spacing--60);padding-left:var(--wp--preset--spacing--60)"><!-- wp:heading {"style":{"elements":{"link":{"color":{"text":"var:preset|color|white"}}}},"textColor":"white","fontSize":"6xl"} -->
    <h2 class="wp-block-heading has-white-color has-text-color has-link-color has-6-xl-font-size">Drinks</h2>
    <!-- /wp:heading --></div>
    <!-- /wp:group -->

    <!-- wp:group {"layout":{"type":"flex","orientation":"vertical"}} -->
    <div class="wp-block-group"><!-- wp:group {"align":"full","className":"cafejp-first-main-row","layout":{"type":"flex","orientation":"horizontal"}} -->
    <div class="wp-block-group alignfull cafejp-first-main-row"><!-- wp:group {"className":"cafejp-koffie-fris","style":{"color":{"background":"#abd8bf"},"spacing":{"padding":{"top":"0.5rem","right":"0.5rem","bottom":"0.5rem","left":"0.5rem"}}},"layout":{"type":"flex","orientation":"vertical"}} -->
    <div class="wp-block-group cafejp-koffie-fris has-background" style="background-color:#abd8bf;padding-top:0.5rem;padding-right:0.5rem;padding-bottom:0.5rem;padding-left:0.5rem"><!-- wp:heading {"level":3,"className":"first-block-header","style":{"typography":{"fontWeight":"500","fontSize":"28px"},"color":{"text":"#014521"}}} -->
    <h3 class="wp-block-heading first-block-header has-text-color" style="color:#014521;font-size:28px;font-weight:500">Koffie &amp; Thee</h3>
    <!-- /wp:heading -->

    ....

    <!-- wp:paragraph {"className":"first-group-paragraph","style":{"typography":{"fontFamily":"'Asap Condensed', sans-serif","fontWeight":"500","fontSize":"15px"},"color":{"text":"#dcb171"}}} -->
    <p class="first-group-paragraph has-text-color" style="color:#dcb171;font-family:'Asap Condensed', sans-serif;font-size:15px;font-weight:500">|</p>
    <!-- /wp:paragraph -->

    <!-- wp:paragraph {"className":"second-group-paragraph","style":{"typography":{"fontFamily":"'Asap Condensed', sans-serif","fontWeight":"500","fontSize":"15px"},"color":{"text":"#014521"}}} -->
    <p class="second-group-paragraph has-text-color" style="color:#014521;font-family:'Asap Condensed', sans-serif;font-size:15px;font-weight:500">4.5</p>
    <!-- /wp:paragraph --></div>
    <!-- /wp:group --></div>
    <!-- /wp:group --></div>
    <!-- /wp:group --></div>
    <!-- /wp:group --></div>
    <!-- /wp:group --></div></div>
    <!-- /wp:cover -->


    with a cover and other elements I get:

    Block validation: Expected token of type StartTag ({type: 'StartTag', tagName: 'div', attributes: Array(2), selfClosing: false}), instead saw EndTag ({type: 'EndTag', tagName: 'div'}).
    (anonymous) @ blocks.min.js?ver=0d232d232463200f5cfd:19
    (anonymous) @ blocks.min.js?ver=0d232d232463200f5cfd:19
    ro @ blocks.min.js?ver=0d232d232463200f5cfd:19
    (anonymous) @ blocks.min.js?ver=0d232d232463200f5cfd:19
    no @ blocks.min.js?ver=0d232d232463200f5cfd:19
    (anonymous) @ block-editor.min.js?ver=f989eae66982c6c90d6e:21
    ....
    Hl @ react-dom.min.js?ver=18.3.1:2
    (anonymous) @ react-dom.min.js?ver=18.3.1:2Understand this warning
    blocks.min.js?ver=0d232d232463200f5cfd:19 Block validation: Block validation failed for core/cover ({name: 'core/cover', icon: {…}, keywords: Array(0), attributes: {…}, providesContext: {…},?…}).

    Content generated by save function:

    <div class="wp-block-cover alignfull cafejp-drinks-menu-pattern"><span aria-hidden="true" class="wp-block-cover__background has-black-background-color has-background-dim-0 has-background-dim"></span><img class="wp-block-cover__image-background wp-image-293" alt="" src="https://via.placeholder.com/1200x600" data-object-fit="cover"/><div class="wp-block-cover__inner-container"></div><div class="wp-block-cover__play-button-overlay" style="position:absolute;z-index:10"><button class="wp-block-cover__play-button" aria-label="Play Video"></button></div></div>

    Content retrieved from post body:

    <div class="wp-block-cover alignfull cafejp-drinks-menu-pattern"><span aria-hidden="true" class="wp-block-cover__background has-black-background-color has-background-dim-0 has-background-dim"></span><img class="wp-block-cover__image-background wp-image-293" alt="" src="https://via.placeholder.com/1200x600" data-object-fit="cover"/><div class="wp-block-cover__inner-container"></div></div>

    Without the added filter I do not seem to get this . But I am baffled by this. Perhaps a conflict in pattern loading and the filter filtering.. some kind of race issue?

Viewing 9 replies - 1 through 9 (of 9 total)
  • Thread Starter Rhand

    (@rhand)

    Also error with other cover blocks though like:

    lock validation: Block validation failed for core/cover (
    Object
    ).

    Content generated by save function:

    <div class="wp-block-cover is-light">
    <span aria-hidden="true" class="wp-block-cover__background has-background-dim-0 has-background-dim"></span>
    <img class="wp-block-cover__image-background" alt="" src="https://cafejpcoen.test/wp-content/plugins/woocommerce/assets/images/pattern-placeholders/plant-white-leaf-flower-vase-green.jpg" style="object-position:54% 52%" data-object-fit="cover" data-object-position="54% 52%"/>
    <div class="wp-block-cover__inner-container"></div>
    <div class="wp-block-cover__play-button-overlay" style="position:absolute;z-index:10"><button class="wp-block-cover__play-button" aria-label="Play Video"></button>
    </div></div>

    Content retrieved from post body:

    <div class="wp-block-cover is-light">
    <img class="wp-block-cover__image-background" alt="Placeholder image used to represent a product being showcased in a hero section. 1 out of 2." src="https://cafejpcoen.test/wp-content/plugins/woocommerce/assets/images/pattern-placeholders/plant-white-leaf-flower-vase-green.jpg" style="object-position:54% 52%" data-object-fit="cover" data-object-position="54% 52%"/>
    <div class="wp-block-cover__inner-container">
    </div></div>

    So perhaps the filter still has issues.

    • This reply was modified 1 month, 3 weeks ago by Rhand.
    Thread Starter Rhand

    (@rhand)

    When I console log the children using this updated cover.filter.js:

    // Import necessary functions from @wordpress/hooks
    import { addFilter } from '@wordpress/hooks';
    import { createHigherOrderComponent } from '@wordpress/compose';
    import { InspectorControls } from '@wordpress/block-editor';
    import { PanelBody, ToggleControl, TextControl } from '@wordpress/components';
    import { Fragment } from '@wordpress/element';
    import React from 'react'; // Ensure React is imported for JSX and cloneElement

    /**
    * @see {@link https://developer.www.ads-software.com/block-editor/reference-guides/filters/block-filters/#blocks-registerblocktype}
    */
    export const hook = 'blocks.registerBlockType';

    /**
    * Filter handle
    */
    export const name = 'sage/cover';

    /**
    * Filter callback
    *
    * @param {Object} settings Block settings.
    * @param {string} name
    * @returns modified settings
    */
    export function callback(settings, name) {
    if (name !== 'core/cover') return settings;

    return {
    ...settings,
    attributes: {
    ...settings.attributes,
    loopVideo: {
    type: 'boolean',
    default: false,
    },
    autoplayVideo: {
    type: 'boolean',
    default: false,
    },
    showPlayButton: {
    type: 'boolean',
    default: true,
    },
    coverImage: {
    type: 'string',
    default: '',
    },
    },
    };
    }

    /**
    * Additional cover block modifications
    */
    export const editHook = 'editor.BlockEdit';
    export const editCallback = createHigherOrderComponent((BlockEdit) => {
    return (props) => {
    const { attributes, setAttributes, name } = props;

    if (name !== 'core/cover') {
    return <BlockEdit {...props} />;
    }

    const { loopVideo, autoplayVideo, showPlayButton, coverImage } = attributes;

    return (
    <Fragment>
    <BlockEdit {...props} />
    <InspectorControls>
    <PanelBody title="Video Options" initialOpen={true}>
    <ToggleControl
    label="Loop Video"
    checked={loopVideo}
    onChange={(value) => setAttributes({ loopVideo: value })}
    />
    <ToggleControl
    label="Autoplay Video"
    checked={autoplayVideo}
    onChange={(value) => setAttributes({ autoplayVideo: value })}
    />
    <ToggleControl
    label="Show Play Button"
    checked={showPlayButton}
    onChange={(value) => setAttributes({ showPlayButton: value })}
    />
    <TextControl
    label="Cover Image URL"
    value={coverImage}
    onChange={(value) => setAttributes({ coverImage: value })}
    help="This image will be used as the cover image for the video."
    />
    </PanelBody>
    </InspectorControls>
    </Fragment>
    );
    };
    }, 'withCoverControls');

    /**
    * Save element modification for the cover block
    */
    export const saveHook = 'blocks.getSaveElement';
    export const saveCallback = (element, blockType, attributes) => {
    if (blockType.name !== 'core/cover') return element;

    const { loopVideo, autoplayVideo, showPlayButton, coverImage } = attributes;

    const children = Array.isArray(element.props.children) ? element.props.children : [element.props.children];
    console.log('Children:', children);
    const modifiedChildren = children.map((child) => {
    if (child && child.type === 'video') {
    return React.cloneElement(child, {
    loop: loopVideo,
    autoPlay: autoplayVideo,
    poster: coverImage || child.props.poster,
    });
    }
    return child;
    });

    return (
    <div {...element.props}>
    {modifiedChildren}
    {showPlayButton && (
    <div className="wp-block-cover__play-button-overlay" style={{ position: 'absolute', zIndex: 10 }}>
    <button className="wp-block-cover__play-button" aria-label="Play Video" />
    </div>
    )}
    </div>
    );
    console.log('Result:', result);

    return result;
    };

    // Register the filters
    addFilter(hook, name, callback); // Adds custom attributes to the block
    addFilter(editHook, name, editCallback); // Adds the inspector controls in the editor
    addFilter(saveHook, name, saveCallback); // Modifies the save element

    I get

    Children:
    Array (4)
    0 {$$typeof: Symbol(react.element), type: "span", key: null, ref: null, props: {aria-hidden: "true", className: "wp-block-cover__background has-black-background-color has-background-dim-0 has-background-dim", style: {backgroundColor: undefined, background: undefined}}, …}
    1 {$$typeof: Symbol(react.element), type: "img", key: null, ref: null, props: Object, …}
    2 false
    3 {$$typeof: Symbol(react.element), type: "div", key: null, ref: null, props: Object, …}

    Array Prototype

    as well as

    Children:
    Array (4)
    0 undefined
    1 {$$typeof: Symbol(react.element), type: "img", key: null, ref: null, props: Object, …}
    2 false
    3 {$$typeof: Symbol(react.element), type: "div", key: null, ref: null, props: Object, …}

    Array Prototype

    so something may be off. But not sure yet what this means. Need to filter out the false and undefined values I guess.

    Thread Starter Rhand

    (@rhand)

    Moved code to https://github.com/wpvillain/cover-filter/blob/main/src/index.js and working on it there, but going to take some time as I cannot seem to get right of mentioned errors. Working with rest operator https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Functions/rest_parameters should be okay but still new to React and not a senior JS developer either so perhaps the looping over elements or something else is causing the discrepancy.

    Thread Starter Rhand

    (@rhand)

    Checking https://github.com/dmtrmrv/intro-to-block-filters/blob/master/src/filters/button-size.js as example . Perhaps I need to just:

    • function addAttributes() add attributes to settings > attributes – doing more or less
    • const addInspectorControl add editor controls using BlockEdit hook – doing more and also with createHigherOrderComponent((BlockEdit)
    • const addVideoOptionsEditor (own name instead of addSizeClassEditor) add these new settings to cover block in the editor using BlockListBlock perhaps. Not doing this it seems, but I do not need to add options to toolbar. I can load in editor sidebar as I do.
    • function addVideoOptionsFrontEnd (instead of addSizeClassFrontEnd)And then save matters for frontend display using blocks.getSaveContent.extraProps . Doing saving , but that was third step for me.. and not fourth. Reading https://developer.www.ads-software.com/block-editor/reference-guides/filters/block-filters/#blocks-getsavecontent-extraprops I do not need this hook though. Also not sure why we need React.cloneElement ourselves.
    • This reply was modified 1 month, 3 weeks ago by Rhand.
    • This reply was modified 1 month, 3 weeks ago by Rhand.
    • This reply was modified 1 month, 3 weeks ago by Rhand.
    • This reply was modified 1 month, 3 weeks ago by Rhand.
    Thread Starter Rhand

    (@rhand)

    Updated matters to use cloneElement from WordPress now:

    // Import necessary functions and components from WordPress packages
    import { addFilter } from '@wordpress/hooks'; // Allows adding filters to modify block behavior
    import { createHigherOrderComponent } from '@wordpress/compose'; // Allows creating higher-order components (HOCs)
    import { InspectorControls } from '@wordpress/block-editor'; // Provides control UI for block editing
    import { PanelBody, ToggleControl, TextControl } from '@wordpress/components'; // UI components for block settings
    import { Fragment } from '@wordpress/element'; // A wrapper to group multiple elements without adding extra nodes to the DOM
    import { cloneElement } from '@wordpress/element'; // Use cloneElement from WordPress

    /**
    * @see {@link https://developer.www.ads-software.com/block-editor/reference-guides/filters/block-filters/#blocks-registerblocktype}
    * Filter hook used to modify block settings during block registration.
    */
    export const registerHook = 'blocks.registerBlockType';

    /**
    * Name of the filter, used as a unique identifier.
    */
    export const name = 'cafejp/cover'

    /**
    * Filter addAttributes function to modify the block's settings.
    *
    * @param {Object} settings Block settings.
    * @param {string} name Block name.
    * @returns Modified settings with new attributes added.
    */
    export function addAttributes(settings, name) {
    if (name !== 'core/cover') return settings;

    return {
    ...settings, // Retain all existing settings https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Spread_syntax
    attributes: {
    ...settings.attributes, // Retain all existing attributes
    loopVideo: {
    type: 'boolean',
    default: false,
    },
    autoplayVideo: {
    type: 'boolean',
    default: false,
    },
    showPlayButton: {
    type: 'boolean',
    default: true,
    },
    coverImage: {
    type: 'string',
    default: '',
    },
    },
    };
    }

    /**
    * Hook used to modify the block's edit component.
    */
    export const blockEditHook = 'editor.BlockEdit';

    /**
    * Higher-order component (HOC) to modify the block's edit function.
    */
    export const editCallback = createHigherOrderComponent((BlockEdit) => {
    return (props) => {
    const { attributes, setAttributes, name } = props;

    if (name !== 'core/cover') {
    return <BlockEdit {...props} />;
    }

    const { loopVideo, autoplayVideo, showPlayButton, coverImage } = attributes;

    // Render the modified block editor interface with additional controls
    return (
    <Fragment>
    {/* Render the original BlockEdit component */}
    <BlockEdit {...props} />
    {/* Add custom controls to the block's inspector panel */}
    <InspectorControls>
    <PanelBody title="Video Options" initialOpen={true}>
    {/* Toggle control for looping the video */}
    <ToggleControl
    label="Loop Video"
    checked={loopVideo}
    onChange={(value) => setAttributes({ loopVideo: value })}
    />
    {/* Toggle control for autoplaying the video */}
    <ToggleControl
    label="Autoplay Video"
    checked={autoplayVideo}
    onChange={(value) => setAttributes({ autoplayVideo: value })}
    />
    {/* Toggle control for showing the play button */}
    <ToggleControl
    label="Show Play Button"
    checked={showPlayButton}
    onChange={(value) => setAttributes({ showPlayButton: value })}
    />
    {/* Text control for setting the cover image URL */}
    <TextControl
    label="Cover Image URL"
    value={coverImage}
    onChange={(value) => setAttributes({ coverImage: value })}
    help="This image will be used as the cover image for the video."
    />
    </PanelBody>
    </InspectorControls>
    </Fragment>
    );
    };
    }, 'withCoverControls');

    /**
    * Hook used to modify the block's save element.
    */
    export const saveHook = 'blocks.getSaveElement';

    /**
    * Callback function to modify the save output of the block.
    *
    * @param {Object} element The element to be saved.
    * @param {Object} blockType The block type object.
    * @param {Object} attributes The block attributes.
    * @returns Modified element with custom attributes applied.
    */
    export const saveCallback = (element, blockType, attributes) => {
    if (blockType.name !== 'core/cover') return element;

    const { loopVideo, autoplayVideo, showPlayButton, coverImage } = attributes;

    // Ensure children elements are processed correctly, filtering out null/undefined elements
    const children = Array.isArray(element.props.children) ? element.props.children.filter(Boolean) : [];

    // Map over the children to modify any video elements with the specified attributes
    const modifiedChildren = children.map((child) => {
    // checking if a child element of the block is a
    <video> element
    if (child && child.type === 'video') {
    // Clone the video elements and apply the new attributes
    return cloneElement(child, {
    loop: loopVideo,
    autoPlay: autoplayVideo,
    poster: coverImage || child.props.poster,
    });
    }
    return child;
    });

    return (
    <div {...element.props}>
    {modifiedChildren}
    {showPlayButton && (
    <div className="wp-block-cover__play-button-overlay" style={{ position: 'absolute', zIndex: 10 }}>
    <button className="wp-block-cover__play-button" aria-label="Play Video" />
    </div>
    )}
    </div>
    );
    };

    // Register the filters to apply the modifications at the appropriate stages
    addFilter(registerHook, name, addAttributes);
    addFilter(blockEditHook, name, editCallback);
    addFilter(saveHook, name, saveCallback);

    But the issue remains:

    Block validation: Expected token of type StartTag ({type: 'StartTag', tagName: 'div', attributes: Array(2), selfClosing: false}), instead saw EndTag ({type: 'EndTag', tagName: 'div'}).
    (anonymous) @ blocks.min.js?ver=0d232d232463200f5cfd:19
    (anonymous) @ blocks.min.js?ver=0d232d232463200f5cfd:19
    ro @ blocks.min.js?ver=0d232d232463200f5cfd:19
    (anonymous) @ blocks.min.js?ver=0d232d232463200f5cfd:19
    no @ blocks.min.js?ver=0d232d232463200f5cfd:19
    (anonymous) @ block-editor.min.js?ver=f989eae66982c6c90d6e:21
    s @ data.min.js?ver=7c62e39de0308c73d50c:2
    r @ data.min.js?ver=7c62e39de0308c73d50c:2
    r @ data.min.js?ver=7c62e39de0308c73d50c:2
    ....
    (anonymous) @ data.min.js?ver=7c62e39de0308c73d50c:2
    p @ data.min.js?ver=7c62e39de0308c73d50c:9
    (anonymous) @ data.min.js?ver=7c62e39de0308c73d50c:9
    Qe @ data.min.js?ver=7c62e39de0308c73d50c:9
    Ye @ data.min.js?ver=7c62e39de0308c73d50c:9
    Nt @ edit-post.min.js?ver=bf7b57a061aad9bf9020:2
    hu @ react-dom.min.js?ver=18.3.1:2
    xi @ react-dom.min.js?ver=18.3.1:2
    bs @ react-dom.min.js?ver=18.3.1:2
    vs @ react-dom.min.js?ver=18.3.1:2
    gs @ react-dom.min.js?ver=18.3.1:2
    ls @ react-dom.min.js?ver=18.3.1:2
    S @ react-dom.min.js?ver=18.3.1:2
    T @ react-dom.min.js?ver=18.3.1:2Understand this warning
    blocks.min.js?ver=0d232d232463200f5cfd:19 Block validation: Block validation failed for core/cover ({name: 'core/cover', icon: {…}, keywords: Array(0), attributes: {…}, providesContext: {…},?…}).

    Content generated by save function:

    <div class="wp-block-cover alignfull cafejp-drinks-menu-pattern"><span aria-hidden="true" class="wp-block-cover__background has-black-background-color has-background-dim-0 has-background-dim"></span><img class="wp-block-cover__image-background wp-image-293" alt="" src="https://s.w.org/images/core/5.3/Windbuchencom.jpg" data-object-fit="cover"/><div class="wp-block-cover__inner-container"></div><div class="wp-block-cover__play-button-overlay" style="position:absolute;z-index:10"><button class="wp-block-cover__play-button" aria-label="Play Video"></button></div></div>

    Content retrieved from post body:

    <div class="wp-block-cover alignfull cafejp-drinks-menu-pattern"><span aria-hidden="true" class="wp-block-cover__background has-black-background-color has-background-dim-0 has-background-dim"></span><img class="wp-block-cover__image-background wp-image-293" alt="" src="https://s.w.org/images/core/5.3/Windbuchencom.jpg" data-object-fit="cover"/><div class="wp-block-cover__inner-container"></div></div>

    so need to do something better with returned data to make post body and content generated by save function match.

    • This reply was modified 1 month, 3 weeks ago by Rhand.
    Thread Starter Rhand

    (@rhand)

    Doing a check for play button:

    // Import necessary functions and components from WordPress packages
    import { addFilter } from '@wordpress/hooks'; // Allows adding filters to modify block behavior
    import { createHigherOrderComponent } from '@wordpress/compose'; // Allows creating higher-order components (HOCs)
    import { InspectorControls } from '@wordpress/block-editor'; // Provides control UI for block editing
    import { PanelBody, ToggleControl, TextControl } from '@wordpress/components'; // UI components for block settings
    import { Fragment } from '@wordpress/element'; // A wrapper to group multiple elements without adding extra nodes to the DOM
    import { cloneElement } from '@wordpress/element'; // Use cloneElement from WordPress

    /**
    * @see {@link https://developer.www.ads-software.com/block-editor/reference-guides/filters/block-filters/#blocks-registerblocktype}
    * Filter hook used to modify block settings during block registration.
    */
    export const registerHook = 'blocks.registerBlockType';

    /**
    * Name of the filter, used as a unique identifier.
    */
    export const name = 'cafejp/cover'

    /**
    * Filter addAttributes function to modify the block's settings.
    *
    * @param {Object} settings Block settings.
    * @param {string} name Block name.
    * @returns Modified settings with new attributes added.
    */
    export function addAttributes(settings, name) {
    if (name !== 'core/cover') return settings;

    return {
    ...settings, // Retain all existing settings https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Spread_syntax
    attributes: {
    ...settings.attributes, // Retain all existing attributes
    loopVideo: {
    type: 'boolean',
    default: false,
    },
    autoplayVideo: {
    type: 'boolean',
    default: false,
    },
    showPlayButton: {
    type: 'boolean',
    default: true,
    },
    coverImage: {
    type: 'string',
    default: '',
    },
    },
    };
    }

    /**
    * Hook used to modify the block's edit component.
    */
    export const blockEditHook = 'editor.BlockEdit';

    /**
    * Higher-order component (HOC) to modify the block's edit function.
    */
    export const editCallback = createHigherOrderComponent((BlockEdit) => {
    return (props) => {
    const { attributes, setAttributes, name } = props;

    if (name !== 'core/cover') {
    return <BlockEdit {...props} />;
    }

    const { loopVideo, autoplayVideo, showPlayButton, coverImage } = attributes;

    // Render the modified block editor interface with additional controls
    return (
    <Fragment>
    {/* Render the original BlockEdit component */}
    <BlockEdit {...props} />
    {/* Add custom controls to the block's inspector panel */}
    <InspectorControls>
    <PanelBody title="Video Options" initialOpen={true}>
    {/* Toggle control for looping the video */}
    <ToggleControl
    label="Loop Video"
    checked={loopVideo}
    onChange={(value) => setAttributes({ loopVideo: value })}
    />
    {/* Toggle control for autoplaying the video */}
    <ToggleControl
    label="Autoplay Video"
    checked={autoplayVideo}
    onChange={(value) => setAttributes({ autoplayVideo: value })}
    />
    {/* Toggle control for showing the play button */}
    <ToggleControl
    label="Show Play Button"
    checked={showPlayButton}
    onChange={(value) => setAttributes({ showPlayButton: value })}
    />
    {/* Text control for setting the cover image URL */}
    <TextControl
    label="Cover Image URL"
    value={coverImage}
    onChange={(value) => setAttributes({ coverImage: value })}
    help="This image will be used as the cover image for the video."
    />
    </PanelBody>
    </InspectorControls>
    </Fragment>
    );
    };
    }, 'withCoverControls');

    /**
    * Hook used to modify the block's save element.
    */
    export const saveHook = 'blocks.getSaveElement';

    /**
    * Callback function to modify the save output of the block.
    *
    * @param {Object} element The element to be saved.
    * @param {Object} blockType The block type object.
    * @param {Object} attributes The block attributes.
    * @returns Modified element with custom attributes applied.
    */
    export const saveCallback = (element, blockType, attributes) => {
    if (blockType.name !== 'core/cover') return element;

    const { loopVideo, autoplayVideo, showPlayButton, coverImage } = attributes;

    // Ensure children elements are processed correctly, filtering out null/undefined elements
    const children = Array.isArray(element.props.children) ? element.props.children.filter(Boolean) : [];

    // Map over the children to modify any video elements with the specified attributes
    const modifiedChildren = children.map((child) => {
    if (child && child.type === 'video') {
    return cloneElement(child, {
    loop: loopVideo,
    autoPlay: autoplayVideo,
    poster: coverImage || child.props.poster,
    });
    }
    return child;
    });

    // Check if the play button should be added
    const playButton = showPlayButton ? (
    <div className="wp-block-cover__play-button-overlay" style={{ position: 'absolute', zIndex: 10 }}>
    <button className="wp-block-cover__play-button" aria-label="Play Video"></button>
    </div>
    ) : null;

    return (
    <div {...element.props}>
    {modifiedChildren}
    {playButton}
    </div>
    );
    };

    // Register the filters to apply the modifications at the appropriate stages
    addFilter(registerHook, name, addAttributes);
    addFilter(blockEditHook, name, editCallback);
    addFilter(saveHook, name, saveCallback);

    but still same errors like

    lock validation: Expected token of type StartTag ({type: 'StartTag', tagName: 'div', attributes: Array(2), selfClosing: false}), instead saw EndTag ({type: 'EndTag', tagName: 'div'}).
    (anonymous) @ blocks.min.js?ver=0d232d232463200f5cfd:19
    (anonymous) @ blocks.min.js?ver=0d232d232463200f5cfd:19
    ro @ blocks.min.js?ver=0d232d232463200f5cfd:19
    (anonymous) @ blocks.min.js?ver=0d232d232463200f5cfd:19
    ro @ blocks.min.js?ver=0d232d232463200f5cfd:19
    ....
    bs @ react-dom.min.js?ver=18.3.1:2
    vs @ react-dom.min.js?ver=18.3.1:2
    gs @ react-dom.min.js?ver=18.3.1:2
    is @ react-dom.min.js?ver=18.3.1:2
    Hl @ react-dom.min.js?ver=18.3.1:2
    (anonymous) @ react-dom.min.js?ver=18.3.1:2Understand this warning
    blocks.min.js?ver=0d232d232463200f5cfd:19 Block validation: Block validation failed for core/cover ({name: 'core/cover', icon: {…}, keywords: Array(0), attributes: {…}, providesContext: {…},?…}).

    Content generated by save function:

    <div class="wp-block-cover is-light" style="padding-top:80px;padding-right:80px;padding-bottom:80px;padding-left:80px;min-height:430px"><span aria-hidden="true" class="wp-block-cover__background has-background-dim-20 has-background-dim" style="background-color:#9aacbd"></span><img class="wp-block-cover__image-background wp-image-159" alt="" src="https://wooblockpatterns.wpcomstaging.com/wp-content/uploads/2024/06/beach-landscape-sea-coast-nature-person.jpg" data-object-fit="cover"/><div class="wp-block-cover__inner-container"></div><div class="wp-block-cover__play-button-overlay" style="position:absolute;z-index:10"><button class="wp-block-cover__play-button" aria-label="Play Video"></button></div></div>

    Content retrieved from post body:

    <div class="wp-block-cover is-light" style="padding-top:80px;padding-right:80px;padding-bottom:80px;padding-left:80px;min-height:430px"><span aria-hidden="true" class="wp-block-cover__background has-background-dim-20 has-background-dim" style="background-color:#9aacbd"></span><img class="wp-block-cover__image-background wp-image-159" alt="" src="https://wooblockpatterns.wpcomstaging.com/wp-content/uploads/2024/06/beach-landscape-sea-coast-nature-person.jpg" data-object-fit="cover"/><div class="wp-block-cover__inner-container"></div></div>
    Thread Starter Rhand

    (@rhand)

    I am testing new code with play button turned off by default and loaded conditionally . Seems to be working better

    // Import necessary functions and components from WordPress packages
    import { addFilter } from '@wordpress/hooks'; // Allows adding filters to modify block behavior
    import { createHigherOrderComponent } from '@wordpress/compose'; // Allows creating higher-order components (HOCs)
    import { InspectorControls } from '@wordpress/block-editor'; // Provides control UI for block editing
    import { PanelBody, ToggleControl, TextControl } from '@wordpress/components'; // UI components for block settings
    import { Fragment } from '@wordpress/element'; // A wrapper to group multiple elements without adding extra nodes to the DOM
    import { cloneElement } from '@wordpress/element'; // Use cloneElement from WordPress

    /**
    * @see {@link https://developer.www.ads-software.com/block-editor/reference-guides/filters/block-filters/#blocks-registerblocktype}
    * Filter hook used to modify block settings during block registration.
    */
    export const registerHook = 'blocks.registerBlockType';

    /**
    * Name of the filter, used as a unique identifier.
    */
    export const name = 'cafejp/cover';

    /**
    * Filter addAttributes function to modify the block's settings.
    *
    * @param {Object} settings Block settings.
    * @param {string} name Block name.
    * @returns Modified settings with new attributes added.
    */
    export function addAttributes(settings, name) {
    if (name !== 'core/cover') return settings;

    return {
    ...settings, // Retain all existing settings https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Spread_syntax
    attributes: {
    ...settings.attributes, // Retain all existing attributes
    loopVideo: {
    type: 'boolean',
    default: false,
    },
    autoplayVideo: {
    type: 'boolean',
    default: false,
    },
    showPlayButton: {
    type: 'boolean',
    default: false,
    },
    coverImage: {
    type: 'string',
    default: '',
    },
    },
    };
    }

    /**
    * Hook used to modify the block's edit component.
    */
    export const blockEditHook = 'editor.BlockEdit';

    /**
    * Higher-order component (HOC) to modify the block's edit function.
    */
    export const editCallback = createHigherOrderComponent((BlockEdit) => {
    return (props) => {
    const { attributes, setAttributes, name } = props;

    if (name !== 'core/cover') {
    return <BlockEdit {...props} />;
    }

    const { loopVideo, autoplayVideo, showPlayButton, coverImage } = attributes;

    // Render the modified block editor interface with additional controls
    return (
    <Fragment>
    {/* Render the original BlockEdit component */}
    <BlockEdit {...props} />
    {/* Add custom controls to the block's inspector panel */}
    <InspectorControls>
    <PanelBody title="Video Options" initialOpen={true}>
    {/* Toggle control for looping the video */}
    <ToggleControl
    label="Loop Video"
    checked={loopVideo}
    onChange={(value) => setAttributes({ loopVideo: value })}
    />
    {/* Toggle control for autoplaying the video */}
    <ToggleControl
    label="Autoplay Video"
    checked={autoplayVideo}
    onChange={(value) => setAttributes({ autoplayVideo: value })}
    />
    {/* Toggle control for showing the play button */}
    <ToggleControl
    label="Show Play Button"
    checked={showPlayButton}
    onChange={(value) => setAttributes({ showPlayButton: value })}
    />
    {/* Text control for setting the cover image URL */}
    <TextControl
    label="Cover Image URL"
    value={coverImage}
    onChange={(value) => setAttributes({ coverImage: value })}
    help="This image will be used as the cover image for the video."
    />
    </PanelBody>
    </InspectorControls>
    </Fragment>
    );
    };
    }, 'withCoverControls');

    /**
    * Hook used to modify the block's save element.
    */
    export const saveHook = 'blocks.getSaveElement';

    /**
    * Callback function to modify the save output of the block.
    *
    * @param {Object} element The element to be saved.
    * @param {Object} blockType The block type object.
    * @param {Object} attributes The block attributes.
    * @returns Modified element with custom attributes applied.
    */
    export const saveCallback = (element, blockType, attributes) => {
    if (blockType.name !== 'core/cover') return element;

    const { loopVideo, autoplayVideo, showPlayButton, coverImage } = attributes;

    // Ensure children elements are processed correctly, filtering out null/undefined elements
    const children = Array.isArray(element.props.children) ? element.props.children.filter(Boolean) : [];

    // Map over the children to modify any video elements with the specified attributes
    const modifiedChildren = children.map((child) => {
    if (child && child.type === 'video') {
    return cloneElement(child, {
    loop: loopVideo,
    autoPlay: autoplayVideo,
    poster: coverImage || child.props.poster,
    });
    }
    return child;
    });

    // Check if the play button should be added
    let playButton = null;
    if (showPlayButton) {
    playButton = (
    <div className="wp-block-cover__play-button-overlay" style={{ position: 'absolute', zIndex: 10 }}>
    <button className="wp-block-cover__play-button" aria-label="Play Video"></button>
    </div>
    );
    }

    return (
    <div {...element.props}>
    {modifiedChildren}
    {playButton}
    </div>
    );
    };

    // Register the filters to apply the modifications at the appropriate stages
    addFilter(registerHook, name, addAttributes);
    addFilter(blockEditHook, name, editCallback);
    addFilter(saveHook, name, saveCallback);

    I only do get this error occasionally on post load and that points to ACF oddly enough.

    jQuery.Deferred exception: Cannot read properties of undefined (reading 'autop') TypeError: Cannot read properties of undefined (reading 'autop')
    at acf.Model.onReady (https://cafejpcoen.test/wp-content/plugins/advanced-custom-fields-pro/assets/build/js/acf-input.min.js?ver=6.3.5:1:110927)
    at o (https://cafejpcoen.test/wp-content/plugins/advanced-custom-fields-pro/assets/build/js/acf.min.js?ver=6.3.5:1:1406)
    at Object.doAction (https://cafejpcoen.test/wp-content/plugins/advanced-custom-fields-pro/assets/build/js/acf.min.js?ver=6.3.5:1:576)
    at n.doAction (https://cafejpcoen.test/wp-content/plugins/advanced-custom-fields-pro/assets/build/js/acf.min.js?ver=6.3.5:1:19053)
    at HTMLDocument.<anonymous> (https://cafejpcoen.test/wp-content/plugins/advanced-custom-fields-pro/assets/build/js/acf.min.js?ver=6.3.5:1:28605)
    at e (https://cafejpcoen.test/wp-admin/load-scripts.php?c=0&load%5Bchunk_0%5D=wp-hooks,jquery-core,jquery-migrate,utils&ver=6.6.1:4:27028)
    at t (https://cafejpcoen.test/wp-admin/load-scripts.php?c=0&load%5Bchunk_0%5D=wp-hooks,jquery-core,jquery-migrate,utils&ver=6.6.1:4:27330) undefined
    • This reply was modified 1 month, 3 weeks ago by Rhand.
    • This reply was modified 1 month, 3 weeks ago by Rhand.

    You seem to have figured out a lot of things yourself. Regarding your last question, it would be good if you contacted ACF: https://www.ads-software.com/support/plugin/advanced-custom-fields/

    Thread Starter Rhand

    (@rhand)

    I will do that. Will close this thread for now.

Viewing 9 replies - 1 through 9 (of 9 total)
  • You must be logged in to reply to this topic.