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
@@ -13,6 +13,7 @@ import { buildHeadFilters } from "../M01_MasterData/M01_MasterData_UI_Head";
import "../M01_MasterData/M01_MasterData_UI_Style.css";
import { fetchTree, type Layer, type TreeItem } from "./M02_MasterTemplete_Api_Fetch";
import { folderOf, leafName, matchText } from "./M02_MasterTemplete_TreePanel_Model";
import { createEmpty } from "@ui/ui_template_empty";
/** 목록 종류 — 하위 컨테이너 셋 + 항목 통째(복사 생성) */
export type ListKind = "structure" | "area" | "labor" | "item";
@@ -160,10 +161,10 @@ export function buildItemList(): ItemListHandle {
let page = 1;
const searchField = createInputField({ type: "search", placeholder: TEXT.search });
const search = searchField.input;
const grid = el("div", { className: "m01-master__grid-wrap" });
const grid = el("div", { className: "ui-table-wrap m01-master__grid-wrap" });
let pager = buildPager(0, SIZE, 1, () => {});
const note = (text: string): void => {
grid.replaceChildren(el("p", { className: "m01-master__empty", text }));
grid.replaceChildren(createEmpty({ text, small: true }));
};
const show = <R>(
@@ -193,7 +194,7 @@ export function buildItemList(): ItemListHandle {
text,
attrs: { title: note ? `${text} ${note}` : text },
children: note
? [el("small", { className: "m01-master__muted", text: ` ${note}` })]
? [el("small", { className: "ui-text ui-text--muted", text: ` ${note}` })]
: [],
});
}),
@@ -209,7 +210,7 @@ export function buildItemList(): ItemListHandle {
});
grid.replaceChildren(
el("table", {
className: "m01-master__grid",
className: "ui-table m01-master__grid",
children: [
el("thead", {
children: [el("tr", { children: cols.map((c) => el("th", { text: c.head })) })],
@@ -1,13 +1,8 @@
/* 일위대가 화면 — 표는 칸 폭을 채움 ·
흐린 안내 글은 보조 글색(뮤트 글색은 밝은 테마에서 대비 3.1 로 모자람) */
.m01-combo .m01-master__grid {
/* 일위대가 화면 — 표는 칸 폭을 채움 */
.m01-combo table.m01-master__grid {
width: 100%;
}
.m01-combo .m01-logic__muted {
color: var(--color-text-secondary);
}
/* 일위대가 상세(39-3) — 담은 로직 · 자재목록 표 · 금액 칸 두 줄(위 기준 작게 · 아래 반영) */
.m02-labrow {
width: 100%;
@@ -75,6 +75,7 @@ import "../M01_MasterData/M01_MasterData_UI_Logic_Style.css";
import "../M01_MasterData/M01_MasterData_UI_LogicLab_Style.css";
import "@ui/ui_template_general_layout.css";
import "./M02_MasterTemplete_Labor.css";
import { createText } from "@ui/ui_template_text";
export interface LaborDetailOptions {
layer: Layer;
@@ -160,10 +161,7 @@ export function mountLaborDetail(
});
function showSource(): void {
if (side?.current() !== "source") return;
if (!picked)
return void sourceHost.replaceChildren(
el("p", { className: "m01-logic__muted", text: tc("Labor_SourceHint") }),
);
if (!picked) return void sourceHost.replaceChildren(createText(tc("Labor_SourceHint")));
source.show(logics.get(picked)?.출처 ?? "");
if (sourceHost.firstChild !== source.root) sourceHost.replaceChildren(source.root);
}
@@ -28,6 +28,7 @@ import { precomboClasses } from "./M02_MasterTemplete_UI_Side_Precombo";
import { fetchStructureDef } from "./StructOptions/M02_SO_Api";
import "../M01_MasterData/M01_MasterData_UI_Logic_Style.css"; // 창 겉틀
import "./M02_MasterTemplete_Labor.css";
import { openModal } from "@ui/ui_template_modal";
export { withHopyo } from "./M02_MasterTemplete_Hopyo_Pick";
@@ -92,7 +93,7 @@ export function openLogicPicker(
const sub = createSelectField({ options: opts([]), compact: true, label: tc("Sub") });
const detail = createSelectField({ options: opts([]), compact: true, label: tc("Detail") });
detail.select.disabled = true;
const close = (): void => backdrop.remove();
let close = (): void => {};
/* 한 목록 — 구분(단가산출 · 일위조합) 칸 · 층 단추는 서고 줄(사전조합)의 층 */
const layerBar = el("div", { className: "ui-btn-group", attrs: { "data-pick-layers": "" } });
@@ -266,24 +267,14 @@ export function openLogicPicker(
);
});
const dialog = el("div", {
className: "m01-logic__pick m01-combo ui-popup",
attrs: { role: "dialog", "aria-label": tc("Pick_Title") },
children: [
el("div", {
className: "m01-logic__section-head",
children: [
el("h3", { text: lib?.title ?? tc("Pick_Title") }),
createButton({ label: tc("Pick_Close"), variant: "ghost", onClick: close }),
],
}),
finder.root,
],
});
const backdrop = el("div", { className: "m01-logic__backdrop", children: [dialog] });
backdrop.addEventListener("click", (ev) => ev.target === backdrop && close());
backdrop.addEventListener("keydown", (ev) => ev.key === "Escape" && close());
document.body.append(backdrop);
// 모달 부품 — 제목줄 [닫기] · 바깥 누르기 · Esc · 위 고정
({ close } = openModal({
title: lib?.title ?? tc("Pick_Title"),
closeLabel: tc("Pick_Close"),
dialogClass: "m01-logic__pick m01-combo",
backdropClass: "m01-logic__backdrop",
mount: (body) => body.append(finder.root),
}));
finder.search.focus();
void lib?.more?.().then(
@@ -255,11 +255,11 @@ export function mountLibraryList(
host.replaceChildren(
buildHeadFilters([searchField.root, pager]),
el("div", {
className: "m01-master__grid-wrap",
className: "ui-table-wrap m01-master__grid-wrap",
attrs: { "data-lib-list": opt.kind },
children: [
el("table", {
className: "m01-master__grid",
className: "ui-table m01-master__grid",
children: [
el("thead", {
children: [
@@ -9,6 +9,12 @@
* ========================================================================== */
import "@ui/ui_template_workflow_layout.css";
// 부품 상자 겉(553-5) — 페이지 CSS 앞에 실어 페이지 곁값이 이김
import "@ui/ui_template_banner.css";
import "@ui/ui_template_list_row.css";
import "@ui/ui_template_table.css";
import "@ui/ui_template_text.css";
import "@ui/ui_template_toolbar.css";
import { ROUTES } from "@config/config_frontend";
import { el } from "@ui/ui_template_elements";
import { t as L } from "@ui/ui_template_locale";