nomadweb/frontend/src/components/SectionNav.tsx
eric 341fcdd15e Add insurance, expenses, SIM guide, about page; unify trip storage
Co-authored-by: Cursor <cursoragent@cursor.com>
2026-08-29 00:09:10 -05:00

73 lines
2.7 KiB
TypeScript

"use client";
import { useEffect, useState } from "react";
const SECTIONS = [
{ id: "map", emoji: "🗺️", label: "地图" },
{ id: "destinations", emoji: "🌍", label: "目的地" },
{ id: "spin", emoji: "🎲", label: "转盘" },
{ id: "timezone", emoji: "🕐", label: "时区" },
{ id: "jetlag", emoji: "😴", label: "时差" },
{ id: "trip", emoji: "🗓️", label: "行程" },
{ id: "calculator", emoji: "🧮", label: "计算器" },
{ id: "cost-compare", emoji: "💰", label: "省钱" },
{ id: "savings", emoji: "🎯", label: "存款" },
{ id: "expenses", emoji: "📒", label: "账本" },
{ id: "flight", emoji: "✈️", label: "机票" },
{ id: "wifi", emoji: "📶", label: "网速" },
{ id: "lifestyle", emoji: "💻", label: "生活" },
{ id: "visa", emoji: "📋", label: "签证" },
{ id: "insurance", emoji: "🏥", label: "保险" },
{ id: "coworking", emoji: "💻", label: "办公" },
{ id: "housing", emoji: "🏡", label: "住宿" },
{ id: "sim", emoji: "📱", label: "上网" },
{ id: "season", emoji: "🌤️", label: "季节" },
{ id: "packing", emoji: "🧳", label: "行李" },
{ id: "phrases", emoji: "🗣️", label: "短语" },
{ id: "events", emoji: "📅", label: "活动" },
{ id: "blog", emoji: "📝", label: "博客" },
];
export default function SectionNav() {
const [active, setActive] = useState("");
const [visible, setVisible] = useState(false);
useEffect(() => {
const onScroll = () => setVisible(window.scrollY > 400);
window.addEventListener("scroll", onScroll, { passive: true });
onScroll();
return () => window.removeEventListener("scroll", onScroll);
}, []);
useEffect(() => {
const sections = SECTIONS.map((s) => document.getElementById(s.id)).filter(Boolean);
const observer = new IntersectionObserver(
(entries) => entries.forEach((e) => { if (e.isIntersecting) setActive(e.target.id); }),
{ threshold: 0.25, rootMargin: "-80px 0px -55% 0px" }
);
sections.forEach((s) => observer.observe(s!));
return () => observer.disconnect();
}, []);
const scrollTo = (id: string) => {
document.getElementById(id)?.scrollIntoView({ behavior: "smooth" });
};
return (
<nav className={`section-nav${visible ? " visible" : ""}`} aria-label="章节导航">
{SECTIONS.map((s) => (
<button
key={s.id}
className={`section-nav-dot${active === s.id ? " active" : ""}`}
onClick={() => scrollTo(s.id)}
aria-label={s.label}
title={s.label}
>
<span className="section-nav-emoji">{s.emoji}</span>
<span className="section-nav-label">{s.label}</span>
</button>
))}
</nav>
);
}