diff --git a/packages/block-editor/src/components/inspector-controls/fill.js b/packages/block-editor/src/components/inspector-controls/fill.js index 456b33af9137f..ce774b325816e 100644 --- a/packages/block-editor/src/components/inspector-controls/fill.js +++ b/packages/block-editor/src/components/inspector-controls/fill.js @@ -5,6 +5,7 @@ import { __experimentalStyleProvider as StyleProvider, __experimentalToolsPanelContext as ToolsPanelContext, } from '@wordpress/components'; +import { useDispatch } from '@wordpress/data'; import warning from '@wordpress/warning'; import deprecated from '@wordpress/deprecated'; import { useEffect, useContext } from '@wordpress/element'; @@ -12,6 +13,8 @@ import { useEffect, useContext } from '@wordpress/element'; /** * Internal dependencies */ +import { store as blockEditorStore } from '../../store'; +import { unlock } from '../../lock-unlock'; import { useBlockEditContext, mayDisplayControlsKey, @@ -36,7 +39,29 @@ export default function InspectorControlsFill( { group = __experimentalGroup; } - const context = useBlockEditContext(); + const { clientId, ...context } = useBlockEditContext(); + + // Register any clientIds with `contentOnly` controls before the + // `mayDisplayControls` early return so that parent blocks can know whether + // child blocks have those controls. + const { addContentOnlyControlsBlock, removeContentOnlyControlsBlock } = + unlock( useDispatch( blockEditorStore ) ); + useEffect( () => { + if ( group === 'contentOnly' ) { + addContentOnlyControlsBlock( clientId ); + } + return () => { + if ( group === 'contentOnly' ) { + removeContentOnlyControlsBlock( clientId ); + } + }; + }, [ + clientId, + group, + addContentOnlyControlsBlock, + removeContentOnlyControlsBlock, + ] ); + const Fill = groups[ group ]?.Fill; if ( ! Fill ) { warning( `Unknown InspectorControls group "${ group }" provided.` ); diff --git a/packages/block-editor/src/store/private-actions.js b/packages/block-editor/src/store/private-actions.js index 6cfccb17287ff..780efd1d4510b 100644 --- a/packages/block-editor/src/store/private-actions.js +++ b/packages/block-editor/src/store/private-actions.js @@ -391,3 +391,17 @@ export function expandBlock( clientId ) { clientId, }; } + +export function addContentOnlyControlsBlock( clientId ) { + return { + type: 'ADD_CONTENT_ONLY_CONTROLS_BLOCK', + clientId, + }; +} + +export function removeContentOnlyControlsBlock( clientId ) { + return { + type: 'REMOVE_CONTENT_ONLY_CONTROLS_BLOCK', + clientId, + }; +} diff --git a/packages/block-editor/src/store/private-selectors.js b/packages/block-editor/src/store/private-selectors.js index 94de85cbabe70..58979c4f05bd0 100644 --- a/packages/block-editor/src/store/private-selectors.js +++ b/packages/block-editor/src/store/private-selectors.js @@ -449,6 +449,10 @@ export const getContentLockingParent = createSelector( ( state ) => [ state.blocks.parents, state.blockListSettings ] ); +export function getContentOnlyControlsBlocks( state ) { + return state.contentOnlyControlsBlocks; +} + /** * Retrieves the client ID of the block that is content locked but is * currently being temporarily edited as a non-locked block. diff --git a/packages/block-editor/src/store/reducer.js b/packages/block-editor/src/store/reducer.js index 13024d4d2e8fa..f5eca91fc12d7 100644 --- a/packages/block-editor/src/store/reducer.js +++ b/packages/block-editor/src/store/reducer.js @@ -2065,6 +2065,26 @@ export function lastFocus( state = false, action ) { return state; } +export function contentOnlyControlsBlocks( state = [], action ) { + switch ( action.type ) { + case 'ADD_CONTENT_ONLY_CONTROLS_BLOCK': { + if ( state.includes( action.clientId ) ) { + return state; + } + return [ ...state, action.clientId ]; + } + case 'DELETE_CONTENT_ONLY_CONTROLS_BLOCK': { + if ( ! state.includes( action.clientId ) ) { + return state; + } + + return state.filter( ( item ) => item !== action.clientId ); + } + } + + return state; +} + const combinedReducers = combineReducers( { blocks, isDragging, @@ -2097,6 +2117,7 @@ const combinedReducers = combineReducers( { blockRemovalRules, openedBlockSettingsMenu, registeredInserterMediaCategories, + contentOnlyControlsBlocks, } ); function withAutomaticChangeReset( reducer ) { diff --git a/packages/block-library/src/image/image.js b/packages/block-library/src/image/image.js index 8eeeb968f5841..3c32d6b667711 100644 --- a/packages/block-library/src/image/image.js +++ b/packages/block-library/src/image/image.js @@ -553,7 +553,7 @@ export default function Image( { ) } - { isContentOnlyMode && isSingleSelected && ( + { isContentOnlyMode && ( // Add some extra controls for content attributes when content only mode is active.