UNPKG

@wordpress/block-editor

Version:
120 lines (113 loc) 3.62 kB
/** * WordPress dependencies */ import { useMemo } from '@wordpress/element'; import { cloneBlock } from '@wordpress/blocks'; /** * Internal dependencies */ import { getMatchingBlockByName, getRetainedBlockAttributes } from './utils'; /** * Mutate the matched block's attributes by getting * which block type's attributes to retain and prioritize * them in the merging of the attributes. * * @param {WPBlock} match The matched block. * @param {WPBlock} selectedBlock The selected block. * @return {void} */ export const transformMatchingBlock = ( match, selectedBlock ) => { // Get the block attributes to retain through the transformation. const retainedBlockAttributes = getRetainedBlockAttributes( selectedBlock.name, selectedBlock.attributes ); match.attributes = { ...match.attributes, ...retainedBlockAttributes, }; }; /** * By providing the selected blocks and pattern's blocks * find the matching blocks, transform them and return them. * If not all selected blocks are matched, return nothing. * * @param {WPBlock[]} selectedBlocks The selected blocks. * @param {WPBlock[]} patternBlocks The pattern's blocks. * @return {WPBlock[]|void} The transformed pattern's blocks or undefined if not all selected blocks have been matched. */ export const getPatternTransformedBlocks = ( selectedBlocks, patternBlocks ) => { // Clone Pattern's blocks to produce new clientIds and be able to mutate the matches. const _patternBlocks = patternBlocks.map( ( block ) => cloneBlock( block ) ); /** * Keep track of the consumed pattern blocks. * This is needed because we loop the selected blocks * and for example we may have selected two paragraphs and * the pattern's blocks could have more `paragraphs`. */ const consumedBlocks = new Set(); for ( const selectedBlock of selectedBlocks ) { let isMatch = false; for ( const patternBlock of _patternBlocks ) { const match = getMatchingBlockByName( patternBlock, selectedBlock.name, consumedBlocks ); if ( ! match ) { continue; } isMatch = true; consumedBlocks.add( match.clientId ); // We update (mutate) the matching pattern block. transformMatchingBlock( match, selectedBlock ); // No need to loop through other pattern's blocks. break; } // Bail early if a selected block has not been matched. if ( ! isMatch ) { return; } } return _patternBlocks; }; /** * @typedef {WPBlockPattern & {transformedBlocks: WPBlock[]}} TransformedBlockPattern */ /** * Custom hook that accepts patterns from state and the selected * blocks and tries to match these with the pattern's blocks. * If all selected blocks are matched with a Pattern's block, * we transform them by retaining block's attributes with `role:content`. * The transformed pattern's blocks are set to a new pattern * property `transformedBlocks`. * * @param {WPBlockPattern[]} patterns Patterns from state. * @param {WPBlock[]} selectedBlocks The currently selected blocks. * @return {TransformedBlockPattern[]} Returns the eligible matched patterns with all the selected blocks. */ const useTransformedPatterns = ( patterns, selectedBlocks ) => { return useMemo( () => patterns.reduce( ( accumulator, _pattern ) => { const transformedBlocks = getPatternTransformedBlocks( selectedBlocks, _pattern.blocks ); if ( transformedBlocks ) { accumulator.push( { ..._pattern, transformedBlocks, } ); } return accumulator; }, [] ), [ patterns, selectedBlocks ] ); }; export default useTransformedPatterns;