fix(M02): 구조물 조합 표 「일위대가」 줄 = 그 열 일위대가번(SU) — 옛 조합 키(UA) 칸 안 읽음 · 번호 글 줄(도면 · 면적산출과 같음)
- 표 부품: 일위대가 줄도 번호 글 줄(손으로 못 침 · laborLabel) · 조회 후 선택이 열 칸을 안 고침 - 누르면 다른 줄과 같은 길(일위대가 복사해 가져오기) · 열의 옛 「일위대가」 값은 파일에 그대로 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01NPJGtfXo6GMbcpiboQC4uK
This commit is contained in:
@@ -329,6 +329,7 @@ export function createMain(
|
||||
onChange: () => (dirty = !readOnly),
|
||||
...(struct
|
||||
? {
|
||||
laborLabel: (colId: string) => struct.numbers()[colId]?.일위대가번 ?? "",
|
||||
drawingLabel: (colId: string) => struct.numbers()[colId]?.도번 ?? "",
|
||||
areaLabel: (colId: string) => struct.numbers()[colId]?.면적번 ?? "",
|
||||
combo: {
|
||||
|
||||
@@ -8,6 +8,7 @@ master 만 · 단위 줄 바로 아래 차례(키보드) · project 에는 없
|
||||
from __future__ import annotations
|
||||
|
||||
import json
|
||||
import re
|
||||
import shutil
|
||||
import subprocess
|
||||
from pathlib import Path
|
||||
@@ -70,3 +71,18 @@ def test_text_column_pick_rows_blank():
|
||||
assert _run()["blank"] == [
|
||||
[True, False], [True, False], [True, False], [False, False], [False, False],
|
||||
] # fmt: skip
|
||||
|
||||
|
||||
def test_labor_row_shows_su_number():
|
||||
"""마스터 일위대가 줄 = 그 열 일위대가번(SU) — 열의 옛 `일위대가` 칸(UA 키)은 안 읽음."""
|
||||
ops = OPS.read_text(encoding="utf-8")
|
||||
assert "REF_ROWS: readonly string[] = [KEY_PRICE, KEY_DRAWING, KEY_AREA]" in ops
|
||||
render = (OPS.parent / "ui_template_sheet_render.ts").read_text(encoding="utf-8")
|
||||
assert re.search(r"key === KEY_PRICE\s*\?\s*state\.laborLabel", render)
|
||||
assert "col.일위대가" not in render
|
||||
sheet = (OPS.parent / "ui_template_sheet.ts").read_text(encoding="utf-8")
|
||||
assert "col.일위대가" not in sheet # 읽기 · 쓰기 · 조회 후 선택 모두 안 건드림
|
||||
main = (ROOT / "M02_MasterTemplete" / "M02_MasterTemplete_UI_Main.ts").read_text(
|
||||
encoding="utf-8"
|
||||
)
|
||||
assert 'laborLabel: (colId: string) => struct.numbers()[colId]?.일위대가번 ?? ""' in main
|
||||
|
||||
@@ -25,7 +25,6 @@ import {
|
||||
addLevel,
|
||||
deleteColumn,
|
||||
isBlankPick,
|
||||
KEY_PRICE,
|
||||
KEY_UNIT,
|
||||
navRows,
|
||||
PICK_ROWS,
|
||||
@@ -54,6 +53,8 @@ export interface SheetOptions {
|
||||
/** master — 열 더하기 · 지우기 · 머리 · 층 이름 고치기 없음(열 · 머리는 나무가 정함) */
|
||||
lockHead?: boolean;
|
||||
onChange?: (doc: SheetDoc) => void;
|
||||
/** 일위대가 줄 칸 글(일위대가번 SU · 열 id) — `drawingLabel` 과 같음(열의 옛 `일위대가` 칸은 안 읽음). */
|
||||
laborLabel?: (colId: string) => string;
|
||||
/** 도면 줄 칸 글(마스터 첫 쪽 · 열 id) — 없거나 빈 글이면 「없음」 · 글이 바뀌면 `recalc()` 로 다시 그림. */
|
||||
drawingLabel?: (colId: string) => string;
|
||||
/** 면적산출 줄 칸 글(번호) — `drawingLabel` 과 같음. */
|
||||
@@ -61,8 +62,7 @@ export interface SheetOptions {
|
||||
/** 칸을 고름 — 누르기 · 방향키 · Tab. */
|
||||
onSelect?: (sel: SheetSelection) => void;
|
||||
/** 조회 후 선택 — master 일위대가 · 도면 · 면적산출 칸을 두 번 누르기 · Enter · F2 로 부름.
|
||||
* 글을 돌려주면 일위대가 칸에 넣음 · 번호 글 줄은 부른 쪽이 가져온 뒤 `recalc()`.
|
||||
* 없으면 일위대가는 글로 고침 · 번호 글 줄은 읽기만. */
|
||||
* 번호 글 줄은 부른 쪽이 가져온 뒤 `recalc()` · 없으면 읽기만. */
|
||||
onPick?: (sel: SheetSelection) => Promise<string | null | undefined> | void;
|
||||
/** 구조물 조합 표(PLAN 31-1) — 단위 줄 · 안내 글 없음 · 열 폭 자동 · 고른 열은 가장 깊은 이름 칸을 밝힘 ·
|
||||
* `onHeadEdit` 를 주면 이름 칸을 두 번 누르기 · F2 · Enter 로 고쳐 부른 쪽에 넘김(표 문서는 안 고침 —
|
||||
@@ -87,6 +87,7 @@ export function createSheet(host: HTMLElement, input: SheetDoc, opts: SheetOptio
|
||||
mode: opts.mode,
|
||||
result: { 계산: {}, 합계: {}, 오류: [] },
|
||||
sel: null,
|
||||
laborLabel: opts.laborLabel,
|
||||
drawingLabel: opts.drawingLabel,
|
||||
areaLabel: opts.areaLabel,
|
||||
pick: !!opts.onPick,
|
||||
@@ -236,7 +237,6 @@ export function createSheet(host: HTMLElement, input: SheetDoc, opts: SheetOptio
|
||||
// ── 칸 고치기 ─────────────────────────────────────────────────────
|
||||
const rawText = (key: string, col: SheetColumn): string => {
|
||||
if (key === KEY_UNIT) return col.단위 ?? "";
|
||||
if (key === KEY_PRICE) return col.일위대가 ?? "";
|
||||
if (REF_ROWS.includes(key)) return refText(state, key, col.id);
|
||||
const row = state.doc.줄.find((r) => `d:${r.id}` === key);
|
||||
const formula = row?.식?.[col.id] ?? col.식;
|
||||
@@ -248,15 +248,12 @@ export function createSheet(host: HTMLElement, input: SheetDoc, opts: SheetOptio
|
||||
if (ro || REF_ROWS.includes(key)) return; // 읽기만
|
||||
const value = text.trim();
|
||||
if (key === KEY_UNIT) col.단위 = value || null;
|
||||
else if (key === KEY_PRICE) col.일위대가 = value || null;
|
||||
else setCell(state.doc, key.slice(2), col, text);
|
||||
changed();
|
||||
};
|
||||
/** 조회 후 선택 — 부른 쪽이 고른 글을 돌려주면 일위대가 칸에 넣음(그 사이 문서가 바뀌었으면 새 열에) */
|
||||
/** 조회 후 선택 — 부른 쪽이 가져옴(번호 글 줄 · 문서는 안 고침) */
|
||||
const pick = async (key: string, colId: string): Promise<void> => {
|
||||
const got = await opts.onPick?.(selectionOf(state.doc, key, colId));
|
||||
const col = colOf(colId);
|
||||
if (key === KEY_PRICE && typeof got === "string" && col) write(key, col, got);
|
||||
await opts.onPick?.(selectionOf(state.doc, key, colId));
|
||||
root.focus({ preventScroll: true });
|
||||
};
|
||||
/** 칸 고치기 — 두 번 누르기 = 글 전부 고름 · 바로 치기(initial) = 그 글자로 바꿈 · Enter · F2(atEnd) = 커서를 글 끝에 */
|
||||
|
||||
@@ -22,11 +22,11 @@ export const KEY_UNIT = "s:unit";
|
||||
export const KEY_PRICE = "s:price";
|
||||
export const KEY_DRAWING = "s:drawing";
|
||||
export const KEY_AREA = "s:area";
|
||||
/** 마스터 전용 줄(첫 쪽 단위 줄 아래 차례) — 도면 · 면적산출 줄은 손으로 못 침(번호 글). */
|
||||
/** 마스터 전용 줄(첫 쪽 단위 줄 아래 차례) — 일위대가 · 도면 · 면적산출 줄은 손으로 못 침(번호 글). */
|
||||
export const MASTER_ROWS = [KEY_PRICE, KEY_DRAWING, KEY_AREA] as const;
|
||||
export type MasterRowKey = (typeof MASTER_ROWS)[number];
|
||||
/** 번호 글 줄 — 부른 쪽이 준 글(`refLabel`)만 보임. */
|
||||
export const REF_ROWS: readonly string[] = [KEY_DRAWING, KEY_AREA];
|
||||
/** 번호 글 줄 — 부른 쪽이 준 글(`refLabel`)만 보임(일위대가 = SU · 열의 옛 `일위대가` 칸은 안 읽음). */
|
||||
export const REF_ROWS: readonly string[] = [KEY_PRICE, KEY_DRAWING, KEY_AREA];
|
||||
/** 조회 후 선택 줄 — `onPick` 을 주면 이 줄 칸은 치지 않고 고름. */
|
||||
export const PICK_ROWS: readonly string[] = MASTER_ROWS;
|
||||
|
||||
|
||||
@@ -17,6 +17,7 @@ import {
|
||||
isBlankPick,
|
||||
isCalcColumn,
|
||||
KEY_AREA,
|
||||
KEY_PRICE,
|
||||
KEY_UNIT,
|
||||
MASTER_ROWS,
|
||||
type MasterRowKey,
|
||||
@@ -38,7 +39,8 @@ export interface RenderState {
|
||||
mode: SheetMode;
|
||||
result: SheetResult;
|
||||
sel: { r: string; c: string } | null;
|
||||
/** 도면 · 면적산출 줄 칸 글(열 id) — 없거나 빈 글이면 「없음」. */
|
||||
/** 일위대가 · 도면 · 면적산출 줄 칸 글(열 id) — 없거나 빈 글이면 「없음」. */
|
||||
laborLabel?: (colId: string) => string;
|
||||
drawingLabel?: (colId: string) => string;
|
||||
areaLabel?: (colId: string) => string;
|
||||
/** 조회 후 선택 줄(`PICK_ROWS`)을 고름으로 받나 — 칸 겉모양만. */
|
||||
@@ -51,9 +53,13 @@ export interface RenderState {
|
||||
headEditable?: boolean;
|
||||
}
|
||||
|
||||
/** 도면 · 면적산출 줄 칸 글. */
|
||||
/** 일위대가 · 도면 · 면적산출 줄 칸 글(번호). */
|
||||
export const refText = (state: RenderState, key: string, colId: string): string =>
|
||||
(key === KEY_AREA ? state.areaLabel : state.drawingLabel)?.(colId)?.trim() || st("Drawing_None");
|
||||
(key === KEY_AREA
|
||||
? state.areaLabel
|
||||
: key === KEY_PRICE
|
||||
? state.laborLabel
|
||||
: state.drawingLabel)?.(colId)?.trim() || st("Drawing_None");
|
||||
|
||||
/** 열 폭 — 저장 폭(없으면 기본) · 머리 글이 안 잘리는 최소 폭보다 좁지 않게. */
|
||||
const columnWidth = (doc: SheetDoc, col: SheetColumn, mins: Map<string, number>): number =>
|
||||
@@ -68,7 +74,7 @@ function fitMeta(state: RenderState, cols: SheetColumn[], mins: Map<string, numb
|
||||
const rem = parseFloat(getComputedStyle(document.documentElement).fontSize) || 16;
|
||||
const font = `400 ${rem * 0.76}px ${getComputedStyle(document.body).fontFamily}`;
|
||||
for (const col of cols) {
|
||||
const texts = [col.일위대가 ?? "", ...REF_ROWS.map((key) => refText(state, key, col.id))];
|
||||
const texts = REF_ROWS.map((key) => refText(state, key, col.id));
|
||||
const need = Math.ceil(Math.max(...texts.map((t) => textWidth(t, font)))) + 14;
|
||||
mins.set(col.id, Math.max(mins.get(col.id) ?? 0, need));
|
||||
}
|
||||
@@ -191,11 +197,7 @@ export function renderSheet(state: RenderState): HTMLElement {
|
||||
const tr = el("tr", { className: "ui-sheet__meta", children: [gutter("th", st(label))] });
|
||||
cols.forEach((col, j) => {
|
||||
const blank = isBlankPick(key, col);
|
||||
const text = blank
|
||||
? ""
|
||||
: REF_ROWS.includes(key)
|
||||
? refText(state, key, col.id)
|
||||
: (col.일위대가 ?? "");
|
||||
const text = blank ? "" : refText(state, key, col.id); // 마스터 줄 = 모두 번호 글
|
||||
const td = bodyCell(key, col, j, text);
|
||||
td.classList.remove("is-num");
|
||||
if (state.pick && PICK_ROWS.includes(key) && !blank) td.classList.add("is-pick");
|
||||
|
||||
Reference in New Issue
Block a user