/* ============================================================================= * D03_PlanAnalysis_UI_Pieces.ts * D03 좌측 「구역 · 연도」 목록 몸(PLAN 597) — 해 줄(같은 해) · 선 머리 줄(선) · 구간 줄(구간). * 속성 판(`UI_Props.ts`)에서 떼어냄 — 목록 = 보기 + 고르기. * ========================================================================== */ import { createText } from "@ui/ui_template_text"; import { stationLabel } from "../common_util/common_util_station"; import { lineLength } from "./logic/D03_PlanAnalysis_Draw"; import { lineNames, type PlanNetwork } from "./logic/D03_PlanAnalysis_Plan"; import type { PlanEdit } from "./D03_PlanAnalysis_UI_Edit"; import { PLAN_STATION_INTERVAL_M } from "./D03_PlanAnalysis_UI_Map"; const station = (m: number): string => stationLabel(m, PLAN_STATION_INTERVAL_M, 1); const meters = (m: number): string => `${m.toFixed(1)} m`; export interface PlanPiecesParams { network: () => PlanNetwork; edit: PlanEdit; } export interface PlanPieces { /** 좌측 「구역 · 연도」 목록 몸. */ list: HTMLElement; render: () => void; } /** 좌측 줄 — 누름 = 고름(div · 열쇠 Enter 도). */ const pickRow = (row: HTMLElement, onPick: () => void): HTMLElement => { row.tabIndex = 0; row.setAttribute("role", "button"); row.addEventListener("click", onPick); row.addEventListener("keydown", (event) => event.key === "Enter" && onPick()); return row; }; export function createPlanPieces(params: PlanPiecesParams): PlanPieces { const { edit } = params; const list = document.createElement("div"); list.className = "d03-plan__pieces"; function render(): void { const network = params.network(); const names = lineNames(network.lines); const picked = new Set(edit.selected()?.pieces ?? []); const scope = edit.selected()?.scope; const rows: HTMLElement[] = []; const byYear = new Map(); for (const piece of network.pieces) byYear.set(piece.year, [...(byYear.get(piece.year) ?? []), piece]); for (const [year, pieces] of [...byYear].sort(([a], [b]) => a - b)) { const row = document.createElement("div"); row.className = "d03-plan__piece d03-plan__piece-year"; row.dataset.year = String(year); row.classList.toggle( "is-selected", scope === "year" && pieces.every((p) => picked.has(p.id)), ); const length = pieces.reduce((sum, p) => sum + p.to_m - p.from_m, 0); row.append(createText(`${year}년 · 구간 ${pieces.length} · ${meters(length)}`, "muted")); rows.push( pickRow(row, () => { edit.select([pieces[0].id]); edit.setScope("year"); }), ); } network.lines.forEach((line, index) => { const pieces = network.pieces.filter((piece) => piece.line === line.id); if (!pieces.length) return; const head = document.createElement("div"); head.className = "d03-plan__piece d03-plan__piece-head"; head.dataset.line = line.id; head.classList.toggle( "is-selected", scope === "line" && pieces.every((p) => picked.has(p.id)), ); head.append(createText(`${names[index]} · ${meters(lineLength(line.vertices))}`, "muted")); rows.push( pickRow(head, () => { edit.select([pieces[0].id]); edit.setScope("line"); }), ); for (const piece of pieces) { const row = document.createElement("div"); row.className = "d03-plan__piece"; row.classList.toggle("is-selected", picked.has(piece.id)); row.dataset.piece = piece.id; row.append( createText( `${station(piece.from_m)} ~ ${station(piece.to_m)} (${meters(piece.to_m - piece.from_m)})`, "muted", ), createText(`${piece.year}년`, "muted"), ); rows.push(pickRow(row, () => edit.select([piece.id]))); } }); if (!rows.length) rows.push(createText("선을 그으면 구역이 생깁니다.", "muted")); list.replaceChildren(...rows); } return { list, render }; }