From c702aeae44af9b46dfe919a8f1047e5888ded8bc Mon Sep 17 00:00:00 2001 From: umsangdon Date: Sun, 2 Aug 2026 14:05:34 +0900 Subject: [PATCH] =?UTF-8?q?fix(B06):=20=EC=B8=A1=EC=A0=90=20=EB=9D=BC?= =?UTF-8?q?=EB=B2=A8=20=EA=B0=80=EC=9A=B4=EB=8D=B0=20=EC=A0=95=EB=A0=AC,?= =?UTF-8?q?=20=EA=B3=A1=EC=84=A0=20=ED=86=A0=EA=B8=80=20=EC=98=A4=EB=B2=84?= =?UTF-8?q?=EB=A0=88=EC=9D=B4=20=EB=B3=B5=EA=B7=80,=20=EB=A9=B4=EC=A0=81?= =?UTF-8?q?=ED=91=9C=20=EC=83=89=20=ED=85=8C=EB=A7=88=ED=99=94,=20?= =?UTF-8?q?=EC=9E=AC=EC=84=A0=ED=83=9D=20=ED=95=B4=EC=A0=9C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 측점 라벨을 종단면도 축선과 유토곡선 사이 틈의 가운데로 옮긴다. LONG_PAD.bottom 52 → 26, 측점선 끝 +8 연장 제거, 유토곡선 상단 여백 12 → 10. 틈이 53 → 36px로 좁아진다. 좌우 여백은 X축 정렬 값이라 건드리지 않았다. - 곡선 토글을 별도 행에서 유토곡선 우측 상단 오버레이로 되돌린다. 단 스크롤 컨테이너 밖에서 절대 위치로 띄워 컨테이너 폭 계산에 끼어들지 않게 했다(예전 X축 어긋남의 원인). - 면적표·밴드 색을 고정 RGB에서 테마 토큰으로 교체. 발파암(BR)에 쓰던 검정 계열이 다크 테마에서 배경에 묻혔다. 차트 팔레트는 두 테마 모두에 맞춘 밝기다. - 같은 측점을 다시 고르면 선택을 푼다. 카드와 측점 세로선이 같은 경로를 타므로 한 곳에서 처리. Co-Authored-By: Claude Opus 5 (1M context) --- .../B06_wf3_ProfileCross_UI_Longitudinal.ts | 10 +++---- .../B06_wf3_ProfileCross_UI_MassHaul_View.ts | 2 +- .../B06_wf3_ProfileCross_UI_Section_Common.ts | 5 +++- .../B06_wf3_ProfileCross_UI_Section_View.ts | 24 +++++++++++------ .../B06_wf3_ProfileCross_UI_Style_Cross.css | 27 +++++++++++-------- ...B06_wf3_ProfileCross_UI_Style_MassHaul.css | 13 ++++----- 6 files changed, 49 insertions(+), 32 deletions(-) diff --git a/B06_wf3_ProfileCross/B06_wf3_ProfileCross_UI_Longitudinal.ts b/B06_wf3_ProfileCross/B06_wf3_ProfileCross_UI_Longitudinal.ts index 3e7820ec..c4981ee9 100644 --- a/B06_wf3_ProfileCross/B06_wf3_ProfileCross_UI_Longitudinal.ts +++ b/B06_wf3_ProfileCross/B06_wf3_ProfileCross_UI_Longitudinal.ts @@ -274,21 +274,21 @@ export function createLongitudinalProfile( x1: stationX, y1: LONG_PAD.top, x2: stationX, - y2: heightPx - LONG_PAD.bottom + 8, + y2: heightPx - LONG_PAD.bottom, class: "b06-chart__station-hit", }), svgElement("line", { x1: stationX, y1: LONG_PAD.top, x2: stationX, - y2: heightPx - LONG_PAD.bottom + 8, + y2: heightPx - LONG_PAD.bottom, class: `b06-chart__station-line b06-chart__station-line--${selected ? "selected" : station.kind}`, }), - // 측점 라벨은 그래프 바닥에 붙인다 — 바로 아래 유토곡선과의 틈을 줄여 두 그래프가 - // 측점 표기를 공유하는 것처럼 보이게 한다(2026-08-02 사용자 지시). + // 측점 라벨은 축선과 아래 유토곡선 사이 틈의 **가운데**에 놓는다 — 두 그래프가 측점 + // 표기를 함께 쓰는 것처럼 보여야 한다(2026-08-02 사용자 지시). svgText(offsetStationLabel(station.chainage_m, stationInterval, stationNumberOffset), { x: stationX, - y: heightPx - 7, + y: heightPx - 5, "text-anchor": "middle", class: "b06-chart__station-label", }), diff --git a/B06_wf3_ProfileCross/B06_wf3_ProfileCross_UI_MassHaul_View.ts b/B06_wf3_ProfileCross/B06_wf3_ProfileCross_UI_MassHaul_View.ts index c5632aa8..7e3835cb 100644 --- a/B06_wf3_ProfileCross/B06_wf3_ProfileCross_UI_MassHaul_View.ts +++ b/B06_wf3_ProfileCross/B06_wf3_ProfileCross_UI_MassHaul_View.ts @@ -42,7 +42,7 @@ export const MASS_HAUL_HEIGHT = 190; * 붙여 두 그래프가 측점 표기를 공유하는 것처럼 보이게 하기 위해서다(2026-08-02 사용자 지시). * 아래쪽은 X축 제목을 종단면도 상단으로 합치면서 비었다. */ -const MASS_PAD_TOP = 12; +const MASS_PAD_TOP = 10; const MASS_PAD_BOTTOM = 16; const BASIS_LABEL: Record = { diff --git a/B06_wf3_ProfileCross/B06_wf3_ProfileCross_UI_Section_Common.ts b/B06_wf3_ProfileCross/B06_wf3_ProfileCross_UI_Section_Common.ts index b8ebda3c..80a97d5d 100644 --- a/B06_wf3_ProfileCross/B06_wf3_ProfileCross_UI_Section_Common.ts +++ b/B06_wf3_ProfileCross/B06_wf3_ProfileCross_UI_Section_Common.ts @@ -27,7 +27,10 @@ export const CROSS_HEIGHT = 250; // 한 행 맞춤 기준 최소 카드 폭 — 조금 더 넓은 화면 필요(480→560, 약 +17%). export const CROSS_GRID_MIN_WIDTH = 560; export const CROSS_GRID_GAP = 16; -export const LONG_PAD = { left: 62, right: 24, top: 30, bottom: 52 }; +// bottom은 측점 라벨 한 줄 몫이다. X축 제목을 그래프 상단으로 올린 뒤 남던 여백을 걷어내, +// 종단면도 아래 라벨이 그 밑 유토곡선과의 틈 **가운데**에 오도록 좁혔다(2026-08-02 사용자 지시). +// left/right는 유토곡선과 X축을 맞추는 값이라 함부로 바꾸면 두 그래프 측점선이 어긋난다. +export const LONG_PAD = { left: 62, right: 24, top: 30, bottom: 26 }; export const CROSS_PAD = { left: 58, right: 20, top: 10, bottom: 52 }; export interface YScaleOptions { diff --git a/B06_wf3_ProfileCross/B06_wf3_ProfileCross_UI_Section_View.ts b/B06_wf3_ProfileCross/B06_wf3_ProfileCross_UI_Section_View.ts index 89b20bf0..a46e9c87 100644 --- a/B06_wf3_ProfileCross/B06_wf3_ProfileCross_UI_Section_View.ts +++ b/B06_wf3_ProfileCross/B06_wf3_ProfileCross_UI_Section_View.ts @@ -59,11 +59,11 @@ import { export type { CrossDesignChange, DesignChangeHandler, RockBoundaryControl }; /** - * 헤더(캐럿 행)·범례줄·요약줄·테두리가 먹는 세로 공간. 패널 높이에서 이만큼 빼야 그래프 - * 몫이 된다. (`.b06-section__panel-body`가 `overflow: hidden`이라 모자라면 아래가 잘린다. - * 범례는 CSS에서 한 줄 고정 가로 스크롤이라 개수가 늘어도 이 값이 흔들리지 않는다.) + * 헤더(캐럿 행)·요약줄·테두리가 먹는 세로 공간. 패널 높이에서 이만큼 빼야 그래프 몫이 된다. + * (`.b06-section__panel-body`가 `overflow: hidden`이라 모자라면 아래가 잘린다. + * 범례는 그래프 위에 겹치는 오버레이라 흐름 높이를 먹지 않는다.) */ -const PANEL_CHROME_PX = 130; +const PANEL_CHROME_PX = 100; /** 손대지 않았을 때의 패널 높이 — 이 값이 축소 비례의 기준(H₀)이 된다. */ const BASE_PANEL_HEIGHT = LONG_HEIGHT + MASS_HAUL_HEIGHT + PANEL_CHROME_PX; const MIN_LONG_HEIGHT = 110; @@ -260,10 +260,12 @@ export function createSectionView( document.getElementById(`cross-${stationId}`)?.scrollIntoView({ behavior, block: "nearest" }); }; + /** 같은 측점을 다시 고르면 선택을 푼다(2026-08-02 사용자 지시) — 카드·측점선 어느 쪽이든. */ const selectStation = (stationId: string, scroll: boolean): void => { - selectedStationId = stationId; + const deselect = selectedStationId === stationId; + selectedStationId = deselect ? null : stationId; draw(); - if (scroll) revealCard(stationId, "smooth"); + if (scroll && !deselect) revealCard(stationId, "smooth"); }; const buildCrossCard = (section: CrossSection, forcedHeightPx?: number): HTMLElement => @@ -345,8 +347,14 @@ export function createSectionView( panel.querySelector(".b06-masshaul__legend")?.remove(); panel.querySelector(".b06-masshaul__summary")?.remove(); - // 범례는 그래프 아래, 스크롤 컨테이너 밖에 둔다(곡선 폭에 관여하지 않게). - if (series.length) panelBody.append(createMassHaulLegend(series, visibleSeries, toggleSeries)); + // 범례는 유토곡선 우측 상단에 겹쳐 놓는다(2026-08-02 사용자 지시). 다만 **스크롤 컨테이너 + // 밖**에서 절대 위치로 띄운다 — 안에 넣으면 컨테이너 폭 계산에 끼어들어 두 그래프의 + // 측점 세로선이 어긋난다(1차 수정에서 겪은 문제). 세로 위치는 종단면도 높이로 잡는다. + if (series.length) { + const legend = createMassHaulLegend(series, visibleSeries, toggleSeries); + legend.style.top = `${heights.long + 6}px`; + panelBody.append(legend); + } // 요약 수치는 켜 둔 곡선 중 첫 번째 것 — 곡선이 여러 개라 어느 것인지 요약 끝에 밝힌다. // (선택 측점의 구간 물량은 곡선 위 말풍선이 맡는다.) const summarySeries = series.find((entry) => visibleSeries.has(entry.key)); diff --git a/B06_wf3_ProfileCross/B06_wf3_ProfileCross_UI_Style_Cross.css b/B06_wf3_ProfileCross/B06_wf3_ProfileCross_UI_Style_Cross.css index 876b6f96..d14707f2 100644 --- a/B06_wf3_ProfileCross/B06_wf3_ProfileCross_UI_Style_Cross.css +++ b/B06_wf3_ProfileCross/B06_wf3_ProfileCross_UI_Style_Cross.css @@ -58,7 +58,9 @@ height: 8px; } +/* position: relative — 유토곡선 범례가 이 상자를 기준으로 우측 상단에 겹친다. */ .b06-section__panel-body { + position: relative; display: flex; flex: 1; flex-direction: column; @@ -554,26 +556,28 @@ text-align: left; } +/* 면적 표기 색은 **테마 토큰만** 쓴다. 고정 RGB를 박으면 다크 테마에서 글자가 배경에 + 묻힌다(발파암에 쓰던 검정이 그랬다). 차트 팔레트는 밝기가 두 테마 모두에 맞춰져 있다. */ .b06-design__area--cut, .b06-design__area--cut_total { - color: rgb(220 38 38); + color: var(--color-danger); } /* 절토 내역 — 토사(EA), 리핑암(RR), 발파암(BR)을 색으로 갈라 둔다. */ .b06-design__area--cut_soil { - color: rgb(217 119 6); + color: var(--color-chart-2); } .b06-design__area--cut_rr { - color: rgb(71 85 105); + color: var(--color-slate); } .b06-design__area--cut_br { - color: rgb(15 23 42); + color: var(--color-chart-3); } .b06-design__area--fill { - color: rgb(37 99 235); + color: var(--color-chart-0); } .b06-design__area--unset { @@ -607,21 +611,22 @@ cursor: pointer; } +/* 밴드 색은 위 면적표 글자색과 짝을 맞춘다 — 어느 숫자를 켰는지 색으로 바로 읽히게. */ .b06-chart__area--cut_soil.is-active > polygon { - fill: color-mix(in srgb, rgb(217 119 6) 28%, transparent); - stroke: rgb(217 119 6); + fill: color-mix(in srgb, var(--color-chart-2) 28%, transparent); + stroke: var(--color-chart-2); stroke-width: 1.2; } .b06-chart__area--cut_rock.is-active > polygon { - fill: color-mix(in srgb, rgb(71 85 105) 28%, transparent); - stroke: rgb(71 85 105); + fill: color-mix(in srgb, var(--color-slate) 28%, transparent); + stroke: var(--color-slate); stroke-width: 1.2; } .b06-chart__area--fill.is-active > polygon { - fill: color-mix(in srgb, rgb(37 99 235) 28%, transparent); - stroke: rgb(37 99 235); + fill: color-mix(in srgb, var(--color-chart-0) 28%, transparent); + stroke: var(--color-chart-0); stroke-width: 1.2; } diff --git a/B06_wf3_ProfileCross/B06_wf3_ProfileCross_UI_Style_MassHaul.css b/B06_wf3_ProfileCross/B06_wf3_ProfileCross_UI_Style_MassHaul.css index 6a8baa39..02aaa059 100644 --- a/B06_wf3_ProfileCross/B06_wf3_ProfileCross_UI_Style_MassHaul.css +++ b/B06_wf3_ProfileCross/B06_wf3_ProfileCross_UI_Style_MassHaul.css @@ -9,16 +9,17 @@ * 색상은 theme.css 변수만 쓴다. 횡단 기준의 보라는 참고 도면의 자주색에 대응한다. * ========================================================================== */ -/* 범례는 그래프 **아래**, 가로 스크롤 컨테이너 밖에 놓인다(곡선 폭 계산에 끼어들면 종단면도와 - 측점 세로선이 어긋난다). 개수가 늘어도 패널 높이 배분이 흔들리지 않도록 한 줄로 고정하고, - 넘치면 줄바꿈 대신 이 줄만 가로로 민다. */ +/* 범례는 유토곡선 **우측 상단에 겹친다**. 단 가로 스크롤 컨테이너 **밖**에서 절대 위치로 + 띄운다 — 안에 넣으면 컨테이너 폭 계산에 끼어들어 종단면도와 측점 세로선이 어긋난다. + 세로 위치(top)는 종단면도 높이에 맞춰 렌더러가 인라인으로 넣는다. */ .b06-masshaul__legend { + position: absolute; + right: var(--spacing-8); + z-index: 2; display: flex; flex-wrap: nowrap; align-items: center; - gap: var(--spacing-8); - padding: var(--spacing-4) var(--spacing-16); - overflow-x: auto; + gap: var(--spacing-4); } .b06-masshaul__legend-item {