/* ============================================================================= * 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, 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, BoxSideRole, } 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; toggleFord: (role: FordWallRole) => void; toggleBox: (role: BoxSideRole) => void; /** 오버레이가 만들어 준 강조 setter를 등록하고 현재 선택을 다시 칠한다. */ attachFord: (setter: FordHighlightSetter, layout: FordLayout) => void; attachBox: (setter: BoxHighlightSetter, layout: BoxLayout) => void; /** 카드 선택이 풀리거나 면적이 켜지면 구조물 선택도 푼다. */ clearSelection: () => void; panels: Array; } 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(); 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: FordWallRole | BoxSideRole | null, ): void => { if (!dock) return; (dock as { show: (role: FordWallRole | BoxSideRole | null) => void }).show(role); if (role) adoptAdjustPanel(dock.root); else releaseAdjustPanel(dock.root); }; const toggleFord = structureToggle({ 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({ 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, ), }; }