63 lines
2.0 KiB
TypeScript
63 lines
2.0 KiB
TypeScript
|
|
import React, { useState } from 'react';
|
||
|
|
import clsx from 'clsx';
|
||
|
|
|
||
|
|
export interface TabItem {
|
||
|
|
key: string;
|
||
|
|
label: string;
|
||
|
|
content: React.ReactNode;
|
||
|
|
badge?: number;
|
||
|
|
}
|
||
|
|
|
||
|
|
export interface TabsProps {
|
||
|
|
tabs: TabItem[];
|
||
|
|
defaultKey?: string;
|
||
|
|
className?: string;
|
||
|
|
}
|
||
|
|
|
||
|
|
export function Tabs({ tabs, defaultKey, className }: TabsProps) {
|
||
|
|
const [activeKey, setActiveKey] = useState(defaultKey || tabs[0]?.key || '');
|
||
|
|
const activeTab = tabs.find((t) => t.key === activeKey);
|
||
|
|
|
||
|
|
return (
|
||
|
|
<div className={clsx('w-full', className)}>
|
||
|
|
<div className="border-b border-secondary-200" role="tablist">
|
||
|
|
<div className="flex gap-1 px-6 overflow-x-auto">
|
||
|
|
{tabs.map((tab) => (
|
||
|
|
<button
|
||
|
|
key={tab.key}
|
||
|
|
role="tab"
|
||
|
|
aria-selected={activeKey === tab.key}
|
||
|
|
aria-controls={`panel-${tab.key}`}
|
||
|
|
id={`tab-${tab.key}`}
|
||
|
|
tabIndex={activeKey === tab.key ? 0 : -1}
|
||
|
|
onClick={() => setActiveKey(tab.key)}
|
||
|
|
className={clsx(
|
||
|
|
'px-4 py-3 text-sm font-medium border-b-2 min-h-touch whitespace-nowrap',
|
||
|
|
'focus:outline-none focus-visible:ring-2 focus-visible:ring-primary-500 rounded-t-md',
|
||
|
|
activeKey === tab.key
|
||
|
|
? 'border-primary-600 text-primary-700'
|
||
|
|
: 'border-transparent text-secondary-600 hover:text-secondary-900 hover:border-secondary-300'
|
||
|
|
)}
|
||
|
|
>
|
||
|
|
{tab.label}
|
||
|
|
{tab.badge !== undefined && tab.badge > 0 && (
|
||
|
|
<span className="ml-2 inline-flex items-center justify-center px-2 py-0.5 rounded-full text-xs bg-primary-100 text-primary-700">
|
||
|
|
{tab.badge}
|
||
|
|
</span>
|
||
|
|
)}
|
||
|
|
</button>
|
||
|
|
))}
|
||
|
|
</div>
|
||
|
|
</div>
|
||
|
|
<div
|
||
|
|
id={`panel-${activeKey}`}
|
||
|
|
role="tabpanel"
|
||
|
|
aria-labelledby={`tab-${activeKey}`}
|
||
|
|
className="px-6 py-4"
|
||
|
|
>
|
||
|
|
{activeTab?.content}
|
||
|
|
</div>
|
||
|
|
</div>
|
||
|
|
);
|
||
|
|
}
|