{"version":3,"sources":["../../src/waapi/useLoupeWaapi.ts"],"names":["useRef","useState","useTimeline","useEffect","useMotionValueEvent"],"mappings":";;;;;;;AA6FO,SAAS,cACd,IAAA,EACqB;AACrB,EAAA,MAAM,QAAA,GAAWA,YAAA,CAAoB,EAAE,CAAA;AACvC,EAAA,MAAM,CAAC,UAAA,EAAY,aAAa,CAAA,GAAIC,cAAA,CAAsB,EAAE,CAAA;AAC5D,EAAA,MAAM,EAAE,IAAA,EAAK,GAAIC,iBAAA,EAAY;AAI7B,EAAA,MAAM,QAAA,GAAWF,YAAA,CAAO,IAAA,CAAK,KAAK,CAAA;AAClC,EAAA,QAAA,CAAS,UAAU,IAAA,CAAK,KAAA;AACxB,EAAA,MAAM,OAAA,GAAUA,YAAA,CAAO,IAAA,CAAK,IAAA,KAAS,KAAK,CAAA;AAC1C,EAAA,OAAA,CAAQ,OAAA,GAAU,KAAK,IAAA,KAAS,KAAA;AAEhC,EAAAG,eAAA,CAAU,MAAM;AACd,IAAA,MAAM,IAAA,GAAO,SAAS,OAAA,EAAQ;AAC9B,IAAA,MAAM,IAAA,GAAA,CAAQ,KAAA,CAAM,OAAA,CAAQ,IAAI,CAAA,GAAI,IAAA,GAAO,IAAA,GAAO,CAAC,IAAI,CAAA,GAAI,EAAC,EAAG,MAAA;AAAA,MAC7D;AAAA,KACF;AAIA,IAAA,KAAA,MAAW,QAAQ,IAAA,EAAM;AACvB,MAAA,IAAA,CAAK,KAAA,EAAM;AACX,MAAA,SAAA,CAAU,IAAA,EAAM,IAAA,CAAK,GAAA,EAAI,EAAG,QAAQ,OAAO,CAAA;AAAA,IAC7C;AAEA,IAAA,QAAA,CAAS,OAAA,GAAU,IAAA;AACnB,IAAA,aAAA,CAAc,IAAI,CAAA;AAElB,IAAA,OAAO,MAAM;AAGX,MAAA,KAAA,MAAW,IAAA,IAAQ,IAAA,EAAM,IAAA,CAAK,MAAA,EAAO;AACrC,MAAA,QAAA,CAAS,UAAU,EAAC;AACpB,MAAA,aAAA,CAAc,EAAE,CAAA;AAAA,IAClB,CAAA;AAAA,EAEF,CAAA,EAAG,IAAA,CAAK,IAAA,IAAQ,EAAE,CAAA;AAElB,EAAAC,gCAAA,CAAoB,IAAA,EAAM,QAAA,EAAU,CAAC,EAAA,KAAO;AAC1C,IAAA,MAAM,OAAO,QAAA,CAAS,OAAA;AACtB,IAAA,IAAI,IAAA,CAAK,WAAW,CAAA,EAAG;AACvB,IAAA,MAAM,OAAO,OAAA,CAAQ,OAAA;AACrB,IAAA,KAAA,MAAW,IAAA,IAAQ,IAAA,EAAM,SAAA,CAAU,IAAA,EAAM,IAAI,IAAI,CAAA;AAAA,EACnD,CAAC,CAAA;AAED,EAAA,OAAO,EAAE,UAAA,EAAW;AACtB;AASA,SAAS,SAAA,CAAU,IAAA,EAAiB,EAAA,EAAY,IAAA,EAAqB;AACnE,EAAA,MAAM,GAAA,GAAM,IAAA,CAAK,MAAA,EAAQ,iBAAA,EAAkB,CAAE,OAAA;AAC7C,EAAA,MAAM,QAAQ,OAAO,GAAA,KAAQ,QAAA,GAAW,GAAA,GAAM,OAAO,GAAG,CAAA;AACxD,EAAA,IAAI,CAAC,MAAA,CAAO,QAAA,CAAS,KAAK,CAAA,IAAK,SAAS,CAAA,EAAG;AACzC,IAAA,IAAA,CAAK,WAAA,GAAc,EAAA;AACnB,IAAA;AAAA,EACF;AACA,EAAA,IAAA,CAAK,WAAA,GAAc,IAAA,GAAA,CACb,EAAA,GAAK,KAAA,GAAS,KAAA,IAAS,KAAA,GACzB,IAAA,CAAK,GAAA,CAAI,CAAA,EAAG,IAAA,CAAK,GAAA,CAAI,EAAA,EAAI,KAAK,CAAC,CAAA;AACrC","file":"index.cjs","sourcesContent":["import { useEffect, useRef, useState } from 'react';\nimport { useMotionValueEvent } from 'framer-motion';\n// Self-import the main entry so this subpath shares the same\n// TimelineContext instance as `<TimelineProvider>`. If we import\n// from a relative path here, tsup bundles a SECOND copy of the\n// context into the waapi chunk and `useTimeline` can't see the\n// provider. The main package is externalized in tsup.config so the\n// host resolves this import to the already-loaded main bundle.\nimport { useTimeline } from '@arinze-clinton/loupe';\n\n/**\n * Options for {@link useLoupeWaapi}.\n */\nexport type UseLoupeWaapiOptions = {\n  /**\n   * Create the Web Animations API animation(s). Call\n   * `element.animate(keyframes, options)` as you normally would and\n   * return the resulting `Animation` (or an array of them). The hook\n   * pauses each one immediately — Loupe's `time` becomes the only\n   * clock — and drives `currentTime` from there.\n   *\n   * Set `fill: 'both'` in your options so the element holds its\n   * keyframe values when scrubbed to either edge; without it the\n   * element snaps back to its base style outside the active range.\n   *\n   * Returns `null` (or an empty array) when the target element isn't\n   * mounted yet — the hook no-ops until `deps` change.\n   *\n   * @example\n   * ```ts\n   * build: () =>\n   *   boxRef.current?.animate(\n   *     [\n   *       { transform: 'translateY(40px)', opacity: 0 },\n   *       { transform: 'translateY(0)', opacity: 1 },\n   *     ],\n   *     { duration: 500, easing: 'ease-out', fill: 'both' },\n   *   ) ?? null\n   * ```\n   */\n  build: () => Animation | Animation[] | null | undefined;\n  /**\n   * Recreate the animation(s) when any of these change (same\n   * contract as a `useEffect` dep array). Typically include the\n   * target element so the animation rebuilds once the ref attaches.\n   * Defaults to `[]`.\n   */\n  deps?: ReadonlyArray<unknown>;\n  /**\n   * Wrap `currentTime` at the animation's end so it loops with\n   * Loupe's own phase loop. Default `true`. Set `false` to clamp at\n   * the end instead (holds the last frame past its duration).\n   */\n  loop?: boolean;\n};\n\nexport type UseLoupeWaapiResult = {\n  /** The live `Animation` objects the hook is driving. Empty until\n   *  `build` first returns one. */\n  animations: Animation[];\n};\n\n/**\n * Drive Web Animations API animations from the nearest\n * `TimelineProvider`'s `time` MotionValue.\n *\n * WAAPI animations expose `currentTime` in milliseconds — the same\n * unit as Loupe's clock — so this adapter is a thin bridge: it\n * pauses each animation (killing the browser's own playback) and\n * writes `currentTime` on every tick. Playing Loupe scrubs forward,\n * pausing freezes the current frame, and scrubbing the panel scrubs\n * the animation. No extra dependency — WAAPI is built into the\n * browser.\n *\n * @example\n * ```tsx\n * function Card() {\n *   const boxRef = useRef<HTMLDivElement | null>(null);\n *   useLoupeWaapi({\n *     deps: [boxRef.current],\n *     build: () =>\n *       boxRef.current?.animate(\n *         [\n *           { transform: 'translateY(40px)', opacity: 0 },\n *           { transform: 'translateY(0)', opacity: 1 },\n *         ],\n *         { duration: 500, easing: 'ease-out', fill: 'both' },\n *       ) ?? null,\n *   });\n *   return <div ref={boxRef}>Hello</div>;\n * }\n * ```\n */\nexport function useLoupeWaapi(\n  opts: UseLoupeWaapiOptions,\n): UseLoupeWaapiResult {\n  const animsRef = useRef<Animation[]>([]);\n  const [animations, setAnimations] = useState<Animation[]>([]);\n  const { time } = useTimeline();\n\n  // Keep the latest build/loop on refs so the change handler reads\n  // fresh values without re-subscribing.\n  const buildRef = useRef(opts.build);\n  buildRef.current = opts.build;\n  const loopRef = useRef(opts.loop !== false);\n  loopRef.current = opts.loop !== false;\n\n  useEffect(() => {\n    const made = buildRef.current();\n    const list = (Array.isArray(made) ? made : made ? [made] : []).filter(\n      Boolean,\n    ) as Animation[];\n\n    // Loupe owns the clock — pause each animation so the browser's\n    // own playback doesn't fight the panel for the playhead.\n    for (const anim of list) {\n      anim.pause();\n      seekWaapi(anim, time.get(), loopRef.current);\n    }\n\n    animsRef.current = list;\n    setAnimations(list);\n\n    return () => {\n      // cancel() clears currentTime + removes any fill effect so the\n      // element returns to its natural style on unmount/rebuild.\n      for (const anim of list) anim.cancel();\n      animsRef.current = [];\n      setAnimations([]);\n    };\n    // eslint-disable-next-line react-hooks/exhaustive-deps\n  }, opts.deps ?? []);\n\n  useMotionValueEvent(time, 'change', (ms) => {\n    const list = animsRef.current;\n    if (list.length === 0) return;\n    const loop = loopRef.current;\n    for (const anim of list) seekWaapi(anim, ms, loop);\n  });\n\n  return { animations };\n}\n\n/**\n * Set a WAAPI animation's `currentTime` (ms) from Loupe's time,\n * respecting wrap vs. clamp. Uses the computed `endTime` (which\n * already accounts for delay/endDelay/iterations) as the boundary.\n * If that's not a finite positive number — e.g. `iterations:\n * Infinity` — the time is passed through unbounded.\n */\nfunction seekWaapi(anim: Animation, ms: number, loop: boolean): void {\n  const end = anim.effect?.getComputedTiming().endTime;\n  const endMs = typeof end === 'number' ? end : Number(end);\n  if (!Number.isFinite(endMs) || endMs <= 0) {\n    anim.currentTime = ms;\n    return;\n  }\n  anim.currentTime = loop\n    ? ((ms % endMs) + endMs) % endMs\n    : Math.max(0, Math.min(ms, endMs));\n}\n"]}