/* ============================================================================= * ui_template_ui_kit_frames.ts * 부품 모음 화면의 틀(552-4) 견본 — `ui_template_ui_kit_parts.ts` 의 frame 줄이 부름. * 견본 = 진짜 부품 함수 · 클래스로 그림 · 화면에 붙는(fixed) 틀은 칸 안에 놓이게 자리만 인라인으로 풂. * ========================================================================== */ import { createIconButton } from "./ui_template_button"; import { createButton } from "./ui_template_elements"; import { createFloatPanel } from "./ui_template_float_panel"; import { createFormGrid, createPageFrame } from "./ui_template_general_layout"; import { createInputField } from "./ui_template_input"; import { createListRow } from "./ui_template_list_row"; import { createMapTools, mapToolButton } from "./ui_template_map_tools"; import { createWorkflowPanelHandle } from "./ui_template_overlay"; import { createPanelHead } from "./ui_template_panel_head"; import { createPublicFrame } from "./ui_template_public_layout"; import { createSidePanels } from "./ui_template_side_panels"; import { createToolbar } from "./ui_template_toolbar"; 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(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", "몸 — 표 · 카드가 들어감")); // 입력 칸 2열 · 끝 단추 줄(557-2 · D01 · E01 등록 양식) const actions = div("ui-general-block__actions ui-general-block__actions--end"); actions.append( createButton({ label: "취소", variant: "ghost", onClick: noop }), createButton({ label: "등록", onClick: noop }), ); f.body.append( createFormGrid([ createInputField({ label: "프로젝트 이름" }).root, createInputField({ label: "지역" }).root, ]), actions, ); 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(".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; } function floatPanel(): HTMLElement { const place = (root: HTMLElement): HTMLElement => { root.style.position = "relative"; return root; }; const one = createFloatPanel({ title: "측정", onClose: noop, draggable: true, width: 220 }); one.body.append("거리 124.5 m · 기울기 8.2 %"); const pop = createFloatPanel({ popover: true, width: 160 }); pop.body.append("팝오버 — 머리 없음"); return wrap(place(one.root), place(pop.root)); } function drawer(): HTMLElement { const row = div("ui-side-panels-row"); row.style.cssText = "height:220px;width:100%"; const main = div("", "본문"); main.style.padding = "12px"; const body = (text: string): HTMLElement => div("", text); const sp = createSidePanels({ prefKey: "ui-kit-demo", follow: false, defaultWidth: 280, panels: [ { id: "info", title: "상세", body: body("고른 줄의 값") }, { id: "set", title: "설정", body: body("보기 설정") }, ], }); row.append(main, sp.root); return row; } function toolbar(): HTMLElement { const box = div(""); box.style.cssText = "display:flex;flex-direction:column;gap:12px;width:100%"; const items = () => [ createIconButton({ icon: "search", title: "찾기", onClick: noop }), createIconButton({ icon: "adjustments", title: "보기", onClick: noop }), "divider" as const, createButton({ label: "더함", variant: "ghost", onClick: noop }), "spacer" as const, createButton({ label: "저장", onClick: noop }), ]; box.append( createToolbar({ items: items(), size: 24 }), createToolbar({ items: items() }), createToolbar({ items: items().slice(0, 3), size: 32, glass: true }), ); return box; } function mapTools(): HTMLElement { const tools = createMapTools({ label: "평면 도구", hint: { lines: ["휠 = 확대", "가운데 끌기 = 이동", "Ctrl+휠 = 돌리기", "Esc = 그리기 끝"] }, groups: [ ["undo", "redo"], ["view3d", "rotate-ccw", "rotate-cw"], [ "contour-minor", "stations", mapToolButton("slopes", { pressed: true }), "footprint", "measure", ], ["station-snap"], ], }); tools.button("contour-minor")!.disabled = true; return tools.root; } /** 열쇠 → 견본(상태별 · null = 그 상태 없음) */ export const FRAME_DEMOS: Record HTMLElement | null> = { "work-frame": (s) => (s === "default" ? workFrame() : null), "page-frame": (s) => (s === "default" ? pageFrame() : null), "public-frame": (s) => (s === "default" ? publicFrame() : null), "float-panel": (s) => (s === "default" ? floatPanel() : null), drawer: (s) => (s === "default" ? drawer() : null), toolbar: (s) => (s === "default" ? toolbar() : null), "map-tools": (s) => (s === "default" ? mapTools() : null), steps, "panel-head": (s) => (s === "default" ? panelHead() : null), "list-row": listRow, };