-
-
Save gaambo/633bcd83a9596762218ffa65d0cfe22a to your computer and use it in GitHub Desktop.
import { Fragment } from "@wordpress/element"; | |
import { InnerBlocks } from "@wordpress/editor"; | |
/** | |
* Changes the edit function of an ACF-block to allow InnerBlocks | |
* Should be called like this on `editor.BlockEdit` hook: | |
* ` addFilter("editor.BlockEdit", "namespace/block", editWithInnerBlocks("acf/block-name"));` | |
* | |
* @param {string} blockName the name of the block to wrap | |
* @param {object} innerBlockParams params to be passed to the InnerBlocks component (like allowedChildren) | |
*/ | |
const editWithInnerBlocks = ( | |
blockName, | |
innerBlockParams, | |
append = true, | |
hideBlockEdit = false | |
) => BlockEdit => props => { | |
if (props.name !== blockName) { | |
return <BlockEdit {...props} />; | |
} | |
if (append) { | |
return ( | |
<Fragment> | |
{!hideBlockEdit && <BlockEdit {...props} />} | |
<InnerBlocks {...innerBlockParams} /> | |
</Fragment> | |
); | |
} | |
// put before block edit | |
return ( | |
<Fragment> | |
<InnerBlocks {...innerBlockParams} /> | |
{!hideBlockEdit && <BlockEdit {...props} />} | |
</Fragment> | |
); | |
}; | |
/** | |
* Changes the save function of an ACF-block to allow InnerBlocks | |
* Should be called like this on `blocks.getSaveElement` hook: | |
* `addFilter("blocks.getSaveElement", "namespace/block", saveWithInnerBlocks("acf/block-name"));` | |
* | |
* @param {string} blockName the name of the block to wrap | |
*/ | |
const saveWithInnerBlocks = blockName => (BlockSave, block) => { | |
if (typeof block === "undefined") { | |
return BlockSave; | |
} | |
if (block.name !== blockName) { | |
return BlockSave || block.save; | |
} | |
return ( | |
<div> | |
<InnerBlocks.Content /> | |
</div> | |
); | |
}; | |
export { editWithInnerBlocks, saveWithInnerBlocks }; |
Currently there is no way to create a solution out of the box. Maybe @elliotcondon comes with a better solution in the future. A solution which will work fine for all cases.
@CreativeDive thanks for your additions and the extensive documentation - awesome!
@maccyd10 Do you want a Version just with wp.element and wp.editor (because that would be easy to edit, just change the imports to const declarations) or also a version which uses createElement
instead of JSX?
Awesome. Thank you all, will be implementing during the week and will edit this comment if any contribution comes up but just by reading all this I can tell you both saved me quite some time, just wanted to show the love.
Hey guys, this I is a standardisation I am using to move Inner Blocks in the editor:
import editWithInnerBlocks from './editWithInnerBlocks'
import saveWithInnerBlocks from './saveWithInnerBlocks'
import moveInnerBlocks from './moveInnerBlocks';
const { addFilter } = wp.hooks
const blocks = acf.data.blockTypes.filter(block => block.has_inner_blocks) // this property explained further down
blocks.forEach(block => {
addFilter("editor.BlockEdit", `with-inner-blocks/${block.name}`, editWithInnerBlocks(block.name))
addFilter("blocks.getSaveElement", `with-inner-blocks/${block.name}`, saveWithInnerBlocks(block.name))
acf.addAction( `render_block_preview/type=${block.name.replace('acf/', '')}`, (preview) => moveInnerBlocks(preview, block) )
})
// moveInnerBlocks
export default ($preview, block) => {
const preview = $preview[0]
const target = preview.querySelector('.js-inner-blocks') // this className explained further down
if( target ) {
// check cached innerBlocks first otherwise we lose them every time we make change to ACF field for the block
if( block.innerBlocks ) {
target.appendChild(block.innerBlocks)
} else {
const innerBlocks = preview.closest('.wp-block').querySelector('.editor-inner-blocks')
// cache the innerBlocks for later otherwise we lose them every time we make change to ACF field for the block
block.innerBlocks = innerBlocks
target.appendChild(innerBlocks)
}
}
}
These are my args for these kind of Blocks:
[
'name' => 'example-block',
'title' => 'Example Block',
'category' => 'wp-kit-example-blocks',
'icon' => 'welcome-widgets-menus',
'description' => 'An example block',
'has_inner_blocks' => true,
'render_callback' => function($block, $inner_blocks) {
include(locate_template('views/example.block.php'));
}
]
By having has_inner_blocks
set to true
the iteration is handled in Javascript above
Here's my HTML for the block:
// views/example.block.php
<div class="example">
<h1>Hello <?php the_field('text'); ?>!</h1>
<div class="js-inner-blocks">
<?= $inner_blocks; ?>
</div>
</div>
I always have a node with js-inner-blocks
className wrapping where I want my inner blocks so Javascript above can target it in block editor. For the frontend the $inner_blocks
is coming in from second argument of render_callback
. Everything looks nice in the backend and the frontend.
I think these kind of standardisation could be worked into ACF directly, the code is not too opinionated.
@terence1990 that looks great. I also thought about having a supports
flag innerBlocks
and den do everything automatically (also the supported innerblocks etc.). Thanks for your snippet :)
@terence1990 really cool and thank you for your work. It works like a charm :-)
Please note, since the latest Gutenberg version the inner blocks container selector was changed from .editor-inner-blocks
to .block-editor-inner-blocks
.
@gaambo and @terence1990 any idea how we can solve it with multiple inner blocks like different columns inside an ACF block and each column can include different inner blocks? :-)
@gaambo and @terence1990 an other issue is, if you use the same block multiple times the selector class "js-inner-blocks" works only for on block, but not for multiple blocks. The selector class needs a unique identifier e.g. the ACF block id, but I don't know how I can get the ACF block id inside the react code. Is there a filter of ACF which provides the block id?
Thanks for your inputs - I'll have to test & play with WordPress 5.4 in the following days and hopefully I can come up with an solution or at least an idea - I'll let you know :)
@CreativeDive Regarding multiple inner blocks: AFAIK there's still no way to include multiple innerBlocks in a block (even via React) - so we'd have to gez creative here and a solution should be future-compatible.
Right now the only thing that comes into my mind is building multiple blocks:
- "Container"/"Wrapper" block which allows only the following "Slots" block
- "Slots" block which can only be inserted to certain parents and only allows single blocks
- Single block which can only be inserted in the slots block.
Depending on the use case that's not really easiert then using the core group + columbs blocks.
For accordions I solved it like this:
Accordion-ACF-Block which only allows Accordion-Iten Blocks as innerBlocks.
FYI: ACF 5.9 will support innerBlocks: https://www.advancedcustomfields.com/blog/acf-5-9-exciting-new-features/
@gaambo: Very exciting ;-)
Is there any possibility of posting a pre-transpiled version that uses wp.element and wp.editor to make it easier for the folks that don't really understand Babel / Webpack and what not?