82 lines
2.8 KiB
TypeScript
82 lines
2.8 KiB
TypeScript
'use client';
|
|
|
|
import * as React from 'react';
|
|
import { FileText, Home, MoreHorizontal, Package, Search } from 'lucide-react';
|
|
import { Link } from '@/i18n/navigation';
|
|
import { cn } from '@/lib/utils';
|
|
|
|
/**
|
|
* Navigation mobile.
|
|
*
|
|
* Quatre destinations frequentes plus une entree « Plus » qui ouvre la
|
|
* navigation complete : aucune section n'est inaccessible depuis le mobile,
|
|
* contrairement a la version precedente qui n'exposait que 5 des 8 entrees.
|
|
*/
|
|
interface BottomNavProps {
|
|
pathname: string;
|
|
labels: {
|
|
home: string;
|
|
bookings: string;
|
|
documents: string;
|
|
tracking: string;
|
|
more: string;
|
|
};
|
|
onMore: () => void;
|
|
}
|
|
|
|
export function BottomNav({ pathname, labels, onMore }: BottomNavProps) {
|
|
const items = [
|
|
{ href: '/dashboard', icon: Home, label: labels.home, exact: true },
|
|
{ href: '/dashboard/bookings', icon: Package, label: labels.bookings },
|
|
{ href: '/dashboard/documents', icon: FileText, label: labels.documents },
|
|
{ href: '/dashboard/track-trace', icon: Search, label: labels.tracking },
|
|
];
|
|
|
|
const itemClass = (active: boolean) =>
|
|
cn(
|
|
'flex min-h-[44px] flex-col items-center justify-center gap-0.5 transition-colors',
|
|
'focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-inset',
|
|
active ? 'text-brand-navy' : 'text-neutral-500 hover:text-brand-navy'
|
|
);
|
|
|
|
return (
|
|
<nav
|
|
aria-label={labels.more}
|
|
className="fixed inset-x-0 bottom-0 z-30 border-t border-border bg-white pb-[env(safe-area-inset-bottom)] lg:hidden"
|
|
>
|
|
<div className="grid grid-cols-5">
|
|
{items.map(item => {
|
|
const active = item.exact
|
|
? pathname === item.href
|
|
: pathname === item.href || pathname.startsWith(item.href + '/');
|
|
const Icon = item.icon;
|
|
return (
|
|
<Link
|
|
key={item.href}
|
|
href={item.href}
|
|
aria-current={active ? 'page' : undefined}
|
|
className={itemClass(active)}
|
|
>
|
|
<span className="relative">
|
|
<Icon className="size-5" aria-hidden="true" />
|
|
{active && (
|
|
<span
|
|
aria-hidden="true"
|
|
className="absolute -top-1.5 left-1/2 h-0.5 w-4 -translate-x-1/2 rounded-full bg-brand-blue"
|
|
/>
|
|
)}
|
|
</span>
|
|
<span className="text-[10px] font-medium leading-tight">{item.label}</span>
|
|
</Link>
|
|
);
|
|
})}
|
|
|
|
<button type="button" onClick={onMore} className={itemClass(false)}>
|
|
<MoreHorizontal className="size-5" aria-hidden="true" />
|
|
<span className="text-[10px] font-medium leading-tight">{labels.more}</span>
|
|
</button>
|
|
</div>
|
|
</nav>
|
|
);
|
|
}
|