{"version":3,"sources":["../src/presence/index.tsx"],"names":["useState","useRef","useReducedMotion","useLayoutEffect","motion","useEffect","jsx"],"mappings":"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","file":"presence.cjs","sourcesContent":["/**\n * Presence (web). Mount/unmount transition primitive — keeps children in\n * the tree while they animate out, then actually unmounts. Every overlay\n * in the library used to `if (!open) return null`, which meant nothing\n * ever animated its exit; this is the missing half of that story.\n *\n *   <Presence present={open}>{panel}</Presence>\n *   <Presence present={open} from={{ opacity: 0, scale: 0.98 }}>{panel}</Presence>\n *   <Presence present={show} onExitComplete={() => queue.shift()}>{toast}</Presence>\n *\n * Animates via WAAPI (element.animate) with the motion tokens — enter\n * defaults to motion.overlayEnter, exit to motion.overlayExit; a `duration`\n * or `easing` prop overrides both directions. The wrapper div carries\n * data-state=\"open|closed\" so CSS can hook either state. Reduced motion\n * collapses to a fast opacity-only fade.\n */\nimport { useEffect, useLayoutEffect, useRef, useState, type CSSProperties, type ReactNode } from \"react\";\nimport { motion, type MotionSpec } from \"@plyxui/core\";\nimport { useReducedMotion } from \"@plyxui/hooks\";\n\nexport interface PresenceFrom {\n  opacity?: number;\n  translateX?: number;\n  translateY?: number;\n  scale?: number;\n}\n\nexport interface PresenceProps {\n  /** Logical mounted state. Children stay in the DOM while exiting. */\n  present: boolean;\n  /** Hidden pose the content enters from and exits to. Default `{ opacity: 0, translateY: 8 }`. */\n  from?: PresenceFrom;\n  /** Override duration (ms) for both enter and exit. Defaults to the motion tokens. */\n  duration?: number;\n  /** Override easing (CSS string) for both enter and exit. */\n  easing?: string;\n  /** Fires after the exit animation finishes and children unmount. */\n  onExitComplete?: () => void;\n  className?: string;\n  style?: CSSProperties;\n  children?: ReactNode;\n}\n\nconst DEFAULT_FROM: PresenceFrom = { opacity: 0, translateY: 8 };\n\nfunction poseToKeyframe(from: PresenceFrom): Keyframe {\n  const transforms: string[] = [];\n  if (from.translateX !== undefined || from.translateY !== undefined) {\n    transforms.push(`translate(${from.translateX ?? 0}px, ${from.translateY ?? 0}px)`);\n  }\n  if (from.scale !== undefined) transforms.push(`scale(${from.scale})`);\n  const kf: Keyframe = {};\n  if (from.opacity !== undefined) kf.opacity = from.opacity;\n  if (transforms.length > 0) kf.transform = transforms.join(\" \");\n  return kf;\n}\n\nconst IDENTITY: Keyframe = { opacity: 1, transform: \"translate(0px, 0px) scale(1)\" };\n\nexport function Presence({\n  present,\n  from = DEFAULT_FROM,\n  duration,\n  easing,\n  onExitComplete,\n  className,\n  style,\n  children,\n}: PresenceProps) {\n  const [mounted, setMounted] = useState<boolean>(present);\n  const ref = useRef<HTMLDivElement>(null);\n  const animRef = useRef<Animation | null>(null);\n  const reduced = useReducedMotion();\n\n  // Latest callbacks/poses without re-triggering the effects.\n  const latest = useRef({ from, duration, easing, onExitComplete, reduced });\n  latest.current = { from, duration, easing, onExitComplete, reduced };\n\n  if (present && !mounted) setMounted(true);\n\n  const resolve = (base: MotionSpec): MotionSpec => {\n    const l = latest.current;\n    if (l.reduced) return { duration: 50, easing: \"linear\" };\n    return { duration: l.duration ?? base.duration, easing: l.easing ?? base.easing };\n  };\n\n  const hiddenPose = (): Keyframe =>\n    latest.current.reduced ? { opacity: 0 } : poseToKeyframe(latest.current.from);\n\n  useLayoutEffect(() => {\n    const el = ref.current;\n    if (!present || !el) return;\n    animRef.current?.cancel();\n    if (typeof el.animate !== \"function\") return; // SSR/jsdom: appear instantly\n    const spec = resolve(motion.overlayEnter);\n    animRef.current = el.animate([hiddenPose(), IDENTITY], {\n      duration: spec.duration,\n      easing: spec.easing,\n      fill: \"backwards\",\n    });\n    // eslint-disable-next-line react-hooks/exhaustive-deps\n  }, [present]);\n\n  useEffect(() => {\n    if (present || !mounted) return;\n    const el = ref.current;\n    const finish = () => {\n      animRef.current = null;\n      setMounted(false);\n      latest.current.onExitComplete?.();\n    };\n    if (!el || typeof el.animate !== \"function\") {\n      finish();\n      return;\n    }\n    animRef.current?.cancel();\n    const spec = resolve(motion.overlayExit);\n    const anim = el.animate([IDENTITY, hiddenPose()], {\n      duration: spec.duration,\n      easing: spec.easing,\n      fill: \"forwards\",\n    });\n    animRef.current = anim;\n    anim.onfinish = finish;\n    return () => {\n      anim.onfinish = null;\n      anim.cancel();\n    };\n    // eslint-disable-next-line react-hooks/exhaustive-deps\n  }, [present, mounted]);\n\n  if (!mounted) return null;\n\n  return (\n    <div ref={ref} className={className} style={style} data-state={present ? \"open\" : \"closed\"}>\n      {children}\n    </div>\n  );\n}\n"]}