/** * 우측 메뉴 */ "use client"; import Link from "next/link"; import { useEffect, useMemo, useState } from "react"; import { ChevronDown, ChevronUp } from "lucide-react"; export type AnchorSection = { id: string; label: string; }; export type BusinessSubNavMenu = { key: string; label: string; href?: string; current?: boolean; defaultOpen?: boolean; sections?: AnchorSection[]; }; type BusinessSubNavProps = { menus?: BusinessSubNavMenu[]; offsetTop?: number; className?: string; }; export default function BusinessSubNav({ menus = [], offsetTop = 120, className = "", }: BusinessSubNavProps) { const [activeId, setActiveId] = useState(""); const [openMenus, setOpenMenus] = useState>({}); const allSections = useMemo( () => menus.flatMap((menu) => menu.sections ?? []), [menus] ); useEffect(() => { const initialOpenState: Record = {}; menus.forEach((menu) => { initialOpenState[menu.key] = menu.defaultOpen ?? menu.current ?? false; }); setOpenMenus(initialOpenState); }, [menus]); useEffect(() => { if (!allSections.length) return; const targets = allSections .map((section) => document.getElementById(section.id)) .filter(Boolean) as HTMLElement[]; if (!targets.length) return; let rafId = 0; const updateActiveSection = () => { const baseLine = offsetTop + 500; let nextActiveId = targets[0]?.id ?? ""; for (const el of targets) { const { top } = el.getBoundingClientRect(); if (top <= baseLine) { nextActiveId = el.id; continue; } break; } setActiveId((prev) => (prev === nextActiveId ? prev : nextActiveId)); }; const requestUpdate = () => { if (rafId) return; rafId = window.requestAnimationFrame(() => { // 예약번호 반환 updateActiveSection(); rafId = 0; }); }; const hash = window.location.hash.replace("#", ""); const initialActive = allSections.find((section) => section.id === hash)?.id ?? allSections[0]?.id ?? ""; setActiveId(initialActive); updateActiveSection(); window.addEventListener("scroll", requestUpdate, { passive: true }); window.addEventListener("resize", requestUpdate); return () => { window.removeEventListener("scroll", requestUpdate); window.removeEventListener("resize", requestUpdate); if (rafId) { window.cancelAnimationFrame(rafId); } }; }, [allSections, offsetTop]); useEffect(() => { if (!activeId) return; const parentMenu = menus.find((menu) => (menu.sections ?? []).some((section) => section.id === activeId) ); if (!parentMenu) return; setOpenMenus(() => { const nextOpenState: Record = {}; menus.forEach((menu) => { nextOpenState[menu.key] = menu.key === parentMenu.key; }); return nextOpenState; }); }, [activeId, menus]); const handleMove = (id: string) => { setActiveId(id); const target = document.getElementById(id); if (!target) return; window.history.replaceState(null, "", `#${id}`); const top = target.getBoundingClientRect().top + window.scrollY - offsetTop; window.scrollTo({ top, behavior: "smooth", }); }; const toggleMenu = (key: string) => { setOpenMenus((prev) => ({ ...prev, [key]: !prev[key], })); }; if (!menus.length) return null; return ( ); }