/* ============================================================================= * M01_MasterData_UI_LogicLab_New_Var.ts * 변수 한 칸 — 무엇인지(마스터 요소 / 설계 입력 / 고정값 / 표 찾기) · 단위 · 비목을 고름 * 고른 것은 `decided[이름]` 에 쌓음(서버 계약 `_화면_계약.md` 7장의 `decided` 그대로) * ========================================================================== */ import { createButton, createInputField, createSelectField, el } from "@ui/ui_template_elements"; import type { DraftDecided, DraftVar } from "./M01_MasterData_UI_Logic_Api"; import { openElementPick, openTablePick } from "./M01_MasterData_UI_LogicLab_New_Find"; import { tn } from "./M01_MasterData_UI_LogicLab_New_Text"; export interface VarCtx { decided: Record; /** 고른 것이 바뀜 — 화면이 검사를 다시 부름 */ onEdit: () => void; } export interface VarRow { root: HTMLElement; /** 서버가 다시 준 변수(식으로 정해진 단위 등)를 반영 */ update: (v: DraftVar) => void; } const KINDS: [string, () => string][] = [ ["마스터요소", () => tn("K_master")], ["설계입력", () => tn("K_input")], ["고정값", () => tn("K_fixed")], ["표찾기", () => tn("K_table")], ]; const COSTS = ["노무비", "재료비", "경비"]; /** 요소 종류 → 비목 첫 제안 */ const COST_OF: Record = { 인력: "노무비", 재료: "재료비", 기계: "경비" }; const costOptions = (): { value: string; text: string }[] => [ { value: "", text: tn("CostNone") }, ...COSTS.map((c) => ({ value: c, text: c })), ]; const field = (label: string, control: HTMLElement): HTMLElement => el("label", { className: "m01-logic__field", children: [el("span", { text: label }), control], }); export function varRow(v: DraftVar, isResult: boolean, ctx: VarCtx): VarRow { const name = v.이름; const info = el("span", { className: "m01-logic__muted m01lab-new__derived" }); const body = el("div", { className: "m01lab-new__var-body" }); const root = el("div", { className: "m01lab-new__var", attrs: { "data-var": name }, children: [ el("div", { className: "m01lab-new__var-head", children: [el("strong", { text: name }), info], }), body, ], }); const now = (): DraftDecided => (ctx.decided[name] ??= {}); const set = (patch: DraftDecided): void => { ctx.decided[name] = { ...now(), ...patch }; for (const [k, val] of Object.entries(ctx.decided[name])) { if (val === "" || val === undefined) delete (ctx.decided[name] as Record)[k]; } ctx.onEdit(); }; const text = (label: string, key: "단위" | "값" | "설명", wide = false): HTMLElement => { const box = createInputField({ type: "text", value: now()[key] ?? "", onInput: (val) => set({ [key]: val.trim() }), }); box.input.setAttribute("data-key", key); const cell = field(label, box.root); if (wide) cell.classList.add("m01lab-new__wide"); return cell; }; let picked = ""; // 마스터 요소·표 찾기에서 고른 것의 글 const draw = (): void => { const kind = now().무엇 ?? ""; if (v.갈래 === "비목합") { body.replaceChildren(el("span", { className: "m01-logic__muted", text: tn("SumName") })); return; } if (v.갈래 === "정의됨") { if (isResult) return body.replaceChildren(); const cost = createSelectField({ options: costOptions(), value: now().비목 ?? "", compact: true, label: tn("CostExtra"), onChange: (val) => set({ 비목: val }), }); body.replaceChildren(cost.root); return; } const kindBox = createSelectField({ options: [ { value: "", text: "—" }, ...KINDS.map(([value, label]) => ({ value, text: label() })), ], value: kind, compact: true, label: tn("Kind"), onChange: (val) => { ctx.decided[name] = val ? { 무엇: val, ...(now().단위 ? { 단위: now().단위 } : {}) } : {}; picked = ""; draw(); ctx.onEdit(); }, }); const parts: HTMLElement[] = [kindBox.root]; if (kind === "마스터요소") { const cost = createSelectField({ options: costOptions(), value: now().비목 ?? "", compact: true, label: tn("Cost"), onChange: (val) => set({ 비목: val }), }); const choose = createButton({ label: now().요소 ? tn("Repick") : tn("Pick"), variant: "ghost", onClick: () => openElementPick((p) => { picked = `${p.name} · ${p.ref}`; set({ 요소: p.ref, 단위: p.unit, 비목: COST_OF[p.group] ?? now().비목 }); draw(); }), }); choose.setAttribute("data-act", "pick-element"); parts.push( el("div", { className: "m01lab-new__picked", children: [choose, el("span", { text: picked || now().요소 || tn("NotPicked") })], }), text(tn("Unit"), "단위"), cost.root, ); } else if (kind === "설계입력") { parts.push(text(tn("Unit"), "단위"), text(tn("Note"), "설명", true)); } else if (kind === "고정값") { parts.push(text(tn("Value"), "값"), text(tn("Unit"), "단위")); } else if (kind === "표찾기") { const choose = createButton({ label: now().찾기 ? tn("Repick") : tn("TablePick"), variant: "ghost", onClick: () => openTablePick((find, label) => { picked = label; set({ 찾기: find }); draw(); }), }); choose.setAttribute("data-act", "pick-table"); parts.push( el("div", { className: "m01lab-new__picked", children: [choose, el("span", { text: picked || tn("NotPicked") })], }), text(tn("Unit"), "단위"), ); } body.replaceChildren(...parts); }; draw(); const update = (next: DraftVar): void => { v = next; info.textContent = next.갈래 === "정의됨" ? `${tn("Derived")}${next.단위 ? ` · ${next.단위}` : ""}` : (next.단위 ?? ""); }; update(v); return { root, update }; }