From 7ebc19af92df127fa4ec18382b24f7206e9606d1 Mon Sep 17 00:00:00 2001 From: Andrei Draganescu Date: Thu, 17 Oct 2024 16:23:56 +0300 Subject: [PATCH] Manual cherry pick for #65627 (#66119) * Make zoom out vertical toolbar consistent (#65627) * enable vertical toolbar for non full width elements, anchor based on parent * Update packages/block-editor/src/components/block-popover/index.js Co-authored-by: Dave Smith * Update packages/block-editor/src/components/block-popover/index.js Co-authored-by: Dave Smith * make zoom out check a dependency of the memoization, improve code readability * comment typos * subscribe to state instead of calculating zoom out view state when calculating the anchor * get the section wrapper for anchoring instead of the parent * use a selector instead of computing on the fly the parent section * check if the block element exists yet before computing the anchor * check if the block element exists yet before computing the anchor * differentiate between section toolbar and block toolbar for correct positioning when both are visible * address some nits * make the select in anchor setting rerun when block selection changes * fix bug with anchor rect when zoom out not engaged * fix typo * Use root container element in post editor as popover anchor * improve comment * improve comment to max improvement possible Co-authored-by: Dave Smith * mega nit commit Co-authored-by: Dave Smith * Fix bug with Posts with no full width blocks * give up on section root, always seek canvas element to position vertical toolbar, also fix typo * introduce the concept of canvas via a 1st variable * Use `__unstableContentRef` for zoomed out toolbar positioning instead of dom classname --------- Co-authored-by: draganescu Co-authored-by: getdave Co-authored-by: talldan Co-authored-by: ciampo Co-authored-by: jsnajdr Co-authored-by: MaggieCabrera Co-authored-by: richtabor Co-authored-by: stokesman Co-authored-by: andrewserong * bring over two private selectors from trunk the fix depends on --------- Co-authored-by: Dave Smith Co-authored-by: getdave Co-authored-by: talldan Co-authored-by: ciampo Co-authored-by: jsnajdr Co-authored-by: MaggieCabrera Co-authored-by: richtabor Co-authored-by: stokesman Co-authored-by: andrewserong --- .../src/components/block-popover/index.js | 66 ++++++++++++++++++- .../block-tools/use-show-block-tools.js | 12 +++- .../block-tools/zoom-out-popover.js | 3 +- .../src/store/private-selectors.js | 38 +++++++++++ 4 files changed, 113 insertions(+), 6 deletions(-) diff --git a/packages/block-editor/src/components/block-popover/index.js b/packages/block-editor/src/components/block-popover/index.js index 47022e336e486..637ab1cb8a53e 100644 --- a/packages/block-editor/src/components/block-popover/index.js +++ b/packages/block-editor/src/components/block-popover/index.js @@ -8,6 +8,7 @@ import clsx from 'clsx'; */ import { useMergeRefs } from '@wordpress/compose'; import { Popover } from '@wordpress/components'; +import { useSelect } from '@wordpress/data'; import { forwardRef, useMemo, @@ -21,6 +22,8 @@ import { import { useBlockElement } from '../block-list/use-block-props/use-block-refs'; import usePopoverScroll from './use-popover-scroll'; import { rectUnion, getVisibleElementBounds } from '../../utils/dom'; +import { store as blockEditorStore } from '../../store'; +import { unlock } from '../../lock-unlock'; const MAX_POPOVER_RECOMPUTE_COUNTER = Number.MAX_SAFE_INTEGER; @@ -74,12 +77,38 @@ function BlockPopover( }; }, [ selectedElement ] ); + const { isZoomOut, parentSectionBlock, isSectionSelected } = useSelect( + ( select ) => { + const { + isZoomOut: isZoomOutSelector, + getSectionRootClientId, + getParentSectionBlock, + getBlockOrder, + } = unlock( select( blockEditorStore ) ); + + return { + isZoomOut: isZoomOutSelector(), + parentSectionBlock: + getParentSectionBlock( clientId ) ?? clientId, + isSectionSelected: getBlockOrder( + getSectionRootClientId() + ).includes( clientId ), + }; + }, + [ clientId ] + ); + + // This element is used to position the zoom out view vertical toolbar + // correctly, relative to the selected section. + const parentSectionElement = useBlockElement( parentSectionBlock ); + const popoverAnchor = useMemo( () => { if ( // popoverDimensionsRecomputeCounter is by definition always equal or greater // than 0. This check is only there to satisfy the correctness of the // exhaustive-deps rule for the `useMemo` hook. popoverDimensionsRecomputeCounter < 0 || + ( isZoomOut && ! parentSectionElement ) || ! selectedElement || ( bottomClientId && ! lastSelectedElement ) ) { @@ -88,6 +117,35 @@ function BlockPopover( return { getBoundingClientRect() { + // The zoom out view has a vertical block toolbar that should always + // be on the edge of the canvas, aligned to the top of the currently + // selected section. This condition changes the anchor of the toolbar + // to the section instead of the block to handle blocks that are + // not full width and nested blocks to keep section height. + if ( isZoomOut && isSectionSelected ) { + // Compute the height based on the parent section of the + // selected block, because the selected block may be + // shorter than the section. + const canvasElementRect = getVisibleElementBounds( + __unstableContentRef.current + ); + const parentSectionElementRect = + getVisibleElementBounds( parentSectionElement ); + const anchorHeight = + parentSectionElementRect.bottom - + parentSectionElementRect.top; + + // Always use the width of the section root element to make sure + // the toolbar is always on the edge of the canvas. + const anchorWidth = canvasElementRect.width; + return new window.DOMRectReadOnly( + canvasElementRect.left, + parentSectionElementRect.top, + anchorWidth, + anchorHeight + ); + } + return lastSelectedElement ? rectUnion( getVisibleElementBounds( selectedElement ), @@ -98,10 +156,14 @@ function BlockPopover( contextElement: selectedElement, }; }, [ + popoverDimensionsRecomputeCounter, + isZoomOut, + parentSectionElement, + selectedElement, bottomClientId, lastSelectedElement, - selectedElement, - popoverDimensionsRecomputeCounter, + isSectionSelected, + __unstableContentRef, ] ); if ( ! selectedElement || ( bottomClientId && ! lastSelectedElement ) ) { diff --git a/packages/block-editor/src/components/block-tools/use-show-block-tools.js b/packages/block-editor/src/components/block-tools/use-show-block-tools.js index 07e0ebd16a64b..d8bc84a86e67a 100644 --- a/packages/block-editor/src/components/block-tools/use-show-block-tools.js +++ b/packages/block-editor/src/components/block-tools/use-show-block-tools.js @@ -8,6 +8,7 @@ import { isUnmodifiedDefaultBlock } from '@wordpress/blocks'; * Internal dependencies */ import { store as blockEditorStore } from '../../store'; +import { unlock } from '../../lock-unlock'; /** * Source of truth for which block tools are showing in the block editor. @@ -25,7 +26,9 @@ export function useShowBlockTools() { hasMultiSelection, __unstableGetEditorMode, isTyping, - } = select( blockEditorStore ); + getBlockOrder, + getSectionRootClientId, + } = unlock( select( blockEditorStore ) ); const clientId = getSelectedBlockClientId() || getFirstMultiSelectedBlockClientId(); @@ -48,11 +51,14 @@ export function useShowBlockTools() { editorMode === 'navigation'; const isZoomOut = editorMode === 'zoom-out'; + const isSectionSelected = getBlockOrder( + getSectionRootClientId() + ).includes( clientId ); const _showZoomOutToolbar = + clientId && isZoomOut && - block?.attributes?.align === 'full' && ! _showEmptyBlockSideInserter && - ! maybeShowBreadcrumb; + isSectionSelected; const _showBlockToolbarPopover = ! _showZoomOutToolbar && ! getSettings().hasFixedToolbar && diff --git a/packages/block-editor/src/components/block-tools/zoom-out-popover.js b/packages/block-editor/src/components/block-tools/zoom-out-popover.js index a1f2990a5cc1e..7a5c2243cf054 100644 --- a/packages/block-editor/src/components/block-tools/zoom-out-popover.js +++ b/packages/block-editor/src/components/block-tools/zoom-out-popover.js @@ -5,7 +5,7 @@ import clsx from 'clsx'; /** * Internal dependencies */ -import BlockPopover from '../block-popover'; +import { PrivateBlockPopover as BlockPopover } from '../block-popover'; import useBlockToolbarPopoverProps from './use-block-toolbar-popover-props'; import useSelectedBlockToolProps from './use-selected-block-tool-props'; import ZoomOutToolbar from './zoom-out-toolbar'; @@ -29,6 +29,7 @@ export default function ZoomOutPopover( { clientId, __unstableContentRef } ) { return ( { + let current = clientId; + let result; + while ( ! result && ( current = state.blocks.parents.get( current ) ) ) { + if ( isSectionBlock( state, current ) ) { + result = current; + } + } + return result; +}; + +/** + * Retrieves the client ID is a content locking parent + * + * @param {Object} state Global application state. + * @param {string} clientId Client Id of the block. + * + * @return {boolean} Whether the block is a content locking parent. + */ +export function isSectionBlock( state, clientId ) { + const sectionRootClientId = getSectionRootClientId( state ); + const sectionClientIds = getBlockOrder( state, sectionRootClientId ); + return ( + getBlockName( state, clientId ) === 'core/block' || + getTemplateLock( state, clientId ) === 'contentOnly' || + ( isNavigationMode( state ) && sectionClientIds.includes( clientId ) ) + ); +}