feat(804-5): 줄 쌓기 표 값 이름 · 산출근거 고정 · 그룹 글 · 짝 강조 채우기 · 틀로 저장 걷음(요구 683~685)
- 표 값 기호 화면 이름 = 고른 줄 이름(Q_수량2^표 → Q_특별인부 · 겹치면 2 · 3) — 왼쪽 · 산출근거 같음 · 칸 글은 화면 이름 ↔ 기호로 오감(문서 기호 그대로) - 단위 「줄 따름」 안 보임 · 표 값만 든 묶음 머리 = 「표 값」 · [식으로 풀기] → 「식 고치기」 - 산출근거 = 넓을 때 화면 고정 + 자기 스크롤(높이 = 스크롤 영역) · 나란히 놓는 탭 폭 1240 → 1040 - 글 「항목」 → 「그룹」(그룹 이름 · + 그룹 · 그룹 지움) - 짝 강조 = 왼쪽 카드 · 그룹 머리 · 오른쪽 줄 모두 같은 바탕 채우기(테두리 걷음) - [틀로 저장] · 내 틀 목록(브라우저 저장) 걷음 · 공식 틀 고르기 = M01 단가산출 결과만 - 시험 test_804_4 표 값 화면 이름 1,518 전수(겹침 없음 · 식 글 왕복 같음) 더함 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
@@ -3,14 +3,14 @@
|
||||
* 줄 쌓기 공식 묶음(PLAN 804-2 · 요구 675) — 「이름 = 값」 · 「이름 = 식」 줄 묶음 · 틀 이름표 · [식으로 풀기] · [틀로 저장]
|
||||
* 접힘 = 값 줄은 값 칸(연결 값을 고쳐 적으면 숫자 = 고정값 · 요구 664) · 식 줄은 「이름 = 식 = 값」 글 · 단가산출 결과 기호 = [식으로 풀기]
|
||||
* 풀림 = 줄마다 이름 · 식 · 단위 칸 + 지움 · [+ 줄] — 이름 바꿈 = 문서 전부(`rename`) · 식 = 식 줄 · 수 = 숫자 연결
|
||||
* 틀 = 단가산출 결과 문서(굴착기 Q 등)를 이 문서 식으로 옮김(`unfold`) · 내 틀 = 이 브라우저 저장(localStorage) — 꼴이 바뀌면
|
||||
* 「내 틀 (틀 이름 에서 변경)」 · 빈 틀 = [찾아 넣기](돈 아닌 단가산출 결과 — 작업량 등)
|
||||
* 틀 = 단가산출 결과 문서(굴착기 Q 등)를 이 문서 식으로 옮김(`unfold`) — 꼴이 바뀌면 「내 틀 (틀 이름 에서 변경)」 ·
|
||||
* 빈 틀 = [찾아 넣기](M01 단가산출 결과 — 작업량 등) · [틀로 저장] · 내 틀 목록 걷음(요구 685) · 표 값만 = 머리 「표 값」
|
||||
* 표 값(804-3) = [표 · 칸] 단추 → 표 고르기 창(`line_stack_pick.ts`) · 값 칸 누름 = 그 줄 · 칸(`pickCell`)
|
||||
* ========================================================================== */
|
||||
|
||||
import { createButton } from "@ui/ui_template_button";
|
||||
import { eqText } from "@ui/ui_template_equation";
|
||||
import { askText, el, showToast } from "@ui/ui_template_elements";
|
||||
import { el, showToast } from "@ui/ui_template_elements";
|
||||
import { createInputField } from "@ui/ui_template_input";
|
||||
import {
|
||||
DocError,
|
||||
@@ -27,32 +27,25 @@ import {
|
||||
setRow,
|
||||
shapeOf,
|
||||
unfold,
|
||||
type StackDoc,
|
||||
type StackItem,
|
||||
type StackLine,
|
||||
} from "../common_util/common_util_line_stack";
|
||||
import { boundConds, currentRow, pickCell } from "../common_util/common_util_line_stack_pick";
|
||||
import {
|
||||
aliasText,
|
||||
boundConds,
|
||||
currentRow,
|
||||
pickCell,
|
||||
shownUnit,
|
||||
tableAlias,
|
||||
} from "../common_util/common_util_line_stack_pick";
|
||||
import { openFindPicker } from "../M02_MasterTemplete/M02_MasterTemplete_Find_Pick";
|
||||
import type { EditHooks } from "./line_stack_edit";
|
||||
import { openTablePick } from "./line_stack_pick";
|
||||
import { lt } from "./line_stack_text";
|
||||
|
||||
/** 내 틀 — 기호 · 식 조각(이 브라우저) */
|
||||
export interface Tpl {
|
||||
id: string;
|
||||
name: string;
|
||||
문서: FormulaDoc;
|
||||
}
|
||||
|
||||
const clone = <T>(v: T): T => JSON.parse(JSON.stringify(v)) as T;
|
||||
type Block = Extract<StackLine, { 꼴: "공식" }>;
|
||||
|
||||
/** 틀 넣기 — 새 공식 묶음 줄 */
|
||||
export function insertTpl(d: StackDoc, t: Tpl): Block {
|
||||
const moved = unfold(d, t.문서);
|
||||
return { 꼴: "공식", 기호: moved, 틀: `my:${t.id}`, 틀꼴: shapeOf(d, moved) };
|
||||
}
|
||||
|
||||
const LINK_TAG: Record<string, string> = {
|
||||
표: "표",
|
||||
요소: "요소",
|
||||
@@ -81,9 +74,15 @@ export function buildBlock(h: EditHooks, i: number, j: number, line: Block): HTM
|
||||
const vars = new Map((d.변수 ?? []).map((v) => [varId(v), v]));
|
||||
const lines = new Map((d.식 ?? []).map((l) => [String(l.기호 ?? ""), String(l.식 ?? "")]));
|
||||
const blockOf = (items: StackItem[]): Block => items[i].줄[j] as Block;
|
||||
// 표 값 기호 = 화면 이름(요구 683 · 문서 기호 그대로) — 칸 글은 화면 이름 ↔ 기호로 오감
|
||||
const alias = tableAlias(d);
|
||||
const show = (id: string): string => alias[id] ?? id;
|
||||
const shownText = (t: string): string => aliasText(t, alias);
|
||||
const realText = (t: string): string => aliasText(t, alias, true);
|
||||
const valText = (id: string): string => {
|
||||
const v = vals[id];
|
||||
if (isNum(v) || typeof v === "string") return `${eqText(v)} ${vars.get(id)?.단위 ?? ""}`.trim();
|
||||
if (isNum(v) || typeof v === "string")
|
||||
return `${eqText(v)} ${shownUnit(vars.get(id)?.단위)}`.trim();
|
||||
if (v && typeof v === "object")
|
||||
return ["노무비", "재료비", "경비"]
|
||||
.map((c) => `${c} ${eqText((v as Record<string, unknown>)[c])}`)
|
||||
@@ -93,12 +92,11 @@ export function buildBlock(h: EditHooks, i: number, j: number, line: Block): HTM
|
||||
|
||||
// ── 이름표 ──
|
||||
const tplName = (k: string): string =>
|
||||
k.startsWith("my:")
|
||||
? (h.templates().find((t) => `my:${t.id}` === k)?.name ?? lt("My_Tpl"))
|
||||
: (h.subDoc(k)?.이름 ?? (h.name(k) || k));
|
||||
k.startsWith("my:") ? lt("My_Tpl") : (h.subDoc(k)?.이름 ?? (h.name(k) || k)); // my: = 옛 내 틀(걷음 · 요구 685)
|
||||
const changed = !!line.틀 && !!line.틀꼴 && shapeOf(d, line.기호) !== line.틀꼴;
|
||||
const tableOnly = line.기호.every((id) => vars.get(id)?.연결?.갈래 === "표" && !lines.has(id));
|
||||
const label = !line.틀
|
||||
? lt("K_공식")
|
||||
? lt(tableOnly && line.기호.length ? "K_표값" : "K_공식")
|
||||
: changed
|
||||
? `${lt("My_Tpl")} (${tplName(line.틀)} ${lt("Changed_From")})`
|
||||
: tplName(line.틀);
|
||||
@@ -116,28 +114,6 @@ export function buildBlock(h: EditHooks, i: number, j: number, line: Block): HTM
|
||||
h.change(() => undefined);
|
||||
},
|
||||
}),
|
||||
createButton({
|
||||
label: lt("Save_Tpl"),
|
||||
variant: "ghost",
|
||||
size: 24,
|
||||
onClick: () =>
|
||||
void askText({ message: lt("Tpl_Name"), value: label }).then((name) => {
|
||||
if (!name?.trim()) return;
|
||||
const ids = new Set(line.기호);
|
||||
const id = `t${Date.now()}`;
|
||||
h.saveTpl({
|
||||
id,
|
||||
name: name.trim(),
|
||||
문서: {
|
||||
변수: clone((d.변수 ?? []).filter((v) => ids.has(varId(v)))),
|
||||
식: clone((d.식 ?? []).filter((l) => ids.has(String(l.기호 ?? "")))),
|
||||
},
|
||||
});
|
||||
h.change((doc, items) =>
|
||||
Object.assign(blockOf(items), { 틀: `my:${id}`, 틀꼴: shapeOf(doc, line.기호) }),
|
||||
);
|
||||
}),
|
||||
}),
|
||||
],
|
||||
});
|
||||
head.dataset.tpl = changed ? "changed" : line.틀 ? "tpl" : "";
|
||||
@@ -246,16 +222,20 @@ export function buildBlock(h: EditHooks, i: number, j: number, line: Block): HTM
|
||||
}),
|
||||
}),
|
||||
);
|
||||
const name = el("b", { className: "lstk-sym", text: id });
|
||||
const name = el("b", { className: "lstk-sym", text: show(id) });
|
||||
const valueOf =
|
||||
expr ??
|
||||
(link?.갈래 === "숫자" ? String(link.값 ?? "") : isNum(vals[id]) ? String(vals[id]) : "");
|
||||
let body: (HTMLElement | string)[];
|
||||
if (open)
|
||||
body = [
|
||||
input(id, lt("Row_Name"), "lstk-f-s", (t) => renameRow(id, t)),
|
||||
input(show(id), lt("Row_Name"), "lstk-f-s", (t) =>
|
||||
renameRow(id, t.trim() === show(id) ? id : t),
|
||||
),
|
||||
"=",
|
||||
input(valueOf, lt("Row_Expr"), "lstk-f-l", (t) => h.change((doc) => setRow(doc, id, t))),
|
||||
input(shownText(valueOf), lt("Row_Expr"), "lstk-f-l", (t) =>
|
||||
h.change((doc) => setRow(doc, id, realText(t))),
|
||||
),
|
||||
input(v?.단위 ?? "", lt("Unit"), "lstk-f-s", (t) =>
|
||||
h.change((doc) => {
|
||||
let x = doc.변수?.find((y) => varId(y) === id);
|
||||
@@ -274,15 +254,19 @@ export function buildBlock(h: EditHooks, i: number, j: number, line: Block): HTM
|
||||
else if (expr !== undefined || sub || link?.갈래 === "단가산출")
|
||||
body = [
|
||||
name,
|
||||
`= ${expr ?? ""}${expr ? " =" : ""}`,
|
||||
`= ${shownText(expr ?? "")}${expr ? " =" : ""}`,
|
||||
el("span", { className: "lstk-val", text: valText(id) }),
|
||||
];
|
||||
else
|
||||
body = [
|
||||
name,
|
||||
"=",
|
||||
input(valueOf, valText(id), "lstk-f-m", (t) => h.change((doc) => setRow(doc, id, t))),
|
||||
el("span", { className: "lstk-unit", text: v?.단위 ?? "" }),
|
||||
input(shownText(valueOf), valText(id), "lstk-f-m", (t) =>
|
||||
h.change((doc) => setRow(doc, id, realText(t))),
|
||||
),
|
||||
...(shownUnit(v?.단위)
|
||||
? [el("span", { className: "lstk-unit", text: shownUnit(v?.단위) })]
|
||||
: []),
|
||||
];
|
||||
const row = el("div", {
|
||||
className: `lstk-frow ${open ? "is-open" : ""}`,
|
||||
|
||||
Reference in New Issue
Block a user