{"version":3,"sources":["../src/index.ts"],"names":[],"mappings":"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","file":"index.mjs","sourcesContent":["type CssEndType = \"animation\" | \"transition\";\n\nexport type CssEndOptions = {\n  /**\n   * Number of matching end-events to skip before resolving.\n   * `0` resolves on the first matching event.\n   */\n  tolerance?: number;\n  /**\n   * Transition property filter (preferred name).\n   */\n  propertyName?: string;\n  /**\n   * Backward-compatible alias for `propertyName` (README used `property`).\n   */\n  property?: string;\n  /**\n   * Optional animation-name filter for animation events.\n   */\n  animationName?: string;\n};\n\ntype CssEndResult = Event | false;\n\n/**\n * Default FPS used to interpret \"virtual frames\" for refresh-rate independent timing.\n * 1 virtual frame = 1000 / DEFAULT_VIRTUAL_FPS ms.\n */\nexport const DEFAULT_VIRTUAL_FPS = 60;\n\n/**\n * Tracks the active CSS end-event cleanup callback for each element.\n *\n * This preserves the historical \"latest call wins\" behavior without mutating DOM nodes.\n */\nconst activeCssEndCleanup = new WeakMap<HTMLElement, () => void>();\n\n/**\n * Returns a safe `requestAnimationFrame` function bound to `window`, or `null`\n * when running outside a browser / without rAF support.\n */\nfunction getRaf(): ((cb: FrameRequestCallback) => number) | null {\n  if (typeof window === \"undefined\") return null;\n  if (typeof window.requestAnimationFrame !== \"function\") return null;\n  return window.requestAnimationFrame.bind(window);\n}\n\n/**\n * Normalizes a frame count into a non-negative integer.\n *\n * Invalid, negative, or non-finite values become `0`.\n */\nfunction normalizeFrames(frames: number): number {\n  return Number.isFinite(frames) && frames > 0 ? Math.floor(frames) : 0;\n}\n\n/**\n * Normalizes an FPS value to a valid positive number, falling back to\n * `DEFAULT_VIRTUAL_FPS` when invalid.\n */\nfunction normalizeFps(fps: number): number {\n  return Number.isFinite(fps) && fps > 0 ? fps : DEFAULT_VIRTUAL_FPS;\n}\n\n/**\n * Resolves the correct CSS end-event name for an element and type.\n *\n * Prefers standard event names (`transitionend` / `animationend`) and keeps\n * small legacy fallbacks for older WebKit / Opera engines.\n *\n * Returns `null` when no supported event can be inferred.\n */\nfunction resolveCssEndEventName(\n  element: HTMLElement,\n  type: CssEndType,\n): string | null {\n  const style = element.style as CSSStyleDeclaration & Record<string, unknown>;\n  const capitalized = type.charAt(0).toUpperCase() + type.slice(1);\n\n  // Standard modern event names first.\n  if (style[type] !== undefined) {\n    return `${type}end`;\n  }\n\n  // WebKit legacy fallback.\n  if (style[`Webkit${capitalized}`] !== undefined) {\n    return `webkit${capitalized}End`;\n  }\n\n  // Old Opera fallback (kept for backward compatibility).\n  if (type === \"transition\" && style.OTransition !== undefined) {\n    return \"oTransitionEnd\";\n  }\n  if (type === \"animation\" && style.OAnimation !== undefined) {\n    return \"oAnimationEnd\";\n  }\n\n  return null;\n}\n\n/**\n * Converts \"virtual frames\" to milliseconds.\n *\n * By default, virtual frames are interpreted at 60fps:\n * - `1` frame => ~16.67ms\n * - `10` frames => ~166.67ms\n *\n * Invalid inputs are clamped to safe defaults.\n */\nexport function framesToMs(\n  frames: number,\n  fps: number = DEFAULT_VIRTUAL_FPS,\n): number {\n  const f = normalizeFrames(frames);\n  const rate = normalizeFps(fps);\n  return f * (1000 / rate);\n}\n\n/**\n * Recursively waits for a given number of animation frames, then runs `callback`.\n *\n * - If `frames <= 0` (or invalid), callback runs immediately.\n * - If `requestAnimationFrame` is unavailable, callback runs immediately.\n *\n * This is the low-level building block for the library's frame/layout helpers.\n */\nexport const recursiveAnimationFrame = (\n  frames: number,\n  callback: () => void,\n) => {\n  const raf = getRaf();\n  const remaining = normalizeFrames(frames);\n\n  if (!raf || remaining === 0) {\n    callback();\n    return;\n  }\n\n  raf(() => {\n    recursiveAnimationFrame(remaining - 1, callback);\n  });\n};\n\n/**\n * Waits for a single CSS end event (`transitionend` or `animationend`) on `element`.\n *\n * Features:\n * - Promise-based API\n * - exact-target filtering (ignores bubbled child events)\n * - optional `tolerance` to skip the first N matching events\n * - optional property filtering for transitions\n * - optional animation-name filtering for animations\n *\n * Historical behavior preserved:\n * - For the same element, the most recent call replaces any prior pending WAC listener.\n *\n * Returns:\n * - `Event` when a matching end-event fires\n * - `false` when `element` is missing or no supported event name is available\n */\nexport function setCssEndEvent(\n  element: HTMLElement,\n  type: CssEndType,\n  { tolerance = 0, propertyName, property, animationName }: CssEndOptions = {},\n): Promise<CssEndResult> {\n  return new Promise((resolve) => {\n    if (!element) {\n      resolve(false);\n      return;\n    }\n\n    const eventName = resolveCssEndEventName(element, type);\n    if (!eventName) {\n      resolve(false);\n      return;\n    }\n\n    // Preserve original \"latest call wins\" behavior for the same element,\n    // but without mutating DOM nodes.\n    const previousCleanup = activeCssEndCleanup.get(element);\n    if (previousCleanup) {\n      previousCleanup();\n      activeCssEndCleanup.delete(element);\n    }\n\n    const requiredMatches = Math.max(0, Math.trunc(tolerance));\n    const transitionPropertyFilter = propertyName ?? property;\n    let matchCount = 0;\n\n    const cleanup = () => {\n      element.removeEventListener(eventName, onEnd as EventListener);\n      const current = activeCssEndCleanup.get(element);\n      if (current === cleanup) {\n        activeCssEndCleanup.delete(element);\n      }\n    };\n\n    const onEnd = (event: Event) => {\n      const target = (event as any).target ?? (event as any).srcElement;\n      if (target !== element) return;\n\n      if (type === \"transition\" && transitionPropertyFilter) {\n        const e = event as TransitionEvent;\n        if (e.propertyName !== transitionPropertyFilter) return;\n      }\n\n      if (type === \"animation\" && animationName) {\n        const e = event as AnimationEvent;\n        if (e.animationName !== animationName) return;\n      }\n\n      if (matchCount >= requiredMatches) {\n        cleanup();\n        resolve(event);\n        return;\n      }\n\n      matchCount += 1;\n    };\n\n    activeCssEndCleanup.set(element, cleanup);\n    element.addEventListener(eventName, onEnd as EventListener);\n  });\n}\n\n/**\n * Schedules `callback` for the next animation frame.\n *\n * This is useful when you need DOM/style changes to settle before running logic\n * tied to layout/paint timing.\n *\n * Falls back to immediate execution when `requestAnimationFrame` is unavailable.\n */\nexport function beforeCssLayout(callback: () => void) {\n  const raf = getRaf();\n  if (!raf) {\n    callback();\n    return;\n  }\n  raf(() => callback());\n}\n\n/**\n * Schedules `callback` a number of frames into the future, plus one extra frame.\n *\n * Semantics:\n * - `frames = 0` => next rAF (same intent as \"next CSS layout\")\n * - `frames = 1` => roughly two rAFs from now\n *\n * The extra frame preserves the library's historical \"future layout\" behavior.\n * Falls back to immediate execution when `requestAnimationFrame` is unavailable.\n */\nexport function beforeFutureCssLayout(frames: number, callback: () => void) {\n  const raf = getRaf();\n  if (!raf) {\n    callback();\n    return;\n  }\n  recursiveAnimationFrame(normalizeFrames(frames) + 1, callback);\n}\n\n/**\n * Promise wrapper around `beforeFutureCssLayout`.\n *\n * Uses *real display frames* (rAF hops), so elapsed time depends on the\n * device refresh rate (e.g. 60Hz vs 120Hz).\n */\nexport function frameThrower(frames: number) {\n  return new Promise<void>((resolve) => {\n    beforeFutureCssLayout(frames, resolve);\n  });\n}\n\n/**\n * Refresh-rate independent timing helper.\n *\n * Same intent as `frameThrower()`, but `frames` are interpreted as \"virtual frames\"\n * at `fps` (60 by default), so timing is stable across 60Hz/120Hz/etc displays.\n *\n * It still waits at least 1 rAF first, preserving CSS/layout timing semantics similar\n * to `beforeFutureCssLayout`.\n *\n * Examples (fps=60):\n * - fixedFrameThrower(0)  => ~1 rAF (next layout)\n * - fixedFrameThrower(10) => ~1 rAF + ~166.7ms\n */\nexport function fixedFrameThrower(\n  frames: number,\n  fps: number = DEFAULT_VIRTUAL_FPS,\n) {\n  return new Promise<void>((resolve) => {\n    const raf = getRaf();\n    if (!raf) {\n      resolve();\n      return;\n    }\n\n    const durationMs = framesToMs(frames, fps);\n\n    // Always wait at least one rAF (keeps CSS/layout timing sane).\n    raf((t0) => {\n      if (durationMs <= 0) {\n        resolve();\n        return;\n      }\n\n      const start = t0;\n\n      const tick: FrameRequestCallback = (t) => {\n        if (t - start >= durationMs) {\n          resolve();\n          return;\n        }\n        raf(tick);\n      };\n\n      raf(tick);\n    });\n  });\n}\n\n/**\n * Resolves after two animation frames (double-rAF).\n *\n * This is a common pattern to ensure DOM updates and style calculations have\n * progressed enough for reliable transition triggering/measurement.\n */\nexport function onceNextCssLayout() {\n  return new Promise<void>((resolve) => {\n    recursiveAnimationFrame(2, resolve);\n  });\n}\n\n/**\n * Convenience helper that waits for the next matching `transitionend` event on `element`.\n *\n * See `setCssEndEvent()` for filtering and tolerance options.\n */\nexport function onceTransitionEnd(\n  element: HTMLElement,\n  options: CssEndOptions = {},\n) {\n  return setCssEndEvent(element, \"transition\", options);\n}\n\n/**\n * Convenience helper that waits for the next matching `animationend` event on `element`.\n *\n * See `setCssEndEvent()` for filtering and tolerance options.\n */\nexport function onceAnimationEnd(\n  element: HTMLElement,\n  options: CssEndOptions = {},\n) {\n  return setCssEndEvent(element, \"animation\", options);\n}\n"]}