90 lines
2.6 KiB
TypeScript
90 lines
2.6 KiB
TypeScript
import { Link, useMatchRoute } from "@tanstack/react-router";
|
|
import { motion } from "framer-motion";
|
|
import { useAuth } from "../hooks/useAuth";
|
|
import { LucideIcon } from "../lib/iconData";
|
|
import { useUIStore } from "../stores/uiStore";
|
|
|
|
interface TabItemProps {
|
|
icon: string;
|
|
label: string;
|
|
isActive: boolean;
|
|
}
|
|
|
|
function TabItemWrapper({ icon, label, isActive }: TabItemProps) {
|
|
const activeClass = "text-gray-900";
|
|
const inactiveClass = "text-gray-400";
|
|
const colorClass = isActive ? activeClass : inactiveClass;
|
|
|
|
return (
|
|
<span
|
|
className={`flex flex-col items-center gap-0.5 py-2 px-4 ${colorClass}`}
|
|
>
|
|
<LucideIcon name={icon} size={20} />
|
|
<span className="text-xs">{label}</span>
|
|
</span>
|
|
);
|
|
}
|
|
|
|
export function BottomTabBar() {
|
|
const { data: auth } = useAuth();
|
|
const isAuthenticated = !!auth?.user;
|
|
const openCatalogSearch = useUIStore((s) => s.openCatalogSearch);
|
|
const openAuthPrompt = useUIStore((s) => s.openAuthPrompt);
|
|
const matchRoute = useMatchRoute();
|
|
|
|
const isHome = !!matchRoute({ to: "/" });
|
|
const isCollection = !!matchRoute({ to: "/collection", fuzzy: true });
|
|
const isSetups = !!matchRoute({ to: "/setups", fuzzy: true });
|
|
|
|
return (
|
|
<motion.div
|
|
initial={{ y: 20, opacity: 0 }}
|
|
animate={{ y: 0, opacity: 1 }}
|
|
transition={{ duration: 0.2, ease: "easeOut" }}
|
|
className="fixed bottom-0 left-0 right-0 md:hidden z-20 bg-white border-t border-gray-100 pb-[env(safe-area-inset-bottom)]"
|
|
>
|
|
<div className="flex justify-around">
|
|
{/* Home tab — always a Link */}
|
|
<Link to="/">
|
|
<TabItemWrapper icon="house" label="Home" isActive={isHome} />
|
|
</Link>
|
|
|
|
{/* Collection tab — Link if authenticated, button if anonymous */}
|
|
{isAuthenticated ? (
|
|
<Link to="/collection">
|
|
<TabItemWrapper
|
|
icon="package"
|
|
label="Collection"
|
|
isActive={isCollection}
|
|
/>
|
|
</Link>
|
|
) : (
|
|
<button type="button" onClick={openAuthPrompt}>
|
|
<TabItemWrapper
|
|
icon="package"
|
|
label="Collection"
|
|
isActive={isCollection}
|
|
/>
|
|
</button>
|
|
)}
|
|
|
|
{/* Setups tab — Link if authenticated, button if anonymous */}
|
|
{isAuthenticated ? (
|
|
<Link to="/setups">
|
|
<TabItemWrapper icon="layers" label="Setups" isActive={isSetups} />
|
|
</Link>
|
|
) : (
|
|
<button type="button" onClick={openAuthPrompt}>
|
|
<TabItemWrapper icon="layers" label="Setups" isActive={isSetups} />
|
|
</button>
|
|
)}
|
|
|
|
{/* Search tab — always a button, opens CatalogSearchOverlay */}
|
|
<button type="button" onClick={() => openCatalogSearch("collection")}>
|
|
<TabItemWrapper icon="search" label="Search" isActive={false} />
|
|
</button>
|
|
</div>
|
|
</motion.div>
|
|
);
|
|
}
|