From 83a1309a37fdc4b48e7c4836e37eb900945ae666 Mon Sep 17 00:00:00 2001 From: umsangdon Date: Fri, 21 Aug 2026 16:28:27 +0900 Subject: [PATCH] =?UTF-8?q?feat(B06):=20=EA=B5=AC=EC=A1=B0=EB=AC=BC=20?= =?UTF-8?q?=EC=84=A0=ED=83=9D=20=3D=20=EC=B9=B4=EB=93=9C=20=EC=84=A0?= =?UTF-8?q?=ED=83=9D=C2=B7=EB=A9=B4=EC=A0=81=20=EA=B0=95=EC=A1=B0=EC=99=80?= =?UTF-8?q?=20=EA=B0=99=EC=9D=80=20=EB=A0=88=EB=B2=A8,=20=EB=B6=89?= =?UTF-8?q?=EC=9D=80=20=EA=B0=95=EC=A1=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 강조 색을 --color-danger(붉은색)로 변경. - 구조물을 고르면 그 측점 카드도 같이 선택된다. 이미 선택된 카드 안에서 다른 구조물을 고를 때는 강조만 옮겨 가고 카드 선택은 건드리지 않는다. - 구조물 선택과 절·성토 면적 강조는 같은 레벨 — 하나를 고르면 다른 하나가 풀린다. 카드가 선택에서 빠져도 구조물 선택은 풀린다. - 조정 버튼이 안 보이던 원인: .b06-cross-card__revetctl 스타일과 .is-hidden 규칙이 아예 없었다. 스타일을 넣고 "기슭막이" 라벨을 붙여 표시 반폭 그룹과 구분했다. 미선택 카드에서 구조물이 먹히지 않던 버그도 잡았다 — 면적 비우기(onAreaSelect)가 먼저 카드를 선택해 버려, 뒤이은 onSelect가 "같은 측점 재클릭 = 해제"로 상쇄됐다. 검증(공용 브라우저 4+4.3): 미선택 카드에서 구조물 클릭 → 벽 강조 + 화살표 + 카드 선택. 같은 카드 다른 구조물 → 강조만 이동. 면적칩 → 구조물 해제. 다시 구조물 → 면적 해제. tsc 통과, pytest 148 pass. Co-Authored-By: Claude Opus 5 (1M context) --- B06_Section/B06_Section_UI_Cross_View.ts | 28 +++++++++++++++++-- B06_Section/B06_Section_UI_Style_Cross.css | 20 +++++++++++++ .../B06_Section_UI_Style_Cross_Areas.css | 4 +-- ui_template/ui_template_locale_b2.ts | 1 + 4 files changed, 49 insertions(+), 4 deletions(-) diff --git a/B06_Section/B06_Section_UI_Cross_View.ts b/B06_Section/B06_Section_UI_Cross_View.ts index 397294db..255ff8a6 100644 --- a/B06_Section/B06_Section_UI_Cross_View.ts +++ b/B06_Section/B06_Section_UI_Cross_View.ts @@ -211,8 +211,14 @@ export function createCrossSectionCard( let activeRevet: RevetKey | null = revetOffset?.selectedFor(section) ?? null; let setRevetActive: RevetHighlightSetter = () => undefined; let showRevetControl: (visible: boolean) => void = () => undefined; - /** 기슭막이를 고르면 면적 강조는 끈다 — **구조물 선택이 우선**(2026-08-21 사용자 ①). */ + /** + * 기슭막이를 고른다. 구조물 선택과 절·성토 면적 강조는 **같은 레벨**이라 하나를 + * 고르면 다른 하나는 풀린다(2026-08-21 사용자). 또 구조물을 고르면 그 **측점 카드도 + * 같이 선택**된다 — 이미 선택된 카드 안에서 다른 구조물을 고를 때는 선택이 옮겨만 + * 가고 카드 선택은 건드리지 않는다. + */ const toggleRevet = (key: RevetKey): void => { + const wasSelected = isSelected; activeRevet = activeRevet === key ? null : key; setRevetActive(activeRevet); showRevetControl(activeRevet !== null); @@ -221,7 +227,14 @@ export function createCrossSectionCard( activeArea = null; setBandActive(null); setChipActive(null); - onAreaSelect?.(section.station_id, null); + } + if (activeRevet !== null) { + // 미선택 카드는 **카드 선택만** 부른다. 여기서 면적 비우기(onAreaSelect)를 먼저 + // 부르면 그쪽이 카드를 선택해 버려, 뒤이은 onSelect가 "같은 측점 재클릭 = 해제"로 + // 먹혀 아무 일도 일어나지 않는다(2026-08-21 화면 확인). 카드 선택 경로가 부모의 + // 면적 강조를 이미 비운다. + if (wasSelected) onAreaSelect?.(section.station_id, null); + else onSelect(section.station_id); } }; card.applySelection = (nextSelected, areaKey) => { @@ -230,6 +243,13 @@ export function createCrossSectionCard( activeArea = nextSelected ? (areaKey ?? null) : null; setBandActive(activeArea); setChipActive(activeArea); + // 면적이 켜졌거나 카드가 선택에서 빠지면 구조물 선택은 풀린다(같은 레벨). + if ((activeArea !== null || !nextSelected) && activeRevet !== null) { + activeRevet = null; + setRevetActive(null); + showRevetControl(false); + revetOffset?.select(section.chainage_m, null); + } }; const toggleArea = (key: CrossAreaKey): void => { if (!isSelected) { @@ -613,7 +633,11 @@ export function createCrossSectionCard( revetOffset.adjust(section.chainage_m, activeRevet, screenDeltaM * outwardOf(activeRevet)); } }; + const revetLabel = document.createElement("span"); + revetLabel.className = "b06-cross-card__revetctl-label"; + revetLabel.textContent = L("B06_Cross_Revet_Label"); revetControl.append( + revetLabel, makeButton("◀", L("B06_Cross_Revet_Left"), nudge(0.1)), makeButton("▶", L("B06_Cross_Revet_Right"), nudge(-0.1)), makeButton("↺", L("B06_Cross_Revet_Reset"), () => { diff --git a/B06_Section/B06_Section_UI_Style_Cross.css b/B06_Section/B06_Section_UI_Style_Cross.css index c45c3193..adb3ce59 100644 --- a/B06_Section/B06_Section_UI_Style_Cross.css +++ b/B06_Section/B06_Section_UI_Style_Cross.css @@ -563,6 +563,26 @@ margin-left: auto; } +/* 기슭막이 X 자리 ◀/▶/↺(2026-08-21) — 벽을 골랐을 때만 보인다. 표시 반폭 그룹과 + 나란히 서므로 어느 쪽 조작구인지 라벨로 가른다. */ +.b06-cross-card__revetctl { + display: flex; + flex: 0 0 auto; + align-items: center; + gap: 2px; + margin-left: var(--spacing-8); +} + +.b06-cross-card__revetctl.is-hidden { + display: none; +} + +.b06-cross-card__revetctl-label { + color: var(--color-danger); + font-size: var(--text-caption); + margin-right: 2px; +} + /* 줌 버튼 — 그래프 우측 상단 오버레이. 휠 대신 쓰는 조작구다(2026-08-02 사용자 확정). */ .b06-cross-card__zoom { position: absolute; diff --git a/B06_Section/B06_Section_UI_Style_Cross_Areas.css b/B06_Section/B06_Section_UI_Style_Cross_Areas.css index 2f2b9e08..57d9bed4 100644 --- a/B06_Section/B06_Section_UI_Style_Cross_Areas.css +++ b/B06_Section/B06_Section_UI_Style_Cross_Areas.css @@ -193,8 +193,8 @@ } .b06-chart__culvert-revet.is-active { - fill: color-mix(in srgb, var(--color-royal-amethyst) 45%, transparent); - stroke: var(--color-royal-amethyst); + fill: color-mix(in srgb, var(--color-danger) 45%, transparent); + stroke: var(--color-danger); stroke-width: 2.2; } diff --git a/ui_template/ui_template_locale_b2.ts b/ui_template/ui_template_locale_b2.ts index d879a71a..3d4ba95c 100644 --- a/ui_template/ui_template_locale_b2.ts +++ b/ui_template/ui_template_locale_b2.ts @@ -239,6 +239,7 @@ export const ui_locales_b2 = { "기슭막이 선택 — 화살표로 자리 조절", "Select revetment — nudge with arrows", ], + B06_Cross_Revet_Label: ["기슭막이", "Revetment"], B06_Cross_Revet_Left: ["기슭막이 0.1m 왼쪽으로", "Move revetment 0.1m left"], B06_Cross_Revet_Right: ["기슭막이 0.1m 오른쪽으로", "Move revetment 0.1m right"], B06_Cross_Revet_Reset: ["기슭막이 자동 자리로 초기화", "Reset revetment to solved position"],