diff --git a/packages/block-editor/src/hooks/use-bindings-attributes.js b/packages/block-editor/src/hooks/use-bindings-attributes.js index 5cd8cb46b3b7e7..ff7a1409f3334c 100644 --- a/packages/block-editor/src/hooks/use-bindings-attributes.js +++ b/packages/block-editor/src/hooks/use-bindings-attributes.js @@ -3,7 +3,7 @@ */ import { getBlockType, store as blocksStore } from '@wordpress/blocks'; import { createHigherOrderComponent } from '@wordpress/compose'; -import { useSelect } from '@wordpress/data'; +import { useSelect, select } from '@wordpress/data'; import { useLayoutEffect, useCallback, useState } from '@wordpress/element'; import { addFilter } from '@wordpress/hooks'; import { RichTextData } from '@wordpress/rich-text'; @@ -23,7 +23,7 @@ import { unlock } from '../lock-unlock'; * @return {WPHigherOrderComponent} Higher-order component. */ -const BLOCK_BINDINGS_ALLOWED_BLOCKS = { +const DEFAULT_BLOCK_BINDINGS_ALLOWED_BLOCKS = { 'core/paragraph': [ 'content' ], 'core/heading': [ 'content' ], 'core/image': [ 'url', 'title', 'alt' ], @@ -38,7 +38,22 @@ const BLOCK_BINDINGS_ALLOWED_BLOCKS = { * @return {boolean} Whether it is possible to bind the block to sources. */ export function canBindBlock( blockName ) { - return blockName in BLOCK_BINDINGS_ALLOWED_BLOCKS; + // Pick blocks list from all source handlers settings. + const blockBindingsSources = unlock( + select( blocksStore ) + ).getAllBlockBindingsSources(); + + let blockNames = Object.keys( DEFAULT_BLOCK_BINDINGS_ALLOWED_BLOCKS ); + Object.keys( blockBindingsSources ).forEach( ( sourceName ) => { + const blocks = blockBindingsSources[ sourceName ].settings?.blocks; + if ( blocks ) { + blockNames.push( ...Object.keys( blocks ) ); + } + } ); + + blockNames = [ ...new Set( blockNames ) ]; + + return blockNames.includes( blockName ); } /** @@ -47,13 +62,15 @@ export function canBindBlock( blockName ) { * * @param {string} blockName - The block name. * @param {string} attributeName - The attribute name. + * @param {Object} allowBlocks - The allowed blocks settings. * @return {boolean} Whether it is possible to bind the block attribute. */ -export function canBindAttribute( blockName, attributeName ) { - return ( - canBindBlock( blockName ) && - BLOCK_BINDINGS_ALLOWED_BLOCKS[ blockName ].includes( attributeName ) - ); +export function canBindAttribute( + blockName, + attributeName, + allowBlocks = DEFAULT_BLOCK_BINDINGS_ALLOWED_BLOCKS +) { + return allowBlocks[ blockName ]?.includes( attributeName ); } /** @@ -63,7 +80,7 @@ export function canBindAttribute( blockName, attributeName ) { * @param {Object} props - The component props. * @param {string} props.attrName - The attribute name. * @param {Object} props.blockProps - The block props with bound attribute. - * @param {Object} props.source - Source handler. + * @param {Object} props.sourceHandler - Source handler. * @param {Object} props.args - The arguments to pass to the source. * @param {Function} props.onPropValueChange - The function to call when the attribute value changes. * @return {null} Data-handling component. Render nothing. @@ -72,10 +89,10 @@ const BindingConnector = ( { args, attrName, blockProps, - source, + sourceHandler, onPropValueChange, } ) => { - const { placeholder, value: propValue } = source.useSource( + const { placeholder, value: propValue } = sourceHandler.useSource( blockProps, args ); @@ -83,7 +100,7 @@ const BindingConnector = ( { const { name: blockName } = blockProps; const attrValue = blockProps.attributes[ attrName ]; - const updateBoundAttibute = useCallback( + const updateBoundAttribute = useCallback( ( newAttrValue, prevAttrValue ) => { /* * If the attribute is a RichTextData instance, @@ -117,7 +134,7 @@ const BindingConnector = ( { useLayoutEffect( () => { if ( typeof propValue !== 'undefined' ) { - updateBoundAttibute( propValue, attrValue ); + updateBoundAttribute( propValue, attrValue ); } else if ( placeholder ) { /* * Placeholder fallback. @@ -130,14 +147,14 @@ const BindingConnector = ( { getBlockType( blockName ).attributes[ attrName ].attribute; if ( htmlAttribute === 'src' || htmlAttribute === 'href' ) { - updateBoundAttibute( null ); + updateBoundAttribute( null ); return; } - updateBoundAttibute( placeholder ); + updateBoundAttribute( placeholder ); } }, [ - updateBoundAttibute, + updateBoundAttribute, propValue, attrValue, placeholder, @@ -165,24 +182,44 @@ function BlockBindingBridge( { blockProps, bindings, onPropValueChange } ) { useSelect( blocksStore ) ).getAllBlockBindingsSources(); + /* + * Create allow binding object, + * filtering only the valid bindings, + * and populating the source handler. + */ + const allowBindings = Object.entries( bindings ).reduce( + ( acc, [ attrName, settings ] ) => { + const handler = blockBindingsSources[ settings.source ]; + // Check if the block has a valid source handler. + if ( ! handler?.useSource ) { + return false; + } + + // Check if the attribute can be bound. + const allowBlocks = handler?.settings?.blocks; + if ( canBindAttribute( blockProps.name, attrName, allowBlocks ) ) { + acc[ attrName ] = { + ...settings, + handler, // populate the source handler. + }; + } + + return acc; + }, + {} + ); + return ( <> - { Object.entries( bindings ).map( - ( [ attrName, boundAttribute ] ) => { - // Bail early if the block doesn't have a valid source handler. - const source = - blockBindingsSources[ boundAttribute.source ]; - if ( ! source?.useSource ) { - return null; - } - + { Object.entries( allowBindings ).map( + ( [ attrName, settings ] ) => { return ( ); @@ -208,25 +245,19 @@ const withBlockBindingSupport = createHigherOrderComponent( [] ); - /* - * Create binding object filtering - * only the attributes that can be bound. - */ - const bindings = Object.fromEntries( - Object.entries( props.attributes.metadata?.bindings || {} ).filter( - ( [ attrName ] ) => canBindAttribute( props.name, attrName ) - ) - ); + // Bail early if the block has no bindings metadata attribute. + const bindings = props.attributes.metadata?.bindings || {}; + if ( ! Object.keys( bindings ).length ) { + return ; + } return ( <> - { Object.keys( bindings ).length > 0 && ( - - ) } +