구조물도 모달(집계표 행 누름 · 그 측점 조합 전부 · 복사본 먼저) · 공용 표 onRowClick opt-in(95-3) · 수리 상세 폭 제한 풂 · 카드 높이 화면 따라 · @container(95-4). 화면 검증 전. Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01W4uKaV4ZBhsJN4GHG7yRj4
95 lines
3.4 KiB
JavaScript
95 lines
3.4 KiB
JavaScript
/* 공용 표(`ui_template/ui_template_data_table.ts`) 단위검증 헬퍼 —
|
|
* 트랜스파일해 Node 로 견본 표 HTML 을 만들고 판정할 값을 JSON 으로 냄. */
|
|
const fs = require("fs");
|
|
const path = require("path");
|
|
const ts = require(path.join(__dirname, "..", "..", "config", "node_modules", "typescript"));
|
|
|
|
const file = path.join(__dirname, "..", "..", "ui_template", "ui_template_data_table.ts");
|
|
const js = ts.transpileModule(fs.readFileSync(file, "utf8"), {
|
|
compilerOptions: { module: ts.ModuleKind.CommonJS, target: ts.ScriptTarget.ES2020 },
|
|
}).outputText;
|
|
const box = { exports: {} };
|
|
const req = (p) => (p.endsWith(".css") ? {} : require(p));
|
|
new Function("exports", "module", "require", js)(box.exports, box, req);
|
|
const m = box.exports;
|
|
|
|
const src = fs.readFileSync(path.join(__dirname, "fixture_data_table_sample.js"), "utf8");
|
|
const sampleDataTable = new Function(src + "; return sampleDataTable;")();
|
|
|
|
const s = sampleDataTable(66);
|
|
const html = m.renderDataTableHtml(s);
|
|
const head = html.split("<thead>")[1].split("</thead>")[0];
|
|
const headRows = head.split("</tr>").filter(Boolean);
|
|
const body = html.split("<tbody>")[1].split("</tbody>")[0];
|
|
const bodyRows = body.split("</tr>").filter(Boolean);
|
|
const foot = html.split("<tfoot>")[1].split("</tfoot>")[0];
|
|
|
|
const t0 = process.hrtime.bigint();
|
|
const big = m.renderDataTableHtml(sampleDataTable(1000));
|
|
const ms = Number(process.hrtime.bigint() - t0) / 1e6;
|
|
|
|
const one = (c, v) =>
|
|
m.formatCell(
|
|
v,
|
|
c.kind === "number"
|
|
? new Intl.NumberFormat("ko-KR", {
|
|
minimumFractionDigits: c.decimals ?? 0,
|
|
maximumFractionDigits: c.decimals ?? 0,
|
|
})
|
|
: null,
|
|
)[0];
|
|
const nc = { key: "a", label: "a", kind: "number", decimals: 2 };
|
|
const tc = { key: "b", label: "b" };
|
|
|
|
console.log(
|
|
JSON.stringify({
|
|
cols: s.columns.length,
|
|
headRows: headRows.length,
|
|
firstHeadRow: headRows[0],
|
|
secondHeadRow: headRows[1],
|
|
bodyRows: bodyRows.length,
|
|
firstBodyRow: bodyRows[0],
|
|
foot,
|
|
frozenCells: (body.match(/class="is-frozen is-edge"/g) || []).length,
|
|
bigRows: big.split("<tbody>")[1].split("</tbody>")[0].split("</tr>").length - 1,
|
|
bigMs: ms,
|
|
fmt: {
|
|
thousands: one(nc, 1234567.891),
|
|
neg: one(nc, -97.9),
|
|
zero: one(nc, 0),
|
|
nul: one(nc, null),
|
|
undef: one(nc, undefined),
|
|
blank: one(nc, ""),
|
|
nan: one(nc, NaN),
|
|
text: one(tc, "120+18"),
|
|
textNum: one(tc, 5),
|
|
},
|
|
win: (() => {
|
|
const b = m
|
|
.renderDataTableHtml(sampleDataTable(1000), { from: 61, to: 121, rowH: 25 })
|
|
.split("<tbody>")[1]
|
|
.split("</tbody>")[0]
|
|
.split("</tr>")
|
|
.filter(Boolean);
|
|
return { n: b.length, top: b[0], second: b[1].slice(0, 20), bottom: b[b.length - 1] };
|
|
})(),
|
|
opts: m.renderDataTableHtml({
|
|
columns: [
|
|
{ key: "sta", label: "측점" },
|
|
{ key: "c1", label: "메쌓기", group: ["기슭막이"], title: "풍선", className: "is-missing" },
|
|
],
|
|
rows: [{ sta: "0+0" }],
|
|
frozen: 1,
|
|
empty: "",
|
|
}),
|
|
pick: m.renderDataTableHtml({
|
|
columns: [{ key: "x", label: "X" }],
|
|
rows: [{ x: 1 }, { x: 2 }],
|
|
total: { x: 3 },
|
|
onRowClick: () => undefined,
|
|
}),
|
|
escaped: m.renderDataTableHtml({ columns: [{ key: "x", label: "<b>" }], rows: [{ x: "a&<" }] }),
|
|
noTotal: m.renderDataTableHtml({ columns: [tc], rows: [] }).includes("<tfoot>"),
|
|
}),
|
|
);
|