UNPKG

ivt

Version:

Ivt Components Library

146 lines (143 loc) 5.5 kB
import * as React from 'react'; import { u as useComposedRefs } from './index-D4jPaBZ2.mjs'; import { u as useLayoutEffect2 } from './index-C8u7IIcP.mjs'; function useStateMachine(initialState, machine) { return React.useReducer((state, event)=>{ const nextState = machine[state][event]; return nextState ?? state; }, initialState); } // src/presence.tsx var Presence = (props)=>{ const { present, children } = props; const presence = usePresence(present); const child = typeof children === "function" ? children({ present: presence.isPresent }) : React.Children.only(children); const ref = useComposedRefs(presence.ref, getElementRef(child)); const forceMount = typeof children === "function"; return forceMount || presence.isPresent ? React.cloneElement(child, { ref }) : null; }; Presence.displayName = "Presence"; function usePresence(present) { const [node, setNode] = React.useState(); const stylesRef = React.useRef(null); const prevPresentRef = React.useRef(present); const prevAnimationNameRef = React.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" } }); React.useEffect(()=>{ const currentAnimationName = getAnimationName(stylesRef.current); prevAnimationNameRef.current = state === "mounted" ? currentAnimationName : "none"; }, [ state ]); useLayoutEffect2(()=>{ 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 ]); useLayoutEffect2(()=>{ if (node) { let timeoutId; const ownerWindow = node.ownerDocument.defaultView ?? window; const handleAnimationEnd = (event)=>{ const currentAnimationName = getAnimationName(stylesRef.current); const isCurrentAnimation = currentAnimationName.includes(CSS.escape(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: React.useCallback((node2)=>{ stylesRef.current = node2 ? getComputedStyle(node2) : null; 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 as P }; //# sourceMappingURL=index-DfeRLazC.mjs.map