167 lines
5.4 KiB
TypeScript
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>
|
|
)
|
|
}
|