From 4a6cfed9bbd1ad27eef0cdffd9b8735c43202163 Mon Sep 17 00:00:00 2001 From: ljpark Date: Wed, 13 May 2026 15:24:56 +0900 Subject: [PATCH] =?UTF-8?q?dm:=20=EC=8A=A4=ED=81=AC=EB=A1=A4=20=EC=9D=B4?= =?UTF-8?q?=EC=83=81=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- apps/dm/app/(with-layout)/example/page.tsx | 3 + .../_components/SewageStatusSection.tsx | 2 +- apps/dm/app/(with-layout)/status/page.tsx | 2 +- apps/dm/components/BusinessSubNav.tsx | 92 ++++++++++++------- 4 files changed, 64 insertions(+), 35 deletions(-) diff --git a/apps/dm/app/(with-layout)/example/page.tsx b/apps/dm/app/(with-layout)/example/page.tsx index b32b3346e..94e908d81 100644 --- a/apps/dm/app/(with-layout)/example/page.tsx +++ b/apps/dm/app/(with-layout)/example/page.tsx @@ -1,3 +1,6 @@ +/** + * 물 재이용 사례 + */ import SubPageHeader from "@/components/SubPageHeader"; import BusinessSubNav, { type BusinessSubNavMenu } from "@/components/BusinessSubNav"; diff --git a/apps/dm/app/(with-layout)/status/_components/SewageStatusSection.tsx b/apps/dm/app/(with-layout)/status/_components/SewageStatusSection.tsx index cd660a929..f7eb49a84 100644 --- a/apps/dm/app/(with-layout)/status/_components/SewageStatusSection.tsx +++ b/apps/dm/app/(with-layout)/status/_components/SewageStatusSection.tsx @@ -287,7 +287,7 @@ export default function SewageStatusSection() { const isSubItem = type === "red-item" || type === "blue-item"; if (index === 0) { - console.log(header.id, value); + // console.log(header.id, value); return ( {isSubItem ? ( diff --git a/apps/dm/app/(with-layout)/status/page.tsx b/apps/dm/app/(with-layout)/status/page.tsx index b18662c06..8bed3d04f 100644 --- a/apps/dm/app/(with-layout)/status/page.tsx +++ b/apps/dm/app/(with-layout)/status/page.tsx @@ -58,7 +58,7 @@ const menus: BusinessSubNavMenu[] = [ }, { id: "status-treated-usage", - label: "연도별 하수처리수 재이용 현황", + label: "용도별 하수처리수 재이용 현황", }, { id: "status-treated-facility", diff --git a/apps/dm/components/BusinessSubNav.tsx b/apps/dm/components/BusinessSubNav.tsx index 6b4246412..d8753b5dd 100644 --- a/apps/dm/components/BusinessSubNav.tsx +++ b/apps/dm/components/BusinessSubNav.tsx @@ -1,3 +1,6 @@ +/** + * 우측 메뉴 + */ "use client"; import Link from "next/link"; @@ -47,44 +50,67 @@ export default function BusinessSubNav({ setOpenMenus(initialOpenState); }, [menus]); - useEffect(() => { - if (!allSections.length) return; +useEffect(() => { + if (!allSections.length) return; - const observer = new IntersectionObserver( - (entries) => { - const visible = entries - .filter((entry) => entry.isIntersecting) - .sort((a, b) => b.intersectionRatio - a.intersectionRatio); + const targets = allSections + .map((section) => document.getElementById(section.id)) + .filter(Boolean) as HTMLElement[]; - if (visible[0]) { - setActiveId(visible[0].target.id); - } - }, - { - rootMargin: `-${offsetTop}px 0px -55% 0px`, - threshold: [0.2, 0.35, 0.5, 0.7], + 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; } - ); - const targets = allSections - .map((section) => document.getElementById(section.id)) - .filter(Boolean) as HTMLElement[]; + break; + } - targets.forEach((el) => observer.observe(el)); + setActiveId((prev) => (prev === nextActiveId ? prev : nextActiveId)); + }; - const hash = window.location.hash.replace("#", ""); - const initialActive = - allSections.find((section) => section.id === hash)?.id ?? - allSections[0]?.id ?? - ""; + const requestUpdate = () => { + if (rafId) return; - setActiveId(initialActive); + rafId = window.requestAnimationFrame(() => { // 예약번호 반환 + updateActiveSection(); + rafId = 0; + }); + }; - return () => observer.disconnect(); - }, [allSections, offsetTop]); + const hash = window.location.hash.replace("#", ""); + const initialActive = + allSections.find((section) => section.id === hash)?.id ?? + allSections[0]?.id ?? + ""; - useEffect(() => { - if (!activeId) return; + 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) @@ -93,13 +119,13 @@ export default function BusinessSubNav({ if (!parentMenu) return; setOpenMenus(() => { - const nextOpenState: Record = {}; + const nextOpenState: Record = {}; - menus.forEach((menu) => { + menus.forEach((menu) => { nextOpenState[menu.key] = menu.key === parentMenu.key; - }); + }); - return nextOpenState; + return nextOpenState; }); }, [activeId, menus]);