-
Notifications
You must be signed in to change notification settings - Fork 4.2k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Pattern block: Add experimental flag and syncStatus attrib to allow testing of partial syncing #50533
Pattern block: Add experimental flag and syncStatus attrib to allow testing of partial syncing #50533
Changes from 5 commits
c6e6662
f7c1418
b4e01bb
39efc21
6016dd7
f7df5d9
cc90aee
6fd82a7
f542328
a888f55
31427d4
e580bc3
e286463
538eeeb
4ac3f8e
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,46 +1,121 @@ | ||
/** | ||
* WordPress dependencies | ||
*/ | ||
import { cloneBlock } from '@wordpress/blocks'; | ||
import { useSelect, useDispatch } from '@wordpress/data'; | ||
import { useEffect } from '@wordpress/element'; | ||
import { | ||
store as blockEditorStore, | ||
useBlockProps, | ||
useInnerBlocksProps, | ||
} from '@wordpress/block-editor'; | ||
|
||
const PatternEdit = ( { attributes, clientId } ) => { | ||
const selectedPattern = useSelect( | ||
( select ) => | ||
select( blockEditorStore ).__experimentalGetParsedPattern( | ||
attributes.slug | ||
), | ||
[ attributes.slug ] | ||
const PatternEdit = ( { attributes, clientId, setAttributes } ) => { | ||
const { inheritedAlignment, slug, syncStatus } = attributes; | ||
const { selectedPattern, innerBlocks } = useSelect( | ||
( select ) => { | ||
return { | ||
selectedPattern: | ||
select( blockEditorStore ).__experimentalGetParsedPattern( | ||
slug | ||
), | ||
innerBlocks: | ||
select( blockEditorStore ).getBlock( clientId ) | ||
?.innerBlocks, | ||
}; | ||
}, | ||
[ slug, clientId ] | ||
); | ||
|
||
const { replaceBlocks, __unstableMarkNextChangeAsNotPersistent } = | ||
useDispatch( blockEditorStore ); | ||
const { | ||
replaceBlocks, | ||
replaceInnerBlocks, | ||
__unstableMarkNextChangeAsNotPersistent, | ||
} = useDispatch( blockEditorStore ); | ||
|
||
// 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 ( selectedPattern?.blocks ) { | ||
if ( selectedPattern?.blocks && ! innerBlocks?.length ) { | ||
// 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( () => { | ||
__unstableMarkNextChangeAsNotPersistent(); | ||
if ( syncStatus === 'partial' ) { | ||
replaceInnerBlocks( | ||
clientId, | ||
selectedPattern.blocks.map( ( block ) => | ||
cloneBlock( block ) | ||
) | ||
); | ||
return; | ||
} | ||
replaceBlocks( clientId, selectedPattern.blocks ); | ||
} ); | ||
} | ||
}, [ clientId, selectedPattern?.blocks ] ); | ||
}, [ | ||
clientId, | ||
selectedPattern?.blocks, | ||
replaceInnerBlocks, | ||
__unstableMarkNextChangeAsNotPersistent, | ||
innerBlocks, | ||
syncStatus, | ||
replaceBlocks, | ||
] ); | ||
|
||
useEffect( () => { | ||
if ( syncStatus !== 'partial' ) { | ||
return; | ||
} | ||
const alignments = [ 'wide', 'full' ]; | ||
const blocks = innerBlocks; | ||
if ( ! blocks || blocks.length === 0 ) { | ||
return; | ||
} | ||
// Determine the widest setting of all the contained blocks. | ||
const widestAlignment = blocks.reduce( ( accumulator, block ) => { | ||
const { align } = block.attributes; | ||
return alignments.indexOf( align ) > | ||
alignments.indexOf( accumulator ) | ||
? align | ||
: accumulator; | ||
}, undefined ); | ||
|
||
const props = useBlockProps(); | ||
// Set the attribute of the Pattern block to match the widest | ||
// alignment. | ||
setAttributes( { | ||
glendaviesnz marked this conversation as resolved.
Show resolved
Hide resolved
|
||
inheritedAlignment: widestAlignment ?? '', | ||
} ); | ||
}, [ | ||
innerBlocks, | ||
selectedPattern?.blocks, | ||
setAttributes, | ||
inheritedAlignment, | ||
syncStatus, | ||
] ); | ||
|
||
return <div { ...props } />; | ||
const blockProps = useBlockProps( { | ||
className: | ||
syncStatus === 'partial' && | ||
inheritedAlignment && | ||
`align${ inheritedAlignment }`, | ||
} ); | ||
|
||
const innerBlocksProps = useInnerBlocksProps( blockProps, { | ||
templateLock: syncStatus === 'partial' ? 'contentOnly' : false, | ||
} ); | ||
|
||
if ( syncStatus !== 'partial' ) { | ||
return <div { ...blockProps } />; | ||
} | ||
|
||
return ( | ||
<> | ||
glendaviesnz marked this conversation as resolved.
Show resolved
Hide resolved
|
||
<div { ...innerBlocksProps } /> | ||
</> | ||
); | ||
}; | ||
|
||
export default PatternEdit; |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -22,23 +22,31 @@ function register_block_core_pattern() { | |
/** | ||
* Renders the `core/pattern` block on the server. | ||
* | ||
* @param array $attributes Block attributes. | ||
* @param array $attributes Block attributes. | ||
* @param string $content The block rendered content. | ||
* | ||
* @return string Returns the output of the pattern. | ||
*/ | ||
function render_block_core_pattern( $attributes ) { | ||
function render_block_core_pattern( $attributes, $content ) { | ||
if ( empty( $attributes['slug'] ) ) { | ||
return ''; | ||
} | ||
|
||
$align_class = isset($attributes['inheritedAlignment']) ? 'class="align' . $attributes['inheritedAlignment'] . '"' : ''; | ||
$wrapper = '<div '. $align_class . ' data-pattern-slug="' . $attributes['slug'] . '">%s</div>'; | ||
glendaviesnz marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
||
if ( isset( $attributes['syncStatus'] ) && 'unsynced' === $attributes['syncStatus'] ) { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The thinking was that if there is no Adding There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Good catch @gziolo 👍
That's what the block.json attribute definition indicates. What I think @gziolo is questioning is the There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🤦 missed the first code snippet in the original comment! Ignore all of my previous comment, I will get the There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. |
||
return sprintf( $wrapper, $content ); | ||
} | ||
|
||
$slug = $attributes['slug']; | ||
$registry = WP_Block_Patterns_Registry::get_instance(); | ||
if ( ! $registry->is_registered( $slug ) ) { | ||
return ''; | ||
} | ||
|
||
$pattern = $registry->get_registered( $slug ); | ||
return do_blocks( $pattern['content'] ); | ||
return sprintf( $wrapper, do_blocks( $pattern['content'] ) ); | ||
} | ||
|
||
add_action( 'init', 'register_block_core_pattern' ); |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,18 @@ | ||
/** | ||
* WordPress dependencies | ||
*/ | ||
import { useBlockProps, useInnerBlocksProps } from '@wordpress/block-editor'; | ||
|
||
export default function save( { | ||
attributes: { inheritedAlignment, syncStatus }, | ||
innerBlocks, | ||
} ) { | ||
if ( innerBlocks?.length === 0 || syncStatus !== 'partial' ) { | ||
return; | ||
} | ||
const blockProps = useBlockProps.save( { | ||
className: inheritedAlignment && `align${ inheritedAlignment }`, | ||
} ); | ||
const innerBlocksProps = useInnerBlocksProps.save( blockProps ); | ||
return <div { ...innerBlocksProps }>{ innerBlocksProps.children }</div>; | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,51 @@ | ||
/** | ||
* WordPress dependencies | ||
*/ | ||
import { useSelect, useDispatch } from '@wordpress/data'; | ||
import { useEffect } from '@wordpress/element'; | ||
import { | ||
store as blockEditorStore, | ||
useBlockProps, | ||
} from '@wordpress/block-editor'; | ||
|
||
const PatternEdit = ( { attributes, clientId } ) => { | ||
const selectedPattern = useSelect( | ||
( select ) => | ||
select( blockEditorStore ).__experimentalGetParsedPattern( | ||
attributes.slug | ||
), | ||
[ attributes.slug ] | ||
); | ||
|
||
const { replaceBlocks, __unstableMarkNextChangeAsNotPersistent } = | ||
useDispatch( blockEditorStore ); | ||
|
||
// 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 ( 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, | ||
// because nested pattern blocks cannot be inserted if the parent block supports | ||
// inner blocks but doesn't have blockSettings in the state. | ||
window.queueMicrotask( () => { | ||
__unstableMarkNextChangeAsNotPersistent(); | ||
replaceBlocks( clientId, selectedPattern.blocks ); | ||
} ); | ||
} | ||
}, [ | ||
clientId, | ||
selectedPattern?.blocks, | ||
__unstableMarkNextChangeAsNotPersistent, | ||
replaceBlocks, | ||
] ); | ||
|
||
const props = useBlockProps(); | ||
|
||
return <div { ...props } />; | ||
}; | ||
|
||
export default PatternEdit; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The naming of this attribute,
inheritedAlignment
, could be a little awkward as "inheriting" from a pattern's inner or child blocks doesn't make much sense to me.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It might be worth moving this change into a separate PR, as I'm sure there will be some other thoughts on it. I think the main thing right now is to get a first implementation of the partial syncing behavior, so this isn't something of high urgency, though it is something that needs to be fixed.
Some random thoughts - I wonder if it could be made part of the
align
block supports. While other blocks might have something likealign: [ 'wide', 'full' ]
, this could possibly bealign: 'auto'
? When set to that it could avoid showing the UI. Just throwing some ideas out there. It might be good to start some discussions with other contributors who may have thought about this problem.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
As they say 'Insanity is inherited, you get it from your children', so the same could apply here 😄. I have removed it now anyway. As Dan suggests, probably better as a follow-up PR so as not to slow things down if people have different opinions on it.