Aimpress_site/src/components/Header.tsx
Vadym Samoilenko 8e5ba6f687 Close mobile menu automatically on scroll
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-08 16:11:42 +00:00

212 lines
10 KiB
TypeScript

import React, { useState, useEffect } from 'react';
import { useNavigate, useLocation } from 'react-router-dom';
import { motion } from 'framer-motion';
import Modal from './Modal';
import './Header.css';
const navItems = [
{ name: 'Home', link: '/' },
{ name: 'About Us', link: '/about' },
{ name: 'Services', link: '/services' },
{ name: 'Pricing', link: '/pricing' },
{ name: 'Blog', link: '/blog' },
{ name: 'Contacts', link: '#contact' },
];
const Header: React.FC = () => {
const [activeTab, setActiveTab] = useState('Home');
const [hoveredTab, setHoveredTab] = useState<string | null>(null);
const [isMobileMenuOpen, setIsMobileMenuOpen] = useState(false);
const [isLangOpen, setIsLangOpen] = useState(false);
const [isLoginOpen, setIsLoginOpen] = useState(false);
const [currentLang, setCurrentLang] = useState('Eng');
const [isScrolled, setIsScrolled] = useState(false);
const navigate = useNavigate();
const location = useLocation();
useEffect(() => {
const onScroll = () => {
setIsScrolled(window.scrollY > 50);
if (isMobileMenuOpen) setIsMobileMenuOpen(false);
};
window.addEventListener('scroll', onScroll, { passive: true });
return () => window.removeEventListener('scroll', onScroll);
}, [isMobileMenuOpen]);
// Close mobile menu on route change
useEffect(() => {
setIsMobileMenuOpen(false);
}, [location.pathname]);
const handleLangSelect = (lang: string) => {
setCurrentLang(lang);
setIsLangOpen(false);
};
const handleNavClick = (e: React.MouseEvent<HTMLAnchorElement>, item: typeof navItems[0]) => {
if (item.link.startsWith('/')) {
e.preventDefault();
navigate(item.link);
setIsMobileMenuOpen(false);
return;
}
// Hash links - if not on homepage, navigate there first
if (location.pathname !== '/') {
e.preventDefault();
navigate('/' + item.link);
setIsMobileMenuOpen(false);
return;
}
// On homepage, let the default anchor behavior work
setIsMobileMenuOpen(false);
};
return (
<>
<header className={`header ${isScrolled ? 'scrolled' : ''} ${isMobileMenuOpen ? 'mobile-open' : ''}`}>
<div className="container header-content">
<div className="logo-container">
<a href="/" onClick={(e) => { e.preventDefault(); navigate('/'); }}>
<picture>
<source media="(max-width: 768px)" srcSet="/logo/aimpress-logo-mobile.svg" />
<img src="/logo/aimpress-logo-desktop.svg" alt="AIMPRESS Logo" className="logo" />
</picture>
</a>
</div>
<nav className="desktop-nav">
<ul className="nav-list" onMouseLeave={() => setHoveredTab(null)}>
{navItems.map((item) => {
const isActive = (hoveredTab || activeTab) === item.name;
return (
<li
key={item.name}
className={`nav-item ${isActive ? 'active' : ''}`}
onMouseEnter={() => setHoveredTab(item.name)}
onClick={() => setActiveTab(item.name)}
>
<a
href={item.link}
className="nav-link"
onClick={(e) => handleNavClick(e, item)}
>
{item.name}
{isActive && (
<motion.div
className="active-pill"
layoutId="nav-pill"
transition={{ type: 'spring', stiffness: 500, damping: 30 }}
/>
)}
</a>
</li>
);
})}
</ul>
</nav>
<div className="header-actions">
<div className="auth-lang-group">
<div className="language-selector-wrapper">
<div
className="language-selector"
onClick={() => setIsLangOpen(!isLangOpen)}
>
<img src="/icons/planet.svg" alt="Language" className="lang-icon" />
<span>{currentLang}</span>
</div>
{isLangOpen && (
<motion.div
className="language-dropdown"
initial={{ opacity: 0, y: 10 }}
animate={{ opacity: 1, y: 0 }}
exit={{ opacity: 0, y: 10 }}
>
<div
className={`lang-option ${currentLang === 'Eng' ? 'active' : ''}`}
onClick={() => handleLangSelect('Eng')}
>
Eng
</div>
<div
className={`lang-option ${currentLang === 'Ukr' ? 'active' : ''}`}
onClick={() => handleLangSelect('Ukr')}
>
Ukr
</div>
</motion.div>
)}
</div>
<button className="login-btn" onClick={() => setIsLoginOpen(true)}>Log in</button>
</div>
<button className="mobile-menu-toggle" onClick={() => setIsMobileMenuOpen(!isMobileMenuOpen)}>
<span className="hamburger-line"></span>
<span className="hamburger-line"></span>
<span className="hamburger-line"></span>
</button>
</div>
</div>
{/* Mobile Menu Overlay */}
{isMobileMenuOpen && (
<motion.div
className="mobile-menu-overlay"
initial={{ opacity: 0, y: -20 }}
animate={{ opacity: 1, y: 0 }}
exit={{ opacity: 0, y: -20 }}
>
<ul className="mobile-nav-list">
{navItems.map((item) => (
<li
key={item.name}
className={`mobile-nav-item ${activeTab === item.name ? 'active' : ''}`}
>
<a href={item.link} onClick={(e) => {
handleNavClick(e, item);
setActiveTab(item.name);
}}>
<span style={{ position: 'relative', zIndex: 1 }}>{item.name}</span>
{activeTab === item.name && (
<motion.div
className="active-pill-mobile"
layoutId="nav-pill-mobile"
transition={{ type: 'spring', stiffness: 500, damping: 30 }}
/>
)}
</a>
</li>
))}
</ul>
</motion.div>
)}
</header>
{/* Login Modal */}
<Modal isOpen={isLoginOpen} onClose={() => setIsLoginOpen(false)}>
<div className="login-form-container">
<h2 className="login-title">Welcome Back</h2>
<form className="login-form" onSubmit={(e) => e.preventDefault()}>
<div className="form-group">
<label>Email / Login</label>
<input type="text" placeholder="Enter your email" className="glass-input" />
</div>
<div className="form-group">
<label>Password</label>
<input type="password" placeholder="Enter your password" className="glass-input" />
</div>
<button type="submit" className="submit-btn full-width">Log In</button>
<p className="login-footer">
Don't have an account? <a href="#">Sign up</a>
</p>
</form>
</div>
</Modal>
</>
);
};
export default Header;