diff --git a/packages/block-library/package.json b/packages/block-library/package.json index 6ade355e846c67..cce804ef3d8e21 100644 --- a/packages/block-library/package.json +++ b/packages/block-library/package.json @@ -27,7 +27,8 @@ "sideEffects": [ "build-style/**", "src/**/*.scss", - "src/navigation-link/index.js" + "src/navigation-link/index.js", + "src/template-part/index.js" ], "dependencies": { "@babel/runtime": "^7.13.10", diff --git a/packages/block-library/src/template-part/fallback-variations.js b/packages/block-library/src/template-part/fallback-variations.js new file mode 100644 index 00000000000000..089ec29f0bbfab --- /dev/null +++ b/packages/block-library/src/template-part/fallback-variations.js @@ -0,0 +1,51 @@ +/** + * WordPress dependencies + */ +import { __ } from '@wordpress/i18n'; +import { header as headerIcon, footer as footerIcon } from '@wordpress/icons'; +import { store as coreDataStore } from '@wordpress/core-data'; +import { select } from '@wordpress/data'; + +const fallbackVariations = [ + { + name: 'header', + icon: headerIcon, + title: __( 'Header' ), + description: __( + 'The Header template defines a page area that typically contains a title, logo, and main navigation.' + ), + attributes: { area: 'header' }, + scope: [ 'inserter' ], + }, + { + name: 'footer', + icon: footerIcon, + title: __( 'Footer' ), + description: __( + 'The Footer template defines a page area that typically contains site credits, social links, or any other combination of blocks.' + ), + attributes: { area: 'footer' }, + scope: [ 'inserter' ], + }, +]; + +fallbackVariations.forEach( ( variation ) => { + if ( variation.isActive ) return; + variation.isActive = ( blockAttributes, variationAttributes ) => { + const { area, theme, slug } = blockAttributes; + // We first check the `area` block attribute which is set during insertion. + // This property is removed on the creation of a template part. + if ( area ) return area === variationAttributes.area; + // Find a matching variation from the created template part + // by checking the entity's `area` property. + if ( ! slug ) return false; + const entity = select( coreDataStore ).getEntityRecord( + 'postType', + 'wp_template_part', + `${ theme }//${ slug }` + ); + return entity?.area === variationAttributes.area; + }; +} ); + +export default fallbackVariations; diff --git a/packages/block-library/src/template-part/index.js b/packages/block-library/src/template-part/index.js index 1ae06629156c97..673ca4586e5aad 100644 --- a/packages/block-library/src/template-part/index.js +++ b/packages/block-library/src/template-part/index.js @@ -9,13 +9,14 @@ import { startCase } from 'lodash'; import { store as coreDataStore } from '@wordpress/core-data'; import { select } from '@wordpress/data'; import { layout } from '@wordpress/icons'; +import { addFilter } from '@wordpress/hooks'; /** * Internal dependencies */ import metadata from './block.json'; import edit from './edit'; -import './variations'; +import { enhanceTemplatePartVariations } from './variations'; const { name } = metadata; export { metadata, name }; @@ -42,3 +43,10 @@ export const settings = { }, edit, }; + +// Importing this file includes side effects. This is whitelisted in block-library/package.json under sideEffects +addFilter( + 'blocks.registerBlockType', + 'core/template-part', + enhanceTemplatePartVariations +); diff --git a/packages/block-library/src/template-part/index.php b/packages/block-library/src/template-part/index.php index b81a9f0ede5b01..15fc8b93c95e6c 100644 --- a/packages/block-library/src/template-part/index.php +++ b/packages/block-library/src/template-part/index.php @@ -125,6 +125,31 @@ function render_block_core_template_part( $attributes ) { return "<$html_tag $wrapper_attributes>" . str_replace( ']]>', ']]>', $content ) . ""; } +/** + * Returns an array of variation objects for the template part block. + * + * @return array Array containing the block variation objects. + */ +function build_template_part_block_variations() { + $variations = array(); + $defined_areas = gutenberg_get_allowed_template_part_areas(); + foreach ( $defined_areas as $area ) { + if ( 'uncategorized' !== $area['area'] ) { + $variations[] = array( + 'name' => $area['area'], + 'title' => $area['label'], + 'description' => $area['description'], + 'attributes' => array( + 'area' => $area['area'], + ), + 'scope' => array( 'inserter' ), + 'icon' => $area['icon'], + ); + } + } + return $variations; +} + /** * Registers the `core/template-part` block on the server. */ @@ -133,6 +158,7 @@ function register_block_core_template_part() { __DIR__ . '/template-part', array( 'render_callback' => 'render_block_core_template_part', + 'variations' => build_template_part_block_variations(), ) ); } diff --git a/packages/block-library/src/template-part/variations.js b/packages/block-library/src/template-part/variations.js index ef7e09e86405cd..da41d03f22339d 100644 --- a/packages/block-library/src/template-part/variations.js +++ b/packages/block-library/src/template-part/variations.js @@ -2,41 +2,43 @@ * WordPress dependencies */ import { store as coreDataStore } from '@wordpress/core-data'; -import { store as editorStore } from '@wordpress/editor'; -import { store as blocksStore } from '@wordpress/blocks'; -import { dispatch, select, subscribe } from '@wordpress/data'; +import { select } from '@wordpress/data'; +import { + header as headerIcon, + footer as footerIcon, + sidebar as sidebarIcon, + layout as layoutIcon, +} from '@wordpress/icons'; -const unsubscribe = subscribe( () => { - const definedVariations = select( - editorStore - ).__experimentalGetDefaultTemplatePartAreas(); +/** + * Internal dependencies + */ +import fallbackVariations from './fallback-variations'; - if ( ! definedVariations?.length ) { - return; +function getTemplatePartIcon( iconName ) { + if ( 'header' === iconName ) { + return headerIcon; + } else if ( 'footer' === iconName ) { + return footerIcon; + } else if ( 'sidebar' === iconName ) { + return sidebarIcon; } - unsubscribe(); + return layoutIcon; +} - const variations = definedVariations - .filter( ( { area } ) => 'uncategorized' !== area ) - .map( ( { area, label, description, icon } ) => { - return { - name: area, - title: label, - description, - icon, - attributes: { area }, - scope: [ 'inserter' ], - }; - } ); +export function enhanceTemplatePartVariations( settings, name ) { + if ( name !== 'core/template-part' ) { + return settings; + } + + // WordPress versions pre-5.8 do not support server side variation registration. + // So we must register the fallback variations until those versions are no longer supported. + if ( ! ( settings.variations && settings.variations.length ) ) { + return { ...settings, variations: fallbackVariations }; + } - /** - * Add `isActive` function to all `Template Part` variations, if not defined. - * `isActive` function is used to find a variation match from a created - * Block by providing its attributes. - */ - variations.forEach( ( variation ) => { - if ( variation.isActive ) return; - variation.isActive = ( blockAttributes, variationAttributes ) => { + if ( settings.variations ) { + const isActive = ( blockAttributes, variationAttributes ) => { const { area, theme, slug } = blockAttributes; // We first check the `area` block attribute which is set during insertion. // This property is removed on the creation of a template part. @@ -51,10 +53,21 @@ const unsubscribe = subscribe( () => { ); return entity?.area === variationAttributes.area; }; - } ); - dispatch( blocksStore ).addBlockVariations( - 'core/template-part', - variations - ); -} ); + const variations = settings.variations.map( ( variation ) => { + return { + ...variation, + ...( ! variation.isActive && { isActive } ), + ...( typeof variation.icon === 'string' && { + icon: getTemplatePartIcon( variation.icon ), + } ), + }; + } ); + + return { + ...settings, + variations, + }; + } + return settings; +} diff --git a/packages/editor/src/store/utils/get-template-part-icon.js b/packages/editor/src/store/utils/get-template-part-icon.js index e6f846519b3b26..1e5c052b0e35de 100644 --- a/packages/editor/src/store/utils/get-template-part-icon.js +++ b/packages/editor/src/store/utils/get-template-part-icon.js @@ -1,7 +1,12 @@ /** * WordPress dependencies */ -import * as icons from '@wordpress/icons'; +import { + header as headerIcon, + footer as footerIcon, + sidebar as sidebarIcon, + layout as layoutIcon, +} from '@wordpress/icons'; /** * Helper function to retrieve the corresponding icon by name. * @@ -10,5 +15,12 @@ import * as icons from '@wordpress/icons'; * @return {Object} The corresponding icon. */ export function getTemplatePartIcon( iconName ) { - return icons[ iconName ] || icons.layout; + if ( 'header' === iconName ) { + return headerIcon; + } else if ( 'footer' === iconName ) { + return footerIcon; + } else if ( 'sidebar' === iconName ) { + return sidebarIcon; + } + return layoutIcon; }