feat(M01): 단가산출 막대 시험판 넷을 「시험판 ▾」 목록 하나로 묶음 · CostFlow 원가 칸에 입력 값도 넘김

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_016PSXsvyDxjNmufteN5bqmt
This commit is contained in:
2026-10-01 16:49:02 +09:00
co-authored by Claude Opus 5.5
parent 2be767d186
commit 19fed5b90f
2 changed files with 64 additions and 10 deletions
+55 -9
View File
@@ -7,7 +7,7 @@
* 409 = 그 사이 파일이 바뀜 · 422 = 검사 걸림(아무것도 안 씀) — 까닭은 서버 글 그대로 보임.
* ========================================================================== */
import { t as L } from "@ui/ui_template_locale";
import { currentLanguageIndex, t as L } from "@ui/ui_template_locale";
import {
createButton,
el,
@@ -75,6 +75,50 @@ interface Opened extends Draft {
const cacheKey = "m01_logic_drafts_lab";
const ownHost = (): HTMLElement => el("div", { className: "m01lab__own" });
/** 시험판 묶음 — 「시험판 ▾」 단추 하나 · 누르면 시험판 단추들이 목록으로(바깥 · Esc · 고르면 닫힘) */
function trialMenu(items: HTMLElement[]): HTMLElement {
const list = el("div", { attrs: { role: "menu" }, children: items });
Object.assign(list.style, {
position: "absolute",
top: "calc(100% + 4px)",
left: "0",
zIndex: "20",
display: "none",
flexDirection: "column",
gap: "var(--spacing-4)",
padding: "var(--spacing-8)",
border: "1px solid var(--color-border)",
borderRadius: "var(--radius-lg)",
background: "var(--color-surface-raised)",
boxShadow: "var(--shadow-lg)",
whiteSpace: "nowrap",
});
const shut = (): void => {
list.style.display = "none";
button.setAttribute("aria-expanded", "false");
document.removeEventListener("click", outside, true);
};
const outside = (ev: Event): void => {
if (!wrap.contains(ev.target as Node)) shut();
};
const button = createButton({
label: ["시험판 ▾", "Trials ▾"][currentLanguageIndex === 1 ? 1 : 0],
variant: "ghost",
onClick: () => {
if (list.style.display !== "none") return shut();
list.style.display = "flex";
button.setAttribute("aria-expanded", "true");
document.addEventListener("click", outside, true);
},
});
button.setAttribute("aria-haspopup", "menu");
list.addEventListener("click", () => shut()); // 고른 시험판은 제 단추가 엶
const wrap = el("div", { children: [button, list] });
Object.assign(wrap.style, { position: "relative", display: "inline-flex" });
wrap.addEventListener("keydown", (ev) => ev.key === "Escape" && shut());
return wrap;
}
const clone = <T>(v: T): T => JSON.parse(JSON.stringify(v)) as T;
function loadDrafts(): Record<string, Draft> {
@@ -406,14 +450,16 @@ export async function mountM01LogicLab(
el("h2", { text: tx("Title") }),
pending,
createButton({ label: tn("Open"), variant: "filled", onClick: startNew }),
// 쉬운 단가산출 시험 길 — 폴더째 지우면 이 두 줄도 지움
createEasyCalcButton(),
// 로직 만들기 시험 길 — 폴더째 지우면 이 두 줄도 지움
createLogicBuilderButton(),
// 시트 로직 시험 길 — 폴더째 지우면 이 두 줄도 지움
createSheetLogicButton(),
// 단가산출 다이어그램 시험 길 — 폴더째 지우면 이 두 줄도 지움
createCostFlowButton(),
trialMenu([
// 쉬운 단가산출 시험 길 — 폴더째 지우면 이 두 줄도 지움
createEasyCalcButton(),
// 로직 만들기 시험 길 — 폴더째 지우면 이 두 줄도 지움
createLogicBuilderButton(),
// 시트 로직 시험 길 — 폴더째 지우면 이 두 줄도 지움
createSheetLogicButton(),
// 단가산출 다이어그램 시험 길 — 폴더째 지우면 이 두 줄도 지움
createCostFlowButton(),
]),
createButton({ label: tx("Bar_Delete"), variant: "danger", onClick: () => void onDelete() }),
discardButton,
saveButton,
+9 -1
View File
@@ -447,7 +447,15 @@ function openCostFlow(): void {
deleteButton.hidden = !lg.key;
M.diagram.renderDiagram(diagramHost, lg, diagramOn, { values: result.ports, selected });
drawPanel();
M.summary.renderSummary(summaryHost, lg, result, { change: edit });
// 「고칠 것」 검사가 받은 입력 값으로 하도록 값도 넘김 — Summary 가 opts.values 를 받으면 쓰임
// ponytail: 지금 Summary 는 checkLogic(lg) 을 값 없이 불러 「값이 아직 없음」 이 남음(sub5 고침 뒤 이 꼴 그대로 둠)
(M.summary.renderSummary as (...args: unknown[]) => void)(
summaryHost,
lg,
result,
{ change: edit },
{ values },
);
note.textContent = [...notes, ...result.errors].slice(0, 4).join("\n");
};