- ui_template_input.ts · .css 새로 — createInputField · createSelectField 를 옮김(ui_template_elements 가 다시 내보냄 · 부르는 길 그대로) · size(data-size 단 높이) · unit(칸 안 단위) · multiline · createSlider(이름 | 막대 | 값 글 3열) - 옛 compact 는 단을 안 붙임 — 옛 작은 칸 30 그대로(32 로 맞추면 페이지 칸이 바뀜 · 553 에서 단으로 옮김) - 입력 · 고르기 상태 = 마우스 선 짙게 · 막힘 흐림 · 오류 빨강(aria-invalid 같이) · 고르기 화살표 = 보라 → 보조 글 색(밝음 · 어두움 따로) - 체크 = 라디오 · 칩 꼴 더함(createCheckbox 부름 그대로) · 켜기(스위치 · 색 점 칩) · 분할 고르기(대시보드 .b01-sys-seg 꼴) · 탭(밑줄 · 보조 알약 = 옛 .ui-subtabs · 수 배지) - 배지(알약 · 수 · 램프 ✓ ✗ · 점 · 옛 .ui-tag 같이) · 툴팁 제자리 상자(tooltipBox) · 보조 글(흐린 · 안내 · 오류 ✗ · 경고 ! · 성공 ✓) - 부품 모음 기본 묶음 13 줄 모두 새 부품 견본 · 상태 다섯 칸 · 여덟 단 한 줄 - 시험 test_552_2_parts 새로(머리 · 옛 이름 · 크기 단 · 상태 · 날 색 없음 · 부품 모음 13) Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
89 lines
3.2 KiB
TypeScript
89 lines
3.2 KiB
TypeScript
/* =============================================================================
|
|
* ui_template_tabs.ts
|
|
* 부품 「탭」(552-2 · 부품 모음 `#/b01-account?view=ui-kit`)
|
|
*
|
|
* 꼴 = 밑줄 탭(line · 한 꼴 · 대시보드 `tabBar` 꼴) · 보조 탭(pill · 작은 알약 · `.ui-subtabs`) · 탭 오른쪽 수 배지(0 = 숨김)
|
|
* 문서 탭(`cad-tabs` · `ss-tabs`)은 꼴 그대로 · 토큰만(553)
|
|
* 크기 = data-size 24 · 32 · 36(밑줄 기본 36 · 보조 기본 24)
|
|
* 상태 = 마우스 `:hover` · 막힘 `disabled` · 고름 = aria-selected + `.is-active` — CSS 는 `[data-state]` 로도 받음
|
|
* 쓰기 = `const tabs = createTabs({ tabs: [{ key: "a", label: "개요" }], value: "a", onChange }); tabs.setBadge("a", 3)`
|
|
* 대신 = B01 `.b01-pg-tabs` · 공용 `.ui-subtabs` · B08 `.b08-quantity__tabs` · B09 `.b09-estimation__tabs` · `app-settings__tabs` ·
|
|
* M02 `m02-sod__tabs` · 시트 `.ss-fmtdlg__tabs`
|
|
* 클래스 = 보조 탭 = `ui-subtabs` + 칸 `ui-btn ui-btn--pill`(옛 이름 · 시험이 잡음)
|
|
* ========================================================================== */
|
|
|
|
import { createButton, setUiSize, type UiSize } from "./ui_template_button";
|
|
import "./ui_template_tabs.css";
|
|
|
|
export interface TabItem {
|
|
key: string;
|
|
label: string;
|
|
disabled?: boolean;
|
|
}
|
|
|
|
export interface TabsHandle {
|
|
root: HTMLDivElement;
|
|
/** 고름을 바꿈(onChange 는 안 부름) */
|
|
select: (key: string) => void;
|
|
/** 탭 오른쪽 수 — 0 이하면 숨김 */
|
|
setBadge: (key: string, count: number) => void;
|
|
}
|
|
|
|
export function createTabs(opts: {
|
|
tabs: TabItem[];
|
|
value: string;
|
|
onChange?: (key: string) => void;
|
|
kind?: "line" | "pill";
|
|
/** 24 · 32 · 36 권함 */
|
|
size?: UiSize;
|
|
}): TabsHandle {
|
|
const pill = opts.kind === "pill";
|
|
const root = document.createElement("div");
|
|
root.className = pill ? "ui-tabs ui-tabs--pill ui-subtabs" : "ui-tabs";
|
|
root.setAttribute("role", "tablist");
|
|
setUiSize(root, opts.size ?? (pill ? 24 : 36));
|
|
const buttons = new Map<string, HTMLButtonElement>();
|
|
const badges = new Map<string, HTMLSpanElement>();
|
|
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 opts.tabs) {
|
|
const b = pill
|
|
? createButton({ label: tab.label, variant: "pill", size: opts.size ?? 24 })
|
|
: document.createElement("button");
|
|
if (!pill) {
|
|
b.type = "button";
|
|
b.className = "ui-tabs__tab";
|
|
b.append(tab.label);
|
|
}
|
|
b.setAttribute("role", "tab");
|
|
b.dataset.tab = tab.key;
|
|
b.disabled = tab.disabled ?? false;
|
|
const count = document.createElement("span");
|
|
count.className = "ui-tabs__count";
|
|
count.hidden = true;
|
|
b.append(count);
|
|
b.addEventListener("click", () => {
|
|
select(tab.key);
|
|
opts.onChange?.(tab.key);
|
|
});
|
|
buttons.set(tab.key, b);
|
|
badges.set(tab.key, count);
|
|
root.append(b);
|
|
}
|
|
select(opts.value);
|
|
return {
|
|
root,
|
|
select,
|
|
setBadge: (key, n) => {
|
|
const c = badges.get(key);
|
|
if (!c) return;
|
|
c.textContent = String(n);
|
|
c.hidden = n <= 0;
|
|
},
|
|
};
|
|
}
|