diff --git a/packages/blocks/README.md b/packages/blocks/README.md index e01008015e2a47..40861862585739 100644 --- a/packages/blocks/README.md +++ b/packages/blocks/README.md @@ -190,24 +190,6 @@ _Returns_ - `?Object`: Block type. -### getBlockTypeActiveVariation - -Returns the active block variation for a given block based on its attributes. Variations are determined by their `isActive` property. Which is either an array of block attribute keys or a function. - -In case of an array of block attribute keys, the `attributes` are compared to the variation's attributes using strict equality check. - -In case of function type, the function should accept a block's attributes and the variation's attributes and determines if a variation is active. A function that accepts a block's attributes and the variation's attributes and determines if a variation is active. - -_Parameters_ - -- _variations_ `Array`: Data state. -- _blockType_ `Object`: Name of block (example: “core/columns”). -- _attributes_ `Object`: Block attributes used to determine active variation. - -_Returns_ - -- `(WPBlockVariation|undefined)`: Active block variation. - ### getBlockTypes Returns all registered blocks. diff --git a/packages/blocks/src/store/selectors.js b/packages/blocks/src/store/selectors.js index fd32cb184763a9..72a49d3c9748d2 100644 --- a/packages/blocks/src/store/selectors.js +++ b/packages/blocks/src/store/selectors.js @@ -7,11 +7,12 @@ import removeAccents from 'remove-accents'; * WordPress dependencies */ import { createSelector } from '@wordpress/data'; +import { RichTextData } from '@wordpress/rich-text'; /** * Internal dependencies */ -import { getValueFromObjectPath, getBlockTypeActiveVariation } from './utils'; +import { getValueFromObjectPath, matchesAttributes } from './utils'; /** @typedef {import('../api/registration').WPBlockVariation} WPBlockVariation */ /** @typedef {import('../api/registration').WPBlockVariationScope} WPBlockVariationScope */ @@ -241,8 +242,57 @@ export function getActiveBlockVariation( state, blockName, attributes, scope ) { } const blockType = getBlockType( state, blockName ); - - return getBlockTypeActiveVariation( variations, blockType, attributes ); + const attributeKeys = Object.keys( blockType?.attributes || {} ); + let match; + let maxMatchedAttributes = 0; + + for ( const variation of variations ) { + if ( Array.isArray( variation.isActive ) ) { + const definedAttributes = variation.isActive.filter( + ( attribute ) => { + // We support nested attribute paths, e.g. `layout.type`. + // In this case, we need to check if the part before the + // first dot is a known attribute. + const topLevelAttribute = attribute.split( '.' )[ 0 ]; + return attributeKeys.includes( topLevelAttribute ); + } + ); + const definedAttributesLength = definedAttributes.length; + if ( definedAttributesLength === 0 ) { + continue; + } + const isMatch = definedAttributes.every( ( attribute ) => { + const variationAttributeValue = getValueFromObjectPath( + variation.attributes, + attribute + ); + if ( variationAttributeValue === undefined ) { + return false; + } + let blockAttributeValue = getValueFromObjectPath( + attributes, + attribute + ); + if ( blockAttributeValue instanceof RichTextData ) { + blockAttributeValue = blockAttributeValue.toHTMLString(); + } + return matchesAttributes( + blockAttributeValue, + variationAttributeValue + ); + } ); + if ( isMatch && definedAttributesLength > maxMatchedAttributes ) { + match = variation; + maxMatchedAttributes = definedAttributesLength; + } + } else if ( variation.isActive?.( attributes, variation.attributes ) ) { + // If isActive is a function, we cannot know how many attributes it matches. + // This means that we cannot compare the specificity of our matches, + // and simply return the best match we have found. + return match || variation; + } + } + return match; } /** diff --git a/packages/blocks/src/store/utils.js b/packages/blocks/src/store/utils.js index 8ddee4f52e838d..b5e9c9dbac40c8 100644 --- a/packages/blocks/src/store/utils.js +++ b/packages/blocks/src/store/utils.js @@ -1,8 +1,3 @@ -/** - * WordPress dependencies - */ -import { RichTextData } from '@wordpress/rich-text'; - /** * Helper util to return a value from a certain path of the object. * Path is specified as either: @@ -54,80 +49,3 @@ export function matchesAttributes( blockAttributes, variationAttributes ) { return blockAttributes === variationAttributes; } -/** @typedef {import('../api/registration').WPBlockVariation} WPBlockVariation */ - -/** - * Returns the active block variation for a given block based on its attributes. - * Variations are determined by their `isActive` property. - * Which is either an array of block attribute keys or a function. - * - * In case of an array of block attribute keys, the `attributes` are compared - * to the variation's attributes using strict equality check. - * - * In case of function type, the function should accept a block's attributes - * and the variation's attributes and determines if a variation is active. - * A function that accepts a block's attributes and the variation's attributes and determines if a variation is active. - * - * @param {Array} variations Data state. - * @param {Object} blockType Name of block (example: “core/columns”). - * @param {Object} attributes Block attributes used to determine active variation. - * - * @return {(WPBlockVariation|undefined)} Active block variation. - */ -export function getBlockTypeActiveVariation( - variations, - blockType, - attributes -) { - const attributeKeys = Object.keys( blockType?.attributes || {} ); - let match; - let maxMatchedAttributes = 0; - - for ( const variation of variations ) { - if ( Array.isArray( variation.isActive ) ) { - const definedAttributes = variation.isActive.filter( - ( attribute ) => { - // We support nested attribute paths, e.g. `layout.type`. - // In this case, we need to check if the part before the - // first dot is a known attribute. - const topLevelAttribute = attribute.split( '.' )[ 0 ]; - return attributeKeys.includes( topLevelAttribute ); - } - ); - const definedAttributesLength = definedAttributes.length; - if ( definedAttributesLength === 0 ) { - continue; - } - const isMatch = definedAttributes.every( ( attribute ) => { - const variationAttributeValue = getValueFromObjectPath( - variation.attributes, - attribute - ); - if ( variationAttributeValue === undefined ) { - return false; - } - let blockAttributeValue = getValueFromObjectPath( - attributes, - attribute - ); - if ( blockAttributeValue instanceof RichTextData ) { - blockAttributeValue = blockAttributeValue.toHTMLString(); - } - return matchesAttributes( - blockAttributeValue, - variationAttributeValue - ); - } ); - if ( isMatch && definedAttributesLength > maxMatchedAttributes ) { - match = variation; - maxMatchedAttributes = definedAttributesLength; - } - } else if ( variation.isActive?.( attributes, variation.attributes ) ) { - // If isActive is a function, we cannot know how many attributes it matches. - // This means that we cannot compare the specificity of our matches, - // and simply return the best match we have found. - return match || variation; - } - } - return match; -}