Files
Aislo/M02_MasterTemplete/M02_MasterTemplete_AreaFromDrawing.ts
T
eomsangdonandClaude Opus 5.5 ef00ee6431 fix(m02): 화면 글 「더하기」 → 「추가」 두 곳(105-5)
- 구조물 옵션 안내 「칸 더하기」 · 도면 → 면적 알림 「시트 더하기」

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_015KYZ2HWWdUwZWYnn3tQz7X
2026-10-08 16:10:58 +09:00

112 lines
4.7 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
/* =============================================================================
* M02_MasterTemplete_AreaFromDrawing.ts
* 도면 → 면적산출 한 번 옮기기(PLAN 83-3 · 연동 아님) — 오른쪽 도면 패널(`AreaDrawing`)이 받은 표 자료를
* 면적 통합문서의 새 시트(「도면에서 n」)로 만듦. 기존 시트는 안 덮음 · 병합 · 열 폭 · 행 높이 반영.
* 표 자료 꼴 = CAD `helpers/table-from-drawing.ts` 의 `DrawingTable`(rows · merges · colWidths · rowHeights · 도면 mm).
* 스프레드시트 부품은 안 고침 — 시트 자료만 만들고 싣기는 공개 손잡이 `addSheet`(되돌리기 한 번 · onChange → 화면 고침 표시).
* ========================================================================== */
import { showToast } from "@ui/ui_template_elements";
import type { Cell, Sheet, Workbook } from "../A00_Common/spreadsheet/spreadsheet_types";
const TEXT = {
done: (name: string) =>
`도면 표를 「${name}」 시트로 옮김 — [저장]을 눌러야 저장 · 되돌리기 Ctrl+Z`,
loose: "표 선을 못 찾아 글 자리로 줄 · 칸을 나눔 — 칸이 맞는지 확인",
noHandle: "스프레드시트가 시트 추가를 아직 못 함 — 옮기지 않음",
noSheet: "면적산출 시트가 열려 있지 않음",
};
/** CAD 가 보낸 표 자료(`DrawingTable`) */
export interface DrawingTable {
kind: "table" | "grid" | "text";
rows: (string | number)[][];
merges: { row: number; col: number; rowSpan: number; colSpan: number }[];
colWidths: number[];
rowHeights: number[];
}
export const SHEET_PREFIX = "도면에서";
/** 도면 mm → 엑셀 열 폭(글자 단위 · 1글자 ≈ 7 px ≈ 1.85 mm) · 행 높이(pt = mm × 72 / 25.4) */
const CHAR_PER_MM = 0.54;
const PT_PER_MM = 72 / 25.4;
const clamp = (v: number, lo: number, hi: number): number => Math.min(hi, Math.max(lo, v));
const round1 = (v: number): number => Math.round(v * 10) / 10;
/** 0 → `A` · 27 → `AB`(`spreadsheet_address.colName` 과 같은 규칙) */
function colName(c: number): string {
let s = "";
for (let n = c + 1; n > 0; n = Math.floor((n - 1) / 26))
s = String.fromCharCode(65 + ((n - 1) % 26)) + s;
return s;
}
const a1 = (r: number, c: number): string => `${colName(c)}${r + 1}`;
/** 「도면에서 n」 — 안 쓴 가장 작은 n */
export function nextSheetName(book: Pick<Workbook, "시트">): string {
const used = new Set(book.시트.map((s) => s.이름));
let n = 1;
while (used.has(`${SHEET_PREFIX} ${n}`)) n += 1;
return `${SHEET_PREFIX} ${n}`;
}
/** 안 쓴 시트 id(`s1` …) */
function nextSheetId(book: Pick<Workbook, "시트">): string {
const used = new Set(book.시트.map((s) => s.id));
let n = book.시트.length + 1;
while (used.has(`s${n}`)) n += 1;
return `s${n}`;
}
/** 표 자료 → 새 시트(이름 · id 는 문서에서 안 쓴 것) */
export function tableToSheet(table: DrawingTable, book: Pick<Workbook, "시트">): Sheet {
const 칸: Record<string, Cell> = {};
table.rows.forEach((line, r) =>
line.forEach((value, c) => {
if (value === "" || value === null || value === undefined) return;
칸[a1(r, c)] = { 값: value };
}),
);
const 병합 = table.merges
.filter((m) => m.rowSpan > 1 || m.colSpan > 1)
.map((m) => `${a1(m.row, m.col)}:${a1(m.row + m.rowSpan - 1, m.col + m.colSpan - 1)}`);
const sheet: Sheet = { id: nextSheetId(book), 이름: nextSheetName(book), 칸 };
if (병합.length) sheet.병합 = 병합;
if (table.colWidths.length) {
sheet.열 = {};
table.colWidths.forEach((w, c) => {
if (w > 0) sheet.열![colName(c)] = { 폭: round1(clamp(w * CHAR_PER_MM, 2, 60)) };
});
}
if (table.rowHeights.length) {
sheet.행 = {};
table.rowHeights.forEach((h, r) => {
if (h > 0) sheet.행![String(r + 1)] = { 높이: round1(clamp(h * PT_PER_MM, 12, 100)) };
});
}
return sheet;
}
/** 스프레드시트 공개 손잡이 중 쓰는 것 — 시트 더하기(되돌리기 한 번 · onChange · 탭 줄은 부품 몫) */
export interface AddSheetHandle {
getDoc(): Workbook;
addSheet?: (sheet: Sheet, opts?: { activate?: boolean }) => unknown;
}
/** 받은 표 → 면적 새 시트(활성) · 알림 — 옮긴 시트 이름 · 못 하면 null */
export function importDrawingTable(
handle: AddSheetHandle | null,
table: DrawingTable,
): string | null {
if (!handle?.addSheet) {
showToast(handle ? TEXT.noHandle : TEXT.noSheet, "error");
return null;
}
const sheet = tableToSheet(table, handle.getDoc());
handle.addSheet(sheet, { activate: true });
showToast(TEXT.done(sheet.이름), "success");
if (table.kind === "text") showToast(TEXT.loose, "info");
return sheet.이름;
}