veza/apps/web/src/components/layout/MobileBottomNav.tsx
senke 1a013ed829 feat(ui): table polish + mobile bottom navigation
Table improvements (8 files):
- Sticky headers: bg-background/95 backdrop-blur-sm on all thead elements
- Sort indicators: SortableTableHead with ChevronsUpDown/ChevronUp/ChevronDown
- Row hover: hover:bg-muted/50 duration-150 with active state
- Borders softened: border-border/50, last row no border
- Header typography: text-xs font-medium uppercase tracking-wider
- Consistent cell padding: px-4 py-3 for body, px-4 py-2.5 for headers
- Applied to: ui/table, data/table, TrackList, TrackListRow, FileManager, studio files

Mobile bottom navigation:
- New MobileBottomNav component (5 items: Home, Search, Library, Chat, Profile)
- Only visible on mobile (lg:hidden), z-40 below player
- 48px touch targets, safe-area-inset-bottom for iPhone
- Active state with primary color + top indicator bar
- Integrated into Layout with pb-20 lg:pb-0 main padding
- Header touch target fix: theme toggle min-h-10 min-w-10

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-02-09 23:37:25 +01:00

50 lines
2 KiB
TypeScript

import { useLocation, Link } from 'react-router-dom';
import { Home, Search, Layers, MessageSquare, User } from 'lucide-react';
import { cn } from '@/lib/utils';
const navItems = [
{ id: 'home', label: 'Home', icon: Home, path: '/dashboard' },
{ id: 'search', label: 'Search', icon: Search, path: '/search' },
{ id: 'library', label: 'Library', icon: Layers, path: '/library' },
{ id: 'chat', label: 'Chat', icon: MessageSquare, path: '/chat' },
{ id: 'profile', label: 'Profile', icon: User, path: '/settings' },
];
export function MobileBottomNav() {
const location = useLocation();
return (
<nav className="fixed bottom-0 left-0 right-0 z-40 bg-background/95 backdrop-blur-lg border-t border-border/50 lg:hidden">
<div className="flex items-center justify-around px-2 py-1">
{navItems.map((item) => {
const isActive = location.pathname.startsWith(item.path);
const Icon = item.icon;
return (
<Link
key={item.id}
to={item.path}
className={cn(
'relative flex flex-col items-center justify-center gap-0.5 py-2 px-3 min-w-12 min-h-12 rounded-xl transition-colors',
isActive
? 'text-primary'
: 'text-muted-foreground active:text-foreground',
)}
>
<Icon className={cn('h-5 w-5', isActive && 'fill-primary/20')} />
{/* text-[10px] exception: Tailwind has no scale below text-xs (12px); 10px is standard for mobile bottom nav labels */}
<span className="text-[10px] font-medium leading-none">
{item.label}
</span>
{isActive && (
<span className="absolute top-0 left-1/2 -translate-x-1/2 w-4 h-0.5 rounded-full bg-primary" />
)}
</Link>
);
})}
</div>
{/* Safe area for iPhone home indicator */}
<div className="h-[env(safe-area-inset-bottom)]" />
</nav>
);
}