- 길: POST analyze2d(뒤에서 · 일꾼 프로세스 병렬 · 바뀐 측점만) · GET progress · result2d · section · corridor3d · PUT representative - 입력 = E01 복사본(측점 · 횡단 · DTM 넓힘) + E02 지반 조건 · 엔진 = sub4 analyze_station · 벽 = 다음 일감(532-4) - 화면: 측점 목록(조건 · 정렬 · N.G 만 · 대표 단면) · 횡단(지층 · 지하수선 · 임계면 · 후보 · 등안전율 지도 · 중심 격자 · 절편) · 결과 · 절편표 · 자유물체도 · 절편력 그래프 · 평면 띠 · 3D 보기 - 계산 = logic/E03_StabSection_Logic.ts · 시험 test_532_3_e03_section.py Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
110 lines
3.9 KiB
TypeScript
110 lines
3.9 KiB
TypeScript
/* =============================================================================
|
|
* E03_StabSection_UI_List.ts
|
|
* E03 좌측 측점 목록(PLAN 532-3) — 측점 · 깎기/쌓기 · 조건별 최소 안전율 · 판정(아이콘 + 글) ·
|
|
* 대표 단면 별(자동 = 채움 · 사용자 켬/끔 = 밑줄 · 누르면 바꿈). 정렬 · 거름은 logic `listRows`.
|
|
* ========================================================================== */
|
|
|
|
import { el } from "@ui/ui_template_elements";
|
|
import { t, type LocaleKey } from "@ui/ui_template_locale";
|
|
import {
|
|
listRows,
|
|
roleText,
|
|
verdictOf,
|
|
type Condition,
|
|
type SortMode,
|
|
type StationRow,
|
|
} from "./logic/E03_StabSection_Logic";
|
|
|
|
const fmt = (v: number | null | undefined): string =>
|
|
v === null || v === undefined || !Number.isFinite(v) ? "-" : v.toFixed(2);
|
|
|
|
export interface ListDraw {
|
|
rows: StationRow[];
|
|
conditions: Condition[];
|
|
condition: Condition;
|
|
sort: SortMode;
|
|
ngOnly: boolean;
|
|
active: string | null;
|
|
/** 사용자 켬/끔 — 저장 전 캐시 포함 · 없으면 서버 값 */
|
|
representative: (row: StationRow) => { on: boolean; user: boolean | null };
|
|
editable: boolean;
|
|
}
|
|
|
|
export function buildList(
|
|
draw: ListDraw,
|
|
onSelect: (key: string) => void,
|
|
onToggle: (key: string) => void,
|
|
): HTMLElement {
|
|
const box = el("div", { className: "e03-list" });
|
|
const table = el("table", { attrs: { "data-role": "station-list" } });
|
|
const head = el("tr");
|
|
head.append(
|
|
el("th", { text: t("E03_Col_Station") }),
|
|
el("th", { text: t("E03_Col_Role") }),
|
|
...draw.conditions.map((c) =>
|
|
el("th", {
|
|
className: "is-num",
|
|
text: t(`E03_Cond_${c}` as LocaleKey),
|
|
attrs: c === draw.condition ? { "aria-sort": "other" } : {},
|
|
}),
|
|
),
|
|
el("th", { text: t("E03_Verdict") }),
|
|
el("th", { text: t("E03_Col_Rep"), attrs: { title: t("E03_Rep_Hint") } }),
|
|
);
|
|
table.append(el("thead", { children: [head] }));
|
|
const body = el("tbody");
|
|
const rows = listRows(draw.rows, draw.condition, draw.sort, draw.ngOnly);
|
|
for (const row of rows) {
|
|
const tr = el("tr", {
|
|
className: row.key === draw.active ? "is-active" : "",
|
|
attrs: { "data-key": row.key, "data-verdict": verdictOf(row, draw.condition) },
|
|
});
|
|
const v = verdictOf(row, draw.condition);
|
|
const rep = draw.representative(row);
|
|
const star = el("button", {
|
|
className: `e03-star${rep.on ? " is-on" : ""}${rep.user !== null ? " is-user" : ""}`,
|
|
text: rep.on ? "★" : "☆",
|
|
attrs: {
|
|
type: "button",
|
|
title:
|
|
[
|
|
...(row.representative?.auto ?? []).map((r) => t(`E03_Rep_${r}` as LocaleKey)),
|
|
rep.user !== null ? t("E03_Rep_User") : "",
|
|
]
|
|
.filter(Boolean)
|
|
.join(" · ") || t("E03_Rep_Hint"),
|
|
"aria-pressed": String(rep.on),
|
|
},
|
|
});
|
|
star.disabled = !draw.editable;
|
|
star.addEventListener("click", (ev) => {
|
|
ev.stopPropagation();
|
|
onToggle(row.key);
|
|
});
|
|
tr.append(
|
|
el("td", { text: row.label?.replace(/^STA\./, "") ?? row.key }),
|
|
el("td", {
|
|
text: row.sides.length ? roleText(row, { cut: t("E03_Cut"), fill: t("E03_Fill") }) : "-",
|
|
}),
|
|
...draw.conditions.map((c) => el("td", { className: "is-num", text: fmt(row.min[c]) })),
|
|
el("td", {
|
|
children: [
|
|
el("span", {
|
|
className: `e03-verdict e03-verdict--${v}`,
|
|
text: row.error ? t("E03_Error") : t(`E03_Verdict_${v}` as LocaleKey),
|
|
}),
|
|
],
|
|
}),
|
|
el("td", { children: [star] }),
|
|
);
|
|
if (row.error) tr.title = row.error;
|
|
tr.addEventListener("click", () => onSelect(row.key));
|
|
body.append(tr);
|
|
}
|
|
table.append(body);
|
|
box.append(table);
|
|
if (!rows.length) box.append(el("p", { className: "e03-empty", text: t("E03_ListEmpty") }));
|
|
queueMicrotask(() => box.querySelector("tr.is-active")?.scrollIntoView({ block: "nearest" }));
|
|
return box;
|
|
}
|