/* ============================================================================= * ui_template_button.ts * 부품 「단추」 · 「아이콘 단추」 · 크기 여덟 단(552-2 · 부품 모음 `#/b01-account?view=ui-kit`) * * 꼴 = 단추: 주(filled) · 보조(ghost) · 고스트 글(pill) · 위험(danger) · 위험 고스트(danger-ghost) · 유리(glass · 그림 위) · 글 링크(link) * 아이콘 단추: 고스트(ghost · 기본) · 선(outline) · 채움(filled) · 켜짐 = aria-pressed * 크기 = `data-size="18|20|22|24|28|32|36|42"`(높이 px) — 단추 · 아이콘 단추 · 입력 · 고르기 · 분할 · 탭 같은 값 * 옛 `size: "sm"` = 18 · 크기를 안 주면 data-size 없이 옛 꼴(높이 32 · 아이콘 붙으면 34) 그대로 * 실측 높이 = 단(꼴 · 페이지 글 크기 · 왼쪽 패널 양식과 상관없이) · `hidden` 이면 늘 숨음 * 상태 = 마우스 `:hover` · 눌림 `:active` · 막힘 `disabled` — CSS 는 `[data-state]` 로도 받아 부품 모음이 굳혀 보임 * 쓰기 = `createButton({ label: "저장", size: 24, onClick })` · `createIconButton({ icon: "settings", title: "설정", size: 24 })` * 글자 기호 단추(▲ ▼ ✕ ?)는 `glyph: "▲"` — 시험이 글자로 잡음 * 대신 = 페이지마다 만든 16 ~ 26 짜리 작은 단추 · `b01-shell__icon-btn` · `b01-pg-menu__btn` · `b05-profile__zoom-btn` …(목록 4장) * 클래스 = `ui-btn ui-btn--<꼴>`(+ 옛 `ui-btn--sm`) · `ui-btn__icon` · `ui-btn__label` · `ui-icon-btn ui-icon-btn--<꼴>` — 시험 · 가이드 촬영이 잡음 * ========================================================================== */ import { el } from "./ui_template_elements_base"; import { icon, type IconName } from "./ui_template_icons"; import "./ui_template_button.css"; /** 크기 여덟 단(높이 px · 요구 377 · 378) */ export const UI_SIZES = [18, 20, 22, 24, 28, 32, 36, 42] as const; export type UiSize = (typeof UI_SIZES)[number]; /** 단마다 아이콘 크기(`ui_template_button.css` 의 `--ui-size-icon` 과 같은 값) */ const ICON_PX: Record = { 18: 12, 20: 14, 22: 14, 24: 14, 28: 16, 32: 16, 36: 18, 42: 20, }; /** 요소에 크기 단을 붙임 */ export function setUiSize(node: T, size: UiSize): T { node.dataset.size = String(size); return node; } export type ButtonVariant = "filled" | "ghost" | "pill" | "danger" | "danger-ghost" | "glass" | "link"; /** 크기 — 단(18 ~ 42) · 옛 "sm" = 18 · 옛 "md" = 단 없이 옛 꼴 */ export type ButtonSize = "md" | "sm" | UiSize; export interface ButtonOptions { /** 표시 텍스트 (i18n 결과 문자열을 전달) */ label: string; variant?: ButtonVariant; size?: ButtonSize; /** 이벤트 핸들러 (frontend.md §4: on[페이지]_[기능]_[액션] 명명) */ onClick?: (ev: MouseEvent) => void; disabled?: boolean; type?: "button" | "submit" | "reset"; /** 앞쪽 아이콘 요소 (선택) — 이름이면 단 크기 아이콘 */ iconStart?: HTMLElement | IconName; } export function createButton(opts: ButtonOptions): HTMLButtonElement { const variant = opts.variant ?? "filled"; const size: UiSize | null = opts.size === "sm" ? 18 : opts.size === "md" || opts.size === undefined ? null : opts.size; const btn = el("button", { className: `ui-btn ui-btn--${variant}${opts.size === "sm" ? " ui-btn--sm" : ""}`, attrs: { type: opts.type ?? "button" }, }); if (size) setUiSize(btn, size); if (opts.iconStart) { const mark = typeof opts.iconStart === "string" ? icon(opts.iconStart, size ? ICON_PX[size] : 16) : opts.iconStart; mark.classList.add("ui-btn__icon"); btn.append(mark); } btn.append(el("span", { className: "ui-btn__label", text: opts.label })); if (opts.disabled) btn.disabled = true; if (opts.onClick) btn.addEventListener("click", opts.onClick); return btn; } export type IconButtonVariant = "ghost" | "outline" | "filled"; export interface IconButtonOptions { /** 아이콘 이름 — 없으면 glyph */ icon?: IconName; /** 글자 기호(▲ ▼ ✕ ? …) — 시험이 글자로 잡는 단추 */ glyph?: string; /** 마우스 글 · 보조기기 이름(필수) */ title: string; variant?: IconButtonVariant; /** 기본 32 */ size?: UiSize; /** 켜고 끄는 단추면 지금 값(aria-pressed) */ pressed?: boolean; disabled?: boolean; onClick?: (ev: MouseEvent) => void; } export function createIconButton(opts: IconButtonOptions): HTMLButtonElement { const size = opts.size ?? 32; const btn = el("button", { className: `ui-icon-btn ui-icon-btn--${opts.variant ?? "ghost"}`, attrs: { type: "button", title: opts.title, "aria-label": opts.title }, }); setUiSize(btn, size); btn.append(opts.icon ? icon(opts.icon, ICON_PX[size]) : (opts.glyph ?? "")); if (opts.pressed !== undefined) btn.setAttribute("aria-pressed", String(opts.pressed)); if (opts.disabled) btn.disabled = true; if (opts.onClick) btn.addEventListener("click", opts.onClick); return btn; }