Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01QND7Co43B24zK2P937rdxt
156 lines
6.1 KiB
TypeScript
156 lines
6.1 KiB
TypeScript
/* =============================================================================
|
|
* M02_MasterTemplete_Labor_QtyModal.ts
|
|
* 일위대가 수량산출 모달(43-1) — 수량산출 칸 누름(로직 줄 · 자재 줄 같음) = 모달 하나.
|
|
* · 위 = 지금 수량(수기 · 칸 · —) · 수기 입력 칸 · [연결 풀기]
|
|
* · 아래 = 그 열 면적산출 시트를 모달 안에 바로(읽기만 · 실제 값 · 이은 칸 색 · `mountAreaSheet`) ·
|
|
* 칸 누름 = 그 칸을 고름(함께 잇기 · 바꾸기 물음 그대로)
|
|
* · 머리 [확인] = 수기 값이 있으면 수기 · 아니면 고른 칸으로 잇기 · 풀기 · 머리 [닫기] = 그대로
|
|
* 잇기 · 풀기는 화면 문서만(저장은 일위대가 [저장] 때 면적 문서와 함께 · `Labor_Detail`).
|
|
* ========================================================================== */
|
|
|
|
import { createButton, el, showToast } from "@ui/ui_template_elements";
|
|
import { openModal } from "@ui/ui_template_modal";
|
|
import type { Workbook } from "../A00_Common/spreadsheet/spreadsheet";
|
|
import type { Layer } from "./M02_MasterTemplete_Api_Fetch";
|
|
import { mountAreaSheet, type AreaCellPick } from "./M02_MasterTemplete_AreaCellPick";
|
|
import { tc } from "./M02_MasterTemplete_Labor_Text";
|
|
import "./M02_Labor_Values.css";
|
|
import "./M02_MasterTemplete_Labor.css";
|
|
|
|
/** 모달 답 — 손 · 칸 따름(손 지움) · 고른 칸으로 잇기 · 연결 풀기 · 그대로 */
|
|
export type QtyResult =
|
|
| { kind: "hand"; 손: number }
|
|
| { kind: "follow" }
|
|
| { kind: "cell"; pick: AreaCellPick }
|
|
| { kind: "unlink" }
|
|
| { kind: "keep" };
|
|
|
|
export interface QtyModalOptions {
|
|
/** 「GF000219 돌쌓기 메쌓기(인력)」 */
|
|
title: string;
|
|
/** 지금 수량 글(「수기 5 ㎥」 · 「B6 = 12.5」 · 「—」) */
|
|
now: string;
|
|
/** 지금 칸에 이어져 있나 — [연결 풀기] */
|
|
linked: boolean;
|
|
/** 지금 손 값(없으면 null) */
|
|
hand: number | null;
|
|
/** 수기 입력 단위(분모 · 자재 단위) */
|
|
unit: string;
|
|
readOnly: boolean;
|
|
/** 그 열 면적산출 — `pill` 이 없으면(계산 전) 시트 대신 안내 */
|
|
sheet: { doc: Workbook | null; layer: Layer; column: string; pill: string | null };
|
|
/** 수기 입력만(서고 사전조합 줄 · 51-6) — 면적산출 시트 없음 */
|
|
handOnly?: boolean;
|
|
done: (result: QtyResult) => void;
|
|
}
|
|
|
|
export function openQtyModal(opt: QtyModalOptions): void {
|
|
let pending: QtyResult = { kind: "keep" };
|
|
let sheet: { destroy: () => void } | null = null;
|
|
openModal({
|
|
title: `${tc("Labor_QtyCol")} — ${opt.title}`,
|
|
closeLabel: tc("In_Close"),
|
|
dialogClass: "m02-modal m02-labin m02-labqty",
|
|
beforeClose: () => (sheet?.destroy(), true),
|
|
mount: (body, closeNow, head) => {
|
|
const finish = (): void => {
|
|
sheet?.destroy();
|
|
closeNow();
|
|
};
|
|
const now = el("span", {
|
|
className: "m02-labqty__now",
|
|
text: opt.now,
|
|
attrs: { "data-labor-qty-now": "" },
|
|
});
|
|
const state = el("small", {
|
|
className: "m02-labin__muted",
|
|
attrs: { "data-labor-qty-state": "" },
|
|
});
|
|
const hand = el("input", {
|
|
className: "ui-input m02-inputval__control",
|
|
attrs: {
|
|
type: "number",
|
|
step: "any",
|
|
"data-labor-qty": "hand",
|
|
"aria-label": tc("In_QtyHand"),
|
|
},
|
|
}) as HTMLInputElement;
|
|
hand.readOnly = opt.readOnly;
|
|
if (opt.hand !== null) hand.value = String(opt.hand);
|
|
const unlink =
|
|
opt.linked && !opt.readOnly
|
|
? createButton({
|
|
label: tc("Labor_Unlink"),
|
|
variant: "ghost",
|
|
onClick: () => {
|
|
pending = { kind: "unlink" };
|
|
hand.value = "";
|
|
state.textContent = tc("Labor_UnlinkPending");
|
|
},
|
|
})
|
|
: null;
|
|
const host = el("div", {
|
|
className: "m02-cellpick__sheet",
|
|
attrs: { "data-labor-qty-sheet": "" },
|
|
});
|
|
if (opt.handOnly) {
|
|
// 시트 없음
|
|
} else if (opt.sheet.pill) {
|
|
sheet = mountAreaSheet(host, {
|
|
doc: opt.sheet.doc,
|
|
layer: opt.sheet.layer,
|
|
column: opt.sheet.column,
|
|
pill: opt.sheet.pill,
|
|
onPick: (pick) => {
|
|
if (opt.readOnly) return;
|
|
pending = { kind: "cell", pick };
|
|
hand.value = ""; // 칸을 고르면 손 값은 지움(칸을 따름)
|
|
state.textContent = tc("Labor_CellPending").replace("{value}", pick.칸);
|
|
},
|
|
});
|
|
} else
|
|
host.replaceChildren(
|
|
el("p", { className: "m02-labin__muted", text: tc("Labor_PickWait") }),
|
|
);
|
|
hand.addEventListener("input", () => {
|
|
if (hand.value.trim() !== "") state.textContent = "";
|
|
});
|
|
const ok = (): void => {
|
|
const raw = hand.value.trim();
|
|
if (raw !== "") {
|
|
const v = Number(raw);
|
|
if (!Number.isFinite(v)) return showToast(tc("In_QtyNeed"), "error");
|
|
opt.done({ kind: "hand", 손: v });
|
|
} else if (pending.kind !== "keep") opt.done(pending);
|
|
else if (opt.hand !== null) opt.done({ kind: "follow" }); // 손 값을 지움 = 칸을 따름
|
|
finish();
|
|
};
|
|
body.append(
|
|
el("div", {
|
|
className: "m02-labqty__head",
|
|
children: [
|
|
el("span", { className: "m02-inputval__name", text: tc("Labor_QtyNow") }),
|
|
now,
|
|
el("label", {
|
|
className: "m02-labin__qty-line",
|
|
children: [
|
|
el("span", { text: tc("In_QtyHand") }),
|
|
hand,
|
|
el("small", { text: opt.unit }),
|
|
],
|
|
}),
|
|
...(unlink ? [unlink] : []),
|
|
],
|
|
}),
|
|
state,
|
|
...(opt.handOnly
|
|
? []
|
|
: [el("p", { className: "m02-cellpick__hint", text: tc("Labor_SheetHint") }), host]),
|
|
);
|
|
// [확인] = 머리 [닫기] 왼쪽 · 아래 [닫기] 없음(147-3) · 읽기만 = 머리 [닫기]만
|
|
if (!opt.readOnly)
|
|
head.append(createButton({ label: tc("In_Ok"), variant: "filled", onClick: ok }));
|
|
},
|
|
});
|
|
}
|