Wordpress - How to extend the gallery block in Gutenberg?
Ok, I've been playing with this for a little bit and have managed to change the output of the Gallery block, with the following caveats:
- The preview does not match the output. I think this is possible but appears to be a bit more involved.
- Certain classes and markup are required in the output for the block to be able to parse the content and keep it editable. These classes have front-end styles that will need to be dealt with. I'm not sure at this point if there is a way to filter how the block does this. If it were possible it might not even be a good idea if it means Gallery blocks are broken when a theme or plugin is deactivated. A totally new block would probably be the way to go for situations where this would be required.
- I'm not really sure how image sizes work at this stage.
- The method of JavaScript hooks used might not be relevant in the final release. Gutenberg uses
@wordpress/hooks
while discussion about what hooks system to use in Core is ongoing. - Since the output of Blocks is saved as HTML, not a shortcode or meta, it won't be possible to modify the output of existing Galleries without editing them.
The first thing we need to do is register a script. This is done with wp_enqueue_scripts()
, but on the enqueue_block_editor_assets
hook.
The script should have the wp-blocks
script as a dependency. It is almost certainly already loaded in the editor, but making it a dependency presumably ensures it is loaded before our script.
function wpse_298225_enqueue_block_assets() {
wp_enqueue_script(
'wpse-298225-gallery-block',
get_theme_file_uri( 'block.js' ),
['wp-blocks']
);
}
add_action( 'enqueue_block_editor_assets', 'wpse_298225_enqueue_block_assets' );
The HTML for a block's output is handled by the save()
method of the block. You can see the Gallery block's in this file.
At this stage (March 2018) Gutenberg supports a filter on the save method of blocks, blocks.getSaveElement
. We can add a hook to this in JavaScript like this:
wp.hooks.addFilter(
'blocks.getSaveElement',
'wpse-298225',
wpse298225GallerySaveElement
)
The first argument is the hook name, the 2nd argument is - I think - a namespace, and the last argument is the callback function.
Since we are replacing the save()
method of the block, we need to return an new element. However, this is not a normal HTML element we need to return. We need to return a React element.
When you look at the original block’s save()
method what you see is JSX. React, which Gutenberg uses under-the-hood, supports it for rendering elements. See this article for more on that.
JSX normally requires a build step, but since I'm not introducing a build step for this example, we need a way to create an element without JSX. React provides this with createElement()
. WordPress provides a wrapper for this and other react functionality in the form of wp.element
. So to use createElement()
we use wp.element.createElement()
.
In the callback function for blocks.getSaveElement
we get:
element
The original Element created by the block.blockType
An object representing the block being used.attributes
The properties of the block instance. In our example this includes the images in gallery and settings like the number of columns.
So our callback function needs to:
- Return the original element for non-block galleries.
- Take the attributes, particularly the images, and create a new React element out of them representing the gallery.
Here is a complete example that simply outputs a ul
with a class, my-gallery
, and li
s for each image with the class my-gallery-item
and and img
in each one with the src
set to the image URL.
function wpse298225GallerySaveElement( element, blockType, attributes ) {
if ( blockType.name !== 'core/gallery' ) {
return element;
}
var newElement = wp.element.createElement(
'ul',
{
'className': 'wp-block-gallery my-gallery',
},
attributes.images.map(
function( image ) {
return wp.element.createElement(
'li',
{
'className': 'blocks-gallery-item my-gallery-item',
},
wp.element.createElement(
'img',
{
'src': image.url,
}
)
)
}
)
)
return newElement
}
Some things to take note of:
- The original gallery block finds images by looking for
ul.wp-block-gallery .blocks-gallery-item
, so this markup and those classes are required for editing the block to be possible. This markup is also used for the default styling. attributes.images.map
is looping over each image and returning an array of child elements as the content for the main element. Inside these elements there is another child element for the image itself.
Here to provide an updated answer. I found this post extremely helpful in answering the question of how to extend the Gallery Block.
In short, its advisable to just create a new block rather than extending an existing. From the post in my link above:
if you modify the HTML of a block [by extending], it won't be recognized as the original block. Rather than trying to manipulate a core block it seems like unregistering the core block and registering a new replacement block in its place would be a safer approach - that way you're ensuring users of the site use your particular customized gallery, which will validate because it defines its own HTML structure.
The link above also references The Create-Guten_Block plugin which is a command-line tool that will generate everything you need to get you started with Block creation. The tool is very easy to use, and easy setup.
With these resources, I was able to figure out how to develop a custom gallery block in a short time