fix(M01): 로직 개선 시험 — 자체 로직 상세의 호표 중복 정리 · 고치는 중 커서 유지

- 자체 로직은 고치는 자리를 편집기 하나로 두고 읽기용 묶음 소계 표는 접어 둠
- 자동 재계산으로 다시 그려도 고치던 칸에 커서와 선택 위치가 남음

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_016WRFJmmhPi4exAzHgPZHMT
This commit is contained in:
2026-09-21 21:13:10 +09:00
co-authored by Claude Sonnet 5
parent 9407221490
commit f7ecefa2fc
2 changed files with 27 additions and 9 deletions
@@ -309,19 +309,24 @@ function hoBox(ctx: DetailContext): HTMLElement {
}
const all = entries(ctx);
const calculated = ctx.lines !== null;
const blocks = GROUPS.flatMap((group) => {
const tables = GROUPS.flatMap((group) => {
const mine = all.filter((e) => e.group === group);
return group === "other" && !mine.length ? [] : groupTable(group, mine, calculated); // 그 밖은 있을 때만
});
const total = all.reduce((s, e) => s + (e.amount ?? 0), 0);
blocks.push(
el("p", {
className: "m01lab__total",
attrs: { "data-sum": calculated ? String(total) : "" },
text: `${tl("Total")} ${calculated ? formatNumber(total) : "—"}`,
}),
);
if (isOwnLogic(ctx.row)) blocks.push(ownEditor(ctx));
const totalLine = el("p", {
className: "m01lab__total",
attrs: { "data-sum": calculated ? String(total) : "" },
text: `${tl("Total")} ${calculated ? formatNumber(total) : ""}`,
});
// 자체 로직 — 고치는 자리는 편집기 하나 · 읽기용 묶음 표는 접어 둠(펼치면 소계가 보임)
const fold = el("details", {
className: "m01lab__fold",
attrs: ctx.ownHost.dataset.fold ? { open: "" } : {},
children: [el("summary", { text: tl("Ho_Fold") }), ...tables],
});
fold.addEventListener("toggle", () => (ctx.ownHost.dataset.fold = fold.open ? "1" : ""));
const blocks = isOwnLogic(ctx.row) ? [ownEditor(ctx), fold, totalLine] : [...tables, totalLine];
const hint = calculated ? tl("Click_Hint") : `${tl("Click_Hint")} · ${tl("Calc_First")}`;
return section(tl("Ho_Title"), blocks, hint);
}
@@ -330,10 +335,22 @@ function hoBox(ctx: DetailContext): HTMLElement {
export function buildLabDetail(host: HTMLElement, ctx: DetailContext): void {
const formula = el("div", { className: "m01lab__formula" });
buildFormula(formula, { text: ctx.text });
// 다시 그리면 포커스가 빠짐 — 고치던 칸(편집기 · 기본정보)을 기억해 되돌림
const active = document.activeElement;
const kept =
active instanceof HTMLInputElement || active instanceof HTMLTextAreaElement
? { node: active, from: active.selectionStart, to: active.selectionEnd }
: null;
host.replaceChildren(
infoBox(ctx),
hoBox(ctx),
section(tl("Formula_Title"), [formula]),
section(tl("Calc_Title"), [ctx.calcHost]),
);
const own = kept?.node.dataset.own; // 기본정보 칸은 새로 만들어지므로 이름표로 다시 찾음
const back = own ? host.querySelector<HTMLInputElement>(`[data-own="${own}"]`) : kept?.node;
if (kept && back && (own || ctx.ownHost.contains(back))) {
back.focus();
if (kept.from !== null) back.setSelectionRange(kept.from, kept.to);
}
}
@@ -19,6 +19,7 @@ const TEXT = {
"고치기 (호표 · 중간 값 · 덧줄 · 끝수)",
"Edit (lines · middles · extras · rounding)",
],
Ho_Fold: ["묶음 소계 표 (읽기용 — 고치기는 위 편집기)", "Group subtotals (read-only)"],
Ho_Title: ["일위대가 호표", "Unit-cost table"],
Formula_Title: ["텍스트 수식", "Text formula"],
Formula_Slot: ["시험 계산을 누르면 식이 글로 나옴", "Run the trial calc to see the formula"],