92 lines
3.1 KiB
TypeScript
92 lines
3.1 KiB
TypeScript
'use client'
|
|
|
|
import { ChevronDownIcon } from '@heroicons/react/16/solid'
|
|
import clsx from 'clsx'
|
|
|
|
export type TabItem = {
|
|
id: string
|
|
label: string
|
|
count?: number
|
|
}
|
|
|
|
type TabsProps = {
|
|
tabs: TabItem[]
|
|
value: string
|
|
onChange: (id: string) => void
|
|
className?: string
|
|
ariaLabel?: string
|
|
}
|
|
|
|
export default function Tabs({
|
|
tabs,
|
|
value,
|
|
onChange,
|
|
className,
|
|
ariaLabel = 'Ansicht auswählen',
|
|
}: TabsProps) {
|
|
const current = tabs.find((t) => t.id === value) ?? tabs[0]
|
|
|
|
return (
|
|
<div className={className}>
|
|
{/* Mobile: Dropdown */}
|
|
<div className="grid grid-cols-1 sm:hidden">
|
|
<select
|
|
value={current.id}
|
|
onChange={(e) => onChange(e.target.value)}
|
|
aria-label={ariaLabel}
|
|
className="col-start-1 row-start-1 w-full appearance-none rounded-md bg-white py-2 pr-8 pl-3 text-base text-gray-900 outline outline-1 outline-gray-300 focus:outline-2 focus:outline-indigo-600 dark:bg-white/5 dark:text-gray-100 dark:outline-white/10 dark:focus:outline-indigo-500"
|
|
>
|
|
{tabs.map((tab) => (
|
|
<option key={tab.id} value={tab.id}>
|
|
{tab.label}
|
|
</option>
|
|
))}
|
|
</select>
|
|
<ChevronDownIcon
|
|
aria-hidden="true"
|
|
className="pointer-events-none col-start-1 row-start-1 mr-2 size-5 self-center justify-self-end fill-gray-500 dark:fill-gray-400"
|
|
/>
|
|
</div>
|
|
|
|
{/* Desktop: Horizontal Tabs */}
|
|
<div className="hidden sm:block">
|
|
<nav className="border-b border-gray-200 dark:border-white/10" aria-label={ariaLabel}>
|
|
<ul className="-mb-px flex space-x-8">
|
|
{tabs.map((tab) => {
|
|
const selected = tab.id === current.id
|
|
return (
|
|
<li key={tab.id}>
|
|
<button
|
|
type="button"
|
|
onClick={() => onChange(tab.id)}
|
|
className={clsx(
|
|
selected
|
|
? 'border-indigo-500 text-indigo-600 dark:border-indigo-400 dark:text-indigo-400'
|
|
: 'border-transparent text-gray-500 hover:border-gray-300 hover:text-gray-700 dark:text-gray-400 dark:hover:border-white/20 dark:hover:text-white',
|
|
'group inline-flex items-center border-b-2 px-1 py-4 text-sm font-medium'
|
|
)}
|
|
>
|
|
<span>{tab.label}</span>
|
|
{tab.count !== undefined && (
|
|
<span
|
|
className={clsx(
|
|
selected
|
|
? 'bg-indigo-100 text-indigo-600 dark:bg-indigo-500/20 dark:text-indigo-400'
|
|
: 'bg-gray-100 text-gray-900 dark:bg-white/10 dark:text-gray-300',
|
|
'ml-3 rounded-full px-2.5 py-0.5 text-xs font-medium'
|
|
)}
|
|
>
|
|
{tab.count}
|
|
</span>
|
|
)}
|
|
</button>
|
|
</li>
|
|
)
|
|
})}
|
|
</ul>
|
|
</nav>
|
|
</div>
|
|
</div>
|
|
)
|
|
}
|