Files
Aislo/ui_template/ui_template_ui_kit.ts
T
eomsangdonandClaude Opus 5.5 3ccb96fbdb feat(ui-kit): 부품 단추 · 아이콘 단추 · 아이콘 · 크기 여덟 단 (PLAN 552-2)
- ui_template_button.ts · .css 새로 — createButton 을 옮김(ui_template_elements 가 다시 내보냄 · 부르는 길 그대로) · createIconButton 새로(고스트 · 선 · 채움 · aria-pressed · 글자 기호 · title + aria-label)
- 크기 = data-size 18 · 20 · 22 · 24 · 28 · 32 · 36 · 42 · 단마다 높이 · 글자 · 여백 · 아이콘 · 둥글기 값(--ui-size-*) · 입력 · 고르기 · 분할 · 탭이 같은 값을 읽음
- 단추 높이 = 글 한 줄 + 여백 + 테두리(높이를 박지 않음) — 크기를 안 준 옛 단추 · 페이지 여백 덮어쓰기 그대로 · 옛 size "sm" = 18 단(같은 꼴) · 글 링크 꼴 더함
- 상태 = 마우스 · 눌림을 data-state 로도 받음(부품 모음이 굳혀 보임) · 마우스 색 = --color-primary-hover · --color-accent-soft(옛 보라 별칭 대신) · 눌림 = 한 칸 내려앉음
- 아이콘 = Tabler 32 더함(지도 · 길 · 자 · 합 · 크레인 · 핀 · 나침반 · 설정 · 닫기 · 메뉴 · 되돌리기 …) + PageKit 여섯을 옮김 · .ui-icon 규칙 전역(ui_template_icons.css)
- 부품 모음 = 단추 · 아이콘 단추 · 아이콘 견본 · 부품마다 여덟 단 한 줄
- 시험 test_552_2_button 새로(단 값 · 높이 식 · 옛 이름 · 상태 · 날 색 없음 · 아이콘)

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

134 lines
5.2 KiB
TypeScript

/* =============================================================================
* ui_template_ui_kit.ts
* 부품 모음 화면(552-1) — 대시보드 세부 페이지 `#/b01-account?view=ui-kit` · 시스템 관리자만.
* 밝음 · 어두움을 나란히 — 칸마다 iframe(`<html data-theme>`)에 지금 페이지 CSS 를 옮겨 실제 테마 값으로 그림.
* 부품 줄 = `ui_template_ui_kit_parts.ts` 목록 · 상태 다섯 칸(기본 · 마우스 · 눌림 · 막힘 · 오류) · 견본이 없으면 빈 자리.
* 크기 자 = 단추 · 입력 높이 여덟 단(18 ~ 42) · 부품마다 `sizes` 가 있으면 여덟 단 한 줄.
* ========================================================================== */
import { pageFrame } from "../B01_Dashboard/B01_Dashboard_UI_PageKit";
import {
KIT_GROUP_LABEL,
KIT_PARTS,
KIT_SIZES,
KIT_STATES,
KIT_STATE_LABEL,
type KitGroup,
type KitPart,
} from "./ui_template_ui_kit_parts";
import "./ui_template_ui_kit.css";
type Theme = "light" | "dark";
function node<K extends keyof HTMLElementTagNameMap>(
tag: K,
className: string,
text?: string,
): HTMLElementTagNameMap[K] {
const n = document.createElement(tag);
n.className = className;
if (text !== undefined) n.textContent = text;
return n;
}
/** 크기 자 — 여덟 단 높이 상자 */
function sizeRuler(): HTMLElement {
const box = node("section", "ui-kit__group");
box.append(node("h3", "ui-kit__group-title", "크기 여덟 단 (높이 px · data-size)"));
const line = node("div", "ui-kit__sizes");
for (const h of KIT_SIZES) {
const cell = node("div", "ui-kit__size", String(h));
cell.style.height = `${h}px`;
cell.dataset.size = String(h);
line.append(cell);
}
box.append(line);
return box;
}
function partRow(part: KitPart): HTMLElement {
const item = node("article", "ui-kit__part");
item.dataset.part = part.key;
const head = node("header", "ui-kit__part-head");
head.append(
node("strong", "ui-kit__part-name", part.name),
node("span", "ui-kit__part-kinds", part.kinds),
node("code", "ui-kit__part-file", part.file),
);
if (part.legacy) head.append(node("span", "ui-kit__part-flag", "지금 꼴"));
item.append(head);
const states = node("div", "ui-kit__states");
const shown = KIT_STATES.map((s) => part.demo?.(s)); // null = 그 상태 없음 · undefined = 견본 없음
const onlyDefault = shown[0] && shown.slice(1).every((x) => x === null);
KIT_STATES.forEach((s, i) => {
if (onlyDefault && i > 0) return;
const cell = node("div", `ui-kit__state${onlyDefault ? " ui-kit__state--wide" : ""}`);
cell.dataset.state = s;
cell.append(shown[i] ?? node("span", "ui-kit__slot", shown[i] === null ? "-" : "빈 자리"));
states.append(cell);
});
item.append(states);
if (part.sizes) {
const sizes = node("div", "ui-kit__part-sizes");
for (const z of KIT_SIZES) sizes.append(part.sizes(z));
item.append(sizes);
}
return item;
}
/** 한 테마 칸 내용 — 상태 머리 · 크기 자 · 묶음별 부품 줄 */
export function renderKit(): HTMLElement {
// 셸 별칭(`--b01-*`)을 받으려고 .b01-shell 로 감쌈 — 배치 규칙은 CSS 에서 풂
const root = node("div", "b01-shell ui-kit__body");
const legend = node("div", "ui-kit__states ui-kit__legend");
for (const s of KIT_STATES) legend.append(node("span", "ui-kit__state", KIT_STATE_LABEL[s]));
root.append(legend, sizeRuler());
for (const group of Object.keys(KIT_GROUP_LABEL) as KitGroup[]) {
const box = node("section", "ui-kit__group");
box.dataset.group = group;
box.append(node("h3", "ui-kit__group-title", KIT_GROUP_LABEL[group]));
box.append(...KIT_PARTS.filter((p) => p.group === group).map(partRow));
root.append(box);
}
return root;
}
/** 테마 칸 — iframe 에 지금 문서의 CSS 를 옮기고 `<html data-theme>` 를 박음 */
function themePane(theme: Theme): HTMLElement {
const pane = node("div", "ui-kit__pane");
pane.dataset.theme = theme;
pane.append(node("h3", "ui-kit__pane-title", theme === "light" ? "밝음" : "어두움"));
const frame = node("iframe", "ui-kit__frame");
frame.title = `부품 모음 — ${theme === "light" ? "밝음" : "어두움"}`;
pane.append(frame);
frame.addEventListener("load", () => {
const doc = frame.contentDocument;
if (!doc) return;
doc.documentElement.dataset.theme = theme;
doc.documentElement.lang = "ko";
for (const css of document.head.querySelectorAll('style, link[rel="stylesheet"]'))
doc.head.append(css.cloneNode(true));
doc.body.className = "ui-kit__doc";
doc.body.append(renderKit());
const fit = (): void => {
frame.style.height = `${doc.documentElement.scrollHeight}px`;
};
new ResizeObserver(fit).observe(doc.body);
fit();
});
frame.srcdoc = "<!doctype html><html><head></head><body></body></html>";
return pane;
}
export function mountUiKit(host: HTMLElement): void {
const page = pageFrame(
host,
"부품 모음",
"모든 화면이 다시 쓰는 부품 — 밝음 · 어두움 · 상태별 · 빈 자리는 552-2 ~ 552-4 가 채움",
);
page.root.classList.add("ui-kit");
const panes = node("div", "ui-kit__panes");
panes.append(themePane("light"), themePane("dark"));
page.body.append(panes);
}