diff --git a/packages/block-editor/src/hooks/use-bindings-attributes.js b/packages/block-editor/src/hooks/use-bindings-attributes.js index c6c847cbddcd1..4089f469790c4 100644 --- a/packages/block-editor/src/hooks/use-bindings-attributes.js +++ b/packages/block-editor/src/hooks/use-bindings-attributes.js @@ -4,12 +4,12 @@ import { getBlockType } from '@wordpress/blocks'; import { createHigherOrderComponent } from '@wordpress/compose'; import { useRegistry, useSelect } from '@wordpress/data'; +import { useMemo, useCallback } from '@wordpress/element'; import { addFilter } from '@wordpress/hooks'; /** * Internal dependencies */ import { store as blockEditorStore } from '../store'; -import { useBlockEditContext } from '../components/block-edit/context'; import { unlock } from '../lock-unlock'; /** @typedef {import('@wordpress/compose').WPHigherOrderComponent} WPHigherOrderComponent */ @@ -32,69 +32,101 @@ const BLOCK_BINDINGS_ALLOWED_BLOCKS = { const createEditFunctionWithBindingsAttribute = () => createHigherOrderComponent( ( BlockEdit ) => ( props ) => { - const { clientId, name: blockName } = useBlockEditContext(); + const { name, attributes, setAttributes, ...otherProps } = props; const { getBlockBindingsSource } = unlock( useSelect( blockEditorStore ) ); - const { getBlockAttributes, updateBlockAttributes } = - useSelect( blockEditorStore ); - - const updatedAttributes = getBlockAttributes( clientId ); - if ( updatedAttributes?.metadata?.bindings ) { - Object.entries( updatedAttributes.metadata.bindings ).forEach( - ( [ attributeName, settings ] ) => { - const source = getBlockBindingsSource( - settings.source - ); - if ( source ) { - // Second argument (`updateMetaValue`) will be used to update the value in the future. - const { - placeholder, - useValue: [ metaValue = null ] = [], - } = source.useSource( props, settings.args ); - - if ( placeholder && ! metaValue ) { - // If the attribute is `src` or `href`, a placeholder can't be used because it is not a valid url. - // Adding this workaround until attributes and metadata fields types are improved and include `url`. - const htmlAttribute = - getBlockType( blockName ).attributes[ - attributeName - ].attribute; - if ( - htmlAttribute === 'src' || - htmlAttribute === 'href' - ) { - updatedAttributes[ attributeName ] = null; - } else { - updatedAttributes[ attributeName ] = - placeholder; - } - } + const blockType = getBlockType( name ); - if ( metaValue ) { - updatedAttributes[ attributeName ] = metaValue; - } - } + const boundAttributes = {}; + if ( attributes?.metadata?.bindings ) { + Object.entries( attributes?.metadata?.bindings ).forEach( + ( [ attribute, binding ] ) => { + boundAttributes[ attribute ] = getBlockBindingsSource( + binding.source + ).useSource( props, binding.args ); } ); } + const attributesWithBindings = useMemo( () => { + return { + ...attributes, + ...Object.fromEntries( + BLOCK_BINDINGS_ALLOWED_BLOCKS[ name ].map( + ( attributeName ) => { + // Check bindings. + if ( boundAttributes[ attributeName ] ) { + const { + placeholder, + useValue: [ sourceValue = null ] = [], + } = boundAttributes[ attributeName ]; + + const blockTypeAttribute = + blockType.attributes[ attributeName ]; + + if ( placeholder && ! sourceValue ) { + // If the attribute is `src` or `href`, a placeholder can't be used because it is not a valid url. + // Adding this workaround until attributes and metadata fields types are improved and include `url`. + + const htmlAttribute = + blockTypeAttribute.attribute; + if ( + htmlAttribute === 'src' || + htmlAttribute === 'href' + ) { + return [ attributeName, null ]; + } + return [ attributeName, placeholder ]; + } + + if ( sourceValue ) { + // TODO: If it is rich-text, I think we can't edit it this way. + return [ attributeName, sourceValue ]; + } + } + return [ + attributeName, + attributes[ attributeName ], + ]; + } + ) + ), + }; + }, [ attributes, blockType.attributes, boundAttributes, name ] ); + + const updatedSetAttributes = useCallback( + ( nextAttributes ) => { + Object.entries( nextAttributes ?? {} ) + .filter( + ( [ attribute ] ) => attribute in boundAttributes + ) + .forEach( ( [ attribute, value ] ) => { + const { + useValue: [ , setSourceValue = null ] = [], + } = boundAttributes[ attribute ]; + if ( setSourceValue ) { + setSourceValue( value ); + } + } ); + setAttributes( nextAttributes ); + }, + [ setAttributes, attributesWithBindings, boundAttributes ] + ); + const registry = useRegistry(); return ( - <> - - registry.batch( () => - updateBlockAttributes( blockId, newAttributes ) - ) - } - { ...props } - /> - + { + registry.batch( () => + updatedSetAttributes( newAttributes ) + ); + } } + { ...otherProps } + /> ); }, 'useBoundAttributes' diff --git a/packages/editor/src/bindings/post-meta.js b/packages/editor/src/bindings/post-meta.js index 091491b2ed00c..204a63c8ed85b 100644 --- a/packages/editor/src/bindings/post-meta.js +++ b/packages/editor/src/bindings/post-meta.js @@ -38,5 +38,5 @@ export default { useValue: [ metaValue, updateMetaValue ], }; }, - lockAttributesEditing: true, + lockAttributesEditing: false, };