Files
Aislo/B05_Profile/B05_Profile_UI_Drainage_Side.ts
T

94 lines
4.0 KiB
TypeScript

/* =============================================================================
* B05_Profile_UI_Drainage_Side.ts
* 배수유역도를 종단도의 **side 판**으로 끼우는 홀로 선 창구(계획서 24-2e).
*
* 판(`createDrainagePanel`)은 종단 패널을 모른다 — 종단쪽 일은 전부 `DrainageSideHost`
* (= `DrainagePanelCallbacks`, `_Panel_Types` 약속)로 받고, 종단쪽에서 들어오는 일은
* `panel` 의 창구(`setRoute` · `setIntervalSpans` · `pipeChainages` · `setPipeChainages` ·
* `selectPipeAtChainage` …)로만 한다.
* · 관 목록이 바뀜(최소 흙덮기 경고 · 구조물 라인 · 통합 목록) = `host.onPipesChanged`
* · 관 자리 읽기 · 쓰기(되돌리기) = `panel.pipeChainages()` · `panel.setPipeChainages()`
* · 우클릭 메뉴의 구조물 종류 = `host.structureMenuItems`
* · 구간 띠 · 노선 = `panel.setIntervalSpans()` · `panel.setRoute()`
* 두 보기(종단 · 횡단)에서 같은 판을 끼우고 **켜고 끈다** — 끄면 자리를 비우되 관 · 유역
* 상태는 그대로 살아 있다(다시 켜면 이어서 보임). 켬 · 끔을 어디에 적을지는 끼우는 쪽 몫이다.
* ========================================================================== */
import "./B05_Profile_UI_Style_Drainage.css"; // 홀로 서려면 판이 자기 모양을 들고 온다.
import { createDrainagePanel } from "./B05_Profile_UI_Drainage_Panel";
import type { DrainagePanel, DrainagePanelCallbacks } from "./B05_Profile_UI_Drainage_Panel_Types";
/** 종단쪽이 넘기는 이음 — 판 바깥 약속(`DrainagePanelCallbacks`) 그대로. */
export type DrainageSideHost = DrainagePanelCallbacks;
export interface DrainageSide {
/** 종단도 side 자리에 붙일 뿌리. */
root: HTMLElement;
/** 판의 창구 — 종단쪽이 노선 · 구간 띠 · 관 자리 · 선택을 밀어 넣는다. */
panel: DrainagePanel;
/** 켜고 끈다. 켜면 숨겼던 사이 바뀐 크기 · 그림을 판이 스스로 다시 맞춘다. */
setVisible: (visible: boolean) => void;
isVisible: () => boolean;
/** 켬 · 끔이 바뀔 때마다 부른다(단추 표시 · 저장). 해제 함수를 돌려준다. */
onVisibleChange: (listener: (visible: boolean) => void) => () => void;
dispose: () => void;
}
export interface DrainageSideOptions {
/** 처음 켤지(기본 켬). */
visible?: boolean;
}
export function createDrainageSide(
host: DrainageSideHost = {},
options: DrainageSideOptions = {},
): DrainageSide {
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 {
root: panel.root,
panel,
setVisible,
isVisible: () => visible,
onVisibleChange(listener) {
listeners.add(listener);
return () => listeners.delete(listener);
},
dispose() {
listeners.clear();
panel.dispose();
},
};
}
/** 켜고 끄는 단추 — 어느 보기에서든 같은 모양 · 같은 글. 누르면 `side` 를 뒤집는다. */
export function createDrainageToggleButton(side: DrainageSide): 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;
}