- 보조 글: span · small 에 붙은 .ui-text = inline(줄 안 끊김) · p · div 는 한 줄 상자 그대로 - 단추: 위아래 여백 = 1em 기준 + 줄 높이 1 · 왼쪽 패널 양식(.ui-panel-form) 옛 규칙(글 12 · 줄 높이 normal)이 단을 덮지 않음 — B07 도면 단추 35 → 32 - 숨김: .ui-btn · .ui-icon-btn [hidden] = display none(공용 기본 꼴이 안 실린 곳에서도) - 파일 놓기 칸: 칸 어디든 누름 · 초점 Enter · Space = 파일 고르기(옛 B03 동작) · 단추 누름은 칸으로 올라가 한 번만 - 표: 격자 꼴(grid · 세로선 · 칸 테두리 · 몸 안 이름 칸 th) · 합친 칸(colSpan · rowSpan · head) · columns 비우면 머리 줄 없음 · 기본 꼴 그대로 - 부품 모음 견본: 글 줄 안 보조 글 · 격자표(수량 산출표 꼴) - 시험: test_552_5_part_fixes 새 · test_552_2_button 높이 식 기대 고침(1em · 줄 높이 1) Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
103 lines
4.7 KiB
TypeScript
103 lines
4.7 KiB
TypeScript
/* =============================================================================
|
|
* ui_template_table.ts
|
|
* 부품 「표」(552-3 · 목록 18) — 머리 줄 + 줄들 · 세로선 없음 · 카드 테두리 · 넘치면 가로 스크롤.
|
|
* 꼴 = 일반표(줄 40 · 기본) · 데이터표(`density: "data"` · 줄 28 · 글 12) · ⋯ 칸(columns 의 label "" — 줄 끝 `createRowMenu`)
|
|
* · 한글 배지 칸(칸에 배지 요소) · 오른쪽 맞춤 칸(`align: "end"` · 수) · 빈 줄(`emptyRow`) · 펼침 줄(tr.is-sub)
|
|
* · 격자표(`grid: true` · 세로선 · 칸마다 테두리 · 가운데 맞춤 · 몸 안 이름 칸 = th 머리색) — 수량 산출표처럼 칸을 합치는 표
|
|
* · 합친 칸 = 칸 자리에 `{ content, colSpan, rowSpan, head }`(head = th) · columns 를 비우면 머리 줄 없이 줄로만 그림
|
|
* 크기 = 줄 40 / 28 · 머리 36 / 28
|
|
* 상태 = 줄 마우스(옅은 바탕) · 고름(tr.is-active / aria-selected) · 바뀜(tr.is-changed) · 지움(tr.is-removed · 줄 그음)
|
|
* 쓰는 법 = `createTable([{ label: "이름" }, { label: "", width: "44px" }], [["임도 1", createRowMenu([…])]])`
|
|
* · 줄을 나중에 채우면 `const { root, tbody } = tableShell(columns); tbody.append(tableRow(columns, cells))`
|
|
* 대신하는 옛 변종 = B01 `dataTable` · `tableShell`(`.b01-pg-table`) · `.b01-proj-table` · `.ui-general-block__table` · D04 · E02 · E03 · E06 · E07 손 표 · M01 · M02 표(목록 4장)
|
|
* 옛 `.ui-data-table`(끌어 고치는 큰 표 · `ui_template_data_table.ts`)은 따로 그대로.
|
|
* ========================================================================== */
|
|
|
|
import { el } from "./ui_template_elements_base";
|
|
import "./ui_template_table.css";
|
|
|
|
/** 합친 칸 · 몸 안 이름 칸(head = th) */
|
|
export interface SpanCell {
|
|
content: string | Node;
|
|
colSpan?: number;
|
|
rowSpan?: number;
|
|
head?: boolean;
|
|
}
|
|
|
|
export type Cell = string | Node | SpanCell;
|
|
|
|
export interface Column {
|
|
label: string;
|
|
/** 칸 너비(CSS) — 없으면 나눠 가짐 */
|
|
width?: string;
|
|
align?: "end";
|
|
}
|
|
|
|
export interface TableOptions {
|
|
/** 데이터표(줄 28 · 글 12) */
|
|
density?: "data";
|
|
/** 격자표 — 세로선 · 칸 테두리 · 가운데 맞춤 */
|
|
grid?: boolean;
|
|
}
|
|
|
|
/** 표 껍데기 — 머리 + 빈 tbody (줄은 호출 쪽이 채움 · 펼침 줄 같은 것을 끼우려고 떼어 둠) */
|
|
export function tableShell(
|
|
columns: Column[],
|
|
opts: TableOptions = {},
|
|
): { root: HTMLElement; tbody: HTMLElement } {
|
|
const table = el("table", {
|
|
className: `ui-table${opts.density === "data" ? " ui-table--data" : ""}${opts.grid ? " ui-table--grid" : ""}`,
|
|
});
|
|
if (columns.length) {
|
|
const hr = el("tr");
|
|
for (const col of columns) {
|
|
const th = el("th", { className: col.align ? "is-end" : "", text: col.label });
|
|
if (col.width) th.style.width = col.width;
|
|
hr.append(th);
|
|
}
|
|
table.append(el("thead", { children: [hr] }));
|
|
}
|
|
const tbody = el("tbody");
|
|
table.append(tbody);
|
|
return { root: el("div", { className: "ui-table-wrap", children: [table] }), tbody };
|
|
}
|
|
|
|
/** 한 줄 — 칸 수는 columns 와 같게 · label "" 인 칸 = ⋯ 칸 · 합친 칸은 colSpan 만큼 칸 자리를 건넘 */
|
|
export function tableRow(columns: Column[], cells: Cell[]): HTMLTableRowElement {
|
|
const tr = el("tr");
|
|
let i = 0;
|
|
// ponytail: 위 줄 rowSpan 이 차지한 칸 자리는 안 셈 — 합친 표(격자)는 columns 를 비워 맞춤 · ⋯ 칸 꼴이 필요해지면 자리 표를 둠
|
|
for (const cell of cells) {
|
|
const span = typeof cell === "string" || cell instanceof Node ? null : cell;
|
|
const col = columns[i];
|
|
const td = el(span?.head ? "th" : "td", {
|
|
className: col?.align ? "is-end" : col?.label === "" ? "ui-table__menucell" : "",
|
|
});
|
|
td.append(span ? span.content : (cell as string | Node));
|
|
if (span?.colSpan) td.colSpan = span.colSpan;
|
|
if (span?.rowSpan) td.rowSpan = span.rowSpan;
|
|
tr.append(td);
|
|
i += span?.colSpan ?? 1;
|
|
}
|
|
return tr;
|
|
}
|
|
|
|
/** 빈 줄 — 칸을 다 합쳐 한 줄 안내 */
|
|
export function emptyRow(columns: Column[], text: string): HTMLTableRowElement {
|
|
const td = el("td", { className: "ui-table__empty", text });
|
|
td.colSpan = columns.length;
|
|
return el("tr", { children: [td] });
|
|
}
|
|
|
|
/** 표 — 줄이 없으면 `empty` 글 한 줄 */
|
|
export function createTable(
|
|
columns: Column[],
|
|
rows: Cell[][],
|
|
opts: TableOptions & { empty?: string } = {},
|
|
): HTMLElement {
|
|
const { root, tbody } = tableShell(columns, opts);
|
|
tbody.append(...rows.map((cells) => tableRow(columns, cells)));
|
|
if (!rows.length && opts.empty) tbody.append(emptyRow(columns, opts.empty));
|
|
return root;
|
|
}
|