Portfolio / frontend /src /components /Navbar.jsx
parthib07's picture
Upload 32 files
0ed2558 verified
import { useState, useEffect } from 'react'
import { motion, AnimatePresence } from 'framer-motion'
import { FiSun, FiMoon, FiX, FiTerminal } from 'react-icons/fi'
import { useScrollSpy } from '../hooks/useScrollSpy'
import resumePDF from '../assets/Resume.pdf'
const NAV_LINKS = [
{ id: 'about', label: 'About' },
{ id: 'skills', label: 'Skills' },
{ id: 'experience', label: 'Experience' },
{ id: 'publications', label: 'Publications' },
{ id: 'projects', label: 'Projects' },
{ id: 'education', label: 'Education' },
{ id: 'blog', label: 'Blog' },
{ id: 'contact', label: 'Contact' },
]
export default function Navbar({ theme, setTheme }) {
const [scrolled, setScrolled] = useState(false)
const [mobileOpen, setMobileOpen] = useState(false)
const activeId = useScrollSpy(NAV_LINKS.map(l => l.id))
useEffect(() => {
const fn = () => setScrolled(window.scrollY > 40)
window.addEventListener('scroll', fn, { passive: true })
return () => window.removeEventListener('scroll', fn)
}, [])
const scrollTo = (id) => {
document.getElementById(id)?.scrollIntoView({ behavior: 'smooth' })
setMobileOpen(false)
}
return (
<>
<motion.nav
className={`navbar ${scrolled ? 'scrolled' : ''}`}
initial={{ y: -70, opacity: 0 }}
animate={{ y: 0, opacity: 1 }}
transition={{ duration: 0.55, ease: [0.22, 1, 0.36, 1] }}
>
<div className="container">
<div className="navbar-inner">
<motion.div className="navbar-logo" style={{ cursor: 'pointer', letterSpacing: '-0.03em', fontSize: '1.05rem' }}
onClick={() => window.scrollTo({ top: 0, behavior: 'smooth' })}
whileHover={{ scale: 1.04 }}>
Parthib<span style={{ color: 'var(--accent-tertiary)', margin: '0 1px' }}> </span>Karak<span style={{ color: 'var(--accent-tertiary)' }}>.</span>
</motion.div>
<ul className="navbar-links">
{NAV_LINKS.map(link => (
<li key={link.id}>
<a href={`#${link.id}`}
className={activeId === link.id ? 'active' : ''}
onClick={e => { e.preventDefault(); scrollTo(link.id) }}>
{link.label}
</a>
</li>
))}
</ul>
<div className="navbar-actions">
<button className="theme-toggle" onClick={() => setTheme(theme === 'dark' ? 'light' : 'dark')} aria-label="Toggle theme">
{theme === 'dark' ? <FiSun size={15} /> : <FiMoon size={15} />}
</button>
<motion.a href={resumePDF} className="btn btn-primary btn-sm" download="Parthib_Karak_Resume.pdf"
whileHover={{ scale: 1.04 }} whileTap={{ scale: 0.97 }}>
Resume ↓
</motion.a>
<button className="hamburger" onClick={() => setMobileOpen(true)} aria-label="Open menu">
<span /><span /><span />
</button>
</div>
</div>
</div>
</motion.nav>
<AnimatePresence>
{mobileOpen && (
<motion.div className="mobile-menu open"
initial={{ opacity: 0 }} animate={{ opacity: 1 }} exit={{ opacity: 0 }}
transition={{ duration: 0.2 }}>
<button onClick={() => setMobileOpen(false)} aria-label="Close menu"
style={{ position: 'absolute', top: 22, right: 22, background: 'none', border: 'none', cursor: 'pointer', color: 'var(--text-primary)', fontSize: '1.3rem' }}>
<FiX />
</button>
{NAV_LINKS.map((link, i) => (
<motion.a key={link.id} href={`#${link.id}`}
initial={{ opacity: 0, y: 18 }} animate={{ opacity: 1, y: 0 }}
transition={{ delay: i * 0.06 }}
onClick={e => { e.preventDefault(); scrollTo(link.id) }}>
{link.label}
</motion.a>
))}
</motion.div>
)}
</AnimatePresence>
</>
)
}