Skip to content

Commit

Permalink
Try nesting registry to patch setAttributes
Browse files Browse the repository at this point in the history
  • Loading branch information
kevin940726 committed Oct 12, 2023
1 parent ce45bdc commit 108a7a2
Show file tree
Hide file tree
Showing 2 changed files with 146 additions and 104 deletions.
101 changes: 11 additions & 90 deletions packages/block-editor/src/store/actions.js
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,6 @@ import {
switchToBlockType,
synchronizeBlocksWithTemplate,
getBlockSupport,
store as blocksStore,
} from '@wordpress/blocks';
import { speak } from '@wordpress/a11y';
import { __, _n, sprintf } from '@wordpress/i18n';
Expand Down Expand Up @@ -158,96 +157,18 @@ export function receiveBlocks( blocks ) {
* @param {boolean} uniqueByBlock true if each block in clientIds array has a unique set of attributes
* @return {Object} Action object.
*/
export const updateBlockAttributes =
( clientIds, attributes, uniqueByBlock = false ) =>
( { select, dispatch, registry } ) => {
if ( ! window.__experimentalPatterns ) {
dispatch( {
type: 'UPDATE_BLOCK_ATTRIBUTES',
clientIds: castArray( clientIds ),
attributes,
uniqueByBlock,
} );
return;
}

const updates = {};
for ( const clientId of castArray( clientIds ) ) {
const attrs = uniqueByBlock ? attributes[ clientId ] : attributes;
const parentBlocks = select.getBlocksByClientId(
select.getBlockParents( clientId )
);
const parentPattern = parentBlocks.findLast(
( parentBlock ) => parentBlock.name === 'core/block'
);
const block = select.getBlock( clientId );
if (
! parentPattern ||
! registry
.select( blocksStore )
.hasBlockSupport( block.name, '__experimentalPattern' )
) {
updates[ clientId ] = attrs;
continue;
}

const contentAttributes = registry
.select( blocksStore )
.getBlockSupport( block.name, '__experimentalPattern' );
const dynamicContent = {};
const updatedAttributes = {};
for ( const attributeKey of Object.keys( attrs ) ) {
if ( Object.hasOwn( contentAttributes, attributeKey ) ) {
dynamicContent[ attributeKey ] = attrs[ attributeKey ];
} else {
updatedAttributes[ attributeKey ] = attrs[ attributeKey ];
}
}
if ( Object.keys( dynamicContent ).length > 0 ) {
let id = block.attributes.metadata?.id;
if ( ! id ) {
// The id just has to be unique within the pattern context, so we
// use the block's clientId as a convenient unique identifier.
id = block.clientId;
updatedAttributes.metadata = {
...block.attributes.metadata,
id,
};
}

updates[ parentPattern.clientId ] = {
dynamicContent: {
...parentPattern.attributes.dynamicContent,
[ id ]: dynamicContent,
},
};
}
if ( Object.keys( updatedAttributes ).length > 0 ) {
updates[ clientId ] = updatedAttributes;
}
}

if (
Object.values( updates ).every(
( updatedAttributes, _index, arr ) =>
updatedAttributes === arr[ 0 ]
)
) {
dispatch( {
type: 'UPDATE_BLOCK_ATTRIBUTES',
clientIds: Object.keys( updates ),
attributes: Object.values( updates )[ 0 ],
uniqueByBlock: false,
} );
} else {
dispatch( {
type: 'UPDATE_BLOCK_ATTRIBUTES',
clientIds: Object.keys( updates ),
attributes: updates,
uniqueByBlock: true,
} );
}
export function updateBlockAttributes(
clientIds,
attributes,
uniqueByBlock = false
) {
return {
type: 'UPDATE_BLOCK_ATTRIBUTES',
clientIds: castArray( clientIds ),
attributes,
uniqueByBlock,
};
}

/**
* Action that updates the block with the specified client ID.
Expand Down
149 changes: 135 additions & 14 deletions packages/block-library/src/block/edit.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
/**
* WordPress dependencies
*/
import { RegistryProvider, useRegistry } from '@wordpress/data';
import { useMemo } from '@wordpress/element';
import {
useEntityBlockEditor,
useEntityProp,
Expand All @@ -21,9 +23,92 @@ import {
InspectorControls,
useBlockProps,
Warning,
store as blockEditorStore,
} from '@wordpress/block-editor';
import { store as blocksStore } from '@wordpress/blocks';

export default function ReusableBlockEdit( { attributes: { ref } } ) {
function hasAttributeSynced( registry, block ) {
return registry
.select( blocksStore )
.hasBlockSupport( block.name, '__experimentalPattern' );
}
function getAttributeSynced( registry, block ) {
return registry
.select( blocksStore )
.getBlockSupport( block.name, '__experimentalPattern' );
}

const updateBlockAttributes =
( patternClientId ) =>
( clientIds, attributes, uniqueByBlock = false ) =>
( { select, dispatch, registry } ) => {
const updates = {};
for ( const clientId of [].concat( clientIds ) ) {
const attrs = uniqueByBlock ? attributes[ clientId ] : attributes;
const parentPattern = select.getBlock( patternClientId );
const block = select.getBlock( clientId );
if ( ! parentPattern || ! hasAttributeSynced( registry, block ) ) {
updates[ clientId ] = attrs;
continue;
}

const contentAttributes = getAttributeSynced( registry, block );
const dynamicContent = {};
const updatedAttributes = {};
for ( const attributeKey of Object.keys( attrs ) ) {
if ( Object.hasOwn( contentAttributes, attributeKey ) ) {
dynamicContent[ attributeKey ] = attrs[ attributeKey ];
} else {
updatedAttributes[ attributeKey ] = attrs[ attributeKey ];
}
}
if ( Object.keys( dynamicContent ).length > 0 ) {
let id = block.attributes.metadata?.id;
if ( ! id ) {
// The id just has to be unique within the pattern context, so we
// use the block's flattened index as a convenient unique identifier.
const flattenedClientIds =
select.getClientIdsWithDescendants( patternClientId );
id = flattenedClientIds.indexOf( clientId );
}

updates[ parentPattern.clientId ] = {
dynamicContent: {
...parentPattern.attributes.dynamicContent,
[ id ]: dynamicContent,
},
};
}
if ( Object.keys( updatedAttributes ).length > 0 ) {
updates[ clientId ] = updatedAttributes;
}
}

if (
Object.values( updates ).every(
( updatedAttributes, _index, arr ) =>
updatedAttributes === arr[ 0 ]
)
) {
dispatch.updateBlockAttributes(
Object.keys( updates ),
Object.values( updates )[ 0 ],
false
);
} else {
dispatch.updateBlockAttributes(
Object.keys( updates ),
updates,
true
);
}
};

export default function ReusableBlockEdit( {
attributes: { ref },
clientId: patternClientId,
} ) {
const registry = useRegistry();
const hasAlreadyRendered = useHasRecursion( ref );
const { record, hasResolved } = useEntityRecord(
'postType',
Expand Down Expand Up @@ -58,6 +143,40 @@ export default function ReusableBlockEdit( { attributes: { ref } } ) {
: InnerBlocks.ButtonBlockAppender,
} );

const subRegistry = useMemo( () => {
return {
...registry,
dispatch( store ) {
if (
store !== blockEditorStore &&
store !== blockEditorStore.name
) {
return registry.dispatch( store );
}
const dispatch = registry.dispatch( store );
const select = registry.select( store );
return {
...dispatch,
updateBlockAttributes(
clientId,
attributes,
uniqueByBlock
) {
return updateBlockAttributes( patternClientId )(
clientId,
attributes,
uniqueByBlock
)( {
registry,
select,
dispatch,
} );
},
};
},
};
}, [ registry, patternClientId ] );

if ( hasAlreadyRendered ) {
return (
<div { ...blockProps }>
Expand Down Expand Up @@ -89,18 +208,20 @@ export default function ReusableBlockEdit( { attributes: { ref } } ) {
}

return (
<RecursionProvider uniqueId={ ref }>
<InspectorControls>
<PanelBody>
<TextControl
__nextHasNoMarginBottom
label={ __( 'Name' ) }
value={ title }
onChange={ setTitle }
/>
</PanelBody>
</InspectorControls>
<div { ...innerBlocksProps } />
</RecursionProvider>
<RegistryProvider value={ subRegistry }>
<RecursionProvider uniqueId={ ref }>
<InspectorControls>
<PanelBody>
<TextControl
__nextHasNoMarginBottom
label={ __( 'Name' ) }
value={ title }
onChange={ setTitle }
/>
</PanelBody>
</InspectorControls>
<div { ...innerBlocksProps } />
</RecursionProvider>
</RegistryProvider>
);
}

0 comments on commit 108a7a2

Please sign in to comment.