From 1279f8283c8c1953c5253d794afaca88efdeb9b2 Mon Sep 17 00:00:00 2001 From: Mario Santos Date: Tue, 30 Jul 2024 15:01:34 +0200 Subject: [PATCH 1/6] Add `useBlockBindingsUtils` --- packages/block-editor/src/private-apis.js | 2 + .../block-editor/src/utils/block-bindings.js | 60 +++++++++++++++++++ 2 files changed, 62 insertions(+) create mode 100644 packages/block-editor/src/utils/block-bindings.js diff --git a/packages/block-editor/src/private-apis.js b/packages/block-editor/src/private-apis.js index eaf699d5e6939..40f73ad811e2f 100644 --- a/packages/block-editor/src/private-apis.js +++ b/packages/block-editor/src/private-apis.js @@ -47,6 +47,7 @@ import { PrivatePublishDateTimePicker } from './components/publish-date-time-pic import useSpacingSizes from './components/spacing-sizes-control/hooks/use-spacing-sizes'; import useBlockDisplayTitle from './components/block-title/use-block-display-title'; import TabbedSidebar from './components/tabbed-sidebar'; +import { useBlockBindingsUtils } from './utils/block-bindings'; /** * Private @wordpress/block-editor APIs. @@ -93,4 +94,5 @@ lock( privateApis, { useBlockDisplayTitle, __unstableBlockStyleVariationOverridesWithConfig, setBackgroundStyleDefaults, + useBlockBindingsUtils, } ); diff --git a/packages/block-editor/src/utils/block-bindings.js b/packages/block-editor/src/utils/block-bindings.js new file mode 100644 index 0000000000000..f6d15dc1e65e9 --- /dev/null +++ b/packages/block-editor/src/utils/block-bindings.js @@ -0,0 +1,60 @@ +/** + * WordPress dependencies + */ +import { useDispatch, useSelect } from '@wordpress/data'; + +/** + * Internal dependencies + */ +import { store as blockEditorStore } from '../store'; +import { useBlockEditContext } from '../components/block-edit'; + +export function useBlockBindingsUtils() { + const { clientId } = useBlockEditContext(); + const { updateBlockAttributes } = useDispatch( blockEditorStore ); + // TODO: Review if this is correct. + const { getBlockAttributes } = useSelect( blockEditorStore ); + + // TODO: Add docs. + const updateBlockBindings = ( bindings ) => { + const { metadata } = getBlockAttributes( clientId ); + const newBindings = { ...metadata?.bindings }; + Object.entries( bindings ).forEach( ( [ attribute, binding ] ) => { + if ( ! binding && newBindings[ attribute ] ) { + delete newBindings[ attribute ]; + return; + } + newBindings[ attribute ] = binding; + } ); + + const newMetadata = { + ...metadata, + bindings: newBindings, + }; + + if ( Object.keys( newMetadata.bindings ).length === 0 ) { + delete newMetadata.bindings; + } + + updateBlockAttributes( clientId, { + metadata: + Object.keys( newMetadata ).length === 0 + ? undefined + : newMetadata, + } ); + }; + // TODO: Add docs. + const removeAllBlockBindings = () => { + const { metadata } = getBlockAttributes( clientId ); + const newMetadata = { ...metadata }; + delete newMetadata.bindings; + updateBlockAttributes( clientId, { + metadata: + Object.keys( newMetadata ).length === 0 + ? undefined + : newMetadata, + } ); + }; + + return { updateBlockBindings, removeAllBlockBindings }; +} From 5d44e50fe41ea8b6399781186ed3d633f92a6997 Mon Sep 17 00:00:00 2001 From: Mario Santos Date: Tue, 30 Jul 2024 15:01:57 +0200 Subject: [PATCH 2/6] Use utils in custom fields UI --- .../block-editor/src/hooks/block-bindings.js | 92 ++++--------------- 1 file changed, 17 insertions(+), 75 deletions(-) diff --git a/packages/block-editor/src/hooks/block-bindings.js b/packages/block-editor/src/hooks/block-bindings.js index ce3fdd2f327c4..1a96fb160ca1d 100644 --- a/packages/block-editor/src/hooks/block-bindings.js +++ b/packages/block-editor/src/hooks/block-bindings.js @@ -13,7 +13,7 @@ import { __experimentalVStack as VStack, privateApis as componentsPrivateApis, } from '@wordpress/components'; -import { useSelect, useDispatch, useRegistry } from '@wordpress/data'; +import { useRegistry } from '@wordpress/data'; import { useContext, Fragment } from '@wordpress/element'; import { useViewportMatch } from '@wordpress/compose'; @@ -24,10 +24,10 @@ import { canBindAttribute, getBindableAttributes, } from '../hooks/use-bindings-attributes'; -import { store as blockEditorStore } from '../store'; import { unlock } from '../lock-unlock'; import InspectorControls from '../components/inspector-controls'; import BlockContext from '../components/block-context'; +import { useBlockBindingsUtils } from '../utils/block-bindings'; const { DropdownMenuV2: DropdownMenu, @@ -51,12 +51,8 @@ const useToolsPanelDropdownMenuProps = () => { : {}; }; -function BlockBindingsPanelDropdown( { - fieldsList, - addConnection, - attribute, - binding, -} ) { +function BlockBindingsPanelDropdown( { fieldsList, attribute, binding } ) { + const { updateBlockBindings } = useBlockBindingsUtils(); const currentKey = binding?.args?.key; return ( <> @@ -77,7 +73,13 @@ function BlockBindingsPanelDropdown( { - addConnection( key, attribute ) + // TODO: Pass source key and not label and extract the label in the appropriate place. + updateBlockBindings( { + [ attribute ]: { + source: 'core/post-meta', + args: { key: value }, + }, + } ) } name={ attribute + '-binding' } value={ key } @@ -141,9 +143,8 @@ function EditableBlockBindingsPanelItems( { attributes, bindings, fieldsList, - addConnection, - removeConnection, } ) { + const { updateBlockBindings } = useBlockBindingsUtils(); const isMobile = useViewportMatch( 'medium', '<' ); return ( <> @@ -155,7 +156,9 @@ function EditableBlockBindingsPanelItems( { hasValue={ () => !! binding } label={ attribute } onDeselect={ () => { - removeConnection( attribute ); + updateBlockBindings( { + [ attribute ]: undefined, + } ); } } > @@ -191,7 +193,7 @@ export const BlockBindingsPanel = ( { name, metadata } ) => { const registry = useRegistry(); const blockContext = useContext( BlockContext ); const { bindings } = metadata || {}; - + const { removeAllBlockBindings } = useBlockBindingsUtils(); const bindableAttributes = getBindableAttributes( name ); const dropdownMenuProps = useToolsPanelDropdownMenuProps(); @@ -205,68 +207,10 @@ export const BlockBindingsPanel = ( { name, metadata } ) => { } } ); - const { updateBlockAttributes } = useDispatch( blockEditorStore ); - - const { _id } = useSelect( ( select ) => { - const { getSelectedBlockClientId } = select( blockEditorStore ); - - return { - _id: getSelectedBlockClientId(), - }; - }, [] ); - if ( ! bindableAttributes || bindableAttributes.length === 0 ) { return null; } - const removeAllConnections = () => { - const newMetadata = { ...metadata }; - delete newMetadata.bindings; - updateBlockAttributes( _id, { - metadata: - Object.keys( newMetadata ).length === 0 - ? undefined - : newMetadata, - } ); - }; - - const addConnection = ( value, attribute ) => { - // Assuming the block expects a flat structure for its metadata attribute - const newMetadata = { - ...metadata, - // Adjust this according to the actual structure expected by your block - bindings: { - ...metadata?.bindings, - [ attribute ]: { - source: 'core/post-meta', - args: { key: value }, - }, - }, - }; - // Update the block's attributes with the new metadata - updateBlockAttributes( _id, { - metadata: newMetadata, - } ); - }; - - const removeConnection = ( key ) => { - const newMetadata = { ...metadata }; - if ( ! newMetadata.bindings ) { - return; - } - - delete newMetadata.bindings[ key ]; - if ( Object.keys( newMetadata.bindings ).length === 0 ) { - delete newMetadata.bindings; - } - updateBlockAttributes( _id, { - metadata: - Object.keys( newMetadata ).length === 0 - ? undefined - : newMetadata, - } ); - }; - const fieldsList = {}; const { getBlockBindingsSources } = unlock( blocksPrivateApis ); const registeredSources = getBlockBindingsSources(); @@ -312,7 +256,7 @@ export const BlockBindingsPanel = ( { name, metadata } ) => { { - removeAllConnections(); + removeAllBlockBindings(); } } dropdownMenuProps={ dropdownMenuProps } className="block-editor-bindings__panel" @@ -327,8 +271,6 @@ export const BlockBindingsPanel = ( { name, metadata } ) => { attributes={ bindableAttributes } bindings={ filteredBindings } fieldsList={ fieldsList } - addConnection={ addConnection } - removeConnection={ removeConnection } /> ) } From 92aa448b39dc7cd1674e202761fc60c4408e6dee Mon Sep 17 00:00:00 2001 From: Mario Santos Date: Tue, 30 Jul 2024 15:02:13 +0200 Subject: [PATCH 3/6] Use utils in pattern overrides --- .../components/pattern-overrides-controls.js | 46 +++++++------------ 1 file changed, 17 insertions(+), 29 deletions(-) diff --git a/packages/patterns/src/components/pattern-overrides-controls.js b/packages/patterns/src/components/pattern-overrides-controls.js index 3a5ff3e0674a6..53d485929be47 100644 --- a/packages/patterns/src/components/pattern-overrides-controls.js +++ b/packages/patterns/src/components/pattern-overrides-controls.js @@ -2,7 +2,10 @@ * WordPress dependencies */ import { useState, useId } from '@wordpress/element'; -import { InspectorControls } from '@wordpress/block-editor'; +import { + InspectorControls, + privateApis as blockEditorPrivateApis, +} from '@wordpress/block-editor'; import { BaseControl, Button } from '@wordpress/components'; import { __ } from '@wordpress/i18n'; @@ -14,22 +17,9 @@ import { AllowOverridesModal, DisallowOverridesModal, } from './allow-overrides-modal'; +import { unlock } from '../lock-unlock'; -function removeBindings( bindings ) { - let updatedBindings = { ...bindings }; - delete updatedBindings.__default; - if ( ! Object.keys( updatedBindings ).length ) { - updatedBindings = undefined; - } - return updatedBindings; -} - -function addBindings( bindings ) { - return { - ...bindings, - __default: { source: PATTERN_OVERRIDES_BINDING_SOURCE }, - }; -} +const { useBlockBindingsUtils } = unlock( blockEditorPrivateApis ); function PatternOverridesControls( { attributes, @@ -49,24 +39,22 @@ function PatternOverridesControls( { const isConnectedToOtherSources = defaultBindings?.source && defaultBindings.source !== PATTERN_OVERRIDES_BINDING_SOURCE; + const { updateBlockBindings } = useBlockBindingsUtils(); function updateBindings( isChecked, customName ) { - const prevBindings = attributes?.metadata?.bindings; - const updatedBindings = isChecked - ? addBindings( prevBindings ) - : removeBindings( prevBindings ); - - const updatedMetadata = { - ...attributes.metadata, - bindings: updatedBindings, - }; - if ( customName ) { - updatedMetadata.name = customName; + setAttributes( { + metadata: { + ...attributes.metadata, + name: customName, + }, + } ); } - setAttributes( { - metadata: updatedMetadata, + updateBlockBindings( { + __default: isChecked + ? { source: PATTERN_OVERRIDES_BINDING_SOURCE } + : undefined, } ); } From 10d3f823f6bad8b6154c0f298d18b8e8724e7396 Mon Sep 17 00:00:00 2001 From: Mario Santos Date: Tue, 30 Jul 2024 16:32:19 +0200 Subject: [PATCH 4/6] Properly populate custom fields --- .../block-editor/src/hooks/block-bindings.js | 25 ++++++++++--------- 1 file changed, 13 insertions(+), 12 deletions(-) diff --git a/packages/block-editor/src/hooks/block-bindings.js b/packages/block-editor/src/hooks/block-bindings.js index 1a96fb160ca1d..a7aa72ae6693a 100644 --- a/packages/block-editor/src/hooks/block-bindings.js +++ b/packages/block-editor/src/hooks/block-bindings.js @@ -52,12 +52,14 @@ const useToolsPanelDropdownMenuProps = () => { }; function BlockBindingsPanelDropdown( { fieldsList, attribute, binding } ) { + const { getBlockBindingsSources } = unlock( blocksPrivateApis ); + const registeredSources = getBlockBindingsSources(); const { updateBlockBindings } = useBlockBindingsUtils(); const currentKey = binding?.args?.key; return ( <> - { Object.entries( fieldsList ).map( ( [ label, fields ], i ) => ( - + { Object.entries( fieldsList ).map( ( [ name, fields ], i ) => ( + { Object.keys( fieldsList ).length > 1 && ( - { label } + { registeredSources[ name ].label } ) } { Object.entries( fields ).map( ( [ key, value ] ) => ( - // TODO: Pass source key and not label and extract the label in the appropriate place. updateBlockBindings( { [ attribute ]: { - source: 'core/post-meta', - args: { key: value }, + source: name, + args: { key }, }, } ) } @@ -189,18 +190,18 @@ function EditableBlockBindingsPanelItems( { ); } -export const BlockBindingsPanel = ( { name, metadata } ) => { +export const BlockBindingsPanel = ( { name: blockName, metadata } ) => { const registry = useRegistry(); const blockContext = useContext( BlockContext ); const { bindings } = metadata || {}; const { removeAllBlockBindings } = useBlockBindingsUtils(); - const bindableAttributes = getBindableAttributes( name ); + const bindableAttributes = getBindableAttributes( blockName ); const dropdownMenuProps = useToolsPanelDropdownMenuProps(); const filteredBindings = { ...bindings }; Object.keys( filteredBindings ).forEach( ( key ) => { if ( - ! canBindAttribute( name, key ) || + ! canBindAttribute( blockName, key ) || filteredBindings[ key ].source === 'core/pattern-overrides' ) { delete filteredBindings[ key ]; @@ -214,8 +215,8 @@ export const BlockBindingsPanel = ( { name, metadata } ) => { const fieldsList = {}; const { getBlockBindingsSources } = unlock( blocksPrivateApis ); const registeredSources = getBlockBindingsSources(); - Object.values( registeredSources ).forEach( - ( { getFieldsList, label, usesContext } ) => { + Object.entries( registeredSources ).forEach( + ( [ sourceName, { getFieldsList, usesContext } ] ) => { if ( getFieldsList ) { // Populate context. const context = {}; @@ -230,7 +231,7 @@ export const BlockBindingsPanel = ( { name, metadata } ) => { } ); // Only add source if the list is not empty. if ( sourceList ) { - fieldsList[ label ] = { ...sourceList }; + fieldsList[ sourceName ] = { ...sourceList }; } } } From 11a35b9ff0f562d7932a4c1ce2d5f25f8d518af4 Mon Sep 17 00:00:00 2001 From: Mario Santos Date: Tue, 30 Jul 2024 16:43:34 +0200 Subject: [PATCH 5/6] Add comments --- .../block-editor/src/utils/block-bindings.js | 44 +++++++++++++++++-- 1 file changed, 41 insertions(+), 3 deletions(-) diff --git a/packages/block-editor/src/utils/block-bindings.js b/packages/block-editor/src/utils/block-bindings.js index f6d15dc1e65e9..8f4aa8b9483a2 100644 --- a/packages/block-editor/src/utils/block-bindings.js +++ b/packages/block-editor/src/utils/block-bindings.js @@ -12,10 +12,37 @@ import { useBlockEditContext } from '../components/block-edit'; export function useBlockBindingsUtils() { const { clientId } = useBlockEditContext(); const { updateBlockAttributes } = useDispatch( blockEditorStore ); - // TODO: Review if this is correct. const { getBlockAttributes } = useSelect( blockEditorStore ); - // TODO: Add docs. + /** + * Updates the value of the bindings connected to block attributes. + * It removes the binding when the new value is `undefined`. + * + * @param {Object} bindings Bindings including the attributes to update and the new object. + * @param {string} bindings.source The source name to connect to. + * @param {Object} [bindings.args] Object containing the arguments needed by the source. + * + * @example + * ```js + * import { useBlockBindingsUtils } from '@wordpress/block-editor' + * + * const { removeAllBlockBindings } = useBlockBindingsUtils(); + * updateBlockBindings( { + * url: { + * source: 'core/post-meta', + * args: { + * key: 'url_custom_field', + * }, + * }, + * alt: { + * source: 'core/post-meta', + * args: { + * key: 'text_custom_field', + * }, + * } + * } ); + * ``` + */ const updateBlockBindings = ( bindings ) => { const { metadata } = getBlockAttributes( clientId ); const newBindings = { ...metadata?.bindings }; @@ -43,7 +70,18 @@ export function useBlockBindingsUtils() { : newMetadata, } ); }; - // TODO: Add docs. + + /** + * Removes the bindings property of the `metadata` attribute. + * + * @example + * ```js + * import { useBlockBindingsUtils } from '@wordpress/block-editor' + * + * const { removeAllBlockBindings } = useBlockBindingsUtils(); + * removeAllBlockBindings(); + * ``` + */ const removeAllBlockBindings = () => { const { metadata } = getBlockAttributes( clientId ); const newMetadata = { ...metadata }; From 9373c1447da400019f3a6494029a9a1b60faa4f6 Mon Sep 17 00:00:00 2001 From: Mario Santos Date: Tue, 30 Jul 2024 16:49:47 +0200 Subject: [PATCH 6/6] Update wrong comment --- packages/block-editor/src/utils/block-bindings.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/block-editor/src/utils/block-bindings.js b/packages/block-editor/src/utils/block-bindings.js index 8f4aa8b9483a2..4ba6e1a362958 100644 --- a/packages/block-editor/src/utils/block-bindings.js +++ b/packages/block-editor/src/utils/block-bindings.js @@ -26,7 +26,7 @@ export function useBlockBindingsUtils() { * ```js * import { useBlockBindingsUtils } from '@wordpress/block-editor' * - * const { removeAllBlockBindings } = useBlockBindingsUtils(); + * const { updateBlockBindings } = useBlockBindingsUtils(); * updateBlockBindings( { * url: { * source: 'core/post-meta',