54 lines
1.5 KiB
TypeScript
54 lines
1.5 KiB
TypeScript
'use client'
|
|
|
|
import { usePathname } from 'next/navigation'
|
|
import Link from 'next/link'
|
|
import type { ReactNode, ReactElement } from 'react'
|
|
|
|
export type TabProps = {
|
|
name: string
|
|
href: string
|
|
}
|
|
|
|
export function Tabs({ children }: { children: ReactNode }) {
|
|
const pathname = usePathname()
|
|
const tabs = Array.isArray(children) ? children : [children]
|
|
|
|
return (
|
|
<nav className="flex gap-x-1" aria-label="Tabs" role="tablist" aria-orientation="horizontal">
|
|
{tabs
|
|
.filter(
|
|
(tab): tab is ReactElement<TabProps> =>
|
|
tab !== null &&
|
|
typeof tab === 'object' &&
|
|
'props' in tab &&
|
|
typeof tab.props.href === 'string'
|
|
)
|
|
.map((tab, index) => {
|
|
const base = tab.props.href.replace(/\/$/, '')
|
|
const current = pathname.replace(/\/$/, '')
|
|
|
|
const isActive = current === base || current.startsWith(base + '/');
|
|
|
|
return (
|
|
<Link
|
|
key={index}
|
|
href={tab.props.href}
|
|
className={`py-2 px-4 text-sm rounded-lg transition-colors ${
|
|
isActive
|
|
? 'bg-gray-100 text-gray-900 dark:bg-neutral-700 dark:text-white'
|
|
: 'text-gray-500 hover:bg-gray-100 dark:text-neutral-400 dark:hover:bg-neutral-700'
|
|
}`}
|
|
>
|
|
{tab.props.name}
|
|
</Link>
|
|
)
|
|
})}
|
|
</nav>
|
|
)
|
|
}
|
|
|
|
// Dummy-Komponente nur zur statischen Verwendung
|
|
Tabs.Tab = function Tab(_props: TabProps) {
|
|
return null
|
|
}
|