I am trying to develop a custom WP block, have read approximately the entire internet, have tried many things and still can't solve the problem(s) I have. I am a WP noob and the more I read, the more I am confused.
What I want to achieve: building an offcanvas drawer block with its handle as a button text or image. In the editor it should be decided where the block should be placed (left, top, right, bottom). So basically I have to add a class to the block element itself on the fly. The user builds the block themselves by adding nested blocks
The problems I have are mostly related to the block validation mechanism. So the questions would be:
How to add a CSS class to the block itself when the user selects the placement?
Why does the block becomes invalid as soon as I uncomment these lines in the
Edit()andSave()functions?:{/* <Button className="offcanvas-drawer-handle"> */} {/* <img className="offcanvas-drawer-handle-image" src={ attributes.handleImage.url }/> */} {/* </Button> */}An last question, more design oriented: it sounds odd to put the handle in the same div and position it no? What do you think?
Here the support and attributes section of the block.json file:
"supports": {
"html": false,
"typography": {
"fontSize": true,
"lineHeight": true,
"textAlign": true
},
"color": {
"text": true,
"link": true,
"background": true
},
"spacing": {
"padding": true
}
},
"attributes": {
"style": {
"type": "object",
"margin": "value",
"padding": "value"
},
"handleType": {
"type": "string",
"default": "button"
},
"handleImage": {
"type": "object",
"default": null
},
"handleText": {
"type": "string",
"default": "Type drawer's button text"
},
"placement": {
"type": "string",
"default": "right"
}
}
Here the edit.js
// imports omitted
const Edit = ({ attributes, setAttributes }) => {
const blockProps = useBlockProps();
const ALLOWED_MEDIA_TYPES = [ 'image/jpg', 'image/png', 'image/svg' ];
const isHandleImageLoaded = attributes.handleImage !== null;
const onHandleTypeChange = (value) => {
setAttributes({ handleType: value });
}
const onHandleTextChange = (value) => {
setAttributes({ handleText: value });
}
const onSelectHandleImage = (value) => {
setAttributes({ handleImage: value })
}
const onPlacementChange = (value) => {
setAttributes({ placement: value })
}
/* this was an attempt to append the placement class -> the block becomes
invalid (which I understand this time but don't know how I can solve it
and it's horrible to do that without validation. I was trying out)
*/
// blockProps.className = `${blockProps.className} offcanvas-drawer-${attributes.placement}`;
return (
<>
<InspectorControls>
<PanelBody title={__("General Settings")}>
<ToggleGroupControl
label="Placement"
value="right"
isBlock
__nextHasNoMarginBottom
__next40pxDefaultSize
onChange={ onPlacementChange }>
<ToggleGroupControlOption value="left" label="Left" />
<ToggleGroupControlOption value="top" label="Top" />
<ToggleGroupControlOption value="right" label="Right" />
<ToggleGroupControlOption value="bottom" label="Bottom" />
</ToggleGroupControl>
<ToggleGroupControl
label="Handle Type"
value={ attributes.handleType }
isBlock
__nextHasNoMarginBottom
__next40pxDefaultSize
onChange={ onHandleTypeChange }>
<ToggleGroupControlOption value="button" label="Button" />
<ToggleGroupControlOption value="image" label="Image" />
</ToggleGroupControl>
{ isHandleImageLoaded && attributes.handleType === 'image' ? (
<figure className="canvas-thumbnail">
<img src={ attributes.handleImage.url } />
<figcaption>{attributes.handleImage.title}</figcaption>
</figure>
)
: null }
{ attributes.handleType == 'button' ? (
<TextControl
__nextHasNoMarginBottom
__next40pxDefaultSize
label="Handle Text"
value={ attributes.handleText }
onChange={ onHandleTextChange }
/>
)
: attributes.handleType == 'image' ? (
<MediaUpload
onSelect={ onSelectHandleImage }
allowedTypes={ ALLOWED_MEDIA_TYPES }
multiple={ false }
render={ ( { open } ) => (
<>
<Button onClick={open} variant="primary">
{ attributes.handleImage === null
? 'Upload new file'
: 'Upload' }
</Button>
</>
)}
/>)
: null}
</PanelBody>
</InspectorControls>
<div { ...blockProps }>
<InnerBlocks/>
// here as soon as I add something (a simple div as well), the block become invalid
{/* <Button className="offcanvas-drawer-handle"> */}
{/* <img className="offcanvas-drawer-handle-image" src={ attributes.handleImage.url }/> */}
{/* </Button> */}
</div>
</>
);
}
Here the save.js
// import omitted
const Save = (props) => {
const { attributes, innerBlocks } = props;
const blockProps = useBlockProps.save();
// here same as in Edit()
// blockProps.className = `${blockProps.className} offcanvas-drawer-${attributes.placement}`;
return (
<div { ...blockProps }>
<InnerBlocks.Content />
{/* <Button className="offcanvas-drawer-handle"> */}
{/* <img className="offcanvas-drawer-handle-image" src={ attributes.handleImage.url }/> */}
{/* </Button> */}
</div>
)
}
So frustrating. In HTML + CSS, this would be done in 20 minutes :D And here it is not possible to build the checkbox-trick to trigger the css open/close animation, right? (I haven't tried yet, just asking)
I am very happy to receive all the comments, critics and insights and I thank you in advance for your help!