/* ============================================================================= * M02_MasterTemplete_AreaCellPick.ts * 엑셀 칸 고르기 모달(PLAN 40-3) — 그 열 면적산출 문서를 읽기만 시트로 띄우고 칸을 누르면 그 주소를 돌려줌. * 일위대가 상세(sub2)가 「수량 → 엑셀에서 고르기」 에 씀 → 돌려받은 칸을 `linkAreaCell` 로 면적 문서에 이어 * 일위대가 [저장] 때 함께 저장(여기는 저장 안 함). * · 이미 이은 칸은 시트에 색 · 세모(알약 색 · `spreadsheet_links`) · 이 알약이 이은 칸은 머리 글에 * · 칸 누름 · Enter = 그 칸 · [연결 풀기] = 풀기 · 닫기 · Esc · 바깥 = 그만둠(null) * ========================================================================== */ import { createButton, el } from "@ui/ui_template_elements"; import { openModal } from "@ui/ui_template_modal"; import type { SpreadsheetHandle, Workbook } from "../A00_Common/spreadsheet/spreadsheet"; import { readTemplate, type Layer } from "./M02_MasterTemplete_Api_Fetch"; import "./M02_MasterTemplete_AreaPanel.css"; export { linkAreaCell } from "../A00_Common/spreadsheet/spreadsheet_link_set"; const TEXT = { title: "엑셀에서 고르기 — {value}", close: "닫기", hint: "칸을 누르면 그 칸으로 이음 · Enter 도 됨", now: "지금 연결 {value}", none: "지금 연결 없음", unlink: "연결 풀기", loading: "면적산출을 읽는 중…", failed: "면적산출을 못 읽음 — {value}", }; const fill = (text: string, value: string): string => text.replace("{value}", value); export interface AreaCellPickOptions { /** 그 열 면적산출 문서(저장 안 한 것 포함) — 없으면 `layer` · `column` 저장본을 읽음 */ doc?: Workbook | null; layer: Layer; /** 집계표 열 id */ column: string; /** 알약 키(로직 「이름|단위」 · 자재 「자재|MT키」) — 지금 이은 칸을 보임 */ pill: string; /** 모달 제목에 쓸 줄 이름 */ label: string; } /** 고른 결과 — `null` = 그만둠 · `{칸: null}` = 연결 풀기 · `{칸: "B6"}` = 그 칸 */ export type AreaCellPick = { 칸: string | null } | null; export function pickAreaCell(opts: AreaCellPickOptions): Promise { return new Promise((resolve) => { let handle: SpreadsheetHandle | null = null; let done = false; let picked = "A1"; let moved = false; // 누른 사이 고름이 바뀜 — 머리 · 굴림 막대를 눌렀다 뗀 것은 안 고름 const finish = (value: AreaCellPick): void => { if (done) return; done = true; handle?.destroy(); handle = null; resolve(value); }; const { close } = openModal({ title: fill(TEXT.title, opts.label), closeLabel: TEXT.close, dialogClass: "m02-modal m02-cellpick", beforeClose: () => (finish(null), true), mount: (body) => { const head = el("p", { className: "m02-cellpick__now", text: TEXT.loading }); const unlink = createButton({ label: TEXT.unlink, variant: "ghost", size: "sm", onClick: () => (finish({ 칸: null }), close()), }); unlink.hidden = true; const host = el("div", { className: "m02-cellpick__sheet" }); body.append( el("div", { className: "m02-cellpick__head", children: [head, unlink] }), el("p", { className: "m02-cellpick__hint", text: TEXT.hint }), host, ); const choose = (): void => (finish({ 칸: picked }), close()); void (async () => { try { const [{ createSpreadsheet }, book] = await Promise.all([ import("../A00_Common/spreadsheet/spreadsheet"), opts.doc ? Promise.resolve(opts.doc) : readTemplate(opts.layer, "area", opts.column, null).then( (got) => got.문서 as Workbook, ), ]); if (done) return; const at = book.시트.flatMap((s) => s.연결 ?? []).find((l) => l.키 === opts.pill)?.칸; head.textContent = at ? fill(TEXT.now, at) : TEXT.none; unlink.hidden = !at; handle = createSpreadsheet(host, structuredClone(book), { readOnly: true, tabs: false, onSelect: (s) => { picked = s.칸; moved = true; }, }); // 칸을 눌러 뗌 = 그 칸(처음 고름 A1 · 화살표 옮김은 안 닫음) const grid = host.querySelector(".ss-grid-host"); grid?.addEventListener("mousedown", () => (moved = false), true); grid?.addEventListener("mouseup", (e) => { if ((e as MouseEvent).button === 0 && moved) choose(); }); host.addEventListener("keydown", (e) => e.key === "Enter" && choose()); } catch (error) { if (done) return; head.textContent = fill(TEXT.failed, error instanceof Error ? error.message : ""); } })(); }, }); }); }