Ship configurator updates with catalog DB included for server deploy. Co-authored-by: Cursor <[email protected]>
76 lines
2.2 KiB
React
76 lines
2.2 KiB
React
import { useEffect, useRef, useState } from 'react';
|
|
import styles from './SectionNav.module.css';
|
|
|
|
export default function SectionNav({ sections }) {
|
|
const [active, setActive] = useState(sections[0]?.id ?? '');
|
|
const navRef = useRef(null);
|
|
const isClickScrolling = useRef(false);
|
|
const clickTimeout = useRef(null);
|
|
|
|
useEffect(() => {
|
|
const els = sections
|
|
.map((s) => document.getElementById(s.id))
|
|
.filter(Boolean);
|
|
if (els.length === 0) return undefined;
|
|
|
|
const ratios = new Map();
|
|
const observer = new IntersectionObserver(
|
|
(entries) => {
|
|
if (isClickScrolling.current) return;
|
|
for (const entry of entries) {
|
|
ratios.set(entry.target.id, entry.intersectionRatio);
|
|
}
|
|
let best = '';
|
|
let bestRatio = -1;
|
|
for (const [id, ratio] of ratios) {
|
|
if (ratio > bestRatio) {
|
|
best = id;
|
|
bestRatio = ratio;
|
|
}
|
|
}
|
|
if (best && bestRatio > 0) setActive(best);
|
|
},
|
|
{ threshold: [0, 0.1, 0.25, 0.5, 0.75, 1] }
|
|
);
|
|
|
|
for (const el of els) observer.observe(el);
|
|
return () => observer.disconnect();
|
|
}, [sections]);
|
|
|
|
useEffect(() => () => clearTimeout(clickTimeout.current), []);
|
|
|
|
const handleClick = (id) => {
|
|
const el = document.getElementById(id);
|
|
if (!el) return;
|
|
setActive(id);
|
|
|
|
isClickScrolling.current = true;
|
|
const nav = navRef.current;
|
|
const stickyBottom = nav ? nav.getBoundingClientRect().bottom : 0;
|
|
const top = el.getBoundingClientRect().top + window.scrollY - stickyBottom - 8;
|
|
window.scrollTo({ top, behavior: 'smooth' });
|
|
|
|
clearTimeout(clickTimeout.current);
|
|
clickTimeout.current = setTimeout(() => {
|
|
isClickScrolling.current = false;
|
|
}, 800);
|
|
};
|
|
|
|
return (
|
|
<nav ref={navRef} className={styles.sectionNav}>
|
|
<div className={styles.inner}>
|
|
{sections.map((s) => (
|
|
<button
|
|
key={s.id}
|
|
type="button"
|
|
onClick={() => handleClick(s.id)}
|
|
className={`${styles.item} ${active === s.id ? styles.itemActive : ''}`}
|
|
>
|
|
{s.label}
|
|
</button>
|
|
))}
|
|
</div>
|
|
</nav>
|
|
);
|
|
}
|