"use client"; // packages/react/presence/src/Presence.tsx import * as React2 from "react"; import { useComposedRefs } from "@radix-ui/react-compose-refs"; import { useLayoutEffect } from "@radix-ui/react-use-layout-effect"; // packages/react/presence/src/useStateMachine.tsx import * as React from "react"; function useStateMachine(initialState, machine) { return React.useReducer((state, event) => { const nextState = machine[state][event]; return nextState ?? state; }, initialState); } // packages/react/presence/src/Presence.tsx var Presence = (props) => { const { present, children } = props; const presence = usePresence(present); const child = typeof children === "function" ? children({ present: presence.isPresent }) : React2.Children.only(children); const ref = useComposedRefs(presence.ref, getElementRef(child)); const forceMount = typeof children === "function"; return forceMount || presence.isPresent ? React2.cloneElement(child, { ref }) : null; }; Presence.displayName = "Presence"; function usePresence(present) { const [node, setNode] = React2.useState(); const stylesRef = React2.useRef({}); const prevPresentRef = React2.useRef(present); const prevAnimationNameRef = React2.useRef("none"); const initialState = present ? "mounted" : "unmounted"; const [state, send] = useStateMachine(initialState, { mounted: { UNMOUNT: "unmounted", ANIMATION_OUT: "unmountSuspended" }, unmountSuspended: { MOUNT: "mounted", ANIMATION_END: "unmounted" }, unmounted: { MOUNT: "mounted" } }); React2.useEffect(() => { const currentAnimationName = getAnimationName(stylesRef.current); prevAnimationNameRef.current = state === "mounted" ? currentAnimationName : "none"; }, [state]); useLayoutEffect(() => { const styles = stylesRef.current; const wasPresent = prevPresentRef.current; const hasPresentChanged = wasPresent !== present; if (hasPresentChanged) { const prevAnimationName = prevAnimationNameRef.current; const currentAnimationName = getAnimationName(styles); if (present) { send("MOUNT"); } else if (currentAnimationName === "none" || styles?.display === "none") { send("UNMOUNT"); } else { const isAnimating = prevAnimationName !== currentAnimationName; if (wasPresent && isAnimating) { send("ANIMATION_OUT"); } else { send("UNMOUNT"); } } prevPresentRef.current = present; } }, [present, send]); useLayoutEffect(() => { if (node) { let timeoutId; const ownerWindow = node.ownerDocument.defaultView ?? window; const handleAnimationEnd = (event) => { const currentAnimationName = getAnimationName(stylesRef.current); const isCurrentAnimation = currentAnimationName.includes(event.animationName); if (event.target === node && isCurrentAnimation) { send("ANIMATION_END"); if (!prevPresentRef.current) { const currentFillMode = node.style.animationFillMode; node.style.animationFillMode = "forwards"; timeoutId = ownerWindow.setTimeout(() => { if (node.style.animationFillMode === "forwards") { node.style.animationFillMode = currentFillMode; } }); } } }; const handleAnimationStart = (event) => { if (event.target === node) { prevAnimationNameRef.current = getAnimationName(stylesRef.current); } }; node.addEventListener("animationstart", handleAnimationStart); node.addEventListener("animationcancel", handleAnimationEnd); node.addEventListener("animationend", handleAnimationEnd); return () => { ownerWindow.clearTimeout(timeoutId); node.removeEventListener("animationstart", handleAnimationStart); node.removeEventListener("animationcancel", handleAnimationEnd); node.removeEventListener("animationend", handleAnimationEnd); }; } else { send("ANIMATION_END"); } }, [node, send]); return { isPresent: ["mounted", "unmountSuspended"].includes(state), ref: React2.useCallback((node2) => { if (node2) stylesRef.current = getComputedStyle(node2); setNode(node2); }, []) }; } function getAnimationName(styles) { return styles?.animationName || "none"; } function getElementRef(element) { let getter = Object.getOwnPropertyDescriptor(element.props, "ref")?.get; let mayWarn = getter && "isReactWarning" in getter && getter.isReactWarning; if (mayWarn) { return element.ref; } getter = Object.getOwnPropertyDescriptor(element, "ref")?.get; mayWarn = getter && "isReactWarning" in getter && getter.isReactWarning; if (mayWarn) { return element.props.ref; } return element.props.ref || element.ref; } export { Presence }; //# sourceMappingURL=index.mjs.map