/* ============================================================================= * B01_Dashboard_UI_PageKit.ts * 대시보드 세부 페이지 공용 조각(549) — 페이지 머리 · 표 · 줄 끝 ⋯ · 배지 · 탭 · 빈 상태. * 색 · 간격 = 셸 변수(`--b01-*`) 만 · 모양은 `B01_Dashboard_UI_Style_Pages.css`. * 아이콘 = `ui_template_icons`(552-2 에 여섯을 옮김). * ========================================================================== */ import { createButton, createTag } from "@ui/ui_template_elements"; import { createEmpty } from "@ui/ui_template_empty"; import { icon, type IconName } from "@ui/ui_template_icons"; import type { BadgeTone } from "./logic/B01_Dashboard_Logic_Pages"; import "./B01_Dashboard_UI_Style_Pages.css"; /** 아이콘 여섯 — 552-2 에 `ui_template_icons` 로 옮김(같은 Tabler 선) · 이름 · 기본 크기 16 그대로 */ export type KitIconName = Extract< IconName, "dots" | "plus" | "pencil" | "upload" | "mail" | "inbox" >; export function kitIcon(name: KitIconName, size = 16): HTMLSpanElement { return icon(name, size); } function node( tag: K, className: string, text?: string, ): HTMLElementTagNameMap[K] { const n = document.createElement(tag); n.className = className; if (text !== undefined) n.textContent = text; return n; } /** 배지 — 한글 글 + 색 */ export function badge(text: string, tone: BadgeTone = "neutral"): HTMLElement { const tag = createTag(text, tone); tag.classList.add("b01-pg-badge"); return tag; } /** 주 단추 — 오른쪽 위 하나 · 앞 아이콘 */ export function primaryButton(label: string, onClick: () => void, name: KitIconName = "plus") { return createButton({ label, onClick, iconStart: kitIcon(name) }); } export interface PageFrame { root: HTMLElement; /** 머리 아래 몸통 */ body: HTMLElement; /** 머리 오른쪽 주 단추 자리 */ setAction: (action: HTMLElement | null) => void; } /** 페이지 제목 + 한 줄 설명 + 오른쪽 위 주 단추 */ export function pageFrame(host: HTMLElement, title: string, desc: string): PageFrame { const root = node("div", "b01-pg"); const head = node("header", "b01-pg__head"); const text = node("div", "b01-pg__title-wrap"); text.append(node("h2", "b01-pg__title", title), node("p", "b01-pg__desc", desc)); const slot = node("div", "b01-pg__action"); head.append(text, slot); const body = node("div", "b01-pg__body"); root.append(head, body); host.append(root); return { root, body, setAction: (action) => slot.replaceChildren(...(action ? [action] : [])), }; } // 부품(552-3) — 표 · 줄 끝 ⋯ · 설정 줄 = ui_template 부품 · 부름 꼴 그대로 export { createRowMenu as rowMenu, type RowMenuItem } from "@ui/ui_template_context_menu"; export { createTable as dataTable, tableRow, tableShell, type Cell, type Column, } from "@ui/ui_template_table"; export { settingCard, settingRow, valueText } from "@ui/ui_template_setting_row"; export interface EmptyOptions { icon: IconName | KitIconName; text: string; hint?: string; action?: HTMLElement; /** 가이드 링크 — 누르면 */ onGuide?: () => void; } /** 빈 상태 — 부품 「빈 상태」(ui_template_empty.ts) */ export function emptyState(opts: EmptyOptions): HTMLElement { return createEmpty(opts); } export interface TabsHandle { root: HTMLElement; setBadge: (key: string, count: number) => void; } /** 탭 줄 — 눌러 바꿈 · 탭 오른쪽 수 배지(0 이면 숨김) */ export function tabBar( tabs: { key: string; label: string }[], initial: string, onChange: (key: string) => void, ): TabsHandle { const root = node("div", "b01-pg-tabs"); root.setAttribute("role", "tablist"); const badges = new Map(); const buttons = new Map(); const select = (key: string): void => { for (const [k, b] of buttons) { b.classList.toggle("is-active", k === key); b.setAttribute("aria-selected", String(k === key)); } }; for (const tab of tabs) { const b = node("button", "b01-pg-tabs__tab"); b.type = "button"; b.setAttribute("role", "tab"); b.dataset.tab = tab.key; const count = node("span", "b01-pg-tabs__count"); count.hidden = true; b.append(node("span", "", tab.label), count); b.addEventListener("click", () => { select(tab.key); onChange(tab.key); }); buttons.set(tab.key, b); badges.set(tab.key, count); root.append(b); } select(initial); return { root, setBadge: (key, n) => { const c = badges.get(key); if (!c) return; c.textContent = String(n); c.hidden = n <= 0; }, }; } /** 줄 첫 칸 — 이름 + (작은 글) 보조 */ export function nameCell(name: string, sub?: string | null): HTMLElement { const cell = node("div", "b01-pg-name"); cell.append(node("span", "b01-pg-name__main", name)); if (sub) cell.append(node("span", "b01-pg-name__sub", sub)); return cell; }