Files
Aislo/ui_template/ui_template_ui_kit_frames.ts
T
eomsangdonandClaude Opus 5.5 f941ddc3f5 feat(ui-kit): 틀 부품 여섯 — 작업 화면 틀 겉 · 일반 화면 틀 · 공개 화면 틀 · 단계 줄 · 패널 머리 · 목록 줄 (PLAN 552-4)
- 작업 화면 틀(overlay): 왼쪽 패널 = 흰 판 + 1 px 선 · 그림자 없음 · 덮개 = --color-scrim · 손잡이 강조색 그대로 · 자리 · 폭 그대로
- 일반 화면 틀 createPageFrame(대시보드 세부 꼴) · 공개 화면 틀 createPublicFrame(A01 hero · section 꼴)
- 단계 줄 data-size 28 · 40 · 상태 = 공통 값 · 패널 머리 createPanelHead · 목록 줄 createListRow(옛 .ui-sidebar-row 겉 함께)
- 부품 모음: 떠 있는 판 · 서랍 · 도구줄 · 단계 줄 · 패널 머리 · 목록 줄 여섯 줄을 frame 묶음으로 옮김 · 견본 = ui_template_ui_kit_frames.ts
- 지킴 시험 test_552_4_frames.py · B02~B09 · D · E · M02 전후 자리 수치 같음

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-26 21:28:17 +09:00

162 lines
6.7 KiB
TypeScript

/* =============================================================================
* ui_template_ui_kit_frames.ts
* 부품 모음 화면의 틀(552-4) 견본 — `ui_template_ui_kit_parts.ts` 의 frame 줄이 부름.
* 견본 = 진짜 부품 함수 · 클래스로 그림 · 화면에 붙는(fixed) 틀은 칸 안에 놓이게 자리만 인라인으로 풂.
* ========================================================================== */
import { createButton } from "./ui_template_elements";
import { createPageFrame } from "./ui_template_general_layout";
import { createListRow } from "./ui_template_list_row";
import { createWorkflowPanelHandle } from "./ui_template_overlay";
import { createPanelHead } from "./ui_template_panel_head";
import { createPublicFrame } from "./ui_template_public_layout";
import type { KitState } from "./ui_template_ui_kit_parts";
import { createStepBar } from "./ui_template_workflow_layout";
const noop = (): void => {};
function div(className: string, text?: string): HTMLDivElement {
const n = document.createElement("div");
n.className = className;
if (text !== undefined) n.textContent = text;
return n;
}
const wrap = (...children: HTMLElement[]): HTMLElement => {
const box = div("ui-kit__demo-row");
box.append(...children);
return box;
};
/** 마우스 · 눌림 = data-state · 막힘 = disabled */
function mark<T extends HTMLElement>(node: T, state: KitState): T {
if (state === "disabled") {
if (node instanceof HTMLButtonElement) node.disabled = true;
node.setAttribute("aria-disabled", "true");
} else if (state !== "default") node.dataset.state = state;
return node;
}
/** 아래 단추 줄 — [?][<][>] + 주 단추 */
function sidebarActions(): HTMLElement {
const bar = div("ui-sidebar-actions");
const help = createButton({ label: "?", variant: "ghost", onClick: noop });
help.classList.add("ui-help-btn");
const prev = createButton({ label: "<", variant: "ghost", onClick: noop });
const next = createButton({ label: ">", variant: "ghost", onClick: noop });
prev.classList.add("ui-step-nav-btn");
next.classList.add("ui-step-nav-btn");
bar.append(help, prev, next, createButton({ label: "저장", onClick: noop }));
return bar;
}
/** 작업 화면 틀 — 왼쪽 패널(제목 줄 · 카드 · 아래 단추 줄 · 손잡이) + 본문 자리 */
function workFrame(): HTMLElement {
const panel = document.createElement("aside");
panel.className = "ui-workflow-overlay__panel ui-workflow-overlay__panel--title";
panel.style.cssText = "position:relative;top:auto;bottom:auto;width:280px;height:260px";
const header = div("ui-workflow-overlay__header");
const title = document.createElement("h2");
title.className = "ui-workflow-overlay__title";
title.textContent = "횡단설계";
const project = document.createElement("span");
project.className = "ui-workflow-overlay__project";
project.textContent = "검증 프로젝트";
header.append(title, project);
const handle = createWorkflowPanelHandle("side");
handle.setOpen(true);
const body = div("ui-workflow-overlay__body");
body.style.cssText = "display:flex;flex-direction:column;gap:8px;padding-top:8px";
const card = div("ui-sidebar-card ui-sidebar-section");
card.append(
createPanelHead({ title: "구조물 배치", sub: "측점 3 곳" }).root,
createListRow({ label: "No.0+020 배수관", active: true, onClick: noop }),
createListRow({ label: "No.0+140 옹벽", onClick: noop }),
);
const fill = div("");
fill.style.flex = "1";
body.append(card, fill, sidebarActions());
panel.append(header, handle.root, body);
const main = div("ui-workflow-layout__main", "본문(지도 · 그림 · 표)");
main.style.cssText = "min-height:260px;padding:12px;border:1px dashed var(--color-border)";
const box = div("ui-kit__frame-demo");
box.style.cssText = "display:grid;grid-template-columns:280px 1fr;width:100%";
box.append(panel, main);
return box;
}
function pageFrame(): HTMLElement {
const f = createPageFrame(null, "구성원", "회사 사람 · 역할 · 초대");
f.setAction(createButton({ label: "초대", onClick: noop }));
f.body.append(div("ui-general-block__empty", "몸 — 표 · 카드가 들어감"));
return f.root;
}
function publicFrame(): HTMLElement {
const f = createPublicFrame({
title: "임도 설계 자동화",
lead: "지형 자료로 노선 · 종단 · 횡단 · 수량을 한 흐름에.",
actions: [
createButton({ label: "시작하기", onClick: noop }),
createButton({ label: "더 알아보기", variant: "ghost", onClick: noop }),
],
});
f.addSection("기능").append(div("ui-general-block__empty", "구역 몸"));
f.root.style.width = "100%";
return f.root;
}
const STEP_NAMES = ["파일", "전처리", "노선", "횡단"];
function steps(state: KitState): HTMLElement | null {
if (state === "active" || state === "error") return null;
const bar = createStepBar(STEP_NAMES, 2, { compact: true });
const target = bar.querySelectorAll<HTMLButtonElement>(".ui-workflow-layout__step")[0];
mark(target, state);
if (state !== "default") return bar;
const list = createStepBar(STEP_NAMES, 2, {
orientation: "vertical",
icons: ["1", "2", "3", "4"],
});
list.style.width = "180px";
return wrap(bar, list);
}
function panelHead(): HTMLElement {
const box = div("");
box.style.cssText = "display:flex;flex-direction:column;gap:12px;width:320px";
box.append(
createPanelHead({ title: "배수관", sub: "3 개", divider: true }).root,
createPanelHead({
title: "표준 횡단면",
actions: [createButton({ label: "더함", variant: "ghost", onClick: noop })],
}).root,
);
return box;
}
function listRow(state: KitState): HTMLElement | null {
if (state === "error") return null;
const box = div("");
box.style.cssText = "display:flex;flex-direction:column;gap:2px;width:240px";
const one = mark(createListRow({ label: "B1 배수관", sub: "No.0+020", onClick: noop }), state);
box.append(one);
if (state === "default")
box.append(
createListRow({ label: "B2 옹벽", active: true, no: 2, size: 28, onClick: noop }),
createListRow({ label: "B3 측구", grip: true, trailing: "12 m", size: 36, onClick: noop }),
createListRow({ label: "B4 비탈", stripe: "var(--color-success)", onClick: noop }),
);
return box;
}
/** 열쇠 → 견본(상태별 · null = 그 상태 없음) */
export const FRAME_DEMOS: Record<string, (state: KitState) => HTMLElement | null> = {
"work-frame": (s) => (s === "default" ? workFrame() : null),
"page-frame": (s) => (s === "default" ? pageFrame() : null),
"public-frame": (s) => (s === "default" ? publicFrame() : null),
steps,
"panel-head": (s) => (s === "default" ? panelHead() : null),
"list-row": listRow,
};