diff --git a/account-kit/react/src/components/dialog/dialog.tsx b/account-kit/react/src/components/dialog/dialog.tsx index 32dd9261a1..05875cc038 100644 --- a/account-kit/react/src/components/dialog/dialog.tsx +++ b/account-kit/react/src/components/dialog/dialog.tsx @@ -1,6 +1,13 @@ "use client"; -import { useCallback, useEffect, useState, type ReactNode } from "react"; +import { + useCallback, + useEffect, + useLayoutEffect, + useRef, + useState, + type ReactNode, +} from "react"; import { createPortal } from "react-dom"; import { RemoveScroll } from "react-remove-scroll"; import { FocusTrap } from "./focustrap.js"; @@ -20,10 +27,33 @@ type DialogProps = { export const Dialog = ({ isOpen, onClose, children }: DialogProps) => { const [isScrollLocked, setScrollLocked] = useState(false); + const [renderPortal, setRenderPortal] = useState(false); + + const dialogCardRef = useRef(null); + const handleBackgroundClick = useCallback(() => { onClose(); }, [onClose]); + useLayoutEffect(() => { + const dialogCard = dialogCardRef.current; + + if (isOpen) { + setRenderPortal(true); + return; + } + + const renderPortalHandler = () => { + setRenderPortal(false); + }; + + dialogCard?.addEventListener("animationend", renderPortalHandler); + + return () => { + dialogCard?.removeEventListener("animationend", renderPortalHandler); + }; + }, [isOpen]); + useEffect(() => { const handleEscape = (event: KeyboardEvent) => { if (event.key === "Escape" && isOpen) { @@ -40,19 +70,24 @@ export const Dialog = ({ isOpen, onClose, children }: DialogProps) => { setScrollLocked(getComputedStyle(document.body).overflow !== "hidden"); }, []); - return isOpen + return renderPortal ? createPortal( {/* Overlay */}
event.stopPropagation()} > {children} diff --git a/account-kit/react/src/tailwind/plugin.ts b/account-kit/react/src/tailwind/plugin.ts index 4493ba383f..9702cc285d 100644 --- a/account-kit/react/src/tailwind/plugin.ts +++ b/account-kit/react/src/tailwind/plugin.ts @@ -116,13 +116,35 @@ export const accountKitUi: ( "100%": { opacity: "1" }, }, "slide-up": { - "0%": { transform: "translateY(100%)", opacity: "0" }, - "100%": { transform: "translateY(0%)", opacity: "1" }, + "0%": { + transform: "translateY(100%)", + opacity: "0", + }, + "100%": { + transform: "translateY(0%)", + opacity: "1", + }, + }, + "fade-out": { + "0%": { opacity: "1" }, + "100%": { opacity: "0" }, + }, + "slide-down": { + "0%": { + transform: "translateY(0%)", + opacity: "1", + }, + "100%": { + transform: "translateY(100%)", + opacity: "0", + }, }, }, animation: { "fade-in": "fade-in 150ms ease", "slide-up": "slide-up 350ms cubic-bezier(.15,1.15,0.6,1.00)", + "fade-out": "fade-out 150ms ease", + "slide-down": "slide-down 350ms cubic-bezier(.15,1.15,0.6,1.00)", }, }, },