"use client" import { useEffect, useState, useMemo, useCallback, useRef } from "react" import { format, parseISO } from "date-fns" import type { DateCountEntry } from "@/lib/types" interface DateScrubberProps { dates: DateCountEntry[] } interface ScrubberEntry { label: string date: string dateId: string } function findVisibleDateId(): string | null { const headers = document.querySelectorAll("[data-date]") const viewportTop = window.scrollY + window.innerHeight * 0.15 let best: HTMLElement | null = null for (const h of headers) { if (h.offsetTop <= viewportTop) best = h else break } return best?.id ?? headers[0]?.id ?? null } export function DateScrubber({ dates }: DateScrubberProps) { const [activeDate, setActiveDate] = useState(null) const scrollingRef = useRef(false) const entries = useMemo(() => { const compact = dates.length > 30 let lastYear = "" let lastMonth = "" const result: ScrubberEntry[] = [] for (const { date } of dates) { const d = parseISO(date) const monthKey = format(d, "yyyy-MM") if (compact && monthKey === lastMonth) continue lastMonth = monthKey const year = format(d, "yyyy") const showYear = year !== lastYear lastYear = year const label = compact ? showYear ? format(d, "MMM yyyy") : format(d, "MMM") : showYear ? format(d, "MMM d, yyyy") : format(d, "MMM d") result.push({ label, date, dateId: `date-${date}` }) } return result }, [dates]) useEffect(() => { let raf = 0 const onScroll = () => { cancelAnimationFrame(raf) raf = requestAnimationFrame(() => { if (!scrollingRef.current) { setActiveDate(findVisibleDateId()) } }) } window.addEventListener("scroll", onScroll, { passive: true }) onScroll() return () => { window.removeEventListener("scroll", onScroll) cancelAnimationFrame(raf) } }, []) const scrollToDate = useCallback((dateId: string) => { const el = document.getElementById(dateId) if (!el) return scrollingRef.current = true setActiveDate(dateId) el.scrollIntoView({ behavior: "smooth", block: "start" }) setTimeout(() => { scrollingRef.current = false }, 800) }, []) const handleClick = useCallback( (entry: ScrubberEntry) => { const el = document.getElementById(entry.dateId) if (el) { scrollToDate(entry.dateId) return } const headers = Array.from( document.querySelectorAll("[data-date]"), ) let closest: HTMLElement | null = null for (const h of headers) { const d = h.dataset.date ?? "" if (d >= entry.date) { closest = h break } } if (!closest) closest = headers[headers.length - 1] ?? null if (closest) { scrollingRef.current = true setActiveDate(entry.dateId) closest.scrollIntoView({ behavior: "smooth", block: "start" }) setTimeout(() => { scrollingRef.current = false }, 800) } }, [scrollToDate], ) if (entries.length < 1) return null return (
{entries.map((entry) => ( ))}
) }