diff --git a/docs/reference-guides/core-blocks.md b/docs/reference-guides/core-blocks.md index 60147a5bac7fd8..8ae0ede80e91c3 100644 --- a/docs/reference-guides/core-blocks.md +++ b/docs/reference-guides/core-blocks.md @@ -456,7 +456,7 @@ Show a block pattern. ([Source](https://github.com/WordPress/gutenberg/tree/trun - **Name:** core/pattern - **Category:** theme - **Supports:** ~~html~~, ~~inserter~~ -- **Attributes:** slug +- **Attributes:** slug, templateLock ## Post Author diff --git a/packages/block-library/src/pattern/block.json b/packages/block-library/src/pattern/block.json index da023142403c87..266e0f81afa47e 100644 --- a/packages/block-library/src/pattern/block.json +++ b/packages/block-library/src/pattern/block.json @@ -13,6 +13,10 @@ "attributes": { "slug": { "type": "string" + }, + "templateLock": { + "type": [ "string", "boolean" ], + "enum": [ "all", "insert", "contentOnly", false ] } } } diff --git a/packages/block-library/src/pattern/edit.js b/packages/block-library/src/pattern/edit.js index be0b778eb4ae19..bcf5c74097db98 100644 --- a/packages/block-library/src/pattern/edit.js +++ b/packages/block-library/src/pattern/edit.js @@ -6,16 +6,43 @@ import { useEffect } from '@wordpress/element'; import { store as blockEditorStore, useBlockProps, + useInnerBlocksProps, + BlockControls, } from '@wordpress/block-editor'; +import { ToolbarButton } from '@wordpress/components'; +import { __ } from '@wordpress/i18n'; -const PatternEdit = ( { attributes, clientId } ) => { - const selectedPattern = useSelect( - ( select ) => - select( blockEditorStore ).__experimentalGetParsedPattern( - attributes.slug - ), - [ attributes.slug ] - ); +const PatternEdit = ( { attributes, clientId, setAttributes } ) => { + const { selectedPattern, isContentLocked, patternsInSameCategories } = + useSelect( + ( select ) => { + const { + __experimentalGetParsedPattern: getParsedPattern, + __unstableGetContentLockingParent: getContentLockingParent, + __experimentalGetAllowedPatterns: getAllowedPatterns, + } = select( blockEditorStore ); + const _selectedPattern = getParsedPattern( attributes.slug ); + return { + selectedPattern: _selectedPattern, + isContentLocked: + ! getContentLockingParent( clientId ) && + attributes.templateLock === 'contentOnly', + patternsInSameCategories: getAllowedPatterns().filter( + ( pattern ) => + pattern.name !== _selectedPattern.name && + pattern.categories?.length > 0 && + pattern.categories?.some( + ( category ) => + _selectedPattern.categories?.length > 0 && + _selectedPattern.categories?.includes( + category + ) + ) + ), + }; + }, + [ attributes.slug, attributes.templateLock, clientId ] + ); const { replaceBlocks, __unstableMarkNextChangeAsNotPersistent } = useDispatch( blockEditorStore ); @@ -25,7 +52,7 @@ const PatternEdit = ( { attributes, clientId } ) => { // 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 ( selectedPattern?.blocks ) { + if ( ! isContentLocked && selectedPattern?.blocks ) { // 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, @@ -38,9 +65,41 @@ const PatternEdit = ( { attributes, clientId } ) => { } }, [ clientId, selectedPattern?.blocks ] ); - const props = useBlockProps(); + const blockProps = useBlockProps(); + const innerBlockProps = useInnerBlocksProps( blockProps, { + templateLock: attributes.templateLock, + onInput: () => {}, + onChange: ( blocks ) => { + replaceBlocks( clientId, blocks ); + }, + value: selectedPattern?.blocks ?? [], + } ); + + const canShuffle = isContentLocked && patternsInSameCategories.length > 0; + + function shuffle() { + const randomIndex = Math.floor( + // We explicitly want the randomness here for shuffling. + // eslint-disable-next-line no-restricted-syntax + Math.random() * patternsInSameCategories.length + ); + const nextPattern = patternsInSameCategories[ randomIndex ]; - return
; + setAttributes( { slug: nextPattern.name } ); + } + + return ( + <> + + { canShuffle && ( +