feat(M01): 로직 개선 시험 줄 모달 — 한 줄 풀이와 알약 단추(누른 자료 하나만 펼침·접힘) (PLAN 5-3)

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01PAdA5ThqmtVSsbzjusk1cJ
This commit is contained in:
2026-09-21 23:34:17 +09:00
co-authored by Claude Sonnet 5
parent 9d6e60f08c
commit cd2e8df39a
4 changed files with 138 additions and 27 deletions
@@ -0,0 +1,44 @@
/* =============================================================================
* M01_MasterData_UI_LogicLab_Pill.ts
* 줄 모달의 알약 단추 — 쓰인 자료를 알약으로만 늘어놓고, 누른 것 하나만 펼침(다시 누르면 접힘)
* ========================================================================== */
import { el } from "@ui/ui_template_elements";
export interface Pill {
/** 알약 글 — 보기 「표 QF000421」 · 「인력 석공」 */
label: string;
/** 펼칠 때 그림 — 표처럼 읽어 와야 하면 Promise */
view: () => HTMLElement | Promise<HTMLElement>;
}
export function pillBar(pills: Pill[]): HTMLElement {
const stage = el("div", { className: "m01lab__pill-stage" });
const buttons: HTMLButtonElement[] = [];
let open = -1;
let turn = 0;
const show = async (i: number): Promise<void> => {
const mine = ++turn;
open = open === i ? -1 : i;
buttons.forEach((b, k) => {
b.setAttribute("aria-expanded", String(k === open));
b.classList.toggle("is-open", k === open);
});
if (open < 0) return stage.replaceChildren();
const view = await pills[open].view();
if (mine === turn) stage.replaceChildren(view); // 빨리 눌러도 마지막 것만
};
pills.forEach((p, i) => {
const b = el("button", {
className: "m01lab__pill",
text: p.label,
attrs: { type: "button", "aria-expanded": "false", "data-pill": p.label },
});
b.addEventListener("click", () => void show(i));
buttons.push(b);
});
return el("div", {
className: "m01lab__pills",
children: [el("div", { className: "m01lab__pill-row", children: buttons }), stage],
});
}