Customizing useBlockProps

I'm making a custom block to insert video. I've added some styling options with the BorderControl component. Options selected with the component are applied by default to the block in the editor without applying useBlockProps: return ( <></> ); See video capture: https://youtu.be/g5s8eBBIeOM That is, I haven't needed to use …useBlockProps. Gutenberg seems to apply those attributes automatically to the root element. If I try to apply useBlockProps to an element of my choice, I get a duplicate of the attributes. One on the element it is applied on by default and one on the chosen element. I am applying useBlockProps like this. return ( <div {...useBlockProps()}></div> ); See video capture: https://youtu.be/nin8bYEZRn4 Default attributes are always rendered and I can't customize their location. Is it possible to remove the default rendering of BlockProps and use my own customization? block.json: export default { name: `sage/video`, title: `Video`, category: `fb`, icon: CustomIcon, attributes: { libraryId: { type: 'string', default: '265348', }, videoId: { type: 'string', default: '', }, thumbnailUrl: { type: 'string', default: '', }, align: { type: 'string', default: 'none', }, autoplay: { type: 'boolean', default: false, }, loop: { type: 'boolean', default: false, }, muted: { type: 'boolean', default: false, }, controls: { type: 'boolean', default: true, }, playsInline: { type: 'boolean', default: true, }, style: { type: 'object', default: { border: { color: '', radius: '0px', style: 'solid', width: '1px', }, }, }, }, supports: { align: ['wide', 'full'], spacing: { margin: false, padding: false, }, }, edit: Edit, save: () => null, }; Edit.jsx: import { TextControl, PanelBody, ToggleControl, Button, ToolbarGroup, ToolbarButton, __experimentalBorderControl as BorderControl, __experimentalUnitControl as UnitControl, } from '@wordpress/components'; import { InspectorControls, BlockControls, MediaUpload, MediaUploadCheck, } from '@wordpress/block-editor'; import { __ } from '@wordpress/i18n'; import PropTypes from 'prop-types'; import { useEffect, useState } from '@wordpress/element'; import VideoPlayer from '../../components/video/VideoPlayer'; import { Eye, Edit3 } from 'react-feather'; const EditVideo = ({ attributes, setAttributes }) => { const { videoId, thumbnailUrl, autoplay, loop, muted, controls, playsInline, libraryId, style, } = attributes; const [isEditing, setIsEditing] = useState(true); const removeImage = () => { setAttributes({ thumbnailUrl: '' }); }; const colors = [ { name: 'Black', color: '#000000' }, { name: 'White', color: '#FFFFFF' }, // Añade más colores según sea necesario ]; return ( <> <BlockControls> <ToolbarGroup> <ToolbarButton icon={ isEditing ? ( <Eye className="feather-icon" /> ) : ( <Edit3 className="feather-icon" /> ) } label={ isEditing ? __('Switch to Preview', 'sage') : __('Switch to Edit', 'sage') } onClick={() => setIsEditing(!isEditing)} /> </ToolbarGroup> </BlockControls> <InspectorControls> <PanelBody title={__('Video Settings', 'sage')}> <ToggleControl label={__('Autoplay', 'sage')} checked={autoplay} onChange={(value) => setAttributes({ autoplay: value })} /> <ToggleControl label={__('Loop', 'sage')} checked={loop} onChange={(value) => setAttributes({ loop: value })} /> <ToggleControl label={__('Muted', 'sage')} checked={muted} onChange={(value) => setAttributes({ muted: value })} /> <ToggleControl label={__('Playback Controls', 'sage')} checked={controls} onChange={(value) => setAttributes({ controls: value })} /> <ToggleControl label={__('Play Inline', 'sage')} checked={playsInline} onChange={(value) => setAttributes({ playsInline: value })} /> </PanelBody> <PanelBody title={__('Style Settings', 'sage')}> <PanelBody title={__('Style Settings', 'sage')}> <BorderControl colors={colors} label={__('Border', 'sage')} value={{ color: style.border.color, style: style.border.style, width: style.border.width, radius: style.border.radius, }} withSlider={true} width="100px" onChange={(newBorder) => { setAttributes({ style: { ...style, border: { color: newBorder.color, style: newBorder.style, width: newBorder.width, radius: newBorder.radius, }, }, }); }} /> <UnitControl label={__('Border Radius', 'sage')} value={style.border.radius} onChange={(newRadius) => { setAttributes({ style: { ...style, border: { ...style.border, radius: newRadius, }, }, }); }} /> </PanelBody> </PanelBody> </InspectorControls> {isEditing ? ( <> <TextControl label={__('Video ID', 'sage')} value={videoId} onChange={(id) => setAttributes({ videoId: id })} placeholder={__('Enter Bunny.net video ID...', 'sage')} className="m-4" /> <TextControl label={__('Library ID', 'sage')} value={libraryId} onChange={(id) => setAttributes({ libraryId: id })} placeholder={__('Leave blank to get default library', 'sage')} className="m-4" /> <MediaUploadCheck> <MediaUpload onSelect={(media) => setAttributes({ thumbnailUrl: media.url })} allowedTypes={['image']} render={({ open }) => ( <div> {thumbnailUrl ? ( <div> <img src={thumbnailUrl} alt={__('Thumbnail', 'sage')} className="block w-60 mx-4 mt-4" /> <Button onClick={open} variant="secondary" className="m-4" > {__('Change Thumbnail', 'sage')} </Button> <Button onClick={removeImage} variant="destructive"> {__('Remove Thumbnail', 'sage')} </Button> </div> ) : ( <Button variant="secondary" onClick={open} className="m-4"> {__('Select Thumbnail', 'sage')} </Button> )} </div> )} /> </MediaUploadCheck> </> ) : ( videoId && ( <VideoPlayer videoId={videoId} thumbnailUrl={thumbnailUrl} autoplay={autoplay} loop={loop} muted={muted} controls={controls} playsInline={playsInline} /> ) )} </> ); };

Comment (1)

Jese Leos

August 10, 2024

Verified user

The problem occurs because the block uses the style property internally to set the styles of the block with useBlockProps(). So your own implementation sets the same set of styles as you would do with a core block. The easiest solution is to enable the - for now - expiremental border controls. This will give you the same border controls as the core blocks have, so you don't have to create them in your Edit.jsx. export default { name: 'sage/video', title: 'Video', category: 'fb', icon: CustomIcon, attributes: { libraryId: { type: 'string', default: '265348', }, videoId: { type: 'string', default: '', }, thumbnailUrl: { type: 'string', default: '', }, align: { type: 'string', default: 'none', }, autoplay: { type: 'boolean', default: false, }, loop: { type: 'boolean', default: false, }, muted: { type: 'boolean', default: false, }, controls: { type: 'boolean', default: true, }, playsInline: { type: 'boolean', default: true, } }, supports: { align: ['wide', 'full'], spacing: { margin: false, padding: false, }, __experimentalBorder: { color: true, radius: true, style: true, width: true, __experimentalDefaultControls: { color: true, radius: true, style: true, width: true, }, } }, edit: Edit, save: () => null }; import { TextControl, PanelBody, ToggleControl, Button, ToolbarGroup, ToolbarButton, } from '@wordpress/components'; import { useBlockProps, InspectorControls, BlockControls, MediaUpload, MediaUploadCheck, } from '@wordpress/block-editor'; import { __ } from '@wordpress/i18n'; import PropTypes from 'prop-types'; import { useEffect, useState } from '@wordpress/element'; import VideoPlayer from '../../components/video/VideoPlayer'; import { Eye, Edit3 } from 'react-feather'; const EditVideo = ({ attributes, setAttributes }) => { const { videoId, thumbnailUrl, autoplay, loop, muted, controls, playsInline, libraryId, style, } = attributes; const blockProps = useBlockProps(); const [isEditing, setIsEditing] = useState(true); const removeImage = () => { setAttributes({ thumbnailUrl: '' }); }; const colors = [ { name: 'Black', color: '#000000' }, { name: 'White', color: '#FFFFFF' }, // Añade más colores según sea necesario ]; return ( <> <BlockControls> <ToolbarGroup> <ToolbarButton icon={ isEditing ? ( <Eye className="feather-icon" /> ) : ( <Edit3 className="feather-icon" /> ) } label={ isEditing ? __('Switch to Preview', 'sage') : __('Switch to Edit', 'sage') } onClick={() => setIsEditing(!isEditing)} /> </ToolbarGroup> </BlockControls> <InspectorControls> <PanelBody title={__('Video Settings', 'sage')}> <ToggleControl label={__('Autoplay', 'sage')} checked={autoplay} onChange={(value) => setAttributes({ autoplay: value })} /> <ToggleControl label={__('Loop', 'sage')} checked={loop} onChange={(value) => setAttributes({ loop: value })} /> <ToggleControl label={__('Muted', 'sage')} checked={muted} onChange={(value) => setAttributes({ muted: value })} /> <ToggleControl label={__('Playback Controls', 'sage')} checked={controls} onChange={(value) => setAttributes({ controls: value })} /> <ToggleControl label={__('Play Inline', 'sage')} checked={playsInline} onChange={(value) => setAttributes({ playsInline: value })} /> </PanelBody> </InspectorControls> {isEditing ? ( <> <TextControl label={__('Video ID', 'sage')} value={videoId} onChange={(id) => setAttributes({ videoId: id })} placeholder={__('Enter Bunny.net video ID...', 'sage')} className="m-4" /> <TextControl label={__('Library ID', 'sage')} value={libraryId} onChange={(id) => setAttributes({ libraryId: id })} placeholder={__('Leave blank to get default library', 'sage')} className="m-4" /> <MediaUploadCheck> <MediaUpload onSelect={(media) => setAttributes({ thumbnailUrl: media.url })} allowedTypes={['image']} render={({ open }) => ( <div> {thumbnailUrl ? ( <div> <img src={thumbnailUrl} alt={__('Thumbnail', 'sage')} className="block w-60 mx-4 mt-4" /> <Button onClick={open} variant="secondary" className="m-4" > {__('Change Thumbnail', 'sage')} </Button> <Button onClick={removeImage} variant="destructive"> {__('Remove Thumbnail', 'sage')} </Button> </div> ) : ( <Button variant="secondary" onClick={open} className="m-4"> {__('Select Thumbnail', 'sage')} </Button> )} </div> )} /> </MediaUploadCheck> </> ) : ( videoId && ( <div {...blockProps}> <VideoPlayer videoId={videoId} thumbnailUrl={thumbnailUrl} autoplay={autoplay} loop={loop} muted={muted} controls={controls} playsInline={playsInline} /> </div> ) )} </> ); };

You’ll be in good company