1 import { useState, useEffect } from "react";
2 import { useLocation } from "wouter";
3 import {
4 Home,
5 Heart,
6 Search,
7 HelpCircle,
8 User,
9 Minimize2,
10 Maximize2,
11 ChevronDown,
12 ChevronUp,
13 Compass
14 } from "lucide-react";
15 import { cn } from "@/lib/utils";
16 import { motion, AnimatePresence } from "framer-motion";
17
18 export function BottomNav() {
19 const [location, setLocation] = useLocation();
20
21 // Size modes: "compact" (slim bar), "mini" (small floating bubble/pill), "standard" (full)
22 const [sizeMode, setSizeMode] = useState<"compact" | "mini" | "standard">(() => {
23 const saved = localStorage.getItem("jcr_bottom_nav_size");
24 return (saved as "compact" | "mini" | "standard") || "compact";
25 });
26
27 useEffect(() => {
28 localStorage.setItem("jcr_bottom_nav_size", sizeMode);
29 // Broadcast event so layout can adjust bottom padding if needed
30 window.dispatchEvent(new CustomEvent("bottom-nav-resize", { detail: { sizeMode } }));
31 }, [sizeMode]);
32
33 const navItems = [
34 { icon: Home, label: "Home", path: "/" },
35 { icon: Search, label: "Finder", path: "/device-finder" },
36 { icon: Compass, label: "Tools", path: "/software-tools" },
37 { icon: HelpCircle, label: "Support", path: "/troubleshooting" },
38 { icon: Heart, label: "Saved", path: "/favorites" },
39 { icon: User, label: "Profile", path: "/offline-settings" },
40 ];
41
42 const toggleSizeMode = () => {
43 setSizeMode((prev) => {
44 if (prev === "compact") return "mini";
45 if (prev === "mini") return "standard";
46 return "compact";
47 });
48 };
49
50 // If in mini bubble mode, show a small floating pill on the bottom-left so floating bubbles on right are 100% visible
51 if (sizeMode === "mini") {
52 return (
53 <div className="fixed bottom-4 left-4 z-40 lg:hidden">
54 <motion.button
55 initial={{ scale: 0.8, opacity: 0 }}
56 animate={{ scale: 1, opacity: 1 }}
57 onClick={() => setSizeMode("compact")}
58 className="flex items-center gap-2 px-3 py-2 rounded-full bg-card/90 backdrop-blur-xl border border-border/60 shadow-xl text-foreground hover:text-primary transition-all group"
59 title="Expand Navigation Bar"
60 >
61 <div className="h-6 w-6 rounded-full bg-primary/10 text-primary flex items-center justify-center">
62 <Maximize2 className="h-3.5 w-3.5" />
63 </div>
64 <span className="text-xs font-semibold pr-1">Nav</span>
65 </motion.button>
66 </div>
67 );
68 }
69
70 // Compact Mode (Default): Slim, low-profile dock that keeps floating bubbles completely unobstructed
71 if (sizeMode === "compact") {
72 return (
73 <nav className="fixed bottom-2 left-3 right-3 sm:left-auto sm:right-auto sm:w-[460px] sm:left-1/2 sm:-translate-x-1/2 z-30 lg:hidden">
74 <div className="bg-card/85 backdrop-blur-2xl border border-border/50 rounded-2xl shadow-xl px-2 py-1.5 flex items-center justify-between">
75 <div className="flex items-center justify-around flex-1">
76 {navItems.map((item) => {
77 const isActive = location === item.path;
78 const Icon = item.icon;
79
80 return (
81 <button
82 key={item.path}
83 onClick={() => setLocation(item.path)}
84 className={cn(
85 "flex flex-col items-center justify-center py-1 px-2 rounded-xl transition-all duration-200",
86 isActive
87 ? "text-primary bg-primary/10 font-bold"
88 : "text-muted-foreground hover:text-foreground hover:bg-muted/40"
89 )}
90 >
91 <Icon className={cn("h-4 w-4 transition-transform", isActive && "scale-110")} />
92 <span className="text-[9px] tracking-tight mt-0.5">{item.label}</span>
93 </button>
94 );
95 })}
96 </div>
97
98 {/* Quick Size Shrink / Expand Switcher */}
99 <button
100 onClick={toggleSizeMode}
101 className="ml-1 p-1.5 rounded-xl text-muted-foreground hover:text-foreground hover:bg-muted/50 transition-colors border-l border-border/30 pl-2"
102 title="Make bottom navigation smaller / mini"
103 >
104 <Minimize2 className="h-3.5 w-3.5" />
105 </button>
106 </div>
107 </nav>
108 );
109 }
110
111 // Standard Mode: Full bar with size shrink button
112 return (
113 <nav className="fixed bottom-0 left-0 right-0 z-30 lg:hidden bg-background/90 backdrop-blur-xl border-t border-border/40 pb-safe shadow-lg">
114 <div className="flex items-center justify-around h-14 px-2">
115 {navItems.map((item) => {
116 const isActive = location === item.path;
117 const Icon = item.icon;
118
119 return (
120 <button
121 key={item.path}
122 onClick={() => setLocation(item.path)}
123 className={cn(
124 "flex flex-col items-center justify-center gap-0.5 w-full h-full transition-all duration-200",
125 isActive ? "text-primary font-bold" : "text-muted-foreground hover:text-foreground"
126 )}
127 >
128 <div className={cn(
129 "p-1 rounded-xl transition-all duration-200",
130 isActive && "bg-primary/10 scale-105"
131 )}>
132 <Icon className="h-4.5 w-4.5" />
133 </div>
134 <span className="text-[9px] uppercase tracking-wider">{item.label}</span>
135 </button>
136 );
137 })}
138
139 {/* Size Shrink toggle */}
140 <button
141 onClick={toggleSizeMode}
142 className="flex flex-col items-center justify-center px-2 text-muted-foreground hover:text-foreground transition-colors"
143 title="Make bottom bar smaller"
144 >
145 <div className="p-1 rounded-lg hover:bg-muted/60">
146 <Minimize2 className="h-4 w-4" />
147 </div>
148 <span className="text-[8px] uppercase tracking-wider">Slim</span>
149 </button>
150 </div>
151 </nav>
152 );
153 }