diff --git a/packages/base-styles/_default-custom-properties.scss b/packages/base-styles/_default-custom-properties.scss
index 52dfeb3899d77..5760753c48ce8 100644
--- a/packages/base-styles/_default-custom-properties.scss
+++ b/packages/base-styles/_default-custom-properties.scss
@@ -1,4 +1,3 @@
-
// It is important to include these styles in all built stylesheets.
// This allows to CSS variables post CSS plugin to generate fallbacks.
// It also provides default CSS variables for npm package consumers.
@@ -6,4 +5,5 @@
@include admin-scheme(#007cba);
--wp-block-synced-color: #7a00df;
--wp-block-synced-color--rgb: #{hex-to-rgb(#7a00df)};
+ --wp-bound-block-color: #9747ff;
}
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
new file mode 100644
index 0000000000000..4b2d3df725a66
--- /dev/null
+++ b/packages/block-editor/src/components/block-bindings-toolbar-indicator/index.js
@@ -0,0 +1,20 @@
+/**
+ * WordPress dependencies
+ */
+import { ToolbarItem, ToolbarGroup, Icon } from '@wordpress/components';
+import { connection } from '@wordpress/icons';
+import { _x } from '@wordpress/i18n';
+
+export default function BlockBindingsToolbarIndicator() {
+ return (
+
+
+
+
+
+ );
+}
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
new file mode 100644
index 0000000000000..4aeabdf8acf6e
--- /dev/null
+++ b/packages/block-editor/src/components/block-bindings-toolbar-indicator/style.scss
@@ -0,0 +1,14 @@
+.block-editor-block-bindings-toolbar-indicator {
+ display: inline-flex;
+ align-items: center;
+ height: 48px;
+ padding: 6px;
+
+ svg g {
+ stroke: var(--wp-bound-block-color);
+ fill: transparent;
+ stroke-width: 1.5;
+ stroke-linecap: round;
+ stroke-linejoin: round;
+ }
+}
diff --git a/packages/block-editor/src/components/block-list/use-block-props/index.js b/packages/block-editor/src/components/block-list/use-block-props/index.js
index 08b43fa46257e..c929c1014dc03 100644
--- a/packages/block-editor/src/components/block-list/use-block-props/index.js
+++ b/packages/block-editor/src/components/block-list/use-block-props/index.js
@@ -19,13 +19,17 @@ import useMovingAnimation from '../../use-moving-animation';
import { PrivateBlockContext } from '../private-block-context';
import { useFocusFirstElement } from './use-focus-first-element';
import { useIsHovered } from './use-is-hovered';
-import { useBlockEditContext } from '../../block-edit/context';
+import {
+ blockBindingsKey,
+ useBlockEditContext,
+} from '../../block-edit/context';
import { useFocusHandler } from './use-focus-handler';
import { useEventHandlers } from './use-selected-block-event-handlers';
import { useNavModeExit } from './use-nav-mode-exit';
import { useBlockRefProvider } from './use-block-refs';
import { useIntersectionObserver } from './use-intersection-observer';
import { useFlashEditableBlocks } from '../../use-flash-editable-blocks';
+import { canBindBlock } from '../../../hooks/use-bindings-attributes';
/**
* This hook is used to lightly mark an element as a block element. The element
@@ -123,6 +127,12 @@ export function useBlockProps( props = {}, { __unstableIsHtml } = {} ) {
] );
const blockEditContext = useBlockEditContext();
+ const hasBlockBindings = !! blockEditContext[ blockBindingsKey ];
+ const bindingsStyle =
+ hasBlockBindings && canBindBlock( name )
+ ? { '--wp-admin-theme-color': 'var(--wp-bound-block-color)' }
+ : {};
+
// Ensures it warns only inside the `edit` implementation for the block.
if ( blockApiVersion < 2 && clientId === blockEditContext.clientId ) {
warning(
@@ -168,7 +178,7 @@ export function useBlockProps( props = {}, { __unstableIsHtml } = {} ) {
wrapperProps.className,
defaultClassName
),
- style: { ...wrapperProps.style, ...props.style },
+ style: { ...wrapperProps.style, ...props.style, ...bindingsStyle },
};
}
diff --git a/packages/block-editor/src/components/block-toolbar/index.js b/packages/block-editor/src/components/block-toolbar/index.js
index a641df0f7e5cf..e566096d54f26 100644
--- a/packages/block-editor/src/components/block-toolbar/index.js
+++ b/packages/block-editor/src/components/block-toolbar/index.js
@@ -35,7 +35,9 @@ import { store as blockEditorStore } from '../../store';
import __unstableBlockNameContext from './block-name-context';
import NavigableToolbar from '../navigable-toolbar';
import Shuffle from './shuffle';
+import BlockBindingsIndicator from '../block-bindings-toolbar-indicator';
import { useHasBlockToolbar } from './use-has-block-toolbar';
+import { canBindBlock } from '../../hooks/use-bindings-attributes';
/**
* Renders the block toolbar.
*
@@ -60,8 +62,10 @@ export function PrivateBlockToolbar( {
blockClientIds,
isDefaultEditingMode,
blockType,
+ blockName,
shouldShowVisualToolbar,
showParentSelector,
+ isUsingBindings,
} = useSelect( ( select ) => {
const {
getBlockName,
@@ -71,6 +75,7 @@ export function PrivateBlockToolbar( {
isBlockValid,
getBlockRootClientId,
getBlockEditingMode,
+ getBlockAttributes,
} = select( blockEditorStore );
const selectedBlockClientIds = getSelectedBlockClientIds();
const selectedBlockClientId = selectedBlockClientIds[ 0 ];
@@ -81,20 +86,21 @@ export function PrivateBlockToolbar( {
const parentBlockType = getBlockType( parentBlockName );
const _isDefaultEditingMode =
getBlockEditingMode( selectedBlockClientId ) === 'default';
+ const _blockName = getBlockName( selectedBlockClientId );
const isValid = selectedBlockClientIds.every( ( id ) =>
isBlockValid( id )
);
const isVisual = selectedBlockClientIds.every(
( id ) => getBlockMode( id ) === 'visual'
);
+ const _isUsingBindings = !! getBlockAttributes( selectedBlockClientId )
+ ?.metadata?.bindings;
return {
blockClientId: selectedBlockClientId,
blockClientIds: selectedBlockClientIds,
isDefaultEditingMode: _isDefaultEditingMode,
- blockType:
- selectedBlockClientId &&
- getBlockType( getBlockName( selectedBlockClientId ) ),
-
+ blockName: _blockName,
+ blockType: selectedBlockClientId && getBlockType( _blockName ),
shouldShowVisualToolbar: isValid && isVisual,
rootClientId: blockRootClientId,
showParentSelector:
@@ -107,6 +113,7 @@ export function PrivateBlockToolbar( {
) &&
selectedBlockClientIds.length === 1 &&
_isDefaultEditingMode,
+ isUsingBindings: _isUsingBindings,
};
}, [] );
@@ -158,6 +165,9 @@ export function PrivateBlockToolbar( {
{ ! isMultiToolbar &&
isLargeViewport &&
isDefaultEditingMode && }
+ { isUsingBindings && canBindBlock( blockName ) && (
+
+ ) }
{ ( shouldShowVisualToolbar || isMultiToolbar ) &&
isDefaultEditingMode && (
) }
+ { isConnected && canBindBlock( blockName ) && (
+
+
+
+ ) }
{ positionLabel && isSticky && (
diff --git a/packages/block-editor/src/components/list-view/style.scss b/packages/block-editor/src/components/list-view/style.scss
index 11cf1fafa0e14..1245bfbabcb7a 100644
--- a/packages/block-editor/src/components/list-view/style.scss
+++ b/packages/block-editor/src/components/list-view/style.scss
@@ -557,3 +557,11 @@ $block-navigation-max-indent: 8;
.list-view-appender__description {
display: none;
}
+
+.block-editor-list-view-block-select-button__bindings svg g {
+ stroke: var(--wp-bound-block-color);
+ fill: transparent;
+ stroke-width: 1.5;
+ stroke-linecap: round;
+ stroke-linejoin: round;
+}
diff --git a/packages/block-editor/src/style.scss b/packages/block-editor/src/style.scss
index 1cbc49f58551e..015cffde42a23 100644
--- a/packages/block-editor/src/style.scss
+++ b/packages/block-editor/src/style.scss
@@ -1,5 +1,6 @@
@import "./autocompleters/style.scss";
@import "./components/block-alignment-control/style.scss";
+@import "./components/block-bindings-toolbar-indicator/style.scss";
@import "./components/block-canvas/style.scss";
@import "./components/block-icon/style.scss";
@import "./components/block-inspector/style.scss";
diff --git a/packages/icons/src/index.js b/packages/icons/src/index.js
index 1d3c8c24c5cfd..788fc0152ba1d 100644
--- a/packages/icons/src/index.js
+++ b/packages/icons/src/index.js
@@ -59,6 +59,7 @@ export { default as commentAuthorName } from './library/comment-author-name';
export { default as commentContent } from './library/comment-content';
export { default as commentReplyLink } from './library/comment-reply-link';
export { default as commentEditLink } from './library/comment-edit-link';
+export { default as connection } from './library/connection';
export { default as cover } from './library/cover';
export { default as create } from './library/create';
export { default as crop } from './library/crop';
diff --git a/packages/icons/src/library/connection.js b/packages/icons/src/library/connection.js
new file mode 100644
index 0000000000000..47cee6c66a3ef
--- /dev/null
+++ b/packages/icons/src/library/connection.js
@@ -0,0 +1,25 @@
+/**
+ * WordPress dependencies
+ */
+import { SVG, Path, G } from '@wordpress/primitives';
+
+const connection = (
+
+);
+
+export default connection;