- DataFlow: 표 넷 = 데이터표 · 함수 판 알림 · 선/알약 경고 · 끊긴 곳 = 띠 알림 · 알약 · 칸 종류 = 배지 · 고르는 줄 = 목록 줄 · 입력 넷 = 입력 · 도구줄 여섯 · 날 색 → 공통 값 - 구조물 옵션 · 구조물 · 도면 · 면적 · 품목 고르기: 판 띠 = 띠 알림 · 한눈/종류별/부품 = 탭 · 표 둘 = 데이터표 · 체크 둘 = 체크 · 카드 · 번호 = 카드 · 배지 · 도면 도구줄 - 일위대가 · 호표 · 실무자료 · 본 화면 · 서고: 알림 = 띠 알림 · 빈 상태 · 보조 글 · 도구줄 여섯 · 옛 보라 별칭 0 - M01 로직 입력칸 = 입력 부품(작은 칸) · 일위대가 상세 줄 수 고정 시험 693 · 지킴 시험 M02 몫 더함 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
121 lines
4.9 KiB
TypeScript
121 lines
4.9 KiB
TypeScript
/* =============================================================================
|
|
* M02_MasterTemplete_AreaCellPick.ts
|
|
* 면적산출 칸 고르기 시트(PLAN 40-3 · 43-1) — 그 열 면적산출 문서를 읽기만 시트로 붙이고 칸을 누르면 그 주소를 알림.
|
|
* 일위대가 수량산출 모달(sub2 `Labor_QtyModal`)이 모달 안에 바로 씀 → 돌려받은 칸을 `linkAreaCell` 로 면적 문서에
|
|
* 이어 일위대가 [저장] 때 함께 저장(여기는 저장 안 함).
|
|
* · 이미 이은 칸은 시트에 색 · 세모(알약 색 · `spreadsheet_links`) · 실제 값(식 칸 = 푼 값)
|
|
* · 칸 누름 · Enter = 그 칸 · 그 칸에 다른 알약이 있으면 [함께 잇기](기본) · [바꾸기](`바꾸기: true`) · [취소](그대로)
|
|
* ========================================================================== */
|
|
|
|
import { el } from "@ui/ui_template_elements";
|
|
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 = {
|
|
loading: "면적산출을 읽는 중…",
|
|
failed: "면적산출을 못 읽음 — {value}",
|
|
};
|
|
|
|
const fill = (text: string, value: string): string => text.replace("{value}", value);
|
|
|
|
/** 고른 칸 — `{칸: "B6"}` = 그 칸(다른 알약과 함께) · `{칸: "B6", 바꾸기: true}` = 그 칸 다른 알약을 빼고 */
|
|
export interface AreaCellPick {
|
|
칸: string;
|
|
바꾸기?: boolean;
|
|
}
|
|
|
|
export interface AreaSheetOptions {
|
|
/** 그 열 면적산출 문서(저장 안 한 것 포함) — 없으면 `layer` · `column` 저장본을 읽음 */
|
|
doc?: Workbook | null;
|
|
layer: Layer;
|
|
/** 집계표 열 id */
|
|
column: string;
|
|
/** 알약 키(로직 「이름|단위」 · 자재 「자재|MT키」) — 같은 칸 물음에서 자기는 뺌 */
|
|
pill: string;
|
|
/** 칸을 고름(물음까지 끝난 뒤) */
|
|
onPick: (pick: AreaCellPick) => void;
|
|
/** 시트를 다 읽음(그 문서) · 못 읽으면 null */
|
|
onLoad?: (book: Workbook | null) => void;
|
|
}
|
|
|
|
export interface AreaSheetHandle {
|
|
destroy: () => void;
|
|
}
|
|
|
|
/** 읽기만 면적산출 시트를 `host` 에 붙임 — 칸을 눌러 뗌 · Enter = 그 칸(처음 고름 A1 · 화살표 옮김은 안 고름) */
|
|
export function mountAreaSheet(host: HTMLElement, opts: AreaSheetOptions): AreaSheetHandle {
|
|
let handle: SpreadsheetHandle | null = null;
|
|
let done = false;
|
|
let picked = "A1";
|
|
let moved = false; // 누른 사이 고름이 바뀜 — 머리 · 굴림 막대를 눌렀다 뗀 것은 안 고름
|
|
let book: Workbook | null = null;
|
|
host.replaceChildren(
|
|
el("p", { className: "ui-text ui-text--muted m02-cellpick__now", text: TEXT.loading }),
|
|
);
|
|
const choose = async (): Promise<void> => {
|
|
const at = picked;
|
|
// 엔진 조각은 고를 때만(정적 import 금지 — 계약 `spreadsheet_types.ts` 머리)
|
|
const [{ askShare }, { linksAt }] = await Promise.all([
|
|
import("../A00_Common/spreadsheet/spreadsheet_link_ask"),
|
|
import("../A00_Common/spreadsheet/spreadsheet_link_set"),
|
|
]);
|
|
const sheetId = book?.시트.find((s) => s.id === book?.활성)?.id ?? book?.시트[0]?.id;
|
|
const others = book && sheetId ? linksAt(book, sheetId, at, opts.pill) : [];
|
|
const choice = others.length
|
|
? await askShare(
|
|
at,
|
|
others.map((l) => l.이름 ?? l.키),
|
|
)
|
|
: "함께";
|
|
if (!choice || done) return;
|
|
opts.onPick(choice === "바꾸기" ? { 칸: at, 바꾸기: true } : { 칸: at });
|
|
};
|
|
void (async () => {
|
|
try {
|
|
const [{ createSpreadsheet }, got] = 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;
|
|
book = got;
|
|
host.replaceChildren();
|
|
handle = createSpreadsheet(host, structuredClone(got), {
|
|
readOnly: true,
|
|
tabs: false,
|
|
onSelect: (s) => {
|
|
picked = s.칸;
|
|
moved = true;
|
|
},
|
|
});
|
|
const grid = host.querySelector(".ss-grid-host");
|
|
grid?.addEventListener("mousedown", () => (moved = false), true);
|
|
grid?.addEventListener("mouseup", (e) => {
|
|
if ((e as MouseEvent).button === 0 && moved) void choose();
|
|
});
|
|
host.addEventListener("keydown", (e) => e.key === "Enter" && void choose());
|
|
opts.onLoad?.(got);
|
|
} catch (error) {
|
|
if (done) return;
|
|
host.replaceChildren(
|
|
el("p", {
|
|
className: "ui-text ui-text--muted m02-cellpick__now",
|
|
text: fill(TEXT.failed, error instanceof Error ? error.message : ""),
|
|
}),
|
|
);
|
|
opts.onLoad?.(null);
|
|
}
|
|
})();
|
|
return {
|
|
destroy: () => {
|
|
done = true;
|
|
handle?.destroy();
|
|
handle = null;
|
|
},
|
|
};
|
|
}
|