Files
Aislo/B06_Section/B06_Section_UI_Page_Pick_Sync.ts
T
eomsangdonandClaude Opus 5.5 b3c1762229 feat(design): 목록 · 폼 · 배수유역도 강조를 고름 한 칸에서 · 풀기 = Esc 한 길 (233-3)
- 종단 보기 구독이 배수유역도 관 마커 · 유역 · 마름모까지 알림 없이 칠함 · 재계산 뒤 다시 얹음
- 지도 고름 · 목록 관 고름 · 목록 다시 누름도 고름 한 칸에 적고 칠하기는 구독 몫
- 목록 관 줄 폭 PIPE_MATCH_M · 조금 옮겨진 관도 고름 따라감 · 저장 뒤 목록 주입이 관 폼 안 닫음
- design_pick_escape — Esc = 고름 한 칸 비움 → 종단 · 3D · 지도 · 폼 · 횡단 카드 · 범위 · 형상 모두 풂

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01STqEqztUthVFBmEP18wBSz
2026-10-10 19:13:39 +09:00

84 lines
3.7 KiB
TypeScript

/* =============================================================================
* B06_Section_UI_Page_Pick_Sync.ts
* 횡단 보기 강조를 고름 한 칸에 묶음 (233-2 · 조사 AF (나) ④).
*
* 고름 한 칸(`structure-pick`)이 유일 원천이다. 횡단 보기가 보일 때 다른 곳(풀기 Esc 등)이 값을
* 바꾸면 구독으로 받아 카드 · 범위 테두리(`--span`) · 포장층 · 벽 · 세월교 · BOX 형상 강조를 그 값으로
* 다시 세운다 — 값이 비면 모두 풂, 값이 있으면 넘김 적용(`applyStructurePick`)과 같은 길로 다시 얹음.
* 횡단 보기 안 누름(카드 · 목록 · 알약 · 형상)은 그 자리에서 그리고 값을 적으므로(from "b06") 되받지 않음.
* 풀기(Esc · 카드 다시 누름 · 목록 다시 누름 · 보기 오감)는 `release` 한 길 — 카드 · 폼 · 범위 테두리 ·
* 형상까지 모두 끔(카드만 풀고 `--span` 이 남던 구멍 · a4).
* ========================================================================== */
import { subscribeSelection } from "../A00_Common/design/design_selection";
import { bindEscapeRelease } from "../A00_Common/design/design_pick_escape";
import type { CrossSection } from "./B06_Section_Api_Fetch";
import type { StationControls } from "./B06_Section_UI_Page_Station_Controls";
export interface CrossPickDeps {
sections: () => ReadonlyArray<CrossSection>;
controls: Pick<StationControls, "revetOffset" | "ford" | "box">;
/** 좌측 폼(구조물 · 관) 닫기. */
releaseForm: () => void;
view: {
focusStation: (stationId: string | null) => void;
refreshCards: (chainages: ReadonlyArray<number>) => void;
selectMark: (structureId: string | null, span?: boolean) => void;
};
/** 지금 값으로 카드 · 형상 · 폼을 다시 세움(넘김 적용 길). */
reapply: () => void;
}
let releasing = false;
/** 횡단 보기 강조를 모두 끔 — 카드 · 벽 · 세월교 · BOX · 폼 관 · 범위 테두리 · 포장층(값은 안 건드림).
* 카드를 풀면 카드 리스너가 다시 풀기를 부름 — 되돌아온 부름은 건너뜀. */
export function releaseCrossHighlights(deps: CrossPickDeps): void {
if (releasing) return;
releasing = true;
try {
releaseAll(deps);
} finally {
releasing = false;
}
}
function releaseAll(deps: CrossPickDeps): void {
const { revetOffset, ford, box } = deps.controls;
const lit = deps
.sections()
.filter(
(s) =>
revetOffset.highlightFor(s) ||
ford.selectedFor(s.chainage_m) ||
box.selectedFor(s.chainage_m),
);
for (const s of lit) {
if (revetOffset.selectedFor(s)) revetOffset.select(s.chainage_m, null);
if (ford.selectedFor(s.chainage_m)) ford.select(s.chainage_m, null);
if (box.selectedFor(s.chainage_m)) box.select(s.chainage_m, null);
}
deps.releaseForm();
deps.view.focusStation(null);
deps.view.selectMark(null);
deps.view.refreshCards(lit.map((s) => s.chainage_m));
}
/** 횡단 보기를 고름 한 칸에 묶음 — 보일 때 · 다른 곳이 바꾼 값만 받음 · 보기를 떠나면 끊음.
* `release` = 강조 모두 끔(값은 부르는 쪽이 비움). */
export function bindCrossPick(
projectId: string | null,
host: { visible: () => boolean; onDispose: (fn: () => void) => void },
deps: CrossPickDeps,
): { release: () => void } {
host.onDispose(
subscribeSelection(projectId, (selection, from) => {
if (!host.visible() || from === "b06") return;
if (selection.chainageM === null) releaseCrossHighlights(deps);
else deps.reapply();
}),
);
bindEscapeRelease(projectId, host); // 풀기 한 길(233-3)
return { release: () => releaseCrossHighlights(deps) };
}