BillTracker/client/components/data/DataNav.jsx

65 lines
2.6 KiB
React
Raw Normal View History

import { cn } from '@/lib/utils';
const DOT_TONES = {
green: 'bg-emerald-500',
amber: 'bg-amber-500',
red: 'bg-rose-500',
gray: 'bg-muted-foreground/40',
};
/**
* Goal-oriented navigation for the Data page. Desktop ( lg): a sticky vertical
* list. Mobile: a horizontally-scrollable segmented control. One <nav> landmark,
* aria-current on the active item, fully keyboard-operable (native buttons).
*
* sections: [{ id, label, description?, icon, dot?, badge? }]
*/
export default function DataNav({ sections, active, onSelect }) {
return (
<nav aria-label="Data sections" className="lg:sticky lg:top-20">
<ul className="flex gap-1 overflow-x-auto pb-1 lg:flex-col lg:overflow-visible lg:pb-0">
{sections.map((s) => {
const isActive = s.id === active;
const Icon = s.icon;
return (
<li key={s.id} className="shrink-0 lg:shrink">
<button
type="button"
onClick={() => onSelect(s.id)}
aria-current={isActive ? 'page' : undefined}
className={cn(
'group flex w-full items-center gap-2.5 rounded-lg px-3 py-2.5 text-left text-sm transition-colors',
'whitespace-nowrap lg:whitespace-normal',
isActive
? 'bg-primary/10 font-semibold text-foreground lg:shadow-sm'
: 'text-muted-foreground hover:bg-muted/50 hover:text-foreground',
)}
>
{Icon && (
<Icon className={cn('h-4 w-4 shrink-0', isActive ? 'text-primary' : 'text-muted-foreground group-hover:text-foreground')} />
)}
<span className="min-w-0 flex-1">
<span className="flex items-center gap-1.5">
{s.dot && <span className={cn('h-1.5 w-1.5 shrink-0 rounded-full', DOT_TONES[s.dot] || DOT_TONES.gray)} />}
<span className="truncate">{s.label}</span>
</span>
{s.description && (
<span className="mt-0.5 hidden truncate text-xs font-normal text-muted-foreground lg:block">
{s.description}
</span>
)}
</span>
{s.badge != null && s.badge !== 0 && (
<span className="ml-auto shrink-0 rounded-full bg-amber-500/15 px-1.5 py-0.5 text-[11px] font-semibold tabular-nums text-amber-600 dark:text-amber-400">
{s.badge}
</span>
)}
</button>
</li>
);
})}
</ul>
</nav>
);
}