From 1c4418b93ae8979823f3ad7ff7be870c966b6b29 Mon Sep 17 00:00:00 2001 From: Michal Date: Thu, 29 Feb 2024 19:32:49 +0000 Subject: [PATCH] Add visual indicator if a block is connected to block binding source (#59185) * Add BlockControlsFirst slot to block controls groups * Add connection icon to BlockControls toolbar button * Add block binding toolbar button if block is connected to a source * Add i18n support for block toolbar button label * Add BlockBindingsButton component and remove BlockControlsFirst group * Refactor BlockBindingsButton to check for block connections * Change the ToolbarButton label * Update block-bindings-button import to block-bindings-indicator * Block Bindings: Add connection icon to list view (#59331) * Add connection icon to list view * Remove extraneous string * Move bindings style to useBlockProps * Remove extraneous comment * Move bindings selector logic to toolbar * Rename indicator file * Move purple stroke style from SVG markup to CSS * Check if block can be bound before adding styles * Simplify the SVG icon: - get rid of 2 unnecessary `` elements - move the stroke styles to CSS - add the `evenodd` rule * Update the CSS namespacing to include the `__` * Fix issues with block binding indicator color --------- Co-authored-by: michalczaplinski Co-authored-by: artemiomorales Co-authored-by: Mamaduka Co-authored-by: gziolo Co-authored-by: SantosGuillamot Co-authored-by: jasmussen Co-authored-by: SaxonF Co-authored-by: afercia --- .../_default-custom-properties.scss | 2 +- .../block-bindings-toolbar-indicator/index.js | 20 +++++++++++++++ .../style.scss | 14 +++++++++++ .../block-list/use-block-props/index.js | 14 +++++++++-- .../src/components/block-toolbar/index.js | 18 ++++++++++--- .../list-view/block-select-button.js | 18 +++++++++++-- .../src/components/list-view/style.scss | 8 ++++++ packages/block-editor/src/style.scss | 1 + packages/icons/src/index.js | 1 + packages/icons/src/library/connection.js | 25 +++++++++++++++++++ 10 files changed, 112 insertions(+), 9 deletions(-) create mode 100644 packages/block-editor/src/components/block-bindings-toolbar-indicator/index.js create mode 100644 packages/block-editor/src/components/block-bindings-toolbar-indicator/style.scss create mode 100644 packages/icons/src/library/connection.js 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;