← back to Norma
components/TabNav.tsx
99 lines
'use client';
import { FileText, Newspaper, Twitter, BookOpen, Settings, Megaphone, Award, Plug } from 'lucide-react';
/* ─── Types ──────────────────────────────────────────────────────────────── */
export type TabId = 'drafts' | 'news' | 'petitions' | 'grants' | 'xsessions' | 'library' | 'integrations' | 'settings';
interface Tab {
id: TabId;
label: string;
Icon: React.ElementType;
}
interface TabNavProps {
activeTab: TabId;
onTabChange: (tab: TabId) => void;
}
/* ─── Tab Config ─────────────────────────────────────────────────────────── */
const TABS: Tab[] = [
{ id: 'drafts', label: 'Drafts', Icon: FileText },
{ id: 'news', label: 'News', Icon: Newspaper },
{ id: 'petitions', label: 'Petitions', Icon: Megaphone },
{ id: 'grants', label: 'Grants', Icon: Award },
{ id: 'xsessions', label: 'X Sessions', Icon: Twitter },
{ id: 'library', label: 'Library', Icon: BookOpen },
{ id: 'integrations', label: 'Integrations', Icon: Plug },
{ id: 'settings', label: 'Settings', Icon: Settings },
];
/* ─── Component ──────────────────────────────────────────────────────────── */
export default function TabNav({ activeTab, onTabChange }: TabNavProps) {
return (
<nav
aria-label="Main navigation"
style={{
backgroundColor: 'var(--color-surface)',
borderBottom: '1px solid var(--color-border)',
}}
>
{/* Scrollable container for mobile */}
<div
className="flex overflow-x-auto"
style={{ scrollbarWidth: 'none' }}
>
{TABS.map(({ id, label, Icon }) => {
const isActive = activeTab === id;
return (
<button
key={id}
onClick={() => onTabChange(id)}
aria-current={isActive ? 'page' : undefined}
className="relative flex items-center gap-2 px-4 py-3 text-sm font-medium whitespace-nowrap transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-inset"
style={{
color: isActive
? 'var(--color-text)'
: 'var(--color-text-secondary)',
borderBottom: isActive
? '2px solid var(--color-primary)'
: '2px solid transparent',
marginBottom: '-1px', /* Overlap container border */
backgroundColor: 'transparent',
cursor: 'pointer',
flexShrink: 0,
}}
onMouseEnter={(e) => {
if (!isActive) {
(e.currentTarget as HTMLButtonElement).style.color =
'var(--color-text)';
}
}}
onMouseLeave={(e) => {
if (!isActive) {
(e.currentTarget as HTMLButtonElement).style.color =
'var(--color-text-secondary)';
}
}}
>
<Icon
size={16}
aria-hidden="true"
style={{
color: isActive
? 'var(--color-primary)'
: 'currentColor',
flexShrink: 0,
}}
/>
<span className="hidden sm:inline">{label}</span>
{/* Mobile: show only icon label (sr-only text) */}
<span className="sr-only sm:hidden">{label}</span>
</button>
);
})}
</div>
</nav>
);
}