Files
vyndr/web/src/components/BottomTabBar.tsx
T

172 lines
5.2 KiB
TypeScript

'use client';
import { usePathname } from 'next/navigation';
import { useParlay } from '@/contexts/ParlayContext';
const TABS = [
{ id: 'home', label: 'Home', href: '/dashboard', icon: HomeIcon },
{ id: 'scan', label: 'Read', href: '/scan', icon: ScanIcon },
{ id: 'parlay', label: 'Parlay', href: null, icon: ParlayIcon },
{ id: 'ledger', label: 'Ledger', href: '/ledger', icon: LedgerIcon },
{ id: 'profile', label: 'Profile', href: '/profile', icon: ProfileIcon },
] as const;
// Pages where the bottom tab bar should stay hidden (auth flows, landing).
const HIDE_ON = new Set(['/login', '/signup', '/auth/callback', '/']);
export default function BottomTabBar() {
const pathname = usePathname() || '/';
const { open, legCount } = useParlay();
if (HIDE_ON.has(pathname)) return null;
return (
<nav
role="navigation"
aria-label="Primary"
className="mobile-tab-bar"
style={{
position: 'fixed',
bottom: 0,
left: 0,
right: 0,
height: 64,
zIndex: 40,
display: 'flex',
borderTop: '1px solid var(--border)',
background: 'rgba(10,10,15,0.92)',
backdropFilter: 'blur(16px)',
WebkitBackdropFilter: 'blur(16px)',
paddingBottom: 'env(safe-area-inset-bottom)',
}}
>
{TABS.map((t) => {
const active = t.href ? (pathname === t.href || pathname.startsWith(`${t.href}/`)) : false;
const color = active ? 'var(--grade-a)' : 'var(--text-secondary)';
const Icon = t.icon;
const isParlay = t.id === 'parlay';
const onClick = () => {
if (isParlay) open();
};
const inner = (
<div
style={{
flex: 1,
display: 'flex',
flexDirection: 'column',
alignItems: 'center',
justifyContent: 'center',
gap: 4,
color,
fontSize: 10,
fontWeight: 600,
textDecoration: 'none',
fontFamily: 'inherit',
border: 'none',
background: 'transparent',
cursor: 'pointer',
position: 'relative',
}}
>
<Icon color={color} />
<span>{t.label}</span>
{isParlay && legCount > 0 && (
<span
className="mono"
style={{
position: 'absolute',
top: 6,
right: 'calc(50% - 22px)',
minWidth: 18,
height: 18,
padding: '0 5px',
borderRadius: 999,
background: 'var(--grade-a)',
color: 'var(--bg-primary)',
fontSize: 10,
fontWeight: 800,
display: 'inline-flex',
alignItems: 'center',
justifyContent: 'center',
}}
>
{legCount}
</span>
)}
</div>
);
if (isParlay || !t.href) {
return (
<button key={t.id} onClick={onClick} style={{ flex: 1, background: 'transparent', border: 'none', padding: 0 }}>
{inner}
</button>
);
}
return (
<a key={t.id} href={t.href} style={{ flex: 1, padding: 0, textDecoration: 'none' }}>
{inner}
</a>
);
})}
<style jsx>{`
@media (min-width: 768px) {
:global(.mobile-tab-bar) {
display: none !important;
}
}
`}</style>
</nav>
);
}
// Lightweight inline SVG icons — keeps the bundle slim and avoids icon-lib install
function HomeIcon({ color }: { color: string }) {
return (
<svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke={color} strokeWidth="2" strokeLinecap="round" strokeLinejoin="round">
<path d="M3 12 12 3l9 9" />
<path d="M5 10v10h14V10" />
</svg>
);
}
function ScanIcon({ color }: { color: string }) {
return (
<svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke={color} strokeWidth="2" strokeLinecap="round" strokeLinejoin="round">
<circle cx="11" cy="11" r="7" />
<path d="M21 21l-4.3-4.3" />
</svg>
);
}
function ParlayIcon({ color }: { color: string }) {
return (
<svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke={color} strokeWidth="2" strokeLinecap="round" strokeLinejoin="round">
<rect x="3" y="4" width="18" height="4" rx="1" />
<rect x="3" y="10" width="18" height="4" rx="1" />
<rect x="3" y="16" width="18" height="4" rx="1" />
</svg>
);
}
function LedgerIcon({ color }: { color: string }) {
return (
<svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke={color} strokeWidth="2" strokeLinecap="round" strokeLinejoin="round">
<path d="M4 4h16v16H4z" />
<path d="M4 9h16" />
<path d="M9 4v16" />
</svg>
);
}
function ProfileIcon({ color }: { color: string }) {
return (
<svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke={color} strokeWidth="2" strokeLinecap="round" strokeLinejoin="round">
<circle cx="12" cy="8" r="4" />
<path d="M4 21c1.5-4 5-6 8-6s6.5 2 8 6" />
</svg>
);
}