refactor(M02): 띠 알림 · 빈 상태 · 보조 글 · 배지 · 탭 · 표 · 목록 줄 · 도구줄 · 체크를 부품으로 (PLAN 553-5)

- DataFlow: 표 넷 = 데이터표 · 함수 판 알림 · 선/알약 경고 · 끊긴 곳 = 띠 알림 · 알약 · 칸 종류 = 배지 · 고르는 줄 = 목록 줄 · 입력 넷 = 입력 · 도구줄 여섯 · 날 색 → 공통 값
- 구조물 옵션 · 구조물 · 도면 · 면적 · 품목 고르기: 판 띠 = 띠 알림 · 한눈/종류별/부품 = 탭 · 표 둘 = 데이터표 · 체크 둘 = 체크 · 카드 · 번호 = 카드 · 배지 · 도면 도구줄
- 일위대가 · 호표 · 실무자료 · 본 화면 · 서고: 알림 = 띠 알림 · 빈 상태 · 보조 글 · 도구줄 여섯 · 옛 보라 별칭 0
- M01 로직 입력칸 = 입력 부품(작은 칸) · 일위대가 상세 줄 수 고정 시험 693 · 지킴 시험 M02 몫 더함

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
2026-10-26 23:44:19 +09:00
co-authored by Claude Opus 5.5
parent 022eff69f5
commit 09134bab84
86 changed files with 583 additions and 705 deletions
+10 -8
View File
@@ -12,6 +12,9 @@ import { openModal } from "@ui/ui_template_modal";
import { searchMaterials } from "../M01_MasterData/M01_MasterData_Api_Fetch";
import { call } from "./M02_MasterTemplete_Api_Fetch";
import { createUnitPick, NO_UNIT } from "./DataFlow/M02_DF_UnitPick";
import { createBadge } from "@ui/ui_template_badge";
import { createText } from "@ui/ui_template_text";
import { createEmpty } from "@ui/ui_template_empty";
/** 고른 자재 한 줄 — M01 자재(키 `MT`) · 개인 자재(키 `PM`) */
export interface MaterialItem {
@@ -93,13 +96,14 @@ export function pickMaterial(done: (item: MaterialItem) => void, own?: OwnLayer)
const search = createInputField({ placeholder: TEXT.search, type: "search" });
const list = el("div", { className: "m02-pick__list" });
const item = (m: MaterialItem, tags: string[] = []): HTMLElement => {
// 목록 줄 부품 겉 · 꼬리표 = 배지 부품
const row = el("button", {
className: "m02-pick__item",
attrs: { type: "button", "data-material": m.코드 },
className: "m02-pick__item ui-list-row ui-sidebar-row",
attrs: { type: "button", "data-material": m.코드, "data-size": "36" },
children: [
el("span", { className: "m02-pick__key", text: m.코드 }),
`${m.이름} ${m.규격} ${m.단위}`.replace(/\s+/g, " "),
...tags.map((t) => el("span", { className: "m02-pick__tag", text: t })),
...tags.map((t) => createBadge(t, "neutral")),
],
});
row.addEventListener("click", () => {
@@ -111,7 +115,7 @@ export function pickMaterial(done: (item: MaterialItem) => void, own?: OwnLayer)
let seq = 0;
const paint = async (): Promise<void> => {
const ticket = ++seq;
list.replaceChildren(el("p", { className: "m02-struct__empty", text: TEXT.searching }));
list.replaceChildren(createText(TEXT.searching));
try {
const q = search.input.value.trim();
const [hits] = await Promise.all([searchMaterials(q), loaded]);
@@ -122,9 +126,7 @@ export function pickMaterial(done: (item: MaterialItem) => void, own?: OwnLayer)
.map((r) => item(toItem(r), r.값 === null ? [TEXT.own, TEXT.noPrice] : [TEXT.own]));
const all = [...ownHits, ...hits.map((m) => item(m))];
list.replaceChildren(
...(all.length
? all
: [el("p", { className: "m02-struct__empty", text: TEXT.noMatch })]),
...(all.length ? all : [createEmpty({ text: TEXT.noMatch, small: true })]),
);
} catch {
if (ticket === seq) showToast(TEXT.searchFailed, "error");
@@ -219,7 +221,7 @@ function newForm(
children: [el("label", { className: "ui-field__label", text: TEXT.unit }), unitHost],
}),
price.root,
el("p", { className: "m02-struct__empty", text: TEXT.key }),
createText(TEXT.key),
el("div", {
className: "ui-btn-group",
children: [createButton({ label: TEXT.back, variant: "ghost", onClick: back }), save],