Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01PAdA5ThqmtVSsbzjusk1cJ
179 lines
6.2 KiB
TypeScript
179 lines
6.2 KiB
TypeScript
/* =============================================================================
|
|
* 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<string, DraftDecided>;
|
|
/** 고른 것이 바뀜 — 화면이 검사를 다시 부름 */
|
|
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<string, string> = { 인력: "노무비", 재료: "재료비", 기계: "경비" };
|
|
|
|
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<string, unknown>)[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 };
|
|
}
|