Files
Skyheal/app/components/layout/Navbar.tsx
2026-01-22 15:43:18 +05:30

242 lines
11 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
"use client";
import { useState, useEffect } from "react";
import { motion, AnimatePresence, stagger } from "framer-motion";
import { Menu, X, Activity, Brain, Mic, ShieldCheck, ChevronRight, Sparkles } from "lucide-react";
const navItems = [
{ name: "Devices", icon: Activity, href: "/devices" },
{ name: "Voice-to-SOAP", icon: Mic, href: "/#voice-soap" },
{ name: "AI Diagnosis", icon: Brain, href: "/#diagnosis" },
{ name: "Apps & Security", icon: ShieldCheck, href: "/apps" },
] as const;
const containerVariants = {
hidden: { opacity: 0 },
visible: {
opacity: 1,
transition: { staggerChildren: 0.07, delayChildren: 0.1 },
},
exit: { opacity: 0, transition: { staggerChildren: 0.05, staggerDirection: -1 } },
};
const itemVariants = {
hidden: { opacity: 0, x: 40, scale: 0.96 },
visible: { opacity: 1, x: 0, scale: 1, transition: { type: "spring", damping: 22, stiffness: 140 } },
exit: { opacity: 0, x: 20, scale: 0.95 },
};
export default function Navbar() {
const [isScrolled, setIsScrolled] = useState(false);
const [isMobileMenuOpen, setIsMobileMenuOpen] = useState(false);
useEffect(() => {
const handleScroll = () => setIsScrolled(window.scrollY > 20);
window.addEventListener("scroll", handleScroll);
return () => window.removeEventListener("scroll", handleScroll);
}, []);
return (
<>
<nav
className={`fixed top-0 left-0 right-0 z-50 transition-all duration-500 ${
isScrolled ? "py-3" : "py-6"
}`}
>
<div className="container mx-auto px-4 sm:px-6 lg:px-8">
<motion.div
initial={{ y: -100 }}
animate={{ y: 0 }}
transition={{ duration: 0.6, ease: "easeOut" }}
className={`relative rounded-2xl lg:rounded-3xl px-4 sm:px-6 lg:px-8 py-3 sm:py-4 flex items-center justify-between transition-all duration-500 ${
isScrolled
? "bg-black/90 backdrop-blur-xl shadow-2xl border border-white/10"
: "bg-gradient-to-r from-white/5 via-white/10 to-white/5 backdrop-blur-lg border border-white/5"
}`}
>
{/* Animated background gradient (unchanged) */}
<div className="absolute inset-0 rounded-2xl lg:rounded-3xl overflow-hidden pointer-events-none">
<motion.div
animate={{
background: [
"radial-gradient(circle at 0% 0%, rgba(59, 130, 246, 0.1) 0%, transparent 50%)",
"radial-gradient(circle at 100% 100%, rgba(139, 92, 246, 0.1) 0%, transparent 50%)",
"radial-gradient(circle at 0% 0%, rgba(59, 130, 246, 0.1) 0%, transparent 50%)",
],
}}
transition={{ duration: 8, repeat: Infinity, ease: "linear" }}
className="absolute inset-0"
/>
</div>
{/* Logo (unchanged) */}
<a href="/" className="flex items-center gap-2 sm:gap-3 group relative z-10">
<div className="relative">
<div className="absolute inset-0 bg-blue-500/20 blur-xl rounded-full group-hover:bg-blue-500/30 transition-all" />
<Activity className="w-7 h-7 sm:w-9 sm:h-9 text-white relative z-10 transition-transform group-hover:scale-110 group-hover:rotate-180 duration-500" />
</div>
<span className="text-xl sm:text-2xl lg:text-3xl font-bold tracking-tight text-white relative">
Skyheal
<span className="absolute -top-1 -right-6 text-[8px] text-blue-400 font-semibold">AI</span>
</span>
</a>
{/* Desktop Menu (unchanged) */}
<div className="hidden lg:flex items-center gap-1 xl:gap-2 relative z-10">
{navItems.map((item, index) => (
<motion.a
key={item.name}
href={item.href}
className="relative px-4 py-2.5 rounded-xl text-sm font-semibold text-zinc-300 hover:text-white transition-colors group"
>
<span className="relative z-10 flex items-center gap-2">
<item.icon className="w-4 h-4 transition-transform group-hover:scale-110" />
{item.name}
</span>
{/* You can keep or remove the active indicator if not needed */}
</motion.a>
))}
<motion.button
whileHover={{ scale: 1.05 }}
whileTap={{ scale: 0.95 }}
className="ml-4 relative group overflow-hidden bg-gradient-to-r from-blue-600 to-purple-600 text-white px-6 py-2.5 rounded-xl text-sm font-bold shadow-lg shadow-blue-500/25 hover:shadow-blue-500/40 transition-all"
>
<span className="relative z-10 flex items-center gap-2">
<Sparkles className="w-4 h-4" />
Get Started
</span>
<motion.div
className="absolute inset-0 bg-gradient-to-r from-purple-600 to-blue-600"
initial={{ x: "100%" }}
whileHover={{ x: 0 }}
transition={{ duration: 0.3 }}
/>
</motion.button>
</div>
{/* Mobile Toggle bigger touch target */}
<motion.button
whileTap={{ scale: 0.92 }}
className="lg:hidden relative z-10 text-white p-3 rounded-full hover:bg-white/10 transition-colors"
onClick={() => setIsMobileMenuOpen(!isMobileMenuOpen)}
>
<AnimatePresence mode="wait">
{isMobileMenuOpen ? (
<motion.div
key="close"
initial={{ rotate: -90, opacity: 0 }}
animate={{ rotate: 0, opacity: 1 }}
exit={{ rotate: 90, opacity: 0 }}
transition={{ duration: 0.25 }}
>
<X className="w-7 h-7" />
</motion.div>
) : (
<motion.div
key="menu"
initial={{ rotate: -90, opacity: 0 }}
animate={{ rotate: 0, opacity: 1 }}
exit={{ rotate: 90, opacity: 0 }}
transition={{ duration: 0.25 }}
>
<Menu className="w-7 h-7" />
</motion.div>
)}
</AnimatePresence>
</motion.button>
</motion.div>
</div>
</nav>
{/* Mobile Menu Slide from right + better visuals */}
<AnimatePresence>
{isMobileMenuOpen && (
<>
{/* Backdrop with subtle vignette */}
<motion.div
initial={{ opacity: 0 }}
animate={{ opacity: 1 }}
exit={{ opacity: 0 }}
transition={{ duration: 0.4 }}
className="fixed inset-0 bg-black/70 backdrop-blur-md z-40 lg:hidden"
onClick={() => setIsMobileMenuOpen(false)}
/>
{/* Side sheet style menu */}
<motion.div
initial={{ x: "100%" }}
animate={{ x: 0 }}
exit={{ x: "100%" }}
transition={{ type: "spring", damping: 30, stiffness: 200 }}
className="fixed top-0 right-0 bottom-0 w-4/5 max-w-sm z-50 lg:hidden"
>
<div className="h-full bg-gradient-to-b from-black/95 via-black/97 to-black/95 backdrop-blur-2xl border-l border-white/10 shadow-2xl flex flex-col">
{/* Header */}
<div className="p-6 border-b border-white/10 flex items-center justify-between">
<div className="flex items-center gap-3">
<Activity className="w-8 h-8 text-blue-400" />
<span className="text-xl font-bold text-white tracking-tight">Skyheal</span>
</div>
<button
onClick={() => setIsMobileMenuOpen(false)}
className="p-2 rounded-full hover:bg-white/10 transition-colors"
>
<X className="w-6 h-6 text-zinc-300" />
</button>
</div>
{/* Menu Items */}
<motion.div
variants={containerVariants}
initial="hidden"
animate="visible"
exit="exit"
className="flex-1 p-6 space-y-2 overflow-y-auto"
>
{navItems.map((item) => (
<motion.a
key={item.name}
href={item.href}
variants={itemVariants}
onClick={() => setIsMobileMenuOpen(false)}
className="group flex items-center gap-4 p-4 rounded-2xl hover:bg-white/5 active:bg-white/8 transition-all duration-200"
>
<div className="w-12 h-12 rounded-xl bg-gradient-to-br from-blue-500/10 to-purple-500/10 flex items-center justify-center border border-white/10 group-hover:border-blue-500/40 group-hover:shadow-[0_0_16px_rgba(59,130,246,0.25)] transition-all duration-300">
<item.icon className="w-6 h-6 text-blue-400 group-hover:scale-110 transition-transform" />
</div>
<span className="text-lg font-medium text-white group-hover:text-blue-300 transition-colors">
{item.name}
</span>
<ChevronRight className="ml-auto w-5 h-5 text-zinc-500 group-hover:text-blue-400 group-hover:translate-x-1 transition-all" />
</motion.a>
))}
</motion.div>
{/* CTA at bottom */}
<div className="p-6 border-t border-white/10 mt-auto">
<motion.button
whileHover={{ scale: 1.03 }}
whileTap={{ scale: 0.97 }}
className="w-full relative overflow-hidden bg-gradient-to-r from-blue-600 to-purple-600 text-white py-4 rounded-2xl font-semibold text-base shadow-xl shadow-blue-600/30 hover:shadow-blue-600/50 transition-all group"
>
<span className="relative z-10 flex items-center justify-center gap-2.5">
<Sparkles className="w-5 h-5" />
Get Started Free
</span>
<motion.div
className="absolute inset-0 bg-gradient-to-r from-purple-600 via-blue-600 to-purple-600 bg-[length:200%_100%]"
initial={{ x: "100%" }}
whileHover={{ x: "-100%" }}
transition={{ duration: 1.2, ease: "linear" }}
/>
</motion.button>
</div>
</div>
</motion.div>
</>
)}
</AnimatePresence>
</>
);
}