diff --git a/packages/block-editor/src/components/block-bindings-toolbar-indicator/index.js b/packages/block-editor/src/components/block-bindings-toolbar-indicator/index.js
index 1c9b0fd8a7cc73..bc04593f7ed2a6 100644
--- a/packages/block-editor/src/components/block-bindings-toolbar-indicator/index.js
+++ b/packages/block-editor/src/components/block-bindings-toolbar-indicator/index.js
@@ -1,19 +1,134 @@
/**
* WordPress dependencies
*/
-import { ToolbarItem, ToolbarGroup, Icon } from '@wordpress/components';
-import { connection } from '@wordpress/icons';
-import { _x } from '@wordpress/i18n';
+import { useId } from '@wordpress/element';
+import { __, sprintf, _x } from '@wordpress/i18n';
+import {
+ DropdownMenu,
+ ToolbarGroup,
+ ToolbarItem,
+ __experimentalText as Text,
+} from '@wordpress/components';
+import { store as blocksStore } from '@wordpress/blocks';
+import { useSelect } from '@wordpress/data';
+import { copy } from '@wordpress/icons';
+
+/**
+ * Internal dependencies
+ */
+import { store as blockEditorStore } from '../../store';
+import BlockIcon from '../block-icon';
+import useBlockDisplayTitle from '../block-title/use-block-display-title';
+
+export default function BlockBindingsToolbarIndicator( { clientIds } ) {
+ const isSingleBlockSelected = clientIds.length === 1;
+ const { icon, firstBlockName, isConnectedToPatternOverrides } = useSelect(
+ ( select ) => {
+ const {
+ getBlockAttributes,
+ getBlockNamesByClientId,
+ getBlocksByClientId,
+ } = select( blockEditorStore );
+ const { getBlockType, getActiveBlockVariation } =
+ select( blocksStore );
+ const blockTypeNames = getBlockNamesByClientId( clientIds );
+ const _firstBlockTypeName = blockTypeNames[ 0 ];
+ const firstBlockType = getBlockType( _firstBlockTypeName );
+ let _icon;
+ if ( isSingleBlockSelected ) {
+ const match = getActiveBlockVariation(
+ _firstBlockTypeName,
+ getBlockAttributes( clientIds[ 0 ] )
+ );
+ // Take into account active block variations.
+ _icon = match?.icon || firstBlockType.icon;
+ } else {
+ const isSelectionOfSameType =
+ new Set( blockTypeNames ).size === 1;
+ // When selection consists of blocks of multiple types, display an
+ // appropriate icon to communicate the non-uniformity.
+ _icon = isSelectionOfSameType ? firstBlockType.icon : copy;
+ }
+
+ return {
+ icon: _icon,
+ firstBlockName: getBlockAttributes( clientIds[ 0 ] ).metadata
+ .name,
+ isConnectedToPatternOverrides: getBlocksByClientId(
+ clientIds
+ ).some( ( block ) =>
+ Object.values( block?.attributes.metadata?.bindings ).some(
+ ( binding ) =>
+ binding.source === 'core/pattern-overrides'
+ )
+ ),
+ };
+ },
+ [ clientIds, isSingleBlockSelected ]
+ );
+ const firstBlockTitle = useBlockDisplayTitle( {
+ clientId: clientIds[ 0 ],
+ maximumLength: 35,
+ } );
+
+ let blockDescription = isSingleBlockSelected
+ ? _x(
+ 'This block is connected.',
+ 'block toolbar button label and description'
+ )
+ : _x(
+ 'These blocks are connected.',
+ 'block toolbar button label and description'
+ );
+ if ( isConnectedToPatternOverrides && firstBlockName ) {
+ blockDescription = isSingleBlockSelected
+ ? sprintf(
+ /* translators: %1s: The block type's name; %2s: The block's user-provided name (the same as the override name). */
+ __( 'This %1$s is editable using the "%2$s" override.' ),
+ firstBlockTitle.toLowerCase(),
+ firstBlockName
+ )
+ : __( 'These blocks are editable using overrides.' );
+ }
+ const descriptionId = useId();
-export default function BlockBindingsToolbarIndicator() {
return (
-
-
+
+ { ( toggleProps ) => (
+
+
+ >
+ }
+ toggleProps={ {
+ describedBy: blockDescription,
+ ...toggleProps,
+ } }
+ menuProps={ {
+ orientation: 'both',
+ 'aria-describedby': descriptionId,
+ } }
+ >
+ { () => (
+
+ { blockDescription }
+
+ ) }
+
+ ) }
);
diff --git a/packages/block-editor/src/components/block-bindings-toolbar-indicator/style.scss b/packages/block-editor/src/components/block-bindings-toolbar-indicator/style.scss
index 4565473ec95eb4..f37276290ca713 100644
--- a/packages/block-editor/src/components/block-bindings-toolbar-indicator/style.scss
+++ b/packages/block-editor/src/components/block-bindings-toolbar-indicator/style.scss
@@ -1,11 +1,10 @@
-.block-editor-block-bindings-toolbar-indicator {
- display: inline-flex;
- align-items: center;
- justify-content: center;
- padding: 6px;
- svg {
- fill: var(--wp-block-synced-color);
- }
+.block-editor-block-bindings-toolbar-indicator__popover .components-popover__content {
+ min-width: 260px;
+ padding: $grid-unit-20;
+}
+
+.block-editor-block-bindings-toolbar-indicator .block-editor-block-bindings-toolbar-indicator-icon.has-colors svg {
+ fill: var(--wp-block-synced-color);
}
.editor-collapsible-block-toolbar .block-editor-block-bindings-toolbar-indicator {
diff --git a/packages/block-editor/src/components/block-toolbar/index.js b/packages/block-editor/src/components/block-toolbar/index.js
index c3570c4a007f16..0e341d32163952 100644
--- a/packages/block-editor/src/components/block-toolbar/index.js
+++ b/packages/block-editor/src/components/block-toolbar/index.js
@@ -168,7 +168,7 @@ export function PrivateBlockToolbar( {
isLargeViewport &&
isDefaultEditingMode && }
{ isUsingBindings && canBindBlock( blockName ) && (
-
+
) }
{ ( shouldShowVisualToolbar || isMultiToolbar ) &&
( isDefaultEditingMode || isSynced ) && (