Files
Nearle_site/src/components/layout/Navbar.tsx
2026-05-27 15:11:56 +05:30

167 lines
5.4 KiB
TypeScript

'use client'
import Link from 'next/link'
import { usePathname } from 'next/navigation'
import { useEffect, useState } from 'react'
import { AnimatePresence, motion } from 'framer-motion'
import { Menu, X } from 'lucide-react'
import { NearleLogo } from '@/components/ui/NearleLogo'
import { useScrollSpy } from '@/lib/useScrollSpy'
type NavItem = {
label: string
href: string
sectionId?: string
}
const HOME_SECTIONS = [
'home',
'features',
'map',
'download',
'contact',
]
const NAV: NavItem[] = [
{ label: 'Home', href: '/', sectionId: 'home' },
{ label: 'About', href: '/about' },
{ label: 'Communities', href: '/communities', sectionId: 'map' },
{ label: 'Businesses', href: '/businesses' },
{ label: 'Download', href: '/#download', sectionId: 'download' },
{ label: 'Contact', href: '/contact', sectionId: 'contact' },
]
export function Navbar() {
const pathname = usePathname()
const [scrolled, setScrolled] = useState(false)
const [open, setOpen] = useState(false)
const activeSection = useScrollSpy(HOME_SECTIONS)
useEffect(() => {
const onScroll = () => setScrolled(window.scrollY > 60)
onScroll()
window.addEventListener('scroll', onScroll, { passive: true })
return () => window.removeEventListener('scroll', onScroll)
}, [])
useEffect(() => {
setOpen(false)
}, [pathname])
const isActive = (item: NavItem) => {
if (item.href.startsWith('/#')) {
if (pathname !== '/') return false
return activeSection === item.sectionId
}
if (item.href === '/') {
return (
pathname === '/' &&
(activeSection === 'home' || activeSection === null)
)
}
return pathname === item.href || pathname.startsWith(item.href + '/')
}
return (
<header
className={`fixed top-0 inset-x-0 z-50 transition-all duration-400 ${
scrolled
? 'bg-white/85 backdrop-blur-xl border-b border-purple-lavender shadow-nearle-sm'
: 'bg-transparent border-b border-transparent'
}`}
>
<nav className="max-w-7xl mx-auto px-5 sm:px-8 h-14 md:h-16 flex items-center justify-between">
<Link href="/" className="flex items-center gap-2 group">
<NearleLogo size={32} />
<span className="font-display font-extrabold text-purple-deep text-xl tracking-tight">
Nearle
</span>
</Link>
<ul className="hidden lg:flex items-center gap-1">
{NAV.map((item) => {
const active = isActive(item)
return (
<li key={item.label}>
<Link
href={item.href}
className={`px-4 py-1.5 rounded-full text-sm transition-all duration-200 ${
active
? 'bg-purple-lavender text-purple-deep font-semibold'
: 'text-nearle-mid hover:text-purple-deep'
}`}
>
{item.label}
</Link>
</li>
)
})}
</ul>
<div className="hidden lg:flex items-center gap-3">
<Link
href="/#download"
className="bg-purple-deep text-white rounded-full px-5 py-2 font-semibold text-sm hover:bg-purple-primary shadow-cta hover:shadow-cta-hover transition-all"
>
Get Ready Now
</Link>
</div>
<button
aria-label="Toggle menu"
onClick={() => setOpen((v) => !v)}
className="lg:hidden inline-flex items-center justify-center w-10 h-10 rounded-full text-purple-deep hover:bg-purple-soft transition"
>
{open ? <X size={22} /> : <Menu size={22} />}
</button>
</nav>
<AnimatePresence>
{open && (
<motion.div
key="mobile-drawer"
initial={{ opacity: 0, y: -12 }}
animate={{ opacity: 1, y: 0 }}
exit={{ opacity: 0, y: -12 }}
transition={{ duration: 0.25 }}
className="lg:hidden bg-white border-b border-purple-lavender shadow-nearle-sm"
>
<ul className="max-w-7xl mx-auto px-5 py-4 flex flex-col gap-1">
{NAV.map((item, i) => {
const active = isActive(item)
return (
<motion.li
key={item.label}
initial={{ opacity: 0, x: -8 }}
animate={{ opacity: 1, x: 0 }}
transition={{ delay: i * 0.04 }}
>
<Link
href={item.href}
className={`block px-4 py-3 rounded-xl text-base transition-all ${
active
? 'bg-purple-lavender text-purple-deep font-semibold'
: 'text-nearle-mid hover:bg-purple-soft hover:text-purple-deep'
}`}
>
{item.label}
</Link>
</motion.li>
)
})}
<li className="pt-3">
<Link
href="/#download"
className="block text-center bg-purple-deep text-white rounded-full px-5 py-3 font-semibold hover:bg-purple-primary shadow-cta transition-all"
>
Get Ready Now
</Link>
</li>
</ul>
</motion.div>
)}
</AnimatePresence>
</header>
)
}