Files
Aislo/ui_template/ui_template_banner.ts
T
eomsangdonandClaude Opus 5.5 7e584c1e7b feat(ui-kit): 묶음 부품 열 — 카드 · 접이 카드 · 빈 상태 · 알림 · 띠 알림 · 숫자 카드 · 값 목록 · 진행 막대 · 작은 그래프 · 범례 (PLAN 552-3)
- 새 부품 파일: ui_template_card · empty · banner · stat · kv · toast · flow_legend(.ts) · 진행 막대 · 막대 · 원 그래프 덧붙임
- createCard · showToast · 로딩 가림막은 부품 파일로 옮기고 본체(ui_template_elements)가 다시 내보냄 — 부름 꼴 그대로
- 대시보드 emptyState = 빈 상태 부품을 씀(한 벌) · 선 카드 그림자 0 · 가림막 옛 보라 → --color-scrim
- 부품 모음 화면 견본(밝음 · 어두움 · 상태) · 지킴 시험 test_552_3_* 열
- 전후 촬영: B01 여덟 화면 자리 · 화소 같음(실시간 CPU 값만 다름) · A01 · B08 카드 그림자만 다름

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

66 lines
2.7 KiB
TypeScript

/* =============================================================================
* ui_template_banner.ts
* 부품 「띠 알림」(552-3 · 목록 25) — 화면 안에 머무는 알림 줄(토스트처럼 사라지지 않음).
* 꼴 = 왼쪽 색 띠 + 옅은 바탕 · 갈래 넷(정보 · 경고 · 위험 · 성공 — 성공은 ✓ 같이) · 제목(굵게) · 펼침(`details` → 「자세히」)
* · 안 단추(`actions`) · 닫기(`onClose` → ✕)
* 크기 = 글 13 · 줄 최소 32 · 여백 8 / 12
* 상태 = 기본만
* 쓰는 법 = `createBanner({ tone: "warning", text: "저장 안 한 고침이 있음", actions: [createButton(…)] })`
* 대신하는 옛 변종 = B01 `.b01-home__stale` · B03 `b03-file__resume` · `__error-message` · B06 `b06-section__notice-bar` · E05 · E06 `__stale`
* · M01 `m01-master__notice` · `m01lab__warn` · M02 `m02-main__notice` · `m02-sod__banner` …(약 20 벌 · 목록 4장)
* ========================================================================== */
import { el } from "./ui_template_elements_base";
import "./ui_template_banner.css";
export type BannerTone = "info" | "warning" | "danger" | "success";
export interface BannerOptions {
tone?: BannerTone;
text: string;
title?: string;
/** 펼쳐 보는 자세한 글 · 목록 */
details?: string | HTMLElement;
actions?: HTMLElement[];
/** 주면 오른쪽 ✕ — 누르면 띠를 지우고 부름 */
onClose?: () => void;
}
export function createBanner(opts: BannerOptions): HTMLElement {
const tone = opts.tone ?? "info";
const root = el("div", {
className: `ui-banner ui-banner--${tone}`,
attrs: { role: tone === "danger" ? "alert" : "status" },
});
const body = el("div", { className: "ui-banner__body" });
if (opts.title) body.append(el("strong", { className: "ui-banner__title", text: opts.title }));
body.append(
el("span", {
className: "ui-banner__text",
text: `${tone === "success" ? "✓ " : ""}${opts.text}`,
}),
);
if (opts.details) {
const more = el("details", { className: "ui-banner__details" });
more.append(el("summary", { text: "자세히" }), opts.details);
body.append(more);
}
root.append(body);
if (opts.actions?.length)
root.append(el("div", { className: "ui-banner__actions", children: opts.actions }));
if (opts.onClose) {
const onClose = opts.onClose;
const close = el("button", {
className: "ui-banner__close",
text: "✕",
attrs: { type: "button", title: "닫기", "aria-label": "닫기" },
});
close.addEventListener("click", () => {
root.remove();
onClose();
});
root.append(close);
}
return root;
}