diff --git a/packages/block-library/src/block/edit.js b/packages/block-library/src/block/edit.js index 5efe245c935fc8..30b6137c07ed1d 100644 --- a/packages/block-library/src/block/edit.js +++ b/packages/block-library/src/block/edit.js @@ -27,7 +27,10 @@ import { store as blockEditorStore, BlockControls, } from '@wordpress/block-editor'; -import { privateApis as patternsPrivateApis } from '@wordpress/patterns'; +import { + privateApis as patternsPrivateApis, + store as patternsStore, +} from '@wordpress/patterns'; import { parse, cloneBlock } from '@wordpress/blocks'; import { RichTextData } from '@wordpress/rich-text'; @@ -38,7 +41,11 @@ import { name as patternBlockName } from './index'; import { unlock } from '../lock-unlock'; const { useLayoutClasses } = unlock( blockEditorPrivateApis ); -const { PARTIAL_SYNCING_SUPPORTED_BLOCKS } = unlock( patternsPrivateApis ); +const { + hasOverridableAttributes, + hasOverridableBlocks, + getOverridableAttributes, +} = unlock( patternsPrivateApis ); const fullAlignments = [ 'full', 'wide', 'left', 'right' ]; @@ -71,33 +78,6 @@ const useInferredLayout = ( blocks, parentLayout ) => { }, [ blocks, parentLayout ] ); }; -function hasOverridableAttributes( block ) { - return ( - Object.keys( PARTIAL_SYNCING_SUPPORTED_BLOCKS ).includes( - block.name - ) && - !! block.attributes.metadata?.bindings && - Object.values( block.attributes.metadata.bindings ).some( - ( binding ) => binding.source === 'core/pattern-overrides' - ) - ); -} - -function hasOverridableBlocks( blocks ) { - return blocks.some( ( block ) => { - if ( hasOverridableAttributes( block ) ) return true; - return hasOverridableBlocks( block.innerBlocks ); - } ); -} - -function getOverridableAttributes( block ) { - return Object.entries( block.attributes.metadata.bindings ) - .filter( - ( [ , binding ] ) => binding.source === 'core/pattern-overrides' - ) - .map( ( [ attributeKey ] ) => attributeKey ); -} - function applyInitialContentValuesToInnerBlocks( blocks, content = {}, @@ -177,22 +157,6 @@ function getContentValuesFromInnerBlocks( blocks, defaultValues ) { return Object.keys( content ).length > 0 ? content : undefined; } -function setBlockEditMode( setEditMode, blocks, mode ) { - blocks.forEach( ( block ) => { - const editMode = - mode || - ( hasOverridableAttributes( block ) ? 'contentOnly' : 'disabled' ); - setEditMode( block.clientId, editMode ); - - setBlockEditMode( - setEditMode, - block.innerBlocks, - // Disable editing for nested patterns. - block.name === patternBlockName ? 'disabled' : mode - ); - } ); -} - export default function ReusableBlockEdit( { name, attributes: { ref, content }, @@ -222,6 +186,7 @@ export default function ReusableBlockEdit( { setBlockEditingMode, } = useDispatch( blockEditorStore ); const { syncDerivedUpdates } = unlock( useDispatch( blockEditorStore ) ); + const { syncPatternEditingMode } = unlock( useDispatch( patternsStore ) ); const { innerBlocks, @@ -253,16 +218,6 @@ export default function ReusableBlockEdit( { [ patternClientId, ref ] ); - // Sync the editing mode of the pattern block with the inner blocks. - useEffect( () => { - setBlockEditMode( - setBlockEditingMode, - innerBlocks, - // Disable editing if the pattern itself is disabled. - editingMode === 'disabled' ? 'disabled' : undefined - ); - }, [ editingMode, innerBlocks, setBlockEditingMode ] ); - const canOverrideBlocks = useMemo( () => hasOverridableBlocks( innerBlocks ), [ innerBlocks ] @@ -285,16 +240,20 @@ export default function ReusableBlockEdit( { // Replace the contents of the blocks with the overrides. registry.batch( () => { setBlockEditingMode( patternClientId, 'default' ); - syncDerivedUpdates( () => { - replaceInnerBlocks( - patternClientId, - applyInitialContentValuesToInnerBlocks( - initialBlocks, - initialContent.current, - defaultContent.current - ) + const blocksWithInitialContent = + applyInitialContentValuesToInnerBlocks( + initialBlocks, + initialContent.current, + defaultContent.current ); + syncDerivedUpdates( () => { + replaceInnerBlocks( patternClientId, blocksWithInitialContent ); } ); + syncPatternEditingMode( + patternClientId, + // Disable editing if the pattern itself is disabled. + originalEditingMode === 'disabled' ? 'disabled' : undefined + ); setBlockEditingMode( patternClientId, originalEditingMode ); } ); }, [ @@ -306,8 +265,18 @@ export default function ReusableBlockEdit( { getBlockEditingMode, setBlockEditingMode, syncDerivedUpdates, + syncPatternEditingMode, ] ); + // Sync the editing mode of the pattern block with the inner blocks. + useEffect( () => { + syncPatternEditingMode( + patternClientId, + // Disable editing if the pattern itself is disabled. + editingMode === 'disabled' ? 'disabled' : undefined + ); + }, [ editingMode, patternClientId, syncPatternEditingMode ] ); + const { alignment, layout } = useInferredLayout( innerBlocks, parentLayout diff --git a/packages/patterns/src/private-apis.js b/packages/patterns/src/private-apis.js index a5fbddb62fd62c..1b5ccc7c9c3647 100644 --- a/packages/patterns/src/private-apis.js +++ b/packages/patterns/src/private-apis.js @@ -24,6 +24,11 @@ import { PATTERN_SYNC_TYPES, PARTIAL_SYNCING_SUPPORTED_BLOCKS, } from './constants'; +import { + hasOverridableAttributes, + hasOverridableBlocks, + getOverridableAttributes, +} from './utils'; export const privateApis = {}; lock( privateApis, { @@ -43,4 +48,7 @@ lock( privateApis, { EXCLUDED_PATTERN_SOURCES, PATTERN_SYNC_TYPES, PARTIAL_SYNCING_SUPPORTED_BLOCKS, + hasOverridableAttributes, + hasOverridableBlocks, + getOverridableAttributes, } ); diff --git a/packages/patterns/src/store/actions.js b/packages/patterns/src/store/actions.js index 7b9090ae4de66c..aff1c96dfcfed7 100644 --- a/packages/patterns/src/store/actions.js +++ b/packages/patterns/src/store/actions.js @@ -10,6 +10,7 @@ import { store as blockEditorStore } from '@wordpress/block-editor'; * Internal dependencies */ import { PATTERN_SYNC_TYPES } from '../constants'; +import { hasOverridableAttributes } from '../utils'; /** * Returns a generator converting one or more static blocks into a pattern, or creating a new empty pattern. @@ -137,3 +138,38 @@ export function setEditingPattern( clientId, isEditing ) { isEditing, }; } + +/** + * Returns a generator converting a synced pattern block into a static block. + * + * @param {string} clientId The client ID of the pattern block. + * @param {string} [mode] The explicit editing mode to set if defined. + */ +export const syncPatternEditingMode = + ( clientId, mode ) => + ( { registry } ) => { + const patternBlocks = registry + .select( blockEditorStore ) + .getBlocks( clientId ); + const { setBlockEditingMode } = registry.dispatch( blockEditorStore ); + + function recursivelySetEditMode( blocks ) { + blocks.forEach( ( block ) => { + const editMode = + mode || + ( hasOverridableAttributes( block ) + ? 'contentOnly' + : 'disabled' ); + setBlockEditingMode( block.clientId, editMode ); + recursivelySetEditMode( + block.innerBlocks, + // Disable editing for nested patterns. + block.name === 'core/block' ? 'disabled' : mode + ); + } ); + } + + registry.batch( () => { + recursivelySetEditMode( patternBlocks ); + } ); + }; diff --git a/packages/patterns/src/utils.js b/packages/patterns/src/utils.js new file mode 100644 index 00000000000000..30bc6123b7eef0 --- /dev/null +++ b/packages/patterns/src/utils.js @@ -0,0 +1,31 @@ +/** + * Internal dependencies + */ +import { PARTIAL_SYNCING_SUPPORTED_BLOCKS } from './constants'; + +export function hasOverridableAttributes( block ) { + return ( + Object.keys( PARTIAL_SYNCING_SUPPORTED_BLOCKS ).includes( + block.name + ) && + !! block.attributes.metadata?.bindings && + Object.values( block.attributes.metadata.bindings ).some( + ( binding ) => binding.source === 'core/pattern-overrides' + ) + ); +} + +export function hasOverridableBlocks( blocks ) { + return blocks.some( ( block ) => { + if ( hasOverridableAttributes( block ) ) return true; + return hasOverridableBlocks( block.innerBlocks ); + } ); +} + +export function getOverridableAttributes( block ) { + return Object.entries( block.attributes.metadata.bindings ) + .filter( + ( [ , binding ] ) => binding.source === 'core/pattern-overrides' + ) + .map( ( [ attributeKey ] ) => attributeKey ); +}