{"version":3,"file":"useStickyGroupAnchor.cjs","sources":["../../../../components/data-view/hooks/useStickyGroupAnchor.tsx"],"sourcesContent":["'use client';\n\nimport { useCallback, useLayoutEffect, useState } from 'react';\nimport { GroupedData } from '../data-view.types';\n\ninterface UseStickyGroupAnchorParams<TData> {\n  enabled: boolean;\n  groupHeaderList: {\n    index: number;\n    start: number;\n    data: GroupedData<TData>;\n  }[];\n  scrollContainerRef: React.RefObject<HTMLDivElement | null>;\n}\n\ninterface UseStickyGroupAnchorResult<TData> {\n  stickyGroup: GroupedData<TData> | null;\n  stickyGroupIndex: number | null;\n  recompute: () => void;\n}\n\n/**\n * Tracks the active group for a virtualized sticky group anchor. Picks the\n * latest group whose `start` offset has been scrolled past so the anchor's\n * content stays in sync with the natural section header underneath.\n *\n * The consumer hides the natural row at `stickyGroupIndex` so it doesn't slide\n * past the anchor.\n */\nexport function useStickyGroupAnchor<TData>({\n  enabled,\n  groupHeaderList,\n  scrollContainerRef\n}: UseStickyGroupAnchorParams<TData>): UseStickyGroupAnchorResult<TData> {\n  const [stickyGroup, setStickyGroup] = useState<GroupedData<TData> | null>(\n    null\n  );\n  const [stickyGroupIndex, setStickyGroupIndex] = useState<number | null>(null);\n\n  const recompute = useCallback(() => {\n    if (!enabled || groupHeaderList.length === 0) {\n      setStickyGroup(null);\n      setStickyGroupIndex(null);\n      return;\n    }\n    const el = scrollContainerRef.current;\n    if (!el) return;\n    const scrollTop = el.scrollTop;\n\n    // Binary search: largest `i` such that groupHeaderList[i].start <= scrollTop.\n    // `groupHeaderList` is built in row order so `start` is strictly increasing,\n    // which makes this safe for thousands of groups (typical large-dataset case).\n    let lo = 0;\n    let hi = groupHeaderList.length - 1;\n    let currentIdx = -1;\n    while (lo <= hi) {\n      const mid = (lo + hi) >> 1;\n      if (groupHeaderList[mid].start <= scrollTop) {\n        currentIdx = mid;\n        lo = mid + 1;\n      } else {\n        hi = mid - 1;\n      }\n    }\n\n    if (currentIdx < 0) {\n      setStickyGroup(null);\n      setStickyGroupIndex(null);\n      return;\n    }\n\n    const next = groupHeaderList[currentIdx];\n    // React bails out on identical values, but we short-circuit before the\n    // setState calls anyway so we don't allocate or schedule needlessly.\n    setStickyGroupIndex(prev => (prev === next.index ? prev : next.index));\n    setStickyGroup(prev => (prev === next.data ? prev : next.data));\n  }, [enabled, groupHeaderList, scrollContainerRef]);\n\n  useLayoutEffect(() => {\n    recompute();\n  }, [recompute]);\n\n  return { stickyGroup, stickyGroupIndex, recompute };\n}\n"],"names":[],"mappings":";;;;;AAqBA;;;;;;;AAOG;AACG;;;AAUJ;;;;;;AAME;AACA;;AACA;;;;;AAMA;AACA;AACA;;;;AAII;;;AAEA;;;AAIJ;;;;;AAMA;;;;;;;AAQA;AACF;AAEA;AACF;;"}