refactor(예정노선): 구역 목록을 속성 판 파일에서 UI_Pieces 로 떼어냄 (PLAN 597-1)
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,105 @@
|
||||
/* =============================================================================
|
||||
* 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<number, typeof network.pieces>();
|
||||
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 };
|
||||
}
|
||||
@@ -8,19 +8,18 @@
|
||||
* [방향 바꾸기] [갈래 시점 지정] [선 지움](선 하나일 때) · [마크 지움](마크 고름) · 보기만 계정 = 값만.
|
||||
* 측점 방향(585-3 · 요구 446 3) = 사용자가 정함 — 본선 = [방향 바꾸기] · 다른 선에 닿은 끝 = [갈래 시점 지정]
|
||||
* (그 점이 시점 · 꼬리에 닿은 머리 = 한 선으로 이음) · 자동 판정 없음.
|
||||
* 좌측 목록 = 보기 + 고르기만(해 고침은 속성 판 한 곳) — 해 줄(같은 해) · 선 머리 줄(선) · 구간 줄(구간).
|
||||
* 좌측 「구역 · 연도」 목록 = `UI_Pieces.ts`.
|
||||
* ========================================================================== */
|
||||
|
||||
import { createButton } from "@ui/ui_template_elements";
|
||||
import { createFloatPanel } from "@ui/ui_template_float_panel";
|
||||
import { createSelectField } from "@ui/ui_template_input";
|
||||
import { createSegment } from "@ui/ui_template_segment";
|
||||
import { createText } from "@ui/ui_template_text";
|
||||
import { stationLabel } from "../common_util/common_util_station";
|
||||
import { lineLength } from "./logic/D03_PlanAnalysis_Draw";
|
||||
import { lineNames, setPiecesYear, type PlanNetwork } from "./logic/D03_PlanAnalysis_Plan";
|
||||
import type { PlanEdit, PlanScope } from "./D03_PlanAnalysis_UI_Edit";
|
||||
import { PLAN_STATION_INTERVAL_M } from "./D03_PlanAnalysis_UI_Map";
|
||||
import { createPlanPieces } from "./D03_PlanAnalysis_UI_Pieces";
|
||||
|
||||
/** 조각 연도 고를 수 — 사업연도 시작부터. */
|
||||
const YEAR_SPAN = 10;
|
||||
@@ -57,8 +56,7 @@ export function createPlanProps(params: PlanPropsParams): PlanProps {
|
||||
float.root.classList.add("d03-plan__props");
|
||||
float.root.dataset.role = "plan-props";
|
||||
float.root.hidden = true;
|
||||
const list = document.createElement("div");
|
||||
list.className = "d03-plan__pieces";
|
||||
const pieceList = createPlanPieces({ network: params.network, edit });
|
||||
|
||||
function yearOptions(): Array<{ value: string; text: string }> {
|
||||
const start = params.yearStart();
|
||||
@@ -167,83 +165,12 @@ export function createPlanProps(params: PlanPropsParams): PlanProps {
|
||||
float.body.replaceChildren(scope.root, year.root, rows, actions);
|
||||
}
|
||||
|
||||
/** 좌측 줄 — 누름 = 고름(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;
|
||||
};
|
||||
|
||||
function renderList(): 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<number, typeof network.pieces>();
|
||||
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 {
|
||||
panel: float.root,
|
||||
list,
|
||||
list: pieceList.list,
|
||||
render() {
|
||||
renderPanel();
|
||||
renderList();
|
||||
pieceList.render();
|
||||
},
|
||||
yearOptions,
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user