v2.1
Ship configurator updates with catalog DB included for server deploy. Co-authored-by: Cursor <[email protected]>
This commit is contained in:
@@ -0,0 +1,75 @@
|
||||
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>
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user