/* ============================================================================= * M02_MasterTemplete_AreaLink.ts * 면적산출 상세 — 칸 지정 모달(PLAN 39-4 D5). 오른쪽 패널의 담은 로직 · 자재 줄을 누르면 엶. * 그 줄 알약 키(로직 물량 「이름|단위」 · 자재 「자재|MT키」)를 면적산출 칸에 잇기 · 옮기기 · 풀기. * 정본 = 면적산출 문서 `연결` 한 곳 — 스프레드시트 손잡이 `setLink`(알약 누름과 같은 규칙 · 되돌리기 됨)라 * 아래 알약 줄 · 칸 세모 · 패널 수량이 같이 따라감. 손 입력 수량은 일위대가 몫(여기 없음) · 저장은 [저장]. * ========================================================================== */ import { createButton, createInputField, el } from "@ui/ui_template_elements"; import { openModal } from "@ui/ui_template_modal"; import type { CalcValue, LinkInfo, SpreadsheetHandle } from "../A00_Common/spreadsheet/spreadsheet"; const TEXT = { title: "칸 지정 — {value}", close: "닫기", now: "지금 연결", none: "연결 없음", picked: "지금 고른 칸으로 ({value})", address: "칸 주소", hint: "예 B6", link: "잇기", unlink: "풀기", bad: "칸 주소가 틀림 — 예 B6", readOnly: "읽기만 — 고칠 수 없음", }; const fill = (text: string, value: string): string => text.replace("{value}", value); export interface AreaLinkOptions { /** 줄 이름(모달 제목) */ label: string; /** 알약 키 */ key: string; /** 연결에 같이 적을 이름 · 단위 · 자재 */ info: LinkInfo; sheet: SpreadsheetHandle; /** 시트에서 지금 고른 칸(A1) */ picked: () => string; readOnly: boolean; } /** 칸 값 글 — 수 · 글 · 참거짓 · 오류 */ export const cellText = (v: CalcValue | undefined): string => !v ? "" : "수" in v ? v.수 : "글" in v ? v.글 : "참" in v ? String(v.참) : v.오류; /** 지금 연결 칸 · 값 글 — 「B6 = 12.5 ㎡」 · 없으면 null */ function linkedText(opts: AreaLinkOptions): string | null { const link = opts.sheet .getDoc() .시트.flatMap((s) => s.연결 ?? []) .find((l) => l.키 === opts.key); if (!link) return null; const value = opts.sheet.getLinkedValues()[opts.key]; return [link.칸, "=", cellText(value), opts.info.단위 ?? ""].join(" ").trim(); } export function openAreaLink(opts: AreaLinkOptions): void { const { close } = openModal({ title: fill(TEXT.title, opts.label), closeLabel: TEXT.close, dialogClass: "m02-modal m02-area-link", mount: (body) => { const now = el("p", { className: "m02-area-link__now" }); const paint = (): void => { const text = linkedText(opts); now.textContent = `${TEXT.now} · ${text ?? TEXT.none}`; unlink.disabled = opts.readOnly || !text; }; const done = (cell: string | null): boolean => { if (!opts.sheet.setLink(opts.key, cell, opts.info)) return false; close(); return true; }; const here = opts.picked(); const pick = createButton({ label: fill(TEXT.picked, here), variant: "filled", size: "sm", onClick: () => void done(here), }); const address = createInputField({ label: TEXT.address, placeholder: TEXT.hint }); const link = createButton({ label: TEXT.link, variant: "ghost", size: "sm", onClick: () => { if (!done(address.input.value)) address.setError(TEXT.bad); }, }); const unlink = createButton({ label: TEXT.unlink, variant: "ghost", size: "sm", onClick: () => void done(null), }); address.input.addEventListener("input", () => address.setError(undefined)); address.input.addEventListener("keydown", (ev) => ev.key === "Enter" && link.click()); pick.disabled = link.disabled = address.input.disabled = opts.readOnly; paint(); body.append( now, ...(opts.readOnly ? [el("p", { className: "m02-struct__empty", text: TEXT.readOnly })] : []), el("div", { className: "m02-area-link__row", children: [pick, unlink] }), el("div", { className: "m02-area-link__row", children: [address.root, link] }), ); if (!opts.readOnly) address.input.focus(); }, }); }