@wordpress/block-library
Version:
Block library for the WordPress editor.
163 lines (149 loc) • 5 kB
JavaScript
/**
* WordPress dependencies
*/
import { cloneBlock } from '@wordpress/blocks';
import { useSelect, useDispatch, useRegistry } from '@wordpress/data';
import { useState, useEffect } from '@wordpress/element';
import {
Warning,
store as blockEditorStore,
useBlockProps,
} from '@wordpress/block-editor';
import { store as coreStore } from '@wordpress/core-data';
import { __, sprintf } from '@wordpress/i18n';
/**
* Internal dependencies
*/
import { useParsePatternDependencies } from './recursion-detector';
const PatternEdit = ( { attributes, clientId } ) => {
const registry = useRegistry();
const selectedPattern = useSelect(
( select ) =>
select( blockEditorStore ).__experimentalGetParsedPattern(
attributes.slug
),
[ attributes.slug ]
);
const currentThemeStylesheet = useSelect(
( select ) => select( coreStore ).getCurrentTheme()?.stylesheet,
[]
);
const {
replaceBlocks,
setBlockEditingMode,
__unstableMarkNextChangeAsNotPersistent,
} = useDispatch( blockEditorStore );
const { getBlockRootClientId, getBlockEditingMode } =
useSelect( blockEditorStore );
const [ hasRecursionError, setHasRecursionError ] = useState( false );
const parsePatternDependencies = useParsePatternDependencies();
// Duplicated in packages/editor/src/components/start-template-options/index.js.
function injectThemeAttributeInBlockTemplateContent( block ) {
if (
block.innerBlocks.find(
( innerBlock ) => innerBlock.name === 'core/template-part'
)
) {
block.innerBlocks = block.innerBlocks.map( ( innerBlock ) => {
if (
innerBlock.name === 'core/template-part' &&
innerBlock.attributes.theme === undefined
) {
innerBlock.attributes.theme = currentThemeStylesheet;
}
return innerBlock;
} );
}
if (
block.name === 'core/template-part' &&
block.attributes.theme === undefined
) {
block.attributes.theme = currentThemeStylesheet;
}
return block;
}
// Run this effect when the component loads.
// This adds the Pattern's contents to the post.
// This change won't be saved.
// It will continue to pull from the pattern file unless changes are made to its respective template part.
useEffect( () => {
if ( ! hasRecursionError && selectedPattern?.blocks ) {
try {
parsePatternDependencies( selectedPattern );
} catch ( error ) {
setHasRecursionError( true );
return;
}
// We batch updates to block list settings to avoid triggering cascading renders
// for each container block included in a tree and optimize initial render.
// Since the above uses microtasks, we need to use a microtask here as well,
// because nested pattern blocks cannot be inserted if the parent block supports
// inner blocks but doesn't have blockSettings in the state.
window.queueMicrotask( () => {
const rootClientId = getBlockRootClientId( clientId );
// Clone blocks from the pattern before insertion to ensure they receive
// distinct client ids. See https://github.com/WordPress/gutenberg/issues/50628.
const clonedBlocks = selectedPattern.blocks.map( ( block ) =>
cloneBlock(
injectThemeAttributeInBlockTemplateContent( block )
)
);
// If the pattern has a single block and categories, we should add the
// categories of the pattern to the block's metadata.
if (
clonedBlocks.length === 1 &&
selectedPattern.categories?.length > 0
) {
clonedBlocks[ 0 ].attributes = {
...clonedBlocks[ 0 ].attributes,
metadata: {
...clonedBlocks[ 0 ].attributes.metadata,
categories: selectedPattern.categories,
patternName: selectedPattern.name,
name:
clonedBlocks[ 0 ].attributes.metadata.name ||
selectedPattern.title,
},
};
}
const rootEditingMode = getBlockEditingMode( rootClientId );
registry.batch( () => {
// Temporarily set the root block to default mode to allow replacing the pattern.
// This could happen when the page is disabling edits of non-content blocks.
__unstableMarkNextChangeAsNotPersistent();
setBlockEditingMode( rootClientId, 'default' );
__unstableMarkNextChangeAsNotPersistent();
replaceBlocks( clientId, clonedBlocks );
// Restore the root block's original mode.
__unstableMarkNextChangeAsNotPersistent();
setBlockEditingMode( rootClientId, rootEditingMode );
} );
} );
}
}, [
clientId,
hasRecursionError,
selectedPattern,
__unstableMarkNextChangeAsNotPersistent,
replaceBlocks,
getBlockEditingMode,
setBlockEditingMode,
getBlockRootClientId,
] );
const props = useBlockProps();
if ( hasRecursionError ) {
return (
<div { ...props }>
<Warning>
{ sprintf(
// translators: A warning in which %s is the name of a pattern.
__( 'Pattern "%s" cannot be rendered inside itself.' ),
selectedPattern?.name
) }
</Warning>
</div>
);
}
return <div { ...props } />;
};
export default PatternEdit;