{"version":3,"sources":["../src/hooks/use-floating-position.ts"],"names":["useState","useCallback","useEffect"],"mappings":"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","file":"chunk-J2DI6RMR.cjs","sourcesContent":["import { useCallback, useEffect, useState, type RefObject } from \"react\";\nimport type { Placement } from \"../utils/types\";\n\ninterface FloatingPosition {\n  x: number;\n  y: number;\n  placement: Placement;\n}\n\ninterface UseFloatingPositionOptions {\n  placement?: Placement;\n  offset?: number;\n  enabled?: boolean;\n}\n\nfunction getPosition(\n  anchor: DOMRect,\n  floating: DOMRect,\n  placement: Placement,\n  offset: number,\n): FloatingPosition {\n  let x = 0;\n  let y = 0;\n  const base = placement.split(\"-\")[0] as \"top\" | \"bottom\" | \"left\" | \"right\";\n  const align = placement.split(\"-\")[1] as \"start\" | \"end\" | undefined;\n\n  switch (base) {\n    case \"top\":\n      x = anchor.left + anchor.width / 2 - floating.width / 2;\n      y = anchor.top - floating.height - offset;\n      break;\n    case \"bottom\":\n      x = anchor.left + anchor.width / 2 - floating.width / 2;\n      y = anchor.bottom + offset;\n      break;\n    case \"left\":\n      x = anchor.left - floating.width - offset;\n      y = anchor.top + anchor.height / 2 - floating.height / 2;\n      break;\n    case \"right\":\n      x = anchor.right + offset;\n      y = anchor.top + anchor.height / 2 - floating.height / 2;\n      break;\n  }\n\n  if (base === \"top\" || base === \"bottom\") {\n    if (align === \"start\") x = anchor.left;\n    else if (align === \"end\") x = anchor.right - floating.width;\n  }\n\n  if (base === \"left\" || base === \"right\") {\n    if (align === \"start\") y = anchor.top;\n    else if (align === \"end\") y = anchor.bottom - floating.height;\n  }\n\n  // Viewport clamping\n  let finalPlacement = placement;\n  const vw = window.innerWidth;\n  const vh = window.innerHeight;\n\n  if (base === \"bottom\" && y + floating.height > vh) {\n    y = anchor.top - floating.height - offset;\n    finalPlacement = placement.replace(\"bottom\", \"top\") as Placement;\n  } else if (base === \"top\" && y < 0) {\n    y = anchor.bottom + offset;\n    finalPlacement = placement.replace(\"top\", \"bottom\") as Placement;\n  }\n\n  x = Math.max(4, Math.min(x, vw - floating.width - 4));\n  y = Math.max(4, Math.min(y, vh - floating.height - 4));\n\n  return { x, y, placement: finalPlacement };\n}\n\nexport function useFloatingPosition(\n  anchorRef: RefObject<HTMLElement | null>,\n  floatingRef: RefObject<HTMLElement | null>,\n  options: UseFloatingPositionOptions = {},\n): FloatingPosition {\n  const { placement = \"bottom\", offset = 8, enabled = true } = options;\n  const [position, setPosition] = useState<FloatingPosition>({\n    x: -9999,\n    y: -9999,\n    placement,\n  });\n\n  const update = useCallback(() => {\n    const anchor = anchorRef.current;\n    const floating = floatingRef.current;\n    if (!anchor || !floating) return;\n\n    const anchorRect = anchor.getBoundingClientRect();\n    const floatingRect = floating.getBoundingClientRect();\n    const next = getPosition(anchorRect, floatingRect, placement, offset);\n    // Bail out when nothing changed so this can run after every commit\n    // (below) without causing an infinite render loop.\n    setPosition((prev) =>\n      prev.x === next.x && prev.y === next.y && prev.placement === next.placement\n        ? prev\n        : next,\n    );\n  }, [anchorRef, floatingRef, placement, offset]);\n\n  // Re-measure after every commit while open.\n  //\n  // The floating element mounts a frame or two AFTER `enabled` flips — its\n  // render is gated behind a state flip (e.g. useAnimation's `mounted`), so at\n  // the moment `enabled` turns true the floating ref is still null and a single\n  // measurement no-ops, leaving the popover parked at its off-screen sentinel.\n  //\n  // A passive effect with no deps runs after *every* commit — including the one\n  // that finally mounts the floating element — and, crucially, keeps running in\n  // hidden/background tabs where `requestAnimationFrame` is paused. The bail-out\n  // in `update` makes the steady-state calls free, so this both fixes the\n  // late-mount race and survives tab-visibility throttling.\n  useEffect(() => {\n    if (enabled) update();\n  });\n\n  // …but the per-commit effect ISN'T enough when the floating element mounts\n  // through a <Portal> whose own mount gate (SSR-safe; renders nothing until its\n  // useEffect runs) attaches the node WITHOUT re-rendering this hook's component.\n  // Then no commit happens here, the measurement never runs, and the popover\n  // stays parked at its off-screen sentinel until an unrelated scroll/resize\n  // fires `update()` — the \"menu only appears after I scroll\" bug. Poll with rAF\n  // until the node is actually in the DOM, measure it, then keep it observed for\n  // size changes (async content / lazily-loaded icons).\n  useEffect(() => {\n    if (!enabled) return;\n    let raf = 0;\n    let ro: ResizeObserver | null = null;\n    const tick = () => {\n      const el = floatingRef.current;\n      if (el) {\n        update();\n        if (typeof ResizeObserver !== \"undefined\") {\n          ro = new ResizeObserver(() => update());\n          ro.observe(el);\n        }\n      } else {\n        raf = requestAnimationFrame(tick);\n      }\n    };\n    raf = requestAnimationFrame(tick);\n    return () => {\n      cancelAnimationFrame(raf);\n      ro?.disconnect();\n    };\n  }, [enabled, update, floatingRef]);\n\n  // Park off-screen when closed so the next open re-measures from scratch.\n  useEffect(() => {\n    if (!enabled) {\n      setPosition((prev) =>\n        prev.x === -9999 ? prev : { x: -9999, y: -9999, placement },\n      );\n    }\n  }, [enabled, placement]);\n\n  // Reposition on scroll/resize while open.\n  useEffect(() => {\n    if (!enabled) return;\n\n    window.addEventListener(\"scroll\", update, true);\n    window.addEventListener(\"resize\", update);\n\n    return () => {\n      window.removeEventListener(\"scroll\", update, true);\n      window.removeEventListener(\"resize\", update);\n    };\n  }, [update, enabled]);\n\n  return position;\n}\n"]}