Files
Aislo/B06_Section/B06_Section_UI_Cross_View_Bodies.ts
T
eomsangdonandClaude Opus 5.5 5c0c8fd9c4 fix(B06): 횡단 세월교 · BOX 본체 클릭 = 좌측 본체 상자 고름 · 강조(PLAN 66-1)
- 세월교 바닥판 · 관 판정면 추가 → 「세월교」 상자 강조 · 스크롤 · 그림 바닥판 · 관 붉게
- BOX 구체 가운데 1/3 = 본체 · 양쪽 = 그 끝(유입 · 유출 상자 그대로)
- 고름 값에 body 추가(FordPick · BoxPick) · 조정창은 본체에서 안 뜸
- B05 넘김 키 body 도 같은 길

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01EYJJRCfHfdCGNhmXTjQKV7
2026-10-06 23:36:17 +09:00

164 lines
6.2 KiB
TypeScript

/* =============================================================================
* B06_Section_UI_Cross_View_Bodies.ts
* 횡단 카드 ↔ **구체 구조물(세월교·BOX암거)** 배선 한 벌 — 선택 토글·강조 setter·
* 조정창을 묶어 카드 렌더러(`_UI_Cross_View.ts`)에서 700줄 제한으로 분리했다
* (2026-08-25). 기하나 그리기는 하지 않는다.
* ========================================================================== */
import type { CrossSection } from "./B06_Section_Api_Fetch";
import type {
FordAdjust,
FordHighlightSetter,
FordLayout,
FordPick,
FordWallRole,
} from "./B06_Section_UI_Cross_Ford";
import { buildFordPanel } from "./B06_Section_UI_Cross_Ford_Panel";
import type { FordControl, FordPanelHandle } from "./B06_Section_UI_Cross_Ford_Panel";
import { fordCardState, fordPanelDeps } from "./B06_Section_UI_Cross_View_Ford";
import type { BoxAdjust, BoxHighlightSetter, BoxLayout, BoxPick } from "./B06_Section_UI_Cross_Box";
import { buildBoxPanel } from "./B06_Section_UI_Cross_Box_Panel";
import type { BoxControl, BoxPanelHandle } from "./B06_Section_UI_Cross_Box_Panel";
import { boxPanelDeps, structureToggle } from "./B06_Section_UI_Cross_View_Box";
import { adoptAdjustPanel, releaseAdjustPanel } from "./B06_Section_UI_Adjust_Dock";
export interface BodyWiringDeps {
section: CrossSection;
ford?: FordControl;
box?: BoxControl;
/** 카드가 지금 선택 상태인가 — 구조물을 고르면 카드도 함께 선택된다. */
isCardSelected: () => boolean;
/** 면적 강조 해제(구조물 선택과 배타). */
clearArea: () => void;
selectCard: () => void;
}
export interface BodyWiring {
fordAdjust: () => FordAdjust | undefined;
boxAdjust: () => BoxAdjust | undefined;
/** 본체(`body`)는 조정창 없이 좌측 본체 상자만 고름(66-1). */
toggleFord: (role: FordPick) => void;
toggleBox: (role: BoxPick) => void;
/** 오버레이가 만들어 준 강조 setter를 등록하고 현재 선택을 다시 칠한다. */
attachFord: (setter: FordHighlightSetter, layout: FordLayout) => void;
attachBox: (setter: BoxHighlightSetter, layout: BoxLayout) => void;
/** 카드 선택이 풀리거나 면적이 켜지면 구조물 선택도 푼다. */
clearSelection: () => void;
panels: Array<FordPanelHandle | BoxPanelHandle>;
}
export function createBodyWiring(deps: BodyWiringDeps): BodyWiring {
const { section, ford, box } = deps;
const chainage = section.chainage_m;
let fordRole = section.ford ? (ford?.selectedFor(chainage) ?? null) : null;
let boxRole = section.box ? (box?.selectedFor(chainage) ?? null) : null;
let setFordActive: FordHighlightSetter = () => {};
let setBoxActive: BoxHighlightSetter = () => {};
let fordSlabLengthM = 0;
let fordHeights = new Map<FordWallRole, number>();
let boxLayout: BoxLayout | null = null;
const fordDeps =
section.ford && ford
? fordPanelDeps({
section,
ford,
heightFor: (role) => fordHeights.get(role) ?? 0,
slabLengthM: () => fordSlabLengthM,
close: () => {
if (fordRole) toggleFord(fordRole);
},
})
: null;
const boxDeps =
section.box && box
? boxPanelDeps({
chainageM: chainage,
box,
layout: () => boxLayout,
inletOnLeft: (section.uphill_side ?? "left") === "left",
close: () => {
if (boxRole) toggleBox(boxRole);
},
})
: null;
const fordPanel = fordDeps ? buildFordPanel(fordDeps) : null;
const boxPanel = boxDeps ? buildBoxPanel(boxDeps) : null;
// 좌측 [횡단 조정] 사본 — 배수관 조정창과 같은 규칙(2026-08-29 일원화): 같은 deps로
// 하나 더 만들어 고른 동안만 좌측 슬롯에 세운다.
const fordDock = fordDeps ? buildFordPanel(fordDeps, { dock: true }) : null;
const boxDock = boxDeps ? buildBoxPanel(boxDeps, { dock: true }) : null;
const syncDock = (
dock: FordPanelHandle | BoxPanelHandle | null,
role: FordPick | BoxPick | null,
): void => {
if (!dock) return;
(dock as { show: (role: FordPick | BoxPick | null) => void }).show(role);
if (role) adoptAdjustPanel(dock.root);
else releaseAdjustPanel(dock.root);
};
const toggleFord = structureToggle<FordPick>({
current: () => fordRole,
setCurrent: (value) => (fordRole = value),
setActive: (value) => setFordActive(value),
showPanel: (visible) => {
fordPanel?.show(visible ? fordRole : null);
syncDock(fordDock, visible ? fordRole : null);
},
persist: (value) => ford?.select(chainage, value),
isCardSelected: deps.isCardSelected,
clearArea: deps.clearArea,
selectCard: deps.selectCard,
});
const toggleBox = structureToggle<BoxPick>({
current: () => boxRole,
setCurrent: (value) => (boxRole = value),
setActive: (value) => setBoxActive(value),
showPanel: (visible) => {
boxPanel?.show(visible ? boxRole : null);
syncDock(boxDock, visible ? boxRole : null);
},
persist: (value) => box?.select(chainage, value),
isCardSelected: deps.isCardSelected,
clearArea: deps.clearArea,
selectCard: deps.selectCard,
});
fordPanel?.show(fordRole);
boxPanel?.show(boxRole);
syncDock(fordDock, fordRole);
syncDock(boxDock, boxRole);
return {
fordAdjust: () => ford?.adjustFor(chainage),
boxAdjust: () => box?.adjustFor(chainage),
toggleFord,
toggleBox,
attachFord: (setter, layout) => {
setFordActive = setter;
const state = fordCardState(layout);
fordSlabLengthM = state.slabLengthM;
fordHeights = state.heights;
if (fordRole) setter(fordRole);
// 창은 그리기 전에 만들어져 값이 비어 있다 — 계산 결과가 들어온 뒤 다시 렌더한다.
fordPanel?.show(fordRole);
syncDock(fordDock, fordRole);
},
attachBox: (setter, layout) => {
setBoxActive = setter;
boxLayout = layout;
if (boxRole) setter(boxRole);
boxPanel?.show(boxRole);
syncDock(boxDock, boxRole);
},
clearSelection: () => {
if (fordRole !== null) toggleFord(fordRole);
if (boxRole !== null) toggleBox(boxRole);
},
panels: [fordPanel, boxPanel].filter(
(panel): panel is FordPanelHandle | BoxPanelHandle => !!panel,
),
};
}