feat(709-2): M01 표 · M02 목록 줄 수 = 화면 높이 · 쪽 단추 표 아래 · 한 장 표 화면 안 스크롤

- 쪽 있는 표(요소 · 표 목록 · 로직 목록 · 재료 고르기 모달 · M02 서고 · 새 항목 목록): 줄 수를 표 칸 위치 ~ 화면 아래로 셈(watchFit) · 창 크기 · 글꼴 늦게 실림에도 다시 셈 · 쪽 크기 바뀌어도 첫 줄 유지
- 쪽 단추 = 표 바로 아래 · 줄이 모자라면 표와 함께 위로 붙음
- 쪽 없는 한 장 표(소요량 · 계수): 높이를 화면 아래로 묶고 안에서 스크롤(watchHeight · 표 아래 주 높이 뺌)
- UI_Style.css 의 max-height calc(100dvh - 300px) 제거 · m01-master__pager 규칙 제거(부품 ui-pager 로)
- 낡은 점검 51-10(쪽 단추가 거름 줄 안에 있다고 봄) = 표 바로 아래 ui-pager 로 고침
This commit is contained in:
2026-11-01 15:19:35 +09:00
parent 2e43011069
commit 1a931e2ea1
12 changed files with 265 additions and 107 deletions
+52 -28
View File
@@ -30,8 +30,10 @@ import {
toNumberish,
toRange,
} from "./M01_MasterData_UI_Cells";
import { guessRows, pageKeepFirst, watchFit, watchHeight } from "@ui/ui_template_pager";
const LIST_SIZE = 30;
/** 표 칸 위에 오는 거름 줄 높이 — 첫 줄 수 어림에만 씀 */
const FILTER_H = 52;
const LIST_COLS = ["키", "원문번호", "구분", "상세구분", "이름", "기준", "출처", "비고"] as const;
/** 용도 대상 거름 드롭다운 후보 — PLAN 1-1 셋째 줄 그대로. */
const USAGE_TARGETS = ["인력", "기계", "재료", "할증", "환산", "작업량 조건"];
@@ -45,16 +47,22 @@ export function renderTables(
): () => void {
let stop = (): void => {};
let usage = pick.view?.usage ?? "";
let size = guessRows(host, FILTER_H); // 화면 높이로 셈 — 그린 뒤 다시 재서 어긋나면 바로잡음
let at = 0; // 목록을 보고 있는 쪽(0 = 표 하나를 보는 중)
const list = (page = pick.view?.page ?? 1): void => {
stop();
stop = (): void => {};
at = page;
void showList(
host,
pick,
q,
usage,
page,
size,
list,
(file, key) => {
at = 0;
stop = openTable(host, file, key, () => list(page), openLogic);
},
(next) => {
@@ -62,11 +70,22 @@ export function renderTables(
saveView(pick.group, { usage, page: 1 });
list(1);
},
);
).then(() => fit.ask());
};
const fit = watchFit(
host,
() => host.querySelector<HTMLElement>(".m01-master__tables"),
(rows) => {
if (!at || rows === size) return;
const page = pageKeepFirst(at, size, rows);
size = rows;
list(page);
},
);
list();
const off = onDraftChange(() => host.querySelector(".m01-master__tables") && markChanged(host));
return () => {
fit.stop();
stop();
off();
};
@@ -86,12 +105,14 @@ async function showList(
q: string,
usage: string,
page: number,
size: number,
goPage: (page: number) => void,
open: (file: string, key: string) => void,
onUsageChange: (usage: string) => void,
): Promise<void> {
let got: { total: number; tables: TableHead[] };
try {
got = await fetchTables(pick.group, pick.sub, pick.detail, q, page, LIST_SIZE, usage);
got = await fetchTables(pick.group, pick.sub, pick.detail, q, page, size, usage);
} catch (error) {
showToast(error instanceof Error ? error.message : L("M01_LoadFailed"), "error");
return;
@@ -121,12 +142,6 @@ async function showList(
usageField.select.addEventListener("change", () => onUsageChange(usageField.select.value));
host.replaceChildren(
el("div", { className: "m01-master__filter", children: [usageField.root] }),
buildPager(
got.total,
LIST_SIZE,
page,
(p) => void showList(host, pick, q, usage, p, open, onUsageChange),
),
el("div", {
className: "ui-table-wrap m01-master__grid-wrap m01-master__tables",
children: [
@@ -141,6 +156,7 @@ async function showList(
}),
],
}),
buildPager(got.total, size, page, goPage),
);
}
@@ -153,6 +169,7 @@ function openTable(
openLogic: (key: string) => void,
): () => void {
let off = (): void => {};
let unfit = (): void => {}; // 쪽 없는 한 장 표 — 화면 아래까지 높이 묶음
void (async () => {
let version: string;
let table: Row & { 줄: Row[] };
@@ -211,6 +228,7 @@ function openTable(
const backButton = createButton({ label: L("M01_BackToTables"), variant: "ghost" });
backButton.addEventListener("click", () => {
off();
unfit();
back();
});
const notes = ((table.주 ?? []) as string[]).map((n) =>
@@ -224,6 +242,24 @@ function openTable(
btn.addEventListener("click", () => openLogic(k));
return i === 0 ? btn : el("span", { text: ", ", children: [btn] });
});
const sheet = el("div", {
className: "ui-table-wrap m01-master__grid-wrap m01-master__grid-wrap--sheet",
children: [
el("table", {
className: "ui-table m01-master__grid",
children: [
el("thead", {
children: [
el("tr", {
children: [el("th"), ...cols.map((c) => el("th", { text: headText(c) }))],
}),
],
}),
body,
],
}),
],
});
host.replaceChildren(
el("div", {
className: "ui-toolbar m01-master__bar",
@@ -246,29 +282,17 @@ function openTable(
add,
],
}),
el("div", {
className: "ui-table-wrap m01-master__grid-wrap",
children: [
el("table", {
className: "ui-table m01-master__grid",
children: [
el("thead", {
children: [
el("tr", {
children: [el("th"), ...cols.map((c) => el("th", { text: headText(c) }))],
}),
],
}),
body,
],
}),
],
}),
sheet,
...notes,
);
unfit();
unfit = watchHeight(sheet);
};
off = onDraftChange(paint);
paint();
})();
return () => off();
return () => {
off();
unfit();
};
}