- 일꾼 stderr 를 파이프로 두면 자식 경고가 쌓여 8/57 에서 멈춤 → 임시 파일로 - 횡단 처음 보기 = 탐색 범위 · 임계면 · 격자 중심을 판 가운데(두 번 누름 = 이 보기 · 줌 아웃 = 모형 전체) - 본문 높이 = 화면 높이(커지며 넘치던 것) · 고른 줄 표시 밝음/어두움 같이 · 3D 보기 창 맨 위 - 자유물체도 N · U · S = 바닥으로 들어오는 화살 · 글 = 화살 바깥 끝 - 목록 측점 0+000 꼴 · 좌우 깎/쌓 · 해석 없음 = - Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
116 lines
4.0 KiB
TypeScript
116 lines
4.0 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\./, "").replace(/\.000$/, "") ?? row.key }),
|
|
el("td", {
|
|
text: row.sides.length
|
|
? roleText(row, { cut: t("E03_CutShort"), fill: t("E03_FillShort") })
|
|
: "-",
|
|
}),
|
|
...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")
|
|
: v === "none"
|
|
? "-"
|
|
: 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;
|
|
}
|