Skip to content

Commit

Permalink
Use element boundary as positioning context for toolbar
Browse files Browse the repository at this point in the history
  • Loading branch information
getdave authored and scruffian committed Apr 27, 2022
1 parent 48e2e83 commit 0d939c5
Show file tree
Hide file tree
Showing 2 changed files with 105 additions and 2 deletions.
106 changes: 104 additions & 2 deletions packages/block-editor/src/components/block-popover/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ import classnames from 'classnames';
*/
import { Popover } from '@wordpress/components';
import { getScrollContainer } from '@wordpress/dom';
import { useMemo } from '@wordpress/element';
import { useMemo, useLayoutEffect, useState } from '@wordpress/element';

/**
* Internal dependencies
Expand All @@ -19,14 +19,116 @@ import usePopoverScroll from './use-popover-scroll';
export default function BlockPopover( {
clientId,
bottomClientId,
rootClientId,
children,
__unstableRefreshSize,
__unstableCoverTarget = false,
__unstablePopoverSlot,
__unstableContentRef,
...props
} ) {
const [ layoutBound, setLayoutBound ] = useState( null );
const selectedElement = useBlockElement( clientId );
const rootElement = useBlockElement( rootClientId );

useLayoutEffect( () => {
if ( ! rootElement ) {
return;
}

const parentBoundary = rootElement?.getBoundingClientRect()?.bottom;

const elChildren = selectedElement?.children;

function isVisible( el ) {
const hasHeight = el.offsetHeight > 0;

const compStyle = window.getComputedStyle( el );

return (
hasHeight ||
compStyle.visibility !== 'hidden' ||
compStyle.opacity !== 0 ||
! el.classList.contains( 'components-visually-hidden' )
);
}

function getOutsideBounds( nodes, currCandidate ) {
if ( ! nodes?.length ) {
return null;
}

return Array.from( nodes )?.reduce(
( acc, curr ) => {
if ( ! isVisible( curr ) ) {
return acc;
}

const bottom = curr.getBoundingClientRect().bottom;

// Update
if (
bottom > parentBoundary && // is beyond parent bottom edge
bottom > acc[ 0 ] && // is beyond bototm edge of any of its siblings
bottom > currCandidate[ 0 ] // is beyond the bottom edge of the current largest candidate element
) {
acc = [ bottom, curr ];
}

// Recurse through child elements.
if ( curr?.children?.length ) {
const childResults = getOutsideBounds(
curr.children,
acc
);

if ( childResults ) {
acc.concat( childResults );
}
}

return acc;
},
[ 0 ]
);
}

function checkLayoutBounds() {
if ( ! elChildren?.length ) {
return;
}

const candidate = getOutsideBounds( elChildren, [ 0 ] );

if ( candidate ) {
setLayoutBound( candidate[ 1 ] );
}
}

checkLayoutBounds();

// Watch for DOM changes and check bounds to determine toolbar anchoring.
const observer = new window.MutationObserver( checkLayoutBounds );

observer.observe( selectedElement, {
attributes: true,
childList: true,
subtree: true,
} );

return () => {
observer.disconnect();
};

// console.log( {
// selectedElement,
// rootElement,
// elChildren,
// boundary,
// candidate,
// } );
}, [ rootElement, selectedElement ] );

const lastSelectedElement = useBlockElement( bottomClientId ?? clientId );
const popoverScrollRef = usePopoverScroll( __unstableContentRef );
const style = useMemo( () => {
Expand All @@ -47,7 +149,7 @@ export default function BlockPopover( {

const anchorRef = {
top: selectedElement,
bottom: lastSelectedElement,
bottom: layoutBound ?? lastSelectedElement,
};

const { ownerDocument } = selectedElement;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -129,6 +129,7 @@ function SelectedBlockPopover( {

return (
<BlockPopover
rootClientId={ rootClientId }
clientId={ capturingClientId || clientId }
bottomClientId={ lastClientId }
className={ classnames( 'block-editor-block-list__block-popover', {
Expand Down

0 comments on commit 0d939c5

Please sign in to comment.