- 시험 계산: 로직을 열 때 GET /logic/auto 값을 빈 입력에 채우고 「견본」 딱지(사용자가 고치면 빠짐) 뒤 바로 계산 · 텍스트 수식까지. 서버 길이 없으면 안 채움 - 단가산출 상세 수량 칸: 화면 쪽 식 풀이(qtyFragments) 걷고 /text 줄의 조각으로 그림 — 알약 글 = 조각 글 · 깊이 있는 조각은 흐리게 · 묶음별 줄 수가 안 맞으면 식 글자 그대로 - 표 알약 모달: 참조.행·열에 색(행을 못 받으면 열만) - 재료 고르기 줄(요소가 객체): 모달이 객체를 받게 — 잡힌 품목 단가 + 후보 목록 - 돈 모양 한 벌(Logic_Money.ts): 상세·모달·텍스트 수식 소계와 계·시험 계산 결과·조합 미리 보기 정수 · 수량·비율은 그대로 - 서버 파일은 안 만짐 검증 — typecheck 통과 · GF000160 ORCA: 알약 7 서로 다름 · 계 141,882 · 재료 줄 모달 · GF000219·GF000001 회귀 없음. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01EY2vdBQkHjdih9GgCsp9Fd
98 lines
4.1 KiB
TypeScript
98 lines
4.1 KiB
TypeScript
/* =============================================================================
|
|
* M01_MasterData_UI_LogicLab_Formula.ts
|
|
* 「텍스트 수식」 컨테이너 — 서버(`POST /text`)가 준 줄을 그대로 찍음(화면에서 다시 계산하지 않음)
|
|
* 두 칸 — 왼쪽 = 변수 이름 식(`이름글`) · 오른쪽 = 값이 들어간 식(`글`) · 줄은 `짝` 번호로 같은 높이
|
|
* 한쪽에 올리거나 누르면 같은 줄이 밝아짐 · 비목 제목 · 소계 · 끝수 한 마디 · 까닭 있는 줄은 옅게
|
|
* ========================================================================== */
|
|
|
|
import { el } from "@ui/ui_template_elements";
|
|
import type { TextAnswer } from "./M01_MasterData_UI_Logic_Api";
|
|
import { formatMoney } from "./M01_MasterData_UI_Logic_Money";
|
|
import { tx } from "./M01_MasterData_UI_Logic_Text";
|
|
import { tl } from "./M01_MasterData_UI_LogicLab_Text";
|
|
import "./M01_MasterData_UI_LogicLab_Formula.css";
|
|
|
|
/** 로직 열자마자 값 없이 부른 미리보기 응답은 `계산됨` 을 false 로 표시 — [계산] 을 거친 응답은 안 붙어 true 취급 */
|
|
export type PreviewTextAnswer = TextAnswer & { 계산됨?: boolean };
|
|
|
|
export interface FormulaContext {
|
|
text: PreviewTextAnswer | null;
|
|
}
|
|
|
|
const muted = (text: string): HTMLElement => el("p", { className: "m01-logic__muted", text });
|
|
|
|
export function buildFormula(host: HTMLElement, ctx: FormulaContext): void {
|
|
const text = ctx.text;
|
|
if (!text) {
|
|
host.replaceChildren(muted(tl("Formula_Slot")));
|
|
return;
|
|
}
|
|
if (!text.ok) {
|
|
host.replaceChildren(
|
|
el("div", {
|
|
className: "m01-logic__reasons",
|
|
children: [el("strong", { text: tx("Calc_Stopped") }), el("div", { text: text.reason })],
|
|
}),
|
|
);
|
|
return;
|
|
}
|
|
const heads = el("div", {
|
|
className: "m01lab-fx__head",
|
|
children: [
|
|
el("strong", { text: tl("Formula_Left") }),
|
|
el("strong", { text: tl("Formula_Right") }),
|
|
],
|
|
});
|
|
const rows: HTMLElement[] = [];
|
|
const calculated = text.계산됨 !== false; // [계산] 뒤 응답은 표식이 안 붙음 — 미리보기만 false
|
|
const light = (pair: string, on: boolean): void =>
|
|
rows.forEach((r) => r.dataset.pair === pair && r.classList.toggle("is-on", on));
|
|
const blocks = text.groups.flatMap((g) => [
|
|
el("h4", { className: "m01lab__group m01lab-fx__wide", text: g.비목 }),
|
|
...g.줄.map((line, i) => {
|
|
const pair = String(line.짝 ?? `${g.비목}${i}`);
|
|
const row = el("div", {
|
|
className: `m01lab-fx__row m01lab__text${line.까닭 ? " m01lab__text--off" : ""}`,
|
|
attrs: { "data-text-line": line.이름, "data-pair": pair },
|
|
children: [
|
|
el("div", { className: "m01lab-fx__cell", text: line.이름글 ?? line.이름 }),
|
|
el("div", {
|
|
className: "m01lab-fx__cell",
|
|
children: calculated
|
|
? [
|
|
el("div", { text: `${line.이름} : ${line.글}` }),
|
|
...(line.까닭
|
|
? [el("div", { className: "m01-logic__muted", text: line.까닭 })]
|
|
: []),
|
|
]
|
|
: [el("div", { className: "m01-logic__muted", text: tl("Formula_Slot") })],
|
|
}),
|
|
],
|
|
});
|
|
row.addEventListener("mouseenter", () => light(pair, true));
|
|
row.addEventListener("mouseleave", () => light(pair, false));
|
|
row.addEventListener("click", () => {
|
|
const pinned = row.classList.toggle("is-pin");
|
|
rows.forEach((r) => r !== row && r.classList.remove("is-pin"));
|
|
if (!pinned) row.classList.remove("is-pin");
|
|
});
|
|
rows.push(row);
|
|
return row;
|
|
}),
|
|
el("p", {
|
|
className: "m01lab__subtotal-text m01lab-fx__wide",
|
|
attrs: { "data-text-sub": String(g.소계) },
|
|
text: `${g.비목} ${tl("Subtotal")} ${formatMoney(g.소계)}${g.끝수 ? ` (${g.끝수})` : ""}`,
|
|
}),
|
|
]);
|
|
blocks.unshift(heads);
|
|
blocks.push(
|
|
el("p", {
|
|
className: "m01lab__total m01lab-fx__wide",
|
|
attrs: { "data-text-sum": String(text.계) },
|
|
text: `${tl("Total")} ${formatMoney(text.계)}`,
|
|
}),
|
|
);
|
|
host.replaceChildren(el("div", { className: "m01lab-fx", children: blocks }));
|
|
}
|