fix(802-3): 선택으로 꺼진 금액 줄 숨김 · 값 식 한 번 · 옮김 군더더기 걷음(브레인 사진 지적)
- 663-1: 수량 · 단가가 만약으로 0 을 탄 금액 줄 = 안 보임(GC000211 폼타이 9.5 · 7.9 ㎜) · 규격 바꾸면 그 줄만 · [선택으로 꺼진 줄 보기] = 고침 모드에서만 · 정의 영역도 꺼진 줄 기호 뺌 1,518 문서 금액 줄 5,912 중 기본 옵션에서 꺼짐 145 줄(107 문서) · 옵션에 따라 꺼질 수 있는 줄 305 줄(141 문서) - 식 안 만약이 0 · 1 을 타면 그 항(+ 0 · × 1)도 안 보임 — 고침 글은 그대로 - 수 하나 · 기호 하나 줄 값 식 = 한 번만(「= 1 = 1」 없음) · 수량 없음 「× 1」 자리는 줄에 마우스 올릴 때만 - 옮김 slim(master_doc_convert) — + 0 · × 1 · ÷ 1 지움 · A × k ÷ 100 → A × k% · 빌더 다시(목록 xlsx 는 안 씀) · 대조 그대로(기본 1,519 같음 · 옵션 다름 1 · 저장 1,518) · 31 파일 글만 - 시험 test_802_3 + 군더더기 없음 1(12) · 802_1 · 802_2 통과 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
@@ -304,6 +304,11 @@
|
||||
gap: 0 var(--spacing-4);
|
||||
}
|
||||
|
||||
/* 수량 없음 = 1 — 고치는 자리만(줄에 마우스를 올릴 때 흐리게) */
|
||||
.fdoc__money .is-ghost {
|
||||
opacity: 0;
|
||||
}
|
||||
|
||||
.fdoc__money:hover .is-ghost {
|
||||
opacity: 0.45;
|
||||
}
|
||||
|
||||
@@ -17,8 +17,10 @@ import type { DocVar, FormulaDoc, MoneyLine, Node } from "../common_util/common_
|
||||
import type { DocAnswer } from "../common_util/common_util_formula_doc_calc";
|
||||
import {
|
||||
litsOf,
|
||||
offLine,
|
||||
renderNode,
|
||||
symNode,
|
||||
takenOf,
|
||||
tryParse,
|
||||
valueNode,
|
||||
type Values,
|
||||
@@ -33,6 +35,9 @@ export type EditAt = { kind: "line" | "expr" | "price" | "qty" | "name"; i: numb
|
||||
|
||||
export interface LinesHooks {
|
||||
admin: boolean;
|
||||
/** 선택으로 꺼진 금액 줄도 보임(고침 모드) */
|
||||
showOff?: boolean;
|
||||
onShowOff?: () => void;
|
||||
doc: FormulaDoc;
|
||||
answer: DocAnswer;
|
||||
vars: Map<string, DocVar>;
|
||||
@@ -161,7 +166,14 @@ function lineRow(i: number, h: LinesHooks): HTMLElement[] {
|
||||
vexpr.append(expr(line.식, h, "val").node);
|
||||
const got = vals(h)[head];
|
||||
right.append(span("ui-eq__op", "="), vexpr);
|
||||
if (got !== null && got !== undefined && typeof got !== "object") {
|
||||
// 수 하나 · 기호 하나(탄 갈래) 줄 = 값 한 번만(「= 1 = 1」 없음)
|
||||
let eff = tryParse(line.식);
|
||||
for (let pick = eff ? takenOf(eff, vals(h)) : 0; eff && pick; pick = takenOf(eff, vals(h)))
|
||||
eff = (eff as ["call", string, Node[]])[2][pick];
|
||||
const single = !!eff && (eff[0] === "num" || (eff[0] === "var" && vals(h)[eff[1]] === got));
|
||||
if (single) {
|
||||
/* 값 식이 곧 결과 */
|
||||
} else if (got !== null && got !== undefined && typeof got !== "object") {
|
||||
right.append(span("ui-eq__op", "="), valueNode(got, unitOf(head, h)));
|
||||
} else right.append(span("ui-eq__op", "="), span("fdoc__dash", "—"));
|
||||
if (h.admin) {
|
||||
@@ -333,10 +345,13 @@ export function buildLines(h: LinesHooks): HTMLElement[] {
|
||||
const lines = h.doc.금액 ?? [];
|
||||
const groups: string[] = [];
|
||||
lines.forEach((l) => groups.includes(l.묶음 ?? "") || groups.push(l.묶음 ?? ""));
|
||||
const off = new Set(lines.flatMap((l, i) => (offLine(l, vals(h)) ? [i] : [])));
|
||||
const seen = (i: number): boolean => !!h.showOff || !off.has(i);
|
||||
const many = groups.length > 1;
|
||||
const bundles = (h.answer.묶음 ?? []) as Record<string, unknown>[];
|
||||
const done = h.answer.셈됨;
|
||||
groups.forEach((g, gi) => {
|
||||
if (!lines.some((l, i) => (l.묶음 ?? "") === g && seen(i))) return; // 다 꺼진 묶음
|
||||
if (many) {
|
||||
const head = span("fdoc__group", `${ORDER[gi] ?? gi + 1}. `);
|
||||
const label = span("fdoc__name", g || ft("Unnamed"));
|
||||
@@ -345,7 +360,7 @@ export function buildLines(h: LinesHooks): HTMLElement[] {
|
||||
label.addEventListener("click", () => editName(label, g, (t) => h.onGroupName(g, t)));
|
||||
add([el("div", { className: "fdoc__cell fdoc__grouphead", children: [head] })]); // 두 칸 걸침
|
||||
}
|
||||
lines.forEach((l, i) => (l.묶음 ?? "") === g && add(moneyRow(i, h)));
|
||||
lines.forEach((l, i) => (l.묶음 ?? "") === g && seen(i) && add(moneyRow(i, h)));
|
||||
if (h.admin && lines.length)
|
||||
pair(
|
||||
el("div", {
|
||||
@@ -364,14 +379,12 @@ export function buildLines(h: LinesHooks): HTMLElement[] {
|
||||
}),
|
||||
null,
|
||||
);
|
||||
if (h.admin && lines.length)
|
||||
pair(
|
||||
el("div", {
|
||||
className: "fdoc__cell",
|
||||
children: [button(ft("Add_Group"), () => h.onAddMoney(ft("New_Group")))],
|
||||
}),
|
||||
null,
|
||||
);
|
||||
if (h.admin && lines.length) {
|
||||
const kids = [button(ft("Add_Group"), () => h.onAddMoney(ft("New_Group")))];
|
||||
if (off.size && h.onShowOff)
|
||||
kids.push(button(`${h.showOff ? ft("Hide_Off") : ft("Show_Off")} ${off.size}`, h.onShowOff));
|
||||
pair(el("div", { className: "fdoc__cell", children: kids }), null);
|
||||
}
|
||||
if (lines.length) {
|
||||
const sum = done ? (h.answer as unknown as Record<string, unknown>) : null;
|
||||
add(totalRow(many ? ft("Total") : ft("Sum"), sum, "is-end"));
|
||||
|
||||
@@ -112,6 +112,23 @@ export function evalNode(n: Node, vals: Values): unknown {
|
||||
}
|
||||
}
|
||||
|
||||
/** 만약 사슬이 탄 끝 갈래가 수 하나면 그 수(만약이 아니거나 모르면 undefined) */
|
||||
export function leafNum(n: Node, vals: Values): number | undefined {
|
||||
let at = n;
|
||||
let hop = 0;
|
||||
for (let pick = takenOf(at, vals); pick; pick = takenOf(at, vals), hop++)
|
||||
at = (at as ["call", string, Node[]])[2][pick];
|
||||
return hop && at[0] === "num" ? at[1] : undefined;
|
||||
}
|
||||
|
||||
/** 선택으로 꺼진 금액 줄 — 수량 · 단가가 만약으로 0 을 탐(663-1 · 고침 모드에서만 보임) */
|
||||
export function offLine(line: { 수량?: unknown; 단가?: unknown }, vals: Values): boolean {
|
||||
return [line.수량, line.단가].some((t) => {
|
||||
const n = tryParse(t);
|
||||
return !!n && leafNum(n, vals) === 0;
|
||||
});
|
||||
}
|
||||
|
||||
/** 만약 — 탄 갈래 차례(1 참 · 2 거짓 · 0 모름) */
|
||||
export function takenOf(n: Node, vals: Values): 0 | 1 | 2 {
|
||||
if (n[0] !== "call" || n[1] !== "만약" || n[2].length !== 3) return 0;
|
||||
@@ -262,6 +279,13 @@ export function renderNode(n: Node, c: Ctx): HTMLElement {
|
||||
}
|
||||
case "bin": {
|
||||
const [, o, a, b] = n;
|
||||
// 선택으로 꺼진 항(만약이 0 · 1 을 탐) = 안 보임(663-1) · 고침 글은 그대로
|
||||
const na = leafNum(a, c.vals);
|
||||
const nb = leafNum(b, c.vals);
|
||||
if ((o === "+" || o === "-") && nb === 0) return hidden("(", `) ${o} ${textOf(b)}`, go(a));
|
||||
if ((o === "*" || o === "/") && nb === 1) return hidden("(", `) ${o} ${textOf(b)}`, go(a));
|
||||
if ((o === "+" && na === 0) || (o === "*" && na === 1))
|
||||
return hidden(`${textOf(a)} ${o} (`, ")", go(b));
|
||||
if (o === "/" && !c.inline) {
|
||||
const f = span("ui-eq__frac");
|
||||
const top = span("ui-eq__top");
|
||||
@@ -341,7 +365,7 @@ export function namesTaken(n: Node, vals: Values, out: string[] = []): string[]
|
||||
const RESERVED_MONEY = new Set(["노무비", "재료비", "경비", "번지", "단계"]);
|
||||
|
||||
/** 정의 영역에 보일 기호 — 식 줄 · 금액 줄 · 결과에서 지금 탄 갈래로 닿는 것(보이는 차례) */
|
||||
export function reach(doc: FormulaDoc, vals: Values): string[] {
|
||||
export function reach(doc: FormulaDoc, vals: Values, showOff = false): string[] {
|
||||
const vars = new Map((doc.변수 ?? []).map((v) => [varId(v), v]));
|
||||
const lines = new Map((doc.식 ?? []).map((l) => [String(l.기호 ?? ""), l.식]));
|
||||
const money = new Set((doc.금액 ?? []).map((l) => String(l.기호 ?? "")).filter(Boolean));
|
||||
@@ -365,6 +389,7 @@ export function reach(doc: FormulaDoc, vals: Values): string[] {
|
||||
from(l.식);
|
||||
}
|
||||
for (const l of doc.금액 ?? []) {
|
||||
if (!showOff && offLine(l, vals)) continue;
|
||||
from(l.수량);
|
||||
from(l.단가);
|
||||
}
|
||||
|
||||
@@ -86,6 +86,7 @@ export function createDocScreen(opt: DocScreenOptions) {
|
||||
let given: Record<string, unknown> = {};
|
||||
let openDef: string | null = null;
|
||||
let auto: EditAt = null;
|
||||
let showOff = false; // 선택으로 꺼진 금액 줄(고침 모드)
|
||||
let seq = 0;
|
||||
let asked = "";
|
||||
let ctl: AbortController | null = null;
|
||||
@@ -466,7 +467,7 @@ export function createDocScreen(opt: DocScreenOptions) {
|
||||
const vals = answer.값 ?? {};
|
||||
const vars = new Map((doc.변수 ?? []).map((v) => [varId(v), v]));
|
||||
const lineSet = new Set((doc.식 ?? []).map((l) => String(l.기호 ?? "")).filter(Boolean));
|
||||
const shown = reach(doc, vals);
|
||||
const shown = reach(doc, vals, showOff);
|
||||
const markMap = new Map<string, string>();
|
||||
for (const m of answer.표지 ?? []) {
|
||||
const at = m.기호 || (m as { 자리?: string }).자리;
|
||||
@@ -483,6 +484,11 @@ export function createDocScreen(opt: DocScreenOptions) {
|
||||
markBox();
|
||||
const lines = buildLines({
|
||||
admin: opt.admin,
|
||||
showOff,
|
||||
onShowOff: () => {
|
||||
showOff = !showOff;
|
||||
draw();
|
||||
},
|
||||
doc,
|
||||
answer,
|
||||
vars,
|
||||
@@ -629,6 +635,7 @@ export function createDocScreen(opt: DocScreenOptions) {
|
||||
key = next;
|
||||
doc = answer = null;
|
||||
given = {};
|
||||
showOff = false;
|
||||
openDef = null;
|
||||
asked = "";
|
||||
cache = createDocCache();
|
||||
|
||||
@@ -21,6 +21,8 @@ const TEXT = {
|
||||
Total: ["합계", "Total"],
|
||||
Need: ["정의 필요", "Needs definition"],
|
||||
Unused: ["쓰지 않는 기호", "Unused symbols"],
|
||||
Show_Off: ["선택으로 꺼진 줄 보기", "Show switched-off lines"],
|
||||
Hide_Off: ["선택으로 꺼진 줄 숨김", "Hide switched-off lines"],
|
||||
Save: ["수식 저장", "Save equation"],
|
||||
Discard: ["고침 버림", "Discard"],
|
||||
Saved: ["수식 저장됨", "Equation saved"],
|
||||
|
||||
Reference in New Issue
Block a user