141 lines
4.0 KiB
TypeScript
141 lines
4.0 KiB
TypeScript
'use client'
|
|
|
|
import {Link} from '@/i18n/navigation'
|
|
import { usePathname } from 'next/navigation'
|
|
import { useLocale, useTranslations } from 'next-intl'
|
|
import { Fragment } from 'react'
|
|
import { BreadcrumbsSchema } from './breadcrumbs-schema'
|
|
|
|
interface BreadcrumbItem {
|
|
label: string
|
|
href: string
|
|
current?: boolean
|
|
}
|
|
|
|
function HomeIcon({ className }: { className?: string }) {
|
|
return (
|
|
<svg
|
|
className={className}
|
|
fill="none"
|
|
stroke="currentColor"
|
|
viewBox="0 0 24 24"
|
|
xmlns="http://www.w3.org/2000/svg"
|
|
>
|
|
<path
|
|
strokeLinecap="round"
|
|
strokeLinejoin="round"
|
|
strokeWidth={2}
|
|
d="M3 12l2-2m0 0l7-7 7 7M5 10v10a1 1 0 001 1h3m10-11l2 2m-2-2v10a1 1 0 01-1 1h-3m-6 0a1 1 0 001-1v-4a1 1 0 011-1h2a1 1 0 011 1v4a1 1 0 001 1m-6 0h6"
|
|
/>
|
|
</svg>
|
|
)
|
|
}
|
|
|
|
function ChevronIcon({ className }: { className?: string }) {
|
|
return (
|
|
<svg className={className} fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
|
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M9 5l7 7-7 7" />
|
|
</svg>
|
|
)
|
|
}
|
|
|
|
export function Breadcrumbs({ items }: { items?: BreadcrumbItem[] }) {
|
|
const pathname = usePathname()
|
|
const locale = useLocale()
|
|
const t = useTranslations('Breadcrumbs')
|
|
|
|
// Hide breadcrumbs on main page
|
|
const isMainPage = pathname === `/${locale}` || pathname === '/'
|
|
if (isMainPage) {
|
|
return null
|
|
}
|
|
|
|
const formatSegmentLabel = (segment: string): string => {
|
|
const specialCases: { [key: string]: string } = {
|
|
blog: t('blog'),
|
|
tags: t('tags'),
|
|
about: t('about'),
|
|
}
|
|
|
|
if (specialCases[segment]) {
|
|
return specialCases[segment]
|
|
}
|
|
|
|
return segment
|
|
.split('-')
|
|
.map(word => word.charAt(0).toUpperCase() + word.slice(1))
|
|
.join(' ')
|
|
}
|
|
|
|
let breadcrumbs: BreadcrumbItem[] = items || []
|
|
|
|
if (!items) {
|
|
const segments = pathname.split('/').filter(Boolean)
|
|
breadcrumbs = segments.map((segment, index) => {
|
|
const href = '/' + segments.slice(0, index + 1).join('/')
|
|
const label = formatSegmentLabel(segment)
|
|
const current = index === segments.length - 1
|
|
|
|
return { label, href, current }
|
|
})
|
|
}
|
|
|
|
const schemaItems = [
|
|
{ position: 1, name: t('home'), item: '/' },
|
|
...breadcrumbs.map((item, index) => ({
|
|
position: index + 2,
|
|
name: item.label,
|
|
item: item.href,
|
|
})),
|
|
]
|
|
|
|
return (
|
|
<>
|
|
<nav
|
|
aria-label="Breadcrumb"
|
|
className="bg-gray-50 dark:bg-gray-800 border-b border-gray-200 dark:border-gray-700"
|
|
>
|
|
<div className="container mx-auto px-4 py-3">
|
|
<ol className="flex items-center space-x-2 text-sm overflow-x-auto scrollbar-hide">
|
|
<li className="flex-shrink-0">
|
|
<Link
|
|
href="/"
|
|
className="flex items-center text-gray-500 hover:text-primary-600 transition"
|
|
aria-label={t('home')}
|
|
>
|
|
<HomeIcon className="w-4 h-4" />
|
|
</Link>
|
|
</li>
|
|
|
|
{breadcrumbs.map(item => (
|
|
<Fragment key={item.href}>
|
|
<li className="text-gray-400 flex-shrink-0">
|
|
<ChevronIcon className="w-4 h-4" />
|
|
</li>
|
|
<li className="flex-shrink-0">
|
|
{item.current ? (
|
|
<span
|
|
className="font-medium text-gray-700 dark:text-gray-300 truncate max-w-[150px] sm:max-w-none block"
|
|
aria-current="page"
|
|
>
|
|
{item.label}
|
|
</span>
|
|
) : (
|
|
<Link
|
|
href={item.href}
|
|
className="text-gray-500 hover:text-primary-600 transition truncate max-w-[150px] sm:max-w-none block"
|
|
>
|
|
{item.label}
|
|
</Link>
|
|
)}
|
|
</li>
|
|
</Fragment>
|
|
))}
|
|
</ol>
|
|
</div>
|
|
</nav>
|
|
<BreadcrumbsSchema items={schemaItems} />
|
|
</>
|
|
)
|
|
}
|