/* ============================================================================= * B05_Profile_UI_Drainage_Side.ts * 배수유역도를 종단도 오른쪽 판(`side`)으로 끼우는 홀로 선 함수(계획서 24-2c · 24-2e). * 판 자리 약속은 `A00_Common/design/design_profile.ts` 의 `DesignSideFactory` 다 — * `createDrainageSide` 가 그 모양 그대로다(`(host) => 판`). * * 판(`createDrainagePanel`)은 종단 패널을 모른다 — 종단쪽 일은 전부 `host` * (`DrainagePanelCallbacks` + projectId · view, `_Panel_Types` 약속)로 받고, 종단쪽에서 들어오는 일은 * 판의 창구(`setRoute` · `setIntervalSpans` · `pipeChainages` · `setPipeChainages` · * `selectPipeAtChainage` …)로만 한다. * · 관 목록이 바뀜(최소 흙덮기 경고 · 구조물 라인 · 통합 목록) = `host.onPipesChanged` * · 관 자리 읽기 · 쓰기(되돌리기) = `pipeChainages()` · `setPipeChainages()` * · 우클릭 메뉴의 구조물 종류 = `host.structureMenuItems` * · 구간 띠 · 노선 = `setIntervalSpans()` · `setRoute()` * 두 보기(종단 · 횡단)에서 같은 판을 끼우고 **켜고 끈다** — 끄면 자리를 비우되 관 · 유역 * 상태는 그대로 살아 있다(다시 켜면 이어서 보임). 켬 · 끔을 어디에 적을지는 끼우는 쪽 몫이다. * ========================================================================== */ import "./B05_Profile_UI_Style_Drainage.css"; // 홀로 서려면 판이 자기 모양을 들고 온다. import type { DesignSideBoard, DesignSideHost } from "../A00_Common/design/design_profile"; import { createDrainagePanel } from "./B05_Profile_UI_Drainage_Panel"; /** 종단쪽이 넘기는 이음 — 판 바깥 약속(`DrainagePanelCallbacks`)에 projectId · view 를 더한 것. */ export type DrainageSideHost = DesignSideHost; /** 종단도 side 판 = 배수유역 판 창구 그대로 + 켜고 끔. */ export type DrainageSideBoard = DesignSideBoard & { /** 켜고 끈다. 켜면 숨겼던 사이 바뀐 크기 · 그림을 판이 스스로 다시 맞춘다. */ setVisible: (visible: boolean) => void; isVisible: () => boolean; /** 켬 · 끔이 바뀔 때마다 부른다(단추 표시 · 저장). 해제 함수를 돌려준다. */ onVisibleChange: (listener: (visible: boolean) => void) => () => void; }; export interface DrainageSideOptions { /** 처음 켤지(기본 켬). */ visible?: boolean; } /** `DesignSideFactory` — 종단도를 만들 때 한 번 부른다. */ export function createDrainageSide( host: DrainageSideHost, options: DrainageSideOptions = {}, ): DrainageSideBoard { const panel = createDrainagePanel(host); const listeners = new Set<(visible: boolean) => void>(); let visible = true; function setVisible(next: boolean): void { if (next === visible) return; visible = next; panel.root.hidden = !visible; listeners.forEach((listener) => listener(visible)); } // 처음 상태는 알림 없이 맞춘다. if (options.visible === false) { visible = false; panel.root.hidden = true; } return { ...panel, // 보기가 바뀌어도 판은 다시 안 만든다 — 켬 · 끔은 끼우는 쪽이 `setVisible` 로 정한다. setView: () => {}, setVisible, isVisible: () => visible, onVisibleChange(listener) { listeners.add(listener); return () => listeners.delete(listener); }, dispose() { listeners.clear(); panel.dispose(); }, }; } /** 켜고 끄는 단추 — 어느 보기에서든 같은 모양 · 같은 글. 누르면 판을 뒤집는다. */ export function createDrainageToggleButton(side: DrainageSideBoard): HTMLButtonElement { const button = document.createElement("button"); button.type = "button"; button.className = "ui-btn ui-btn--ghost b05-drainage__toggle"; button.textContent = "배수유역도"; button.title = "배수유역도 판을 켜고 끕니다"; const sync = (visible: boolean): void => { button.setAttribute("aria-pressed", String(visible)); button.classList.toggle("is-active", visible); }; sync(side.isVisible()); side.onVisibleChange(sync); button.addEventListener("click", () => side.setVisible(!side.isVisible())); return button; }