diff --git a/dev/react/src/tests/animate-presence-pop-rtl.tsx b/dev/react/src/tests/animate-presence-pop-rtl.tsx new file mode 100644 index 0000000000..9816f9a801 --- /dev/null +++ b/dev/react/src/tests/animate-presence-pop-rtl.tsx @@ -0,0 +1,47 @@ +import { AnimatePresence, motion } from "framer-motion" +import { useState } from "react" + +export const App = () => { + const [state, setState] = useState(true) + + return ( +
+
setState(!state)} + > + + + {state ? ( + + ) : null} + +
+
+ ) +} diff --git a/packages/framer-motion/cypress/integration/animate-presence-pop-rtl.ts b/packages/framer-motion/cypress/integration/animate-presence-pop-rtl.ts new file mode 100644 index 0000000000..8a47e02bb9 --- /dev/null +++ b/packages/framer-motion/cypress/integration/animate-presence-pop-rtl.ts @@ -0,0 +1,20 @@ +describe("AnimatePresence popLayout RTL", () => { + it("correctly pops exiting elements in RTL direction without shifting", () => { + let initialLeft: number + + cy.visit("?test=animate-presence-pop-rtl") + .wait(50) + .get("#b") + .then(([$b]: any) => { + initialLeft = $b.getBoundingClientRect().left + }) + .get("#container") + .trigger("click", 60, 60, { force: true }) + .wait(100) + .get("#b") + .should(([$b]: any) => { + const bbox = $b.getBoundingClientRect() + expect(bbox.left).to.equal(initialLeft) + }) + }) +}) diff --git a/packages/framer-motion/src/components/AnimatePresence/PopChild.tsx b/packages/framer-motion/src/components/AnimatePresence/PopChild.tsx index 1eb727a4ff..aaf586874d 100644 --- a/packages/framer-motion/src/components/AnimatePresence/PopChild.tsx +++ b/packages/framer-motion/src/components/AnimatePresence/PopChild.tsx @@ -14,6 +14,7 @@ interface Size { left: number right: number bottom: number + direction: string } interface Props { @@ -54,6 +55,7 @@ class PopChildMeasure extends React.Component { size.left = element.offsetLeft size.right = parentWidth - size.width - size.left size.bottom = parentHeight - size.height - size.top + size.direction = computedStyle.direction } return null @@ -79,6 +81,7 @@ export function PopChild({ children, isPresent, anchorX, anchorY, root, pop }: P left: 0, right: 0, bottom: 0, + direction: "ltr", }) const { nonce } = useContext(MotionConfigContext) /** @@ -100,10 +103,13 @@ export function PopChild({ children, isPresent, anchorX, anchorY, root, pop }: P * styles set via the style prop. */ useInsertionEffect(() => { - const { width, height, top, left, right, bottom } = size.current + const { width, height, top, left, right, bottom, direction } = size.current if (isPresent || pop === false || !ref.current || !width || !height) return - const x = anchorX === "left" ? `left: ${left}` : `right: ${right}` + const isRTL = direction === "rtl" + const x = anchorX === "left" + ? (isRTL ? `right: ${right}` : `left: ${left}`) + : (isRTL ? `left: ${left}` : `right: ${right}`) const y = anchorY === "bottom" ? `bottom: ${bottom}` : `top: ${top}` ref.current.dataset.motionPopId = id