관망도 분석도구 위치 조정

This commit is contained in:
2026-08-11 11:11:09 +09:00
parent d23a16905d
commit d8c5344451
3 changed files with 156 additions and 80 deletions
@@ -3,8 +3,8 @@
import { useEffect, useState, type ComponentType, type ReactNode } from "react";
import {
ChartColumnBig,
ChevronLeft,
ChevronRight,
ChevronDown,
ChevronUp,
PanelsTopLeft,
PencilRuler,
Search,
@@ -61,11 +61,19 @@ const ANALYSIS_POPUP_ITEMS: Array<{
{ key: "infoSearch", label: "정보검색", icon: Search },
{ key: "pipeAnalysisPopup", label: "관로분석", icon: ChartColumnBig },
{ key: "floodPredictionMapPopup", label: "침수예측지도", icon: Waves },
{ key: "agedPipeStatusByCtpvPopup", label: "시도별 노후관로현황", icon: ChartColumnBig },
{
key: "agedPipeStatusByCtpvPopup",
label: "시도별 노후관로현황",
icon: ChartColumnBig,
},
{ key: "gisEditPopup", label: "GIS 편집", icon: PencilRuler },
{ key: "gisManagementPopup", label: "GIS 관리", icon: Settings2 },
{ key: "flowRiskPopup", label: "통수 위험", icon: TriangleAlert },
{ key: "inftwtResultPopup", label: "침입수 분석결과", renderIcon: InftwtMenuIcon },
{
key: "inftwtResultPopup",
label: "침입수 분석결과",
renderIcon: InftwtMenuIcon,
},
{ key: "rdiiResultPopup", label: "RDII 분석결과", icon: ChartColumnBig },
];
@@ -78,7 +86,9 @@ export default function AnalysisPopupMenu({
initialActiveKey = null,
}: Props) {
const [isOpen, setIsOpen] = useState(true);
const [activeKey, setActiveKey] = useState<WindowPopupKey | "infoSearch" | null>(initialActiveKey);
const [activeKey, setActiveKey] = useState<
WindowPopupKey | "infoSearch" | null
>(initialActiveKey);
useEffect(() => {
if (!isGisEditActive && activeKey === "gisEditPopup") {
@@ -98,21 +108,36 @@ export default function AnalysisPopupMenu({
}, [initialActiveKey]);
return (
<div className="absolute right-[0.55rem] top-[20px] z-[45]">
<div className="relative">
{isOpen ? (
<div className="absolute right-[28px] w-[154px] overflow-hidden rounded-[6px] border border-border bg-background shadow-[0_8px_18px_rgba(15,23,42,0.14)]">
<div className="flex h-[36px] items-center gap-2 border-b border-border bg-secondary px-[10px] text-[13px] font-bold text-foreground">
<div className="absolute right-[15px] top-[8px] z-[45]">
<div className="flex w-[154px] flex-col overflow-hidden rounded-[6px] border border-border bg-background shadow-[0_8px_18px_rgba(15,23,42,0.14)]">
<div className="flex h-[36px] shrink-0 items-center justify-between gap-2 border-b border-border bg-secondary pl-[10px] pr-[4px] text-[13px] font-bold text-foreground">
<div className="flex items-center gap-2">
<PanelsTopLeft className="h-4 w-4 text-primary" />
<span>분석도구</span>
</div>
<button
type="button"
className="flex h-[28px] w-[28px] shrink-0 items-center justify-center rounded-[4px] text-label transition hover:bg-accent hover:text-primary"
onClick={() => setIsOpen((value) => !value)}
title={isOpen ? "분석 메뉴 숨기기" : "분석 메뉴 보이기"}
aria-label={isOpen ? "분석 메뉴 숨기기" : "분석 메뉴 보이기"}
aria-expanded={isOpen}
>
{isOpen ? (
<ChevronUp className="h-4 w-4" />
) : (
<ChevronDown className="h-4 w-4" />
)}
</button>
</div>
<div className="flex flex-col gap-1 p-[6px]">
{isOpen ? (
<div className="flex flex-col gap-[3px] p-[6px]">
{ANALYSIS_POPUP_ITEMS.map((item) => {
const Icon = item.icon;
const isActive = item.key === "gisEditPopup"
const isActive =
item.key === "gisEditPopup"
? isGisEditActive
: activeKey === item.key;
@@ -120,9 +145,9 @@ export default function AnalysisPopupMenu({
<button
key={item.key}
type="button"
className={`relative flex h-[34px] items-center gap-[7px] rounded-[5px] border px-[8px] pl-[10px] text-left text-[12px] font-semibold transition ${
className={`relative flex h-[33px] items-center gap-[7px] rounded-[5px] border px-[8px] pl-[10px] text-left text-[12px] font-semibold transition ${
isActive
? "border-primary bg-secondary font-bold text-primary before:absolute before:left-0 before:top-[7px] before:bottom-[7px] before:w-[3px] before:rounded-r-[2px] before:bg-primary"
? "border-primary bg-secondary font-bold text-primary before:absolute before:bottom-[7px] before:left-0 before:top-[7px] before:w-[3px] before:rounded-r-[2px] before:bg-primary"
: "border-transparent bg-background text-foreground hover:border-border hover:bg-accent hover:text-primary"
}`}
onClick={() => {
@@ -167,18 +192,7 @@ export default function AnalysisPopupMenu({
);
})}
</div>
</div>
) : null}
<button
type="button"
className="absolute right-0 top-0 flex h-[36px] w-[20px] items-center justify-center rounded-[4px] border border-border bg-background text-label shadow-[0_8px_18px_rgba(15,23,42,0.14)] transition hover:bg-accent hover:text-primary"
onClick={() => setIsOpen((value) => !value)}
title={isOpen ? "분석 메뉴 접기" : "분석 메뉴 펼치기"}
aria-label={isOpen ? "분석 메뉴 접기" : "분석 메뉴 펼치기"}
>
{isOpen ? <ChevronRight className="h-4 w-4" /> : <ChevronLeft className="h-4 w-4" />}
</button>
</div>
</div>
);
+4 -4
View File
@@ -7850,7 +7850,7 @@ const Map = () => {
.sn-map-control-layout .mapbtn {
top: auto;
right: 15px;
bottom: 451px;
bottom: 439px;
}
.sn-map-control-layout .custom-toolbar {
@@ -7860,12 +7860,12 @@ const Map = () => {
.sn-map-control-layout .tool-bar {
top: auto;
right: 15px;
bottom: 248px;
bottom: 230px;
}
.sn-map-control-layout .zoom-level-control {
right: 16px;
bottom: 56px;
right: 15px;
bottom: 36px;
}
.sn-map-control-layout .zoom-level-summary {
@@ -0,0 +1,62 @@
# FE-MAP 우측 도구 배치 개선 구현 기록
## 작업 목적
- 지도 우측의 분석도구 펼침·숨김 버튼을 분석도구 제목 영역 안으로 이동한다.
- 분석도구, 지도 선택 버튼, 지도 도구 모음, 줌 컨트롤의 우측 기준선을 통일한다.
- 우측 도구를 위아래로 재배치해 도구 사이의 겹침을 제거한다.
## 원본 요구사항 추적 근거
- `D:\workspace\IIPBS\docs\기준문서\화면설계서.pptx`
- 슬라이드 12: `SFR-011-003`, 화면 ID `SN_MN010105`, 관로분석·GIS 편집·GIS 관리·통수위험 등 분석도구 구성
- 슬라이드 14, 16: 지도 우측 분석도구 배치 패턴
- 슬라이드 32: `SFR-012-003`, `SFR-012-004`, 화면 ID `SN_MN010301`, GIS 편집
- 슬라이드 33: `SFR-012-002`, 화면 ID `SN_MN010302`, GIS 관리
- `D:\workspace\IIPBS\docs\기준문서\요구사항정의서.xlsx`
- 시트 `3. (기능)요구사항정의 및 추적표`, 행 89~92: `SFR-011-003`~`SFR-011-006`
- 같은 시트 행 97~99: `SFR-012-002`~`SFR-012-004`
이번 변경은 원본에 정의된 분석 기능이나 동작 계약을 변경하지 않고, 해당 도구의 화면 배치와 접기·펼치기 UI만 개선한다.
## 수정 대상
- `app/(map-layout)/map/components/analysisMenu/AnalysisPopupMenu.tsx`
- `app/(map-layout)/map/page.tsx`
## 변경 내용
- 분석도구 토글을 외부 돌출 버튼에서 제목 우측의 내부 버튼으로 변경했다.
- 토글에 `aria-label`, `aria-expanded`를 적용하고 접힘 상태에서도 제목 바는 유지하도록 했다.
- 분석도구를 지도 영역 우측 15px 기준으로 고정하고 다른 도구의 우측 기준선도 15px로 통일했다.
- 분석도구 항목 높이와 간격을 소폭 조정하고, 하단 지도 도구들의 `bottom` 위치를 재배치했다.
## 영향 범위
- 프론트엔드 `sn` 지도 화면의 우측 도구 배치에만 영향이 있다.
- API, 상태관리 계약, 백엔드, DB 스키마 영향은 없다.
## 테스트 및 검증
- 변경 내용 검사: `git diff --check` 통과
- 개발 서버의 Turbopack 컴파일 및 실제 화면 반영 확인
- 실제 브라우저 검증: `http://localhost:3002/map`, 1920×912 뷰포트
- 분석도구 우측 좌표: 1905px
- 지도 선택 버튼·지도 도구 모음·줌 컨트롤 우측 좌표: 모두 1905px
- 분석도구→지도 선택 버튼 간격: 6px
- 지도 선택 버튼→지도 도구 모음 간격: 4px
- 지도 도구 모음→줌 컨트롤 간격: 4px
- 숨김 시 `aria-expanded=false`, 분석 메뉴 항목 0개, 제목 바 유지 확인
- 보이기 시 `aria-expanded=true`, 분석 메뉴 항목 9개 확인
- 검증 화면: `D:\workspace\IIPBS\docs\작업내용\FE-MAP-우측도구배치\검증\20260811-분석도구-우측도구-정렬.png`
## 롤백 방법
- 위 두 소스 파일에서 본 작업의 분석도구 헤더/토글 구조와 우측 도구 위치값 변경을 되돌린다.
- 구현 기록과 검증 이미지를 제거한다.
## 남은 리스크
- 사용자 실행 중인 Next 개발 서버 보호를 위해 별도 프로덕션 빌드는 수행하지 않았다.
- ESLint는 기존 `apps/sn/eslint.config.mjs`의 배열 전개 오류로, TypeScript 전체 검사는 기존 `tsconfig.json`의 `ignoreDeprecations` 설정 오류로 각각 대상 코드 검사 전에 중단됐다.
- 사용자 제보 화면과 동일한 1920×912 크기의 실제 브라우저에서는 정렬·간격·토글 동작을 확인했다.