diff --git a/packages/block-editor/src/components/block-popover/inbetween.js b/packages/block-editor/src/components/block-popover/inbetween.js index c9187afe31965..f9dd7adde42fb 100644 --- a/packages/block-editor/src/components/block-popover/inbetween.js +++ b/packages/block-editor/src/components/block-popover/inbetween.js @@ -237,9 +237,8 @@ function BlockPopoverInbetween( { props.className ) } resize={ false } - overlay={ true } flip={ false } - placement="bottom-start" + placement="overlay" variant="unstyled" >
diff --git a/packages/block-editor/src/components/iframe/index.js b/packages/block-editor/src/components/iframe/index.js index 821868a326417..d32866a2b5ceb 100644 --- a/packages/block-editor/src/components/iframe/index.js +++ b/packages/block-editor/src/components/iframe/index.js @@ -240,6 +240,11 @@ function Iframe( { return '' + renderToString( styleAssets ); }, [] ); + // We need to counter the margin created by scaling the iframe. If the scale + // is e.g. 0.45, then the top + bottom margin is 0.55 (1 - scale). Just the + // top or bottom margin is 0.55 / 2 ((1 - scale) / 2). + const marginFromScaling = ( contentHeight * ( 1 - scale ) ) / 2; + return ( <> { tabIndex >= 0 && before } @@ -249,10 +254,10 @@ function Iframe( { ...props.style, height: expand ? contentHeight : props.style?.height, marginTop: scale - ? -( ( contentHeight * ( 1 - scale ) ) / 2 ) + frameSize + ? -marginFromScaling + frameSize : props.style?.marginTop, marginBottom: scale - ? -( ( contentHeight * ( 1 - scale ) ) / 2 ) + frameSize + ? -marginFromScaling + frameSize : props.style?.marginBottom, transform: scale ? `scale( ${ scale } )` diff --git a/packages/components/src/popover/index.tsx b/packages/components/src/popover/index.tsx index 9910a0d2649e3..b549430cfb36a 100644 --- a/packages/components/src/popover/index.tsx +++ b/packages/components/src/popover/index.tsx @@ -67,6 +67,7 @@ import type { PopoverAnchorRefTopBottom, } from './types'; import { limitShift as customLimitShift } from './limit-shift'; +import { overlayMiddlewares } from './overlay-middlewares'; /** * Name of slot in which popover should fill. @@ -183,7 +184,6 @@ const UnforwardedPopover = ( resize = true, shift = false, variant, - overlay = false, // Deprecated props __unstableForcePosition, @@ -271,16 +271,9 @@ const UnforwardedPopover = ( const frameOffsetRef = useRef( getFrameOffset( referenceOwnerDocument ) ); const middleware = [ + ...( placementProp === 'overlay' ? overlayMiddlewares() : [] ), // Custom middleware which adjusts the popover's position by taking into // account the offset of the anchor's iframe (if any) compared to the page. - overlay - ? { - name: 'overlay', - fn( { rects }: MiddlewareArguments ) { - return rects.reference; - }, - } - : undefined, { name: 'frameOffset', fn( { x, y }: MiddlewareArguments ) { @@ -321,24 +314,6 @@ const UnforwardedPopover = ( }, } ) : undefined, - overlay - ? size( { - apply( { rects } ) { - const { firstElementChild } = - refs.floating.current ?? {}; - - // Only HTMLElement instances have the `style` property. - if ( ! ( firstElementChild instanceof HTMLElement ) ) - return; - - // Reduce the height of the popover to the available space. - Object.assign( firstElementChild.style, { - width: `${ rects.reference.width }px`, - height: `${ rects.reference.height }px`, - } ); - }, - } ) - : undefined, shift ? shiftMiddleware( { crossAxis: true, @@ -390,7 +365,10 @@ const UnforwardedPopover = ( placement: computedPlacement, middlewareData: { arrow: arrowData }, } = useFloating( { - placement: normalizedPlacementFromProps, + placement: + ( normalizedPlacementFromProps === 'overlay' + ? undefined + : normalizedPlacementFromProps ) || 'bottom', middleware, whileElementsMounted: ( referenceParam, floatingParam, updateParam ) => autoUpdate( referenceParam, floatingParam, updateParam, { diff --git a/packages/components/src/popover/overlay-middlewares.tsx b/packages/components/src/popover/overlay-middlewares.tsx new file mode 100644 index 0000000000000..da138af7fa331 --- /dev/null +++ b/packages/components/src/popover/overlay-middlewares.tsx @@ -0,0 +1,29 @@ +/** + * External dependencies + */ +import { size, MiddlewareArguments } from '@floating-ui/react-dom'; + +export function overlayMiddlewares() { + return [ + { + name: 'overlay', + fn( { rects }: MiddlewareArguments ) { + return rects.reference; + }, + }, + size( { + apply( { rects, elements } ) { + const { firstElementChild } = elements.floating ?? {}; + + // Only HTMLElement instances have the `style` property. + if ( ! ( firstElementChild instanceof HTMLElement ) ) return; + + // Reduce the height of the popover to the available space. + Object.assign( firstElementChild.style, { + width: `${ rects.reference.width }px`, + height: `${ rects.reference.height }px`, + } ); + }, + } ), + ]; +} diff --git a/packages/components/src/popover/types.ts b/packages/components/src/popover/types.ts index 8cd30fbad4bba..1a65b26b9022f 100644 --- a/packages/components/src/popover/types.ts +++ b/packages/components/src/popover/types.ts @@ -12,8 +12,10 @@ type DomRectWithOwnerDocument = DOMRect & { ownerDocument?: Document; }; +type PopoverPlacement = Placement | 'overlay'; + export type AnimatedWrapperProps = { - placement: Placement; + placement: PopoverPlacement; shouldAnimate?: boolean; }; @@ -111,7 +113,7 @@ export type PopoverProps = { * * @default 'bottom-start' */ - placement?: Placement; + placement?: PopoverPlacement; /** * Legacy way to specify the popover's position with respect to its anchor. * _Note: this prop is deprecated. Use the `placement` prop instead._ diff --git a/packages/components/src/popover/utils.ts b/packages/components/src/popover/utils.ts index aafbf5bf77bc0..1f31e194260c7 100644 --- a/packages/components/src/popover/utils.ts +++ b/packages/components/src/popover/utils.ts @@ -105,6 +105,7 @@ const PLACEMENT_TO_ANIMATION_ORIGIN: Record< left: { originX: 1, originY: 0.5 }, // open from middle, right 'left-start': { originX: 1, originY: 0 }, // open from top, right 'left-end': { originX: 1, originY: 1 }, // open from bottom, right + overlay: { originX: 0.5, originY: 0.5 }, // open from center, center }; /** @@ -157,13 +158,21 @@ export const getFrameOffset = ( return { x: iframeRect.left, y: iframeRect.top }; }; -export const getFrameScale = ( document?: Document ): number => { +export const getFrameScale = ( + document?: Document +): { + x: number; + y: number; +} => { const frameElement = document?.defaultView?.frameElement as HTMLElement; if ( ! frameElement ) { - return 1; + return { x: 1, y: 1 }; } - const transform = frameElement.style.transform; - return parseFloat( transform.replace( /scale\((\d+\.?\d*)\)/, '$1' ) ); + const rect = frameElement.getBoundingClientRect(); + return { + x: rect.width / frameElement.offsetWidth, + y: rect.height / frameElement.offsetHeight, + }; }; export const getReferenceOwnerDocument = ( { @@ -228,7 +237,7 @@ export const getReferenceElement = ( { 'anchorRef' | 'anchorRect' | 'getAnchorRect' | 'anchor' > & { fallbackReferenceElement: Element | null; - scale: number; + scale: { x: number; y: number }; } ): ReferenceType | null => { let referenceElement = null; @@ -290,17 +299,17 @@ export const getReferenceElement = ( { referenceElement = fallbackReferenceElement.parentElement; } - if ( referenceElement && scale !== 1 ) { + if ( referenceElement && ( scale.x !== 1 || scale.y !== 1 ) ) { // If the popover is inside an iframe, the coordinates of the // reference element need to be scaled to match the iframe's scale. const rect = referenceElement.getBoundingClientRect(); referenceElement = { getBoundingClientRect() { return new window.DOMRect( - rect.x * scale, - rect.y * scale, - rect.width * scale, - rect.height * scale + rect.x * scale.x, + rect.y * scale.y, + rect.width * scale.x, + rect.height * scale.y ); }, };