diff --git a/packages/block-editor/src/components/block-list/block-popover.js b/packages/block-editor/src/components/block-list/block-popover.js index d197e663964791..2919019d849a9e 100644 --- a/packages/block-editor/src/components/block-list/block-popover.js +++ b/packages/block-editor/src/components/block-list/block-popover.js @@ -20,7 +20,7 @@ import { useViewportMatch } from '@wordpress/compose'; import BlockBreadcrumb from './breadcrumb'; import BlockContextualToolbar from './block-contextual-toolbar'; import Inserter from '../inserter'; -import { SelectedBlockNode } from './root-container'; +import { BlockNodes } from './root-container'; function selector( select ) { const { @@ -30,6 +30,7 @@ function selector( select ) { isTyping, isCaretWithinFormattedText, getSettings, + getLastMultiSelectedBlockClientId, } = select( 'core/block-editor' ); return { isNavigationMode: isNavigationMode(), @@ -38,6 +39,7 @@ function selector( select ) { isCaretWithinFormattedText: isCaretWithinFormattedText(), hasMultiSelection: hasMultiSelection(), hasFixedToolbar: getSettings().hasFixedToolbar, + lastClientId: getLastMultiSelectedBlockClientId(), }; } @@ -58,11 +60,12 @@ function BlockPopover( { isCaretWithinFormattedText, hasMultiSelection, hasFixedToolbar, + lastClientId, } = useSelect( selector, [] ); const isLargeViewport = useViewportMatch( 'medium' ); const [ isToolbarForced, setIsToolbarForced ] = useState( false ); const [ isInserterShown, setIsInserterShown ] = useState( false ); - let [ node ] = useContext( SelectedBlockNode ); + const [ blockNodes ] = useContext( BlockNodes ); const showEmptyBlockSideInserter = ! isNavigationMode && isEmptyDefaultBlock && isValid; const shouldShowBreadcrumb = isNavigationMode; @@ -94,6 +97,8 @@ function BlockPopover( { return null; } + let node = blockNodes[ clientId ]; + if ( capturingClientId ) { node = document.getElementById( 'block-' + capturingClientId ); } @@ -107,6 +112,15 @@ function BlockPopover( { node = node.querySelector( '.is-block-content' ) || node; } + let anchorRef = node; + + if ( hasMultiSelection ) { + anchorRef = { + top: blockNodes[ clientId ], + bottom: blockNodes[ lastClientId ], + }; + } + function onFocus() { setIsInserterShown( true ); } @@ -120,7 +134,6 @@ function BlockPopover( { // position in the right corner. // To do: refactor `Popover` to make this prop clearer. const popoverPosition = showEmptyBlockSideInserter ? 'top left right' : 'top right left'; - const popoverIsSticky = hasMultiSelection ? '.wp-block.is-multi-selected' : true; return ( { @@ -90,14 +91,14 @@ function BlockListBlock( { const wrapper = useRef( null ); useLayoutEffect( () => { - if ( isSelected || isFirstMultiSelected ) { + if ( isSelected || isFirstMultiSelected || isLastMultiSelected ) { const node = wrapper.current; - setSelectedBlockNode( node ); + setBlockNodes( ( nodes ) => ( { ...nodes, [ clientId ]: node } ) ); return () => { - setSelectedBlockNode( ( n ) => n === node ? null : n ); + setBlockNodes( ( nodes ) => omit( nodes, clientId ) ); }; } - }, [ isSelected, isFirstMultiSelected ] ); + }, [ isSelected, isFirstMultiSelected, isLastMultiSelected ] ); // Handling the error state const [ hasError, setErrorState ] = useState( false ); @@ -332,6 +333,7 @@ const applyWithSelect = withSelect( isAncestorMultiSelected, isBlockMultiSelected, isFirstMultiSelectedBlock, + getLastMultiSelectedBlockClientId, isTyping, getBlockMode, isSelectionEnabled, @@ -362,6 +364,7 @@ const applyWithSelect = withSelect( isPartOfMultiSelection: isBlockMultiSelected( clientId ) || isAncestorMultiSelected( clientId ), isFirstMultiSelected: isFirstMultiSelectedBlock( clientId ), + isLastMultiSelected: getLastMultiSelectedBlockClientId() === clientId, // We only care about this prop when the block is selected // Thus to avoid unnecessary rerenders we avoid updating the prop if the block is not selected. diff --git a/packages/block-editor/src/components/block-list/root-container.js b/packages/block-editor/src/components/block-list/root-container.js index aae8c476c418b5..84985f8fd7c072 100644 --- a/packages/block-editor/src/components/block-list/root-container.js +++ b/packages/block-editor/src/components/block-list/root-container.js @@ -15,7 +15,7 @@ import BlockPopover from './block-popover'; /** @typedef {import('@wordpress/element').WPSyntheticEvent} WPSyntheticEvent */ export const Context = createContext(); -export const SelectedBlockNode = createContext(); +export const BlockNodes = createContext(); function selector( select ) { const { @@ -81,7 +81,7 @@ function RootContainer( { children, className }, ref ) { selectedBlockClientId={ selectedBlockClientId } containerRef={ ref } > - +
-
+ ); } diff --git a/packages/components/src/popover/index.js b/packages/components/src/popover/index.js index 82cb3874db2fb3..a97c964e065972 100644 --- a/packages/components/src/popover/index.js +++ b/packages/components/src/popover/index.js @@ -62,7 +62,25 @@ function computeAnchorRect( return getRectangleFromRange( anchorRef ); } - const rect = anchorRef.getBoundingClientRect(); + if ( anchorRef instanceof window.Element ) { + const rect = anchorRef.getBoundingClientRect(); + + if ( shouldAnchorIncludePadding ) { + return rect; + } + + return withoutPadding( rect, anchorRef ); + } + + const { top, bottom } = anchorRef; + const topRect = top.getBoundingClientRect(); + const bottomRect = bottom.getBoundingClientRect(); + const rect = new window.DOMRect( + topRect.left, + topRect.top, + topRect.width, + bottomRect.bottom - topRect.top + ); if ( shouldAnchorIncludePadding ) { return rect; @@ -301,7 +319,7 @@ const Popover = ( { yAxis, contentHeight, contentWidth, - } = computePopoverPosition( anchor, contentRect.current, position, __unstableSticky, anchorRef, relativeOffsetTop ); + } = computePopoverPosition( anchor, contentRect.current, position, __unstableSticky, containerRef.current, relativeOffsetTop ); if ( typeof popoverTop === 'number' && typeof popoverLeft === 'number' ) { if ( subpixels && __unstableAllowVerticalSubpixelPosition ) { diff --git a/packages/components/src/popover/utils.js b/packages/components/src/popover/utils.js index f729247c98d7f5..187f3c292a5642 100644 --- a/packages/components/src/popover/utils.js +++ b/packages/components/src/popover/utils.js @@ -128,27 +128,13 @@ export function computePopoverYAxisPosition( anchorRect, contentSize, yAxis, cor const { height } = contentSize; if ( sticky ) { - let topEl = anchorRef; - let bottomEl = anchorRef; - - if ( typeof sticky === 'string' ) { - const elements = document.querySelectorAll( sticky ); - - if ( elements.length ) { - topEl = elements[ 0 ]; - bottomEl = elements[ elements.length - 1 ]; - } - } - - const scrollContainerEl = getScrollContainer( topEl ) || document.body; + const scrollContainerEl = getScrollContainer( anchorRef ) || document.body; const scrollRect = scrollContainerEl.getBoundingClientRect(); - const topRect = topEl.getBoundingClientRect(); - const bottomRect = bottomEl.getBoundingClientRect(); - if ( topRect.top - height <= scrollRect.top ) { + if ( anchorRect.top - height <= scrollRect.top ) { return { yAxis, - popoverTop: Math.min( bottomRect.bottom - relativeOffsetTop, scrollRect.top + height - relativeOffsetTop ), + popoverTop: Math.min( anchorRect.bottom - relativeOffsetTop, scrollRect.top + height - relativeOffsetTop ), }; } }