"use client"; import { usePathname, useSearchParams } from "next/navigation"; import { useEffect, useRef, useState } from "react"; /** * Thin brand-colored progress bar at the top of the page. * Animates to 100% on route change, then fades out. * Pure CSS animation — no external dependency. */ export function NavProgressBar() { const pathname = usePathname(); const searchParams = useSearchParams(); const [visible, setVisible] = useState(false); const [width, setWidth] = useState(0); const timerRef = useRef | null>(null); const prevPathRef = useRef(null); // Detect link clicks to start the bar early useEffect(() => { function handleClick(e: MouseEvent) { const target = (e.target as Element).closest("a"); if (!target) return; const href = target.getAttribute("href"); if (!href || href.startsWith("http") || href.startsWith("#") || href.startsWith("mailto")) return; // Internal navigation — start bar setVisible(true); setWidth(60); // jump to 60% immediately, await route change for completion } document.addEventListener("click", handleClick); return () => document.removeEventListener("click", handleClick); }, []); // Complete bar when route actually changes useEffect(() => { const current = pathname + searchParams.toString(); if (prevPathRef.current !== null && prevPathRef.current !== current) { // Route changed — complete the bar setWidth(100); if (timerRef.current) clearTimeout(timerRef.current); timerRef.current = setTimeout(() => { setVisible(false); setWidth(0); }, 350); } prevPathRef.current = current; }, [pathname, searchParams]); // Cleanup useEffect(() => () => { if (timerRef.current) clearTimeout(timerRef.current); }, []); if (!visible && width === 0) return null; return (