// ----------------------------------------------------------------------------- // Lou-Ka — Agrégateur de logements à louer (province de Québec) // Auteur : Simon-Pierre Boucher — contact@spboucher.ai // fiche/SectionNav.tsx : navigation sticky par sections — pastilles // défilables horizontalement, section active suivie au scroll // (IntersectionObserver), défilement doux au clic sans modifier l'URL // (pas de #hash : la page s'ouvre toujours en haut). // ----------------------------------------------------------------------------- import { useEffect, useRef, useState } from "react"; export interface NavItem { id: string; label: string; } export default function SectionNav({ items }: { items: NavItem[] }) { const [active, setActive] = useState(items[0]?.id); const track = useRef(null); useEffect(() => { const els = items.map((i) => document.getElementById(i.id)).filter((e): e is HTMLElement => !!e); if (els.length === 0) return; const visible = new Map(); const io = new IntersectionObserver((entries) => { for (const e of entries) visible.set((e.target as HTMLElement).id, e.isIntersecting ? e.intersectionRatio : 0); // section active = la première (ordre DOM) visible sous le header const first = items.find((i) => (visible.get(i.id) ?? 0) > 0); if (first) setActive(first.id); }, { rootMargin: "-120px 0px -55% 0px", threshold: [0, 0.1, 0.5] }); els.forEach((e) => io.observe(e)); return () => io.disconnect(); }, [items]); // garder la pastille active visible dans la barre — défilement HORIZONTAL de // la piste seulement (jamais scrollIntoView : il ferait défiler la page // verticalement, y compris à l'ouverture) useEffect(() => { const t = track.current, a = t?.querySelector(".on"); if (!t || !a) return; const left = a.offsetLeft, right = left + a.offsetWidth; if (left < t.scrollLeft) t.scrollTo({ left: Math.max(0, left - 12), behavior: "smooth" }); else if (right > t.scrollLeft + t.clientWidth) t.scrollTo({ left: right - t.clientWidth + 12, behavior: "smooth" }); }, [active]); const go = (id: string) => (e: React.MouseEvent) => { e.preventDefault(); document.getElementById(id)?.scrollIntoView({ behavior: "smooth", block: "start" }); setActive(id); }; return ( ); }