refactor(M02): 재료산출 시트 지움 — 종류 material · 재료번 SM · 좌측 줄 · 집계표 「재료산출」 줄 · 카드 · 전체 편집 · 복사 길 · 프로젝트 복사본 「재료」 · 고르기 모달 · 시스템 층 · fixture · 시험

- 새 프로젝트 씨앗(KINDS)에서도 빠짐 · 새 열은 구조물 도면 · 면적산출만 만듦
- storage 층(회사 · 개인 · 프로젝트) material/ 파일은 지우지 않음(읽기만 끊음)
- 장부 재료번 칸은 다음 집계표 [저장] 때 빠짐

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01NPJGtfXo6GMbcpiboQC4uK
This commit is contained in:
2026-10-04 17:56:49 +09:00
co-authored by Claude Opus 5.5
parent 54ce67ceb8
commit 61d2ed5997
53 changed files with 198 additions and 1811 deletions
+7 -15
View File
@@ -3,8 +3,8 @@
* 표 그리기 — 문서 + 풀이 결과 → 쪽마다 `<table>`(머리 층 · 단위 줄 · 본문 · 합계 줄).
*
* 칸마다 `data-r`(줄 열쇠) · `data-c`(열 id) — 고치기 · 키보드는 격자(`ui_template_sheet.ts`)가 위임으로 받음.
* 줄 열쇠 — 본문 `d:<줄id>` · 합계 `t:<합계id>` · 마스터 줄 `s:unit|price|drawing|area|material`.
* master = 본문 위에 열마다 일위대가 · 도면 · 면적산출 · 재료산출 줄 · project = 쪽줄마다 쪽을 나눔(쪽마다 머리 · 합계는 마지막 쪽).
* 줄 열쇠 — 본문 `d:<줄id>` · 합계 `t:<합계id>` · 마스터 줄 `s:unit|price|drawing|area`.
* master = 본문 위에 열마다 일위대가 · 도면 · 면적산출 줄 · project = 쪽줄마다 쪽을 나눔(쪽마다 머리 · 합계는 마지막 쪽).
* 머리 층 줄 머리 칸은 `data-layer`(층 차례) — master 에서 층 이름을 고침.
* ========================================================================== */
@@ -17,7 +17,6 @@ import {
isBlankPick,
isCalcColumn,
KEY_AREA,
KEY_MATERIAL,
KEY_UNIT,
MASTER_ROWS,
type MasterRowKey,
@@ -39,10 +38,9 @@ export interface RenderState {
mode: SheetMode;
result: SheetResult;
sel: { r: string; c: string } | null;
/** 도면 · 면적산출 · 재료산출 줄 칸 글(열 id) — 없거나 빈 글이면 「없음」. */
/** 도면 · 면적산출 줄 칸 글(열 id) — 없거나 빈 글이면 「없음」. */
drawingLabel?: (colId: string) => string;
areaLabel?: (colId: string) => string;
materialLabel?: (colId: string) => string;
/** 조회 후 선택 줄(`PICK_ROWS`)을 고름으로 받나 — 칸 겉모양만. */
pick?: boolean;
/** 머리 · 층 이름을 못 고침(`lockHead`) — 칸 겉모양만. */
@@ -53,13 +51,9 @@ export interface RenderState {
headEditable?: boolean;
}
/** 도면 · 면적산출 · 재료산출 줄 칸 글. */
/** 도면 · 면적산출 줄 칸 글. */
export const refText = (state: RenderState, key: string, colId: string): string =>
(key === KEY_AREA
? state.areaLabel
: key === KEY_MATERIAL
? state.materialLabel
: state.drawingLabel)?.(colId)?.trim() || st("Drawing_None");
(key === KEY_AREA ? state.areaLabel : state.drawingLabel)?.(colId)?.trim() || st("Drawing_None");
/** 열 폭 — 저장 폭(없으면 기본) · 머리 글이 안 잘리는 최소 폭보다 좁지 않게. */
const columnWidth = (doc: SheetDoc, col: SheetColumn, mins: Map<string, number>): number =>
@@ -68,7 +62,7 @@ const columnWidth = (doc: SheetDoc, col: SheetColumn, mins: Map<string, number>)
mins.get(col.id) ?? 0,
);
/** 구조물 조합 — 일위대가 · 도면 · 면적산출 · 재료산출 줄 글(키 · 번호)이 줄바꿈 없이 들게 최소 폭을 넓힘
/** 구조물 조합 — 일위대가 · 도면 · 면적산출 줄 글(키 · 번호)이 줄바꿈 없이 들게 최소 폭을 넓힘
* (`.ui-sheet__meta td` 글꼴과 같게 잼). */
function fitMeta(state: RenderState, cols: SheetColumn[], mins: Map<string, number>): void {
const rem = parseFloat(getComputedStyle(document.documentElement).fontSize) || 16;
@@ -115,7 +109,7 @@ export function renderSheet(state: RenderState): HTMLElement {
const mins = headMinWidths(cols, depth, font);
if (state.combo) fitMeta(state, cols, mins);
const widthDoc = state.combo ? { ...doc, 보기: { ...doc.보기, 열너비: {} } } : doc;
// 줄 머리 칸 폭 — 층 이름 · 단위 · 일위대가 · 도면 · 면적산출 · 재료산출 · 합계 이름이 안 잘리게
// 줄 머리 칸 폭 — 층 이름 · 단위 · 일위대가 · 도면 · 면적산출 · 합계 이름이 안 잘리게
const GUTTER = Math.min(
GUTTER_MAX,
Math.max(
@@ -126,7 +120,6 @@ export function renderSheet(state: RenderState): HTMLElement {
st("Row_Unit_Price"),
st("Row_Drawing"),
st("Row_Area"),
st("Row_Material"),
...(doc.합계줄 ?? []).map((t) => t.이름),
].map((label) => Math.ceil(textWidth(label, font)) + 16),
),
@@ -193,7 +186,6 @@ export function renderSheet(state: RenderState): HTMLElement {
"s:price": "Row_Unit_Price",
"s:drawing": "Row_Drawing",
"s:area": "Row_Area",
"s:material": "Row_Material",
} as const
)[key];
const tr = el("tr", { className: "ui-sheet__meta", children: [gutter("th", st(label))] });