Created
May 19, 2025 09:47
-
-
Save philipjohn/3eed21d8920fe0e30a8662f9cb65d409 to your computer and use it in GitHub Desktop.
Attempting to override the core/query and core/post-template WordPress blocks
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/** | |
* WordPress dependencies | |
*/ | |
import { unregisterBlockVariation, registerBlockVariation } from '@wordpress/blocks' | |
import { addFilter } from '@wordpress/hooks'; | |
import { registerPlugin } from '@wordpress/plugins'; | |
import { select, subscribe } from '@wordpress/data'; | |
/** | |
* Internal dependencies | |
*/ | |
import { isValidTemplate, getPostItems, getPostItemsForTemplate } from '../templates'; | |
import { memo } from '@wordpress/element'; | |
const VARIATION_NAME = 'my/post-template'; | |
registerBlockVariation( 'core/post-template', { | |
name: 'my/post-template', | |
title: 'Post Template', | |
description: 'Displays a single post.', | |
icon: 'post', | |
isActive: ( blockAttributes ) => { | |
return isValidTemplate( blockAttributes.layout ) | |
}, | |
isDefault: true, | |
} ); | |
const alterCorePostTemplateInnerBlocks = () => { | |
subscribe( () => { | |
// Make sure we have blocks to modify. | |
const currentBlocks = select('core/block-editor').getBlocks(); | |
if ( currentBlocks.length === 0 ) { | |
return; | |
} | |
// Find any blocks that we want to modify. Bail if there aren't any. | |
const queryBlocks = currentBlocks.filter( block => ( | |
'core/query' === block.name && | |
block.attributes?.namespace?.startsWith('my') && | |
block.innerBlocks?.length > 0 && | |
block.innerBlocks.find( iB => iB.name === 'core/post-template' ) | |
) ); | |
if ( queryBlocks.length === 0 ) { | |
return; | |
} | |
// Loop through the blocks and modify child post-template blocks. | |
queryBlocks.forEach( ( queryBlock ) => { | |
const slabTemplateName = queryBlock.attributes.namespace.replace( 'my/slab-', '' ); | |
const postItemBlocks = getPostItemsForTemplate( slabTemplateName ).map( postItem => { | |
return [ { name: 'my/post', attributes: { template: postItem } } ]; | |
} ); | |
queryBlock.innerBlocks.map( ( postTemplateBlock, index ) => { | |
// Don't replace existing my/post blocks. | |
if ( postTemplateBlock?.innerBlocks?.[0]?.name === 'my/post' ) { | |
return; | |
} | |
// postTemplateBlock.innerBlocks = postItemBlocks[ index ]; | |
postTemplateBlock.innerBlocks = [ { name: 'my/post' } ]; | |
} ); | |
} ); | |
return ''; | |
} ); | |
} | |
registerPlugin( 'my-core-post-template-innerblocks', { render: alterCorePostTemplateInnerBlocks } ); |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/** | |
* WordPress dependencies | |
*/ | |
import { unregisterBlockVariation, registerBlockVariation } from '@wordpress/blocks' | |
import { addFilter } from '@wordpress/hooks'; | |
import { registerPlugin } from '@wordpress/plugins'; | |
import { select, subscribe } from '@wordpress/data'; | |
/** | |
* Internal dependencies | |
*/ | |
import { getInnerBlocksForTemplate, getPostsPerPageForTemplate, getTemplates, getTotalPostsForTemplate } from '../templates'; | |
// Remove the default variations of the core/query block. | |
unregisterBlockVariation('core/query', 'title-date'); | |
unregisterBlockVariation('core/query', 'title-excerpt'); | |
unregisterBlockVariation('core/query', 'title-date-excerpt'); | |
unregisterBlockVariation('core/query', 'image-date-title'); | |
const VARIATION_PREFIX = 'my/slab-'; | |
const MY_SLAB_DEFAULT_QUERY = { | |
perPage: 15, // The most in any of our templates. | |
pages: 1, | |
offset: 0, | |
postType: "post", | |
order: "desc", | |
orderBy: "date", | |
author: "", | |
search: "", | |
exclude: [], | |
sticky: "", | |
inherit: true, | |
taxQuery: null, | |
parents: [], | |
format: [], | |
}; | |
const MY_DEFAULT_TEMPLATE = '4xTeaser'; | |
getTemplates().forEach( template => { | |
const variation = VARIATION_PREFIX + template.toLowerCase(); | |
registerBlockVariation( 'core/query', { | |
name: variation, | |
title: template, | |
description: 'Display a slab of posts.', | |
icon: 'welcome-widgets-menus', | |
isActive: ({namespace}) => namespace === variation, | |
isDefault: template === '4xTeaser', | |
attributes: { | |
query: { | |
...MY_SLAB_DEFAULT_QUERY, | |
perPage: getPostsPerPageForTemplate( template ), | |
}, | |
namespace: variation, | |
tagName: 'div', | |
enhancedPagination: false, | |
}, | |
scope: ['inserter', 'transform'], | |
allowedControls: [ 'inherit', 'taxQuery', 'author' ], | |
innerBlocks: [ [ 'core/post-template', { layout: template }, [ [ 'my/post' ] ] ] ], | |
} ); | |
} ); | |
const alterCoreQueryExample = ( settings, name ) => { | |
if ( 'core/query' !== name ) { | |
return settings; | |
} | |
return { | |
...settings, | |
example: { | |
attributes: { | |
namespace: VARIATION_PREFIX + MY_DEFAULT_TEMPLATE.toLowerCase(), | |
query: { | |
...MY_SLAB_DEFAULT_QUERY, | |
perPage: getPostsPerPageForTemplate( MY_DEFAULT_TEMPLATE ), | |
}, | |
}, | |
innerBlock: getInnerBlocksForTemplate( MY_DEFAULT_TEMPLATE ), | |
}, | |
} | |
} | |
addFilter( 'blocks.registerBlockType', 'my/core-query/example', alterCoreQueryExample ); | |
const alterCoreQueryPerPage = () => { | |
subscribe( () => { | |
// Make sure we have blocks to modify. | |
const currentBlocks = select('core/block-editor').getBlocks(); | |
if ( currentBlocks.length === 0 ) { | |
return; | |
} | |
// Find any blocks that we want to modify. Bail if there aren't any. | |
const blocksToModify = currentBlocks.filter( block => 'core/query' === block.name ); | |
if ( blocksToModify.length === 0 ) { | |
return; | |
} | |
// Loop through the blocks and modify. | |
blocksToModify.forEach( ( block ) => { | |
// Get the block namespace and find the corresponding template name. | |
const namespace = block.attributes.namespace; | |
const templateName = getTemplates().filter( template => namespace === VARIATION_PREFIX + template.toLowerCase() ); | |
if ( templateName.length === 0 ) { | |
return; | |
} | |
block.attributes.query.perPage = getPostsPerPageForTemplate( templateName[0] ); | |
} ); | |
return ''; | |
} ); | |
} | |
registerPlugin( 'inews-core-query-perpage', { render: alterCoreQueryPerPage } ); |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/** | |
* Retrieves the list of templates from block.json. | |
* | |
* A simple array of template names, e.g. "1xHero2xTeaser2xJot" | |
* | |
* @returns {Array} | |
*/ | |
export const getTemplates = () => { | |
return [ | |
"1xBanner4xJot", | |
"1xBanner_Black4xJot", | |
"2xHero4xTeaser", | |
"4xTeaser", | |
"1xTeaser1xJot1xHero1xTeaser1xJot", | |
"1xSuperhero2xJot1xTeaser4xJot", | |
"1xJot1xHero2xJot2xTeaser1xPuff2xJot1xPuff5xJot", | |
"1xHero2xTeaser2xJot", | |
"2xTeaser2xJot1xHero", | |
"1xPortrait3xJot", | |
"1xHeroBox4xTeaser4xJot", | |
"1xHeroJot2xJot1xTeaser4xJot1xHero" | |
]; | |
} | |
export const getPostItems = () => { | |
return [ | |
"banner", | |
"hero", | |
"herobox", | |
"herojot", | |
"jot", | |
"portrait", | |
"puff", | |
"superhero", | |
"teaser", | |
]; | |
} | |
/** | |
* Checks that a given template string is in our list of templates. | |
* | |
* @param {string} template The template name | |
* | |
* @returns {bool} True if valid, false otherwise. | |
*/ | |
export const isValidTemplate = ( template ) => { | |
return getTemplates().find( el => el === template ) !== undefined; | |
} | |
/** | |
* Provides a list of post items and how many are included in the given template | |
* | |
* @param {string} template The template name. E.g. "1xHero2xTeaser2xJot" | |
* | |
* @returns {array} An array of objects. E.g.: [ | |
* { name: 'hero', count: 1 }, | |
* { name: 'teaser', count: 2 }, | |
* { name: 'jot', count: 2 }, | |
* ] | |
*/ | |
const getPostItemsWithCountsForTemplate = ( template ) => { | |
const items = []; | |
template.matchAll(/([0-9]+x[a-zA-Z_]+)/g).forEach( ( [ item ] ) => { | |
// item will be something like 4xJot | |
// then we split it to get count (4) and name (Jot) | |
const count = parseInt( item.slice( 0, item.indexOf('x') ) ); | |
const name = item.slice( item.indexOf('x') + 1 ).toLowerCase(); | |
items.push( { name, count } ); | |
} ); | |
return items; | |
} | |
/** | |
* Provides a simple array of post item names for the given template. | |
* | |
* @param {string} template The template name. E.g. "1xHero2xTeaser2xJot" | |
* | |
* @returns {array} The list of post items. E.g. [ | |
* 'hero', 'teaser', 'teaser', 'jot', 'jot' | |
* ] | |
*/ | |
export const getPostItemsForTemplate = ( template ) => { | |
return getPostItemsWithCountsForTemplate( template ).map( item => item.name ); | |
} | |
/** | |
* Provides a simple number of posts that are included in a template. | |
* | |
* @param {string} template The template name. E.g. "1xHero2xTeaser2xJot" | |
* | |
* @returns {int} Number of posts. E.g. 5 | |
*/ | |
export const getTotalPostsForTemplate = ( template ) => { | |
return getPostItemsWithCountsForTemplate( template ) | |
.map( item => item.count ) | |
.reduce( ( acc, cur ) => acc + cur, 0 ); | |
} | |
/** | |
* Provides the block array for the given post item. | |
* | |
* @param {string} postItem The post item. E.g. 'teaser'. | |
* | |
* @returns {array} The block array. E.g. [ "my/post", { "template": "teaser" } ] | |
*/ | |
export const getPostItemBlock = ( postItem ) => { | |
return [ "my/post", { "template": postItem } ]; | |
} | |
/** | |
* Retrieves the posts_per_page parameter to use in a query for a particular template. | |
* | |
* @param {string} template The template name. E.g. "1xHero2xTeaser2xJot" | |
* | |
* @returns {int} The number of posts. E.g. 5 | |
*/ | |
export const getPostsPerPageForTemplate = ( template ) => { | |
return getTotalPostsForTemplate( template ); | |
} | |
/** | |
* Provides a block template for the given template. | |
* | |
* @param {string} template The template name. E.g. "1xHero2xTeaser2xJot" | |
* | |
* @returns {array} The block template array. E.g.: [ | |
* [ "core/post-template", { layout: "1xHero2xTeaser2xJot" }, [ "my/post", { "template": "hero" } ] ], | |
* [ "core/post-template", { layout: "1xHero2xTeaser2xJot" }, [ "my/post", { "template": "teaser" } ] ], | |
* [ "core/post-template", { layout: "1xHero2xTeaser2xJot" }, [ "my/post", { "template": "teaser" } ] ], | |
* [ "core/post-template", { layout: "1xHero2xTeaser2xJot" }, [ "my/post", { "template": "jot" } ] ], | |
* [ "core/post-template", { layout: "1xHero2xTeaser2xJot" }, [ "my/post", { "template": "jot" } ] ], | |
* ] | |
*/ | |
export const getInnerBlocksForTemplate = ( template ) => { | |
if ( ! isValidTemplate( template ) ) { | |
return []; | |
} | |
let innerBlocks = []; | |
getPostItemsWithCountsForTemplate( template ).map( ( { name, count } ) => { | |
innerBlocks = [...innerBlocks, ...Array( parseInt( count ) ).fill( | |
[ 'core/post-template', { layout: template }, [ getPostItemBlock( name ) ] ] | |
) ]; | |
} ); | |
return innerBlocks; | |
} | |
/** | |
* Provides the block array for the given position in the given template. | |
* | |
* @param {string} template The template name. E.g. "1xHero2xTeaser2xJot" | |
* @param {int} position The position. Note, the first position is `0`. E.g. 3. | |
* | |
* @returns {array} The block array. E.g. [ "my/post", { "template": "jot" } ] | |
*/ | |
export const getBlockForTemplatePosition = ( template, position ) => { | |
return getInnerBlocksForTemplate( template )[ position ]; | |
} | |
/** | |
* Provides the post item name for the given position in the given template. | |
* | |
* @param {string} template The template name. E.g. "1xHero2xTeaser2xJot" | |
* @param {int} position The position. Note, the first position is `0`. E.g. 3. | |
* | |
* @returns {string} The post item name. E.g. "jot" | |
*/ | |
export const getPostItemForTemplatePosition = ( template, position ) => { | |
return getBlockForTemplatePosition( template, position )[1].template; | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment