refactor(M01): 표 · 모달 · 띠 알림 · 배지 · 알약 · 보조 글 · 빈 상태를 부품으로 (PLAN 553-5)

- 표 둘 = 표 부품(m01-master__grid = 일반표 · m01-logic__grid = 데이터표) · 페이지 CSS 는 폭 · 머리 고정 · 칸 상태만
- 손 모달 다섯(조달 찾기 · 요소 고르기 · 표 바꾸기 · 복사 시트 · 표 고르기) = 모달 부품 openModal
- 알림 띠 · 막힘 까닭 = 띠 알림 · 태그 = 배지 · 알약 · × · 삭제 · 로직 키 = 단추 부품 · muted 세 벌 = 보조 글 · 빈 줄 = 빈 상태
- M02 가 빌린 M01 클래스(표 · muted · 고르기 창)도 같은 부품으로 · 죽은 CSS 걷음 · 지킴 시험 test_553_5_m01_m02

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
2026-10-26 22:57:33 +09:00
co-authored by Claude Opus 5.5
parent 6d8b38b518
commit 022eff69f5
34 changed files with 367 additions and 559 deletions
@@ -35,6 +35,7 @@ import { fragmentRow, type Fragment } from "./M01_MasterData_UI_LogicLab_Pill";
import { tl, type LabTextKey } from "./M01_MasterData_UI_LogicLab_Text";
import "./M01_MasterData_UI_LogicLab_Style.css";
import "./M01_MasterData_UI_LogicLab_Pill.css";
import { createBadge } from "@ui/ui_template_badge";
/** 서버 조각(`/text` 줄의 `조각`) → 수량 칸 조각 — 요소·표·로직은 알약(글 = 조각 글) · 나머지는 글자 */
function toFragments(pieces: TextPiece[], ctx: DetailContext): Fragment[] {
@@ -131,7 +132,7 @@ const section = (title: string, body: HTMLElement[], hint = ""): HTMLElement =>
className: "m01-logic__section-head",
children: [
el("h3", { text: title }),
...(hint ? [el("span", { className: "m01-logic__muted", text: hint })] : []),
...(hint ? [el("span", { className: "ui-text ui-text--muted", text: hint })] : []),
],
}),
...body,
@@ -151,14 +152,14 @@ function infoBox(ctx: DetailContext): HTMLElement {
});
return el("label", {
className: "m01lab__cell",
children: [el("span", { className: "m01-logic__muted", text: label }), box],
children: [el("span", { className: "ui-text ui-text--muted", text: label }), box],
});
};
const cell = (label: string, value: string): HTMLElement =>
el("div", {
className: "m01lab__cell",
children: [
el("span", { className: "m01-logic__muted", text: label }),
el("span", { className: "ui-text ui-text--muted", text: label }),
el("span", { text: value || "—" }),
],
});
@@ -172,7 +173,7 @@ function infoBox(ctx: DetailContext): HTMLElement {
const reasons = ctx.reasons.length
? [
el("div", {
className: "m01-logic__reasons",
className: "ui-banner ui-banner--danger m01-logic__reasons",
children: [
el("strong", { text: tx("Head_Blocked") }),
...ctx.reasons.map((r) => el("div", { text: safeReason(r) })),
@@ -182,7 +183,7 @@ function infoBox(ctx: DetailContext): HTMLElement {
: [];
return section(tl("Info_Title"), [
el("p", {
className: "m01-logic__muted",
className: "ui-text ui-text--muted",
text: tl(own ? "Info_Own" : ctx.admin ? "Info_Base" : "Info_ReadOnly"),
}),
...reasons,
@@ -198,7 +199,10 @@ function infoBox(ctx: DetailContext): HTMLElement {
cell(tx("Head_Owner"), row.소유 ?? ""),
el("label", {
className: "m01lab__cell m01lab__cell--wide",
children: [el("span", { className: "m01-logic__muted", text: tx("Head_Note") }), note],
children: [
el("span", { className: "ui-text ui-text--muted", text: tx("Head_Note") }),
note,
],
}),
],
}),
@@ -342,27 +346,15 @@ function lineRow(e: Entry): HTMLElement {
el("td", {
children: [
el("div", { text: e.name }),
...(e.share
? [el("div", { className: "m01-logic__tag", text: `${e.share} ${tl("Share")}` })]
: []),
...(e.spec ? [el("div", { className: "m01-logic__muted", text: e.spec })] : []),
...(e.warn
? [
el("div", {
className: "m01-logic__tag m01lab__warn",
text: `${tx("Calc_UnitWarn")} · ${e.warn}`,
}),
]
: []),
...(e.share ? [createBadge(`${e.share} ${tl("Share")}`, "accent")] : []),
...(e.spec ? [el("div", { className: "ui-text ui-text--muted", text: e.spec })] : []),
...(e.warn ? [createBadge(`${tx("Calc_UnitWarn")} · ${e.warn}`, "warning")] : []),
],
}),
cellOf(e.unit),
el("td", {
className: "m01lab__qty-cell",
children: [
el("span", { className: "m01-logic__tag", text: e.qtyTag }),
fragmentRow(e.qtyFrag),
],
children: [createBadge(e.qtyTag, "accent"), fragmentRow(e.qtyFrag)],
}),
cellOf(e.price, "m01-logic__money"),
cellOf(e.amount === null ? "" : formatMoney(e.amount), "m01-logic__money"),
@@ -379,7 +371,7 @@ function groupTable(group: Group, mine: Entry[], calculated: boolean): HTMLEleme
children: [
el("td", {
attrs: { colspan: "5" },
className: "m01-logic__muted",
className: "ui-text ui-text--muted",
text: tl("Empty_Group"),
}),
],
@@ -405,7 +397,7 @@ function groupTable(group: Group, mine: Entry[], calculated: boolean): HTMLEleme
className: "m01-logic__scroll",
children: [
el("table", {
className: "m01-logic__grid m01lab__ho",
className: "ui-table ui-table--data m01-logic__grid m01lab__ho",
children: [
el("thead", {
children: [el("tr", { children: headers.map((h) => el("th", { text: h })) })],
@@ -447,7 +439,7 @@ function hoBox(ctx: DetailContext): HTMLElement {
const money = !("결과" in ctx.row) && (ctx.row.결과단위 ?? "").startsWith("원");
if (!money) {
return section(tl("Ho_Title"), [
el("p", { className: "m01-logic__muted", text: tl("Not_Money") }),
el("p", { className: "ui-text ui-text--muted", text: tl("Not_Money") }),
el("pre", { className: "m01lab__expr", text: ctx.row.결과?.식 ?? "" }),
]);
}