/* ============================================================================= * B05_Profile_UI_Structures_Widening.ts * 노면공 「확폭 구간」 상자(PLAN 87-3) — 상세 내용 아래. 범위 안 확폭 > 0 측점을 이어진 구간으로 묶어 * 구간마다 단추(켬 = 확폭 포함 · 끔 = 규격 폭만 · 기본 켬). 끈 구간 = `options.widening_off`. * 규칙(구간 묶기 · 글 읽고 쓰기)은 `design_structure_widening` · 횡단 측점 읽기는 `pipeSections`. * ========================================================================== */ import { pipeSections } from "../A00_Common/design/design_structure_pipe_calc"; import { isRunOff, offForRuns, toggleRun, wideningRuns, type WideningRun, } from "../A00_Common/design/design_structure_widening"; import { stationLabel } from "../common_util/common_util_station"; import { group } from "./B05_Profile_UI_Drainage_Facility_Fields"; export interface WideningBox { root: HTMLElement; /** 종류 · 저장 값으로 다시 그림(범위형이 아니면 가림). */ paint: (visible: boolean, stored: string | number | undefined) => void; /** 저장할 값 — 끈 구간이 없으면 빈 묶음. 횡단 자료가 없으면 저장 값을 그대로 지킴. */ read: () => Record; /** 범위 · 횡단 자료가 바뀌었을 때 다시 그림. */ refresh: () => void; } export function createWideningBox(hooks: { range: () => { start: number | null; end: number | null }; interval: () => number; onChange: () => void; }): WideningBox { const box = group("확폭 구간"); box.root.classList.add("b05-structure__widening"); box.root.hidden = true; let off = ""; /** 지금 범위의 구간들 — 횡단 자료 · 범위가 없으면 null(+ 안내 글). */ const runs = (): { list: WideningRun[] | null; note: string } => { const sections = pipeSections(); if (!sections?.length) return { list: null, note: "횡단 자료 없음" }; const { start, end } = hooks.range(); if (start === null || end === null || end <= start) return { list: null, note: "시작 · 종료 측점을 넣으면 보임" }; const list = wideningRuns(sections, start, end); return { list, note: list.length ? "" : "범위 안 확폭 없음" }; }; const paintBody = (): void => { const { list, note } = runs(); if (!list?.length) { const text = document.createElement("span"); text.className = "b05-structure__widening-note"; text.textContent = note; box.body.replaceChildren(text); return; } const step = hooks.interval(); box.body.replaceChildren( ...list.map((run) => { const on = !isRunOff(run, off); const button = document.createElement("button"); button.type = "button"; button.className = `b05-route__irregular-btn${on ? " is-on" : ""}`; button.setAttribute("aria-pressed", String(on)); button.dataset.wideningRun = `${run.start_m}-${run.end_m}`; const from = stationLabel(run.start_m, step); const to = stationLabel(run.end_m, step); button.textContent = `${from === to ? from : `${from} ~ ${to}`} · 확폭 ${Number(run.widening_m.toFixed(2))} m`; button.title = on ? "확폭 포함 — 누르면 규격 폭만" : "규격 폭만 — 누르면 확폭 포함"; button.addEventListener("click", () => { off = toggleRun(list, off, run); paintBody(); hooks.onChange(); }); return button; }), ); }; // 횡단 자료가 늦게 오면 상자 위에 올 때 다시 그림 box.root.addEventListener("pointerenter", () => { if (!box.root.hidden) paintBody(); }); return { root: box.root, paint(visible, stored) { box.root.hidden = !visible; off = typeof stored === "string" ? stored : ""; if (visible) paintBody(); }, read() { const { list } = runs(); const text = list ? offForRuns(list, off) : off; const out: Record = text ? { widening_off: text } : {}; return out; }, refresh() { if (!box.root.hidden) paintBody(); }, }; }