feat(M02): 마스터 템플릿 페이지 틀 — 대시보드 단추 · 길 · 좌측 패널(층·목록·새로·본떠·지우기·프로젝트 단추 다섯) · 메인 자리 · 공용 모달 (PLAN 10-1)

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0148XFUpPfpiuTjxF1EK9c95
This commit is contained in:
2026-09-25 09:36:07 +09:00
co-authored by Claude Sonnet 5
parent a0787a7859
commit 483c6fd7c8
14 changed files with 971 additions and 22 deletions
@@ -9,6 +9,7 @@
* ========================================================================== */
import { createButton, el } from "@ui/ui_template_elements";
import { openModal } from "@ui/ui_template_modal";
import {
searchElements,
type ElementBrief,
@@ -207,29 +208,13 @@ function priceView(ref: string, brief: ElementBrief): HTMLElement {
/** 모달 겉틀(배경·닫기·포커스) — 몸은 `mount` 이 채움 */
function openDialog(title: string, mount: (body: HTMLElement) => void): void {
const body = el("div", { className: "m01lab__modal-body" });
const close = (): void => backdrop.remove();
const dialog = el("div", {
className: "m01-logic__pick m01lab__modal",
attrs: { role: "dialog", "aria-label": title },
children: [
el("div", {
className: "m01lab__modal-top",
children: [
el("h3", { text: title }),
createButton({ label: tl("Modal_Close"), variant: "ghost", onClick: close }),
],
}),
body,
],
openModal({
title,
closeLabel: tl("Modal_Close"),
mount,
dialogClass: "m01-logic__pick m01lab__modal",
backdropClass: "m01-logic__backdrop",
});
const backdrop = el("div", { className: "m01-logic__backdrop", children: [dialog] });
backdrop.addEventListener("click", (ev) => ev.target === backdrop && close());
backdrop.addEventListener("keydown", (ev) => ev.key === "Escape" && close());
mount(body);
document.body.append(backdrop);
dialog.tabIndex = -1;
dialog.focus();
}
const muted = (text: string): HTMLElement => el("p", { className: "m01-logic__muted", text });