From 59856b7a2e44c83484ecd006d413871c845f1564 Mon Sep 17 00:00:00 2001 From: Marco Ciampini Date: Mon, 2 Jan 2023 19:51:59 +0100 Subject: [PATCH] Remove custom iframe logic --- packages/components/src/popover/index.tsx | 71 +----- .../components/src/popover/limit-shift.ts | 205 ------------------ packages/components/src/popover/utils.ts | 19 -- 3 files changed, 11 insertions(+), 284 deletions(-) delete mode 100644 packages/components/src/popover/limit-shift.ts diff --git a/packages/components/src/popover/index.tsx b/packages/components/src/popover/index.tsx index 14f1ddd8bab967..0e2ed94f754e0b 100644 --- a/packages/components/src/popover/index.tsx +++ b/packages/components/src/popover/index.tsx @@ -7,12 +7,12 @@ import { useFloating, flip as flipMiddleware, shift as shiftMiddleware, + limitShift, autoUpdate, arrow, offset as offsetMiddleware, size, Middleware, - MiddlewareArguments, } from '@floating-ui/react-dom'; // eslint-disable-next-line no-restricted-imports import { @@ -51,7 +51,6 @@ import Button from '../button'; import ScrollLock from '../scroll-lock'; import { Slot, Fill, useSlot } from '../slot-fill'; import { - getFrameOffset, positionToPlacement, placementToMotionAnimationProps, getReferenceOwnerDocument, @@ -64,7 +63,6 @@ import type { PopoverAnchorRefReference, PopoverAnchorRefTopBottom, } from './types'; -import { limitShift as customLimitShift } from './limit-shift'; /** * Name of slot in which popover should fill. @@ -259,37 +257,7 @@ const UnforwardedPopover = ( ? positionToPlacement( position ) : placementProp; - /** - * Offsets the position of the popover when the anchor is inside an iframe. - * - * Store the offset in a ref, due to constraints with floating-ui: - * https://floating-ui.com/docs/react-dom#variables-inside-middleware-functions. - */ - const frameOffsetRef = useRef( getFrameOffset( referenceOwnerDocument ) ); - const middleware = [ - // 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. - { - name: 'frameOffset', - fn( { x, y }: MiddlewareArguments ) { - if ( ! frameOffsetRef.current ) { - return { - x, - y, - }; - } - - return { - x: x + frameOffsetRef.current.x, - y: y + frameOffsetRef.current.y, - data: { - // This will be used in the customLimitShift() function. - amount: frameOffsetRef.current, - }, - }; - }, - }, offsetMiddleware( offsetProp ), computedFlipProp ? flipMiddleware() : undefined, computedResizeProp @@ -313,7 +281,7 @@ const UnforwardedPopover = ( shift ? shiftMiddleware( { crossAxis: true, - limiter: customLimitShift(), + limiter: limitShift(), padding: 1, // Necessary to avoid flickering at the edge of the viewport. } ) : undefined, @@ -425,35 +393,24 @@ const UnforwardedPopover = ( // If the reference element is in a different ownerDocument (e.g. iFrame), // we need to manually update the floating's position as the reference's owner - // document scrolls. Also update the frame offset if the view resizes. + // document scrolls. useLayoutEffect( () => { if ( - // Reference and root documents are the same. - referenceOwnerDocument === document || - // Reference and floating are in the same document. - referenceOwnerDocument === refs.floating.current?.ownerDocument || - // The reference's document has no view (i.e. window) - // or frame element (ie. it's not an iframe). - ! referenceOwnerDocument?.defaultView?.frameElement + ! referenceOwnerDocument || + ! referenceOwnerDocument.defaultView ) { - frameOffsetRef.current = undefined; return; } const { defaultView } = referenceOwnerDocument; - const updateFrameOffset = () => { - frameOffsetRef.current = getFrameOffset( referenceOwnerDocument ); - update(); - }; - defaultView.addEventListener( 'resize', updateFrameOffset ); - - updateFrameOffset(); + defaultView.addEventListener( 'resize', update ); + update(); return () => { - defaultView.removeEventListener( 'resize', updateFrameOffset ); + defaultView.removeEventListener( 'resize', update ); }; - }, [ referenceOwnerDocument, update, refs.floating ] ); + }, [ referenceOwnerDocument, update ] ); const mergedFloatingRef = useMergeRefs( [ floating, @@ -527,18 +484,12 @@ const UnforwardedPopover = ( left: typeof arrowData?.x !== 'undefined' && Number.isFinite( arrowData.x ) - ? `${ - arrowData.x + - ( frameOffsetRef.current?.x ?? 0 ) - }px` + ? `${ arrowData.x }px` : '', top: typeof arrowData?.y !== 'undefined' && Number.isFinite( arrowData.y ) - ? `${ - arrowData.y + - ( frameOffsetRef.current?.y ?? 0 ) - }px` + ? `${ arrowData.y }px` : '', } } > diff --git a/packages/components/src/popover/limit-shift.ts b/packages/components/src/popover/limit-shift.ts deleted file mode 100644 index 45e65a0b619098..00000000000000 --- a/packages/components/src/popover/limit-shift.ts +++ /dev/null @@ -1,205 +0,0 @@ -/** - * External dependencies - */ -import type { - Axis, - Coords, - Placement, - Side, - MiddlewareArguments, -} from '@floating-ui/react-dom'; - -/** - * Parts of this source were derived and modified from `floating-ui`, - * released under the MIT license. - * - * https://github.com/floating-ui/floating-ui - * - * Copyright (c) 2021 Floating UI contributors - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -/** - * Custom limiter function for the `shift` middleware. - * This function is mostly identical default `limitShift` from ``@floating-ui`; - * the only difference is that, when computing the min/max shift limits, it - * also takes into account the iframe offset that is added by the - * custom "frameOffset" middleware. - * - * All unexported types and functions are also from the `@floating-ui` library, - * and have been copied to this file for convenience. - */ - -type LimitShiftOffset = - | ( ( args: MiddlewareArguments ) => - | number - | { - /** - * Offset the limiting of the axis that runs along the alignment of the - * floating element. - */ - mainAxis?: number; - /** - * Offset the limiting of the axis that runs along the side of the - * floating element. - */ - crossAxis?: number; - } ) - | number - | { - /** - * Offset the limiting of the axis that runs along the alignment of the - * floating element. - */ - mainAxis?: number; - /** - * Offset the limiting of the axis that runs along the side of the - * floating element. - */ - crossAxis?: number; - }; - -type LimitShiftOptions = { - /** - * Offset when limiting starts. `0` will limit when the opposite edges of the - * reference and floating elements are aligned. - * - positive = start limiting earlier - * - negative = start limiting later - */ - offset: LimitShiftOffset; - /** - * Whether to limit the axis that runs along the alignment of the floating - * element. - */ - mainAxis: boolean; - /** - * Whether to limit the axis that runs along the side of the floating element. - */ - crossAxis: boolean; -}; - -function getSide( placement: Placement ): Side { - return placement.split( '-' )[ 0 ] as Side; -} - -function getMainAxisFromPlacement( placement: Placement ): Axis { - return [ 'top', 'bottom' ].includes( getSide( placement ) ) ? 'x' : 'y'; -} - -function getCrossAxis( axis: Axis ): Axis { - return axis === 'x' ? 'y' : 'x'; -} - -export const limitShift = ( - options: Partial< LimitShiftOptions > = {} -): { - options: Partial< LimitShiftOffset >; - fn: ( middlewareArguments: MiddlewareArguments ) => Coords; -} => ( { - options, - fn( middlewareArguments ) { - const { x, y, placement, rects, middlewareData } = middlewareArguments; - const { - offset = 0, - mainAxis: checkMainAxis = true, - crossAxis: checkCrossAxis = true, - } = options; - - const coords = { x, y }; - const mainAxis = getMainAxisFromPlacement( placement ); - const crossAxis = getCrossAxis( mainAxis ); - - let mainAxisCoord = coords[ mainAxis ]; - let crossAxisCoord = coords[ crossAxis ]; - - const rawOffset = - typeof offset === 'function' - ? offset( middlewareArguments ) - : offset; - const computedOffset = - typeof rawOffset === 'number' - ? { mainAxis: rawOffset, crossAxis: 0 } - : { mainAxis: 0, crossAxis: 0, ...rawOffset }; - - // At the moment of writing, this is the only difference - // with the `limitShift` function from `@floating-ui`. - // This offset needs to be added to all min/max limits - // in order to make the shift-limiting work as expected. - const additionalFrameOffset = { - x: 0, - y: 0, - ...middlewareData.frameOffset?.amount, - }; - - if ( checkMainAxis ) { - const len = mainAxis === 'y' ? 'height' : 'width'; - const limitMin = - rects.reference[ mainAxis ] - - rects.floating[ len ] + - computedOffset.mainAxis + - additionalFrameOffset[ mainAxis ]; - const limitMax = - rects.reference[ mainAxis ] + - rects.reference[ len ] - - computedOffset.mainAxis + - additionalFrameOffset[ mainAxis ]; - - if ( mainAxisCoord < limitMin ) { - mainAxisCoord = limitMin; - } else if ( mainAxisCoord > limitMax ) { - mainAxisCoord = limitMax; - } - } - - if ( checkCrossAxis ) { - const len = mainAxis === 'y' ? 'width' : 'height'; - const isOriginSide = [ 'top', 'left' ].includes( - getSide( placement ) - ); - const limitMin = - rects.reference[ crossAxis ] - - rects.floating[ len ] + - ( isOriginSide - ? middlewareData.offset?.[ crossAxis ] ?? 0 - : 0 ) + - ( isOriginSide ? 0 : computedOffset.crossAxis ) + - additionalFrameOffset[ crossAxis ]; - const limitMax = - rects.reference[ crossAxis ] + - rects.reference[ len ] + - ( isOriginSide - ? 0 - : middlewareData.offset?.[ crossAxis ] ?? 0 ) - - ( isOriginSide ? computedOffset.crossAxis : 0 ) + - additionalFrameOffset[ crossAxis ]; - - if ( crossAxisCoord < limitMin ) { - crossAxisCoord = limitMin; - } else if ( crossAxisCoord > limitMax ) { - crossAxisCoord = limitMax; - } - } - - return { - [ mainAxis ]: mainAxisCoord, - [ crossAxis ]: crossAxisCoord, - } as Coords; - }, -} ); diff --git a/packages/components/src/popover/utils.ts b/packages/components/src/popover/utils.ts index f14e2c921f1ede..f97c82b8253162 100644 --- a/packages/components/src/popover/utils.ts +++ b/packages/components/src/popover/utils.ts @@ -138,25 +138,6 @@ export const placementToMotionAnimationProps = ( }; }; -/** - * Returns the offset of a document's frame element. - * - * @param document The iframe's owner document. - * - * @return The offset of the document's frame element, or undefined if the - * document has no frame element. - */ -export const getFrameOffset = ( - document?: Document -): { x: number; y: number } | undefined => { - const frameElement = document?.defaultView?.frameElement; - if ( ! frameElement ) { - return; - } - const iframeRect = frameElement.getBoundingClientRect(); - return { x: iframeRect.left, y: iframeRect.top }; -}; - export const getReferenceOwnerDocument = ( { anchor, anchorRef,