diff --git a/packages/block-editor/src/components/block-controls/use-has-block-controls.js b/packages/block-editor/src/components/block-controls/use-has-block-controls.js
deleted file mode 100644
index f7884cc1882ed5..00000000000000
--- a/packages/block-editor/src/components/block-controls/use-has-block-controls.js
+++ /dev/null
@@ -1,35 +0,0 @@
-/**
- * WordPress dependencies
- */
-import { __experimentalUseSlotFills as useSlotFills } from '@wordpress/components';
-import warning from '@wordpress/warning';
-
-/**
- * Internal dependencies
- */
-import groups from './groups';
-
-export function useHasAnyBlockControls() {
- let hasAnyBlockControls = false;
- for ( const group in groups ) {
- // It is safe to violate the rules of hooks here as the `groups` object
- // is static and will not change length between renders. Do not return
- // early as that will cause the hook to be called a different number of
- // times between renders.
- // eslint-disable-next-line react-hooks/rules-of-hooks
- if ( useHasBlockControls( group ) ) {
- hasAnyBlockControls = true;
- }
- }
- return hasAnyBlockControls;
-}
-
-export function useHasBlockControls( group = 'default' ) {
- const Slot = groups[ group ]?.Slot;
- const fills = useSlotFills( Slot?.__unstableName );
- if ( ! Slot ) {
- warning( `Unknown BlockControls group "${ group }" provided.` );
- return null;
- }
- return !! fills?.length;
-}
diff --git a/packages/block-editor/src/components/block-parent-selector/index.js b/packages/block-editor/src/components/block-parent-selector/index.js
index 80b314eeb42e5c..9090de42f8b7d7 100644
--- a/packages/block-editor/src/components/block-parent-selector/index.js
+++ b/packages/block-editor/src/components/block-parent-selector/index.js
@@ -14,6 +14,7 @@ import useBlockDisplayInformation from '../use-block-display-information';
import BlockIcon from '../block-icon';
import { useShowHoveredOrFocusedGestures } from '../block-toolbar/utils';
import { store as blockEditorStore } from '../../store';
+import { unlock } from '../../lock-unlock';
/**
* Block parent selector component, displaying the hierarchy of the
@@ -23,24 +24,26 @@ import { store as blockEditorStore } from '../../store';
*/
export default function BlockParentSelector() {
const { selectBlock } = useDispatch( blockEditorStore );
- const { firstParentClientId, isVisible } = useSelect( ( select ) => {
+ const { parentClientId, isVisible } = useSelect( ( select ) => {
const {
getBlockName,
getBlockParents,
getSelectedBlockClientId,
getBlockEditingMode,
- } = select( blockEditorStore );
+ getParentSectionBlock,
+ } = unlock( select( blockEditorStore ) );
const { hasBlockSupport } = select( blocksStore );
const selectedBlockClientId = getSelectedBlockClientId();
+ const parentSection = getParentSectionBlock( selectedBlockClientId );
const parents = getBlockParents( selectedBlockClientId );
- const _firstParentClientId = parents[ parents.length - 1 ];
- const parentBlockName = getBlockName( _firstParentClientId );
+ const _parentClientId = parentSection ?? parents[ parents.length - 1 ];
+ const parentBlockName = getBlockName( _parentClientId );
const _parentBlockType = getBlockType( parentBlockName );
return {
- firstParentClientId: _firstParentClientId,
+ parentClientId: _parentClientId,
isVisible:
- _firstParentClientId &&
- getBlockEditingMode( _firstParentClientId ) === 'default' &&
+ _parentClientId &&
+ getBlockEditingMode( _parentClientId ) !== 'disabled' &&
hasBlockSupport(
_parentBlockType,
'__experimentalParentSelector',
@@ -48,7 +51,7 @@ export default function BlockParentSelector() {
),
};
}, [] );
- const blockInformation = useBlockDisplayInformation( firstParentClientId );
+ const blockInformation = useBlockDisplayInformation( parentClientId );
// Allows highlighting the parent block outline when focusing or hovering
// the parent block selector within the child.
@@ -65,13 +68,13 @@ export default function BlockParentSelector() {
return (
selectBlock( firstParentClientId ) }
+ onClick={ () => selectBlock( parentClientId ) }
label={ sprintf(
/* translators: %s: Name of the block's parent. */
__( 'Select parent block: %s' ),
diff --git a/packages/block-editor/src/components/block-settings-menu/block-settings-dropdown.js b/packages/block-editor/src/components/block-settings-menu/block-settings-dropdown.js
index fff5acc7b79c46..ac2b99ac2bb620 100644
--- a/packages/block-editor/src/components/block-settings-menu/block-settings-dropdown.js
+++ b/packages/block-editor/src/components/block-settings-menu/block-settings-dropdown.js
@@ -183,6 +183,9 @@ export function BlockSettingsDropdown( {
}
}
+ const shouldShowBlockParentMenuItem =
+ ! parentBlockIsSelected && !! firstParentClientId;
+
return (
(
-
- { ( { onClose } ) => (
- <>
-
- <__unstableBlockSettingsMenuFirstItem.Slot
- fillProps={ { onClose } }
- />
- { ! parentBlockIsSelected &&
- !! firstParentClientId && (
+ } ) => {
+ // It is possible that some plugins register fills for this menu
+ // even if Core doesn't render anything in the block settings menu.
+ // in which case, we may want to render the menu anyway.
+ // That said for now, we can start more conservative.
+ const isEmpty =
+ ! canRemove &&
+ ! canDuplicate &&
+ ! canInsertBlock &&
+ isContentOnly;
+
+ if ( isEmpty ) {
+ return null;
+ }
+
+ return (
+
+ { ( { onClose } ) => (
+ <>
+
+ <__unstableBlockSettingsMenuFirstItem.Slot
+ fillProps={ { onClose } }
+ />
+ { shouldShowBlockParentMenuItem && (
) }
- { count === 1 && (
-
- ) }
- { ! isContentOnly && (
-
- ) }
- { canDuplicate && (
-
- ) }
- { canInsertBlock && ! isContentOnly && (
- <>
+ { count === 1 && (
+
+ ) }
+ { ! isContentOnly && (
+
+ ) }
+ { canDuplicate && (
+ ) }
+ { canInsertBlock && ! isContentOnly && (
+ <>
+
+
+ >
+ ) }
+
+ { canCopyStyles && ! isContentOnly && (
+
+
+
+
+ ) }
+
+ { typeof children === 'function'
+ ? children( { onClose } )
+ : Children.map( ( child ) =>
+ cloneElement( child, { onClose } )
+ ) }
+ { canRemove && (
+
- >
+
) }
-
- { canCopyStyles && ! isContentOnly && (
-
-
-
-
- ) }
-
- { typeof children === 'function'
- ? children( { onClose } )
- : Children.map( ( child ) =>
- cloneElement( child, { onClose } )
- ) }
- { canRemove && (
-
-
-
- ) }
- >
- ) }
-
- ) }
+ >
+ ) }
+
+ );
+ } }
);
}
diff --git a/packages/block-editor/src/components/block-switcher/index.js b/packages/block-editor/src/components/block-switcher/index.js
index 98e7f7b2d21420..79f33bd30d7537 100644
--- a/packages/block-editor/src/components/block-switcher/index.js
+++ b/packages/block-editor/src/components/block-switcher/index.js
@@ -35,36 +35,40 @@ function BlockSwitcherDropdownMenuContents( {
clientIds,
hasBlockStyles,
canRemove,
- isUsingBindings,
} ) {
const { replaceBlocks, multiSelect, updateBlockAttributes } =
useDispatch( blockEditorStore );
- const { possibleBlockTransformations, patterns, blocks } = useSelect(
- ( select ) => {
- const {
- getBlocksByClientId,
- getBlockRootClientId,
- getBlockTransformItems,
- __experimentalGetPatternTransformItems,
- } = select( blockEditorStore );
- const rootClientId = getBlockRootClientId(
- Array.isArray( clientIds ) ? clientIds[ 0 ] : clientIds
- );
- const _blocks = getBlocksByClientId( clientIds );
- return {
- blocks: _blocks,
- possibleBlockTransformations: getBlockTransformItems(
- _blocks,
- rootClientId
- ),
- patterns: __experimentalGetPatternTransformItems(
- _blocks,
- rootClientId
- ),
- };
- },
- [ clientIds ]
- );
+ const { possibleBlockTransformations, patterns, blocks, isUsingBindings } =
+ useSelect(
+ ( select ) => {
+ const {
+ getBlockAttributes,
+ getBlocksByClientId,
+ getBlockRootClientId,
+ getBlockTransformItems,
+ __experimentalGetPatternTransformItems,
+ } = select( blockEditorStore );
+ const rootClientId = getBlockRootClientId( clientIds[ 0 ] );
+ const _blocks = getBlocksByClientId( clientIds );
+ return {
+ blocks: _blocks,
+ possibleBlockTransformations: getBlockTransformItems(
+ _blocks,
+ rootClientId
+ ),
+ patterns: __experimentalGetPatternTransformItems(
+ _blocks,
+ rootClientId
+ ),
+ isUsingBindings: clientIds.every(
+ ( clientId ) =>
+ !! getBlockAttributes( clientId )?.metadata
+ ?.bindings
+ ),
+ };
+ },
+ [ clientIds ]
+ );
const blockVariationTransformations = useBlockVariationTransforms( {
clientIds,
blocks,
@@ -196,7 +200,7 @@ const BlockIndicator = ( { icon, showTitle, blockTitle } ) => (
>
);
-export const BlockSwitcher = ( { clientIds, disabled, isUsingBindings } ) => {
+export const BlockSwitcher = ( { clientIds } ) => {
const {
hasContentOnlyLocking,
canRemove,
@@ -205,6 +209,7 @@ export const BlockSwitcher = ( { clientIds, disabled, isUsingBindings } ) => {
invalidBlocks,
isReusable,
isTemplate,
+ isDisabled,
} = useSelect(
( select ) => {
const {
@@ -212,6 +217,7 @@ export const BlockSwitcher = ( { clientIds, disabled, isUsingBindings } ) => {
getBlocksByClientId,
getBlockAttributes,
canRemoveBlocks,
+ getBlockEditingMode,
} = select( blockEditorStore );
const { getBlockStyles, getBlockType, getActiveBlockVariation } =
select( blocksStore );
@@ -222,6 +228,7 @@ export const BlockSwitcher = ( { clientIds, disabled, isUsingBindings } ) => {
const [ { name: firstBlockName } ] = _blocks;
const _isSingleBlockSelected = _blocks.length === 1;
const blockType = getBlockType( firstBlockName );
+ const editingMode = getBlockEditingMode( clientIds[ 0 ] );
let _icon;
let _hasTemplateLock;
@@ -256,6 +263,7 @@ export const BlockSwitcher = ( { clientIds, disabled, isUsingBindings } ) => {
isTemplate:
_isSingleBlockSelected && isTemplatePart( _blocks[ 0 ] ),
hasContentOnlyLocking: _hasTemplateLock,
+ isDisabled: editingMode !== 'default',
};
},
[ clientIds ]
@@ -275,7 +283,7 @@ export const BlockSwitcher = ( { clientIds, disabled, isUsingBindings } ) => {
: __( 'Multiple blocks selected' );
const hideDropdown =
- disabled ||
+ isDisabled ||
( ! hasBlockStyles && ! canRemove ) ||
hasContentOnlyLocking;
@@ -339,7 +347,6 @@ export const BlockSwitcher = ( { clientIds, disabled, isUsingBindings } ) => {
clientIds={ clientIds }
hasBlockStyles={ hasBlockStyles }
canRemove={ canRemove }
- isUsingBindings={ isUsingBindings }
/>
) }
diff --git a/packages/block-editor/src/components/block-toolbar/index.js b/packages/block-editor/src/components/block-toolbar/index.js
index 6c4789cb2924f2..2ac2cbb12ff352 100644
--- a/packages/block-editor/src/components/block-toolbar/index.js
+++ b/packages/block-editor/src/components/block-toolbar/index.js
@@ -35,6 +35,7 @@ import { store as blockEditorStore } from '../../store';
import __unstableBlockNameContext from './block-name-context';
import NavigableToolbar from '../navigable-toolbar';
import { useHasBlockToolbar } from './use-has-block-toolbar';
+import { unlock } from '../../lock-unlock';
/**
* Renders the block toolbar.
@@ -58,7 +59,6 @@ export function PrivateBlockToolbar( {
const {
blockClientId,
blockClientIds,
- isContentOnlyEditingMode,
isDefaultEditingMode,
blockType,
toolbarKey,
@@ -78,12 +78,14 @@ export function PrivateBlockToolbar( {
getBlockAttributes,
getBlockParentsByBlockName,
getTemplateLock,
- } = select( blockEditorStore );
+ getParentSectionBlock,
+ } = unlock( select( blockEditorStore ) );
const selectedBlockClientIds = getSelectedBlockClientIds();
const selectedBlockClientId = selectedBlockClientIds[ 0 ];
const parents = getBlockParents( selectedBlockClientId );
- const firstParentClientId = parents[ parents.length - 1 ];
- const parentBlockName = getBlockName( firstParentClientId );
+ const parentSection = getParentSectionBlock( selectedBlockClientId );
+ const parentClientId = parentSection ?? parents[ parents.length - 1 ];
+ const parentBlockName = getBlockName( parentClientId );
const parentBlockType = getBlockType( parentBlockName );
const editingMode = getBlockEditingMode( selectedBlockClientId );
const _isDefaultEditingMode = editingMode === 'default';
@@ -112,21 +114,19 @@ export function PrivateBlockToolbar( {
return {
blockClientId: selectedBlockClientId,
blockClientIds: selectedBlockClientIds,
- isContentOnlyEditingMode: editingMode === 'contentOnly',
isDefaultEditingMode: _isDefaultEditingMode,
blockType: selectedBlockClientId && getBlockType( _blockName ),
shouldShowVisualToolbar: isValid && isVisual,
- toolbarKey: `${ selectedBlockClientId }${ firstParentClientId }`,
+ toolbarKey: `${ selectedBlockClientId }${ parentClientId }`,
showParentSelector:
parentBlockType &&
- getBlockEditingMode( firstParentClientId ) === 'default' &&
+ getBlockEditingMode( parentClientId ) !== 'disabled' &&
hasBlockSupport(
parentBlockType,
'__experimentalParentSelector',
true
) &&
- selectedBlockClientIds.length === 1 &&
- _isDefaultEditingMode,
+ selectedBlockClientIds.length === 1,
isUsingBindings: _isUsingBindings,
hasParentPattern: _hasParentPattern,
hasContentOnlyLocking: _hasTemplateLock,
@@ -179,36 +179,26 @@ export function PrivateBlockToolbar( {
key={ toolbarKey }
>
- { ! isMultiToolbar &&
- isLargeViewport &&
- isDefaultEditingMode &&
}
+ { ! isMultiToolbar && isLargeViewport && (
+
+ ) }
{ ( shouldShowVisualToolbar || isMultiToolbar ) &&
- ( isDefaultEditingMode ||
- ( isContentOnlyEditingMode && ! hasParentPattern ) ||
- isSynced ) && (
+ ! hasParentPattern && (
-
+ { ! isMultiToolbar && isDefaultEditingMode && (
+
+ ) }
+
- { isDefaultEditingMode && (
- <>
- { ! isMultiToolbar && (
-
- ) }
-
- >
- ) }
) }
@@ -242,9 +232,7 @@ export function PrivateBlockToolbar( {
>
) }
- { isDefaultEditingMode && (
-
- ) }
+
);
diff --git a/packages/block-editor/src/components/block-toolbar/style.scss b/packages/block-editor/src/components/block-toolbar/style.scss
index 40d748dd0a1568..2a0f68a6976686 100644
--- a/packages/block-editor/src/components/block-toolbar/style.scss
+++ b/packages/block-editor/src/components/block-toolbar/style.scss
@@ -52,9 +52,18 @@
> :last-child,
> :last-child .components-toolbar-group,
- > :last-child .components-toolbar {
+ > :last-child .components-toolbar,
+ // If the last toolbar group is empty,
+ // we need to remove the double border from the penultimate one.
+ &:has(> :last-child:empty) > :nth-last-child(2),
+ &:has(> :last-child:empty) > :nth-last-child(2) .components-toolbar-group,
+ &:has(> :last-child:empty) > :nth-last-child(2) .components-toolbar {
border-right: none;
}
+
+ .components-toolbar-group:empty {
+ display: none;
+ }
}
.block-editor-block-contextual-toolbar {
diff --git a/packages/block-editor/src/components/block-toolbar/use-has-block-toolbar.js b/packages/block-editor/src/components/block-toolbar/use-has-block-toolbar.js
index c4e228f8a3c07b..80ce3691147834 100644
--- a/packages/block-editor/src/components/block-toolbar/use-has-block-toolbar.js
+++ b/packages/block-editor/src/components/block-toolbar/use-has-block-toolbar.js
@@ -7,7 +7,6 @@ import { getBlockType, hasBlockSupport } from '@wordpress/blocks';
* Internal dependencies
*/
import { store as blockEditorStore } from '../../store';
-import { useHasAnyBlockControls } from '../block-controls/use-has-block-controls';
/**
* Returns true if the block toolbar should be shown.
@@ -15,40 +14,29 @@ import { useHasAnyBlockControls } from '../block-controls/use-has-block-controls
* @return {boolean} Whether the block toolbar component will be rendered.
*/
export function useHasBlockToolbar() {
- const { isToolbarEnabled, isDefaultEditingMode } = useSelect(
- ( select ) => {
- const {
- getBlockEditingMode,
- getBlockName,
- getBlockSelectionStart,
- } = select( blockEditorStore );
+ const { isToolbarEnabled, isBlockDisabled } = useSelect( ( select ) => {
+ const { getBlockEditingMode, getBlockName, getBlockSelectionStart } =
+ select( blockEditorStore );
- // we only care about the 1st selected block
- // for the toolbar, so we use getBlockSelectionStart
- // instead of getSelectedBlockClientIds
- const selectedBlockClientId = getBlockSelectionStart();
+ // we only care about the 1st selected block
+ // for the toolbar, so we use getBlockSelectionStart
+ // instead of getSelectedBlockClientIds
+ const selectedBlockClientId = getBlockSelectionStart();
- const blockType =
- selectedBlockClientId &&
- getBlockType( getBlockName( selectedBlockClientId ) );
+ const blockType =
+ selectedBlockClientId &&
+ getBlockType( getBlockName( selectedBlockClientId ) );
- return {
- isToolbarEnabled:
- blockType &&
- hasBlockSupport( blockType, '__experimentalToolbar', true ),
- isDefaultEditingMode:
- getBlockEditingMode( selectedBlockClientId ) === 'default',
- };
- },
- []
- );
+ return {
+ isToolbarEnabled:
+ blockType &&
+ hasBlockSupport( blockType, '__experimentalToolbar', true ),
+ isBlockDisabled:
+ getBlockEditingMode( selectedBlockClientId ) === 'disabled',
+ };
+ }, [] );
- const hasAnyBlockControls = useHasAnyBlockControls();
-
- if (
- ! isToolbarEnabled ||
- ( ! isDefaultEditingMode && ! hasAnyBlockControls )
- ) {
+ if ( ! isToolbarEnabled || isBlockDisabled ) {
return false;
}
diff --git a/packages/block-editor/src/store/private-selectors.js b/packages/block-editor/src/store/private-selectors.js
index d8955bd6342c4c..9e99176819ae89 100644
--- a/packages/block-editor/src/store/private-selectors.js
+++ b/packages/block-editor/src/store/private-selectors.js
@@ -116,6 +116,7 @@ export const getEnabledClientIdsTree = createSelector(
state.settings.templateLock,
state.blockListSettings,
state.editorMode,
+ getSectionRootClientId( state ),
]
);
diff --git a/packages/block-editor/src/store/utils.js b/packages/block-editor/src/store/utils.js
index af991608238e2e..79e15255e6cc15 100644
--- a/packages/block-editor/src/store/utils.js
+++ b/packages/block-editor/src/store/utils.js
@@ -10,6 +10,7 @@ import { parse as grammarParse } from '@wordpress/block-serialization-default-pa
import { selectBlockPatternsKey } from './private-keys';
import { unlock } from '../lock-unlock';
import { STORE_NAME } from './constants';
+import { getSectionRootClientId } from './private-selectors';
export const withRootClientIdOptionKey = Symbol( 'withRootClientId' );
@@ -118,5 +119,6 @@ export function getInsertBlockTypeDependants( state, rootClientId ) {
state.settings.templateLock,
state.blockEditingModes,
state.editorMode,
+ getSectionRootClientId( state ),
];
}