| 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> |
| </> |
| ) |
| } |
|
|