@olundot/ui · Surfaces
Tabs
같은 페이지 맥락에 속하는 동등한 패널 간 전환을 위한 탭 서피스 패턴입니다.
- API
- tabs(TabItem[]) · activeKey · onChange(key) · baseId(a11y id prefix)
- TabItem
- key · label · badge?(number) · disabled?(boolean)
- TabsContent
- tabKey + baseId — aria-controls/aria-labelledby 자동 연결
- 배지
- badge > 0일 때 자동 표시, 99 초과 시 '99+' 처리
- 접근성
- Radix가 tablist/tab/tabpanel + arrow-key 네비게이션 자동 관리
기본
라이브 (tabs + TabsContent activeKey onChange 상태 관리)시험 기간, 접근 규칙, 준비 상태를 확인합니다.
코드 보기tsx
import { useState } from "react";
import { Tabs, TabsContent } from "@olundot/ui";
export function TabsBasic() {
const [active, setActive] = useState("overview");
const tabs = [
{ key: "overview", label: "개요" },
{ key: "questions", label: "문항" },
{ key: "students", label: "수강생" },
];
return (
<Tabs tabs={tabs} activeKey={active} onChange={setActive} baseId="basic-tabs">
<TabsContent tabKey="overview" baseId="basic-tabs">
<p style={{ padding: "16px 0", color: "var(--text-secondary)", fontSize: "0.875rem" }}>
시험 기간, 접근 규칙, 준비 상태를 확인합니다.
</p>
</TabsContent>
<TabsContent tabKey="questions" baseId="basic-tabs">
<p style={{ padding: "16px 0", color: "var(--text-secondary)", fontSize: "0.875rem" }}>
32개 문항 목록입니다.
</p>
</TabsContent>
<TabsContent tabKey="students" baseId="basic-tabs">
<p style={{ padding: "16px 0", color: "var(--text-secondary)", fontSize: "0.875rem" }}>
128명의 수강생이 등록되어 있습니다.
</p>
</TabsContent>
</Tabs>
);
}배지 포함
라이브 (tab badge — 카운트 표시)응시 대기 12명
코드 보기tsx
import { useState } from "react";
import { Tabs, TabsContent } from "@olundot/ui";
export function TabsWithBadges() {
const [active, setActive] = useState("pending");
const tabs = [
{ key: "all", label: "전체", badge: 48 },
{ key: "pending", label: "대기", badge: 12 },
{ key: "submitted", label: "제출", badge: 36 },
{ key: "graded", label: "채점 완료", badge: 0 },
];
return (
<Tabs tabs={tabs} activeKey={active} onChange={setActive} baseId="badge-tabs">
<TabsContent tabKey="all" baseId="badge-tabs">
<p style={{ padding: "16px 0", color: "var(--text-secondary)", fontSize: "0.875rem" }}>전체 48명</p>
</TabsContent>
<TabsContent tabKey="pending" baseId="badge-tabs">
<p style={{ padding: "16px 0", color: "var(--text-secondary)", fontSize: "0.875rem" }}>응시 대기 12명</p>
</TabsContent>
<TabsContent tabKey="submitted" baseId="badge-tabs">
<p style={{ padding: "16px 0", color: "var(--text-secondary)", fontSize: "0.875rem" }}>제출 완료 36명</p>
</TabsContent>
<TabsContent tabKey="graded" baseId="badge-tabs">
<p style={{ padding: "16px 0", color: "var(--text-secondary)", fontSize: "0.875rem" }}>채점 완료 0명</p>
</TabsContent>
</Tabs>
);
}비활성 탭
정적 (disabled tab — pointer-events-none + text-disabled)발행된 강의 목록입니다.
코드 보기tsx
import { useState } from "react";
import { Tabs, TabsContent } from "@olundot/ui";
export function TabsDisabled() {
const [active, setActive] = useState("published");
const tabs = [
{ key: "published", label: "발행됨" },
{ key: "draft", label: "초안" },
{ key: "archived", label: "보관됨", disabled: true },
{ key: "deleted", label: "삭제됨", disabled: true },
];
return (
<Tabs tabs={tabs} activeKey={active} onChange={setActive} baseId="disabled-tabs">
<TabsContent tabKey="published" baseId="disabled-tabs">
<p style={{ padding: "16px 0", color: "var(--text-secondary)", fontSize: "0.875rem" }}>
발행된 강의 목록입니다.
</p>
</TabsContent>
<TabsContent tabKey="draft" baseId="disabled-tabs">
<p style={{ padding: "16px 0", color: "var(--text-secondary)", fontSize: "0.875rem" }}>
초안 목록입니다.
</p>
</TabsContent>
<TabsContent tabKey="archived" baseId="disabled-tabs">
<p style={{ padding: "16px 0", color: "var(--text-secondary)", fontSize: "0.875rem" }}>
보관된 항목 (비활성)
</p>
</TabsContent>
<TabsContent tabKey="deleted" baseId="disabled-tabs">
<p style={{ padding: "16px 0", color: "var(--text-secondary)", fontSize: "0.875rem" }}>
삭제된 항목 (비활성)
</p>
</TabsContent>
</Tabs>
);
}