'use client'; import { useState, useEffect, useCallback, useRef, useMemo } from 'react'; import Link from 'next/link'; import Image from 'next/image'; import { usePathname } from 'next/navigation'; import { Menu, X } from 'lucide-react'; import { AnimatePresence, motion } from 'framer-motion'; import { Button } from '@/components/ui/button'; import { COMPANY_INFO, NAVIGATION, type NavigationItem } from '@/lib/constants'; import { useFocusTrap } from '@/hooks/use-focus-trap'; export function Header() { const [isOpen, setIsOpen] = useState(false); const [isScrolled, setIsScrolled] = useState(false); const [activeSection, setActiveSection] = useState('home'); const pathname = usePathname(); const focusTrapRef = useFocusTrap(isOpen); const sectionCacheRef = useRef(new Map()); const activeSectionRef = useRef(activeSection); const isManualNavigationRef = useRef(false); const manualNavTimeoutRef = useRef(null); useEffect(() => { activeSectionRef.current = activeSection; }, [activeSection]); useEffect(() => { let ticking = false; const updateSectionCache = () => { const sections = ['home', 'services', 'products', 'cases', 'about', 'news', 'contact']; sections.forEach(sectionId => { const element = document.getElementById(sectionId); if (element) { sectionCacheRef.current.set(sectionId, { offsetTop: element.offsetTop, offsetHeight: element.offsetHeight }); } }); }; updateSectionCache(); const handleScroll = () => { if (!ticking) { requestAnimationFrame(() => { setIsScrolled(window.scrollY > 20); if (pathname === '/' && !isManualNavigationRef.current) { const scrollPosition = window.scrollY + 100; const sections = ['home', 'services', 'products', 'cases', 'about', 'news', 'contact']; let currentSection = 'home'; for (const sectionId of sections) { const cached = sectionCacheRef.current.get(sectionId); if (cached && scrollPosition >= cached.offsetTop && scrollPosition < cached.offsetTop + cached.offsetHeight) { currentSection = sectionId; break; } } if (currentSection !== activeSectionRef.current) { setActiveSection(currentSection); } } ticking = false; }); ticking = true; } }; const handleGlobalKeyDown = (e: KeyboardEvent) => { if (e.key === 'Escape' && isOpen) { setIsOpen(false); } }; window.addEventListener('scroll', handleScroll, { passive: true }); window.addEventListener('keydown', handleGlobalKeyDown); window.addEventListener('resize', updateSectionCache); handleScroll(); return () => { window.removeEventListener('scroll', handleScroll); window.removeEventListener('keydown', handleGlobalKeyDown); window.removeEventListener('resize', updateSectionCache); if (manualNavTimeoutRef.current) { clearTimeout(manualNavTimeoutRef.current); } }; }, [pathname, isOpen]); const handleKeyDown = useCallback((e: React.KeyboardEvent) => { if (e.key === 'Enter' || e.key === ' ') { e.preventDefault(); setIsOpen(!isOpen); } if (e.key === 'Escape' && isOpen) { setIsOpen(false); } }, [isOpen]); const handleNavClick = useCallback((item: NavigationItem) => { if (pathname === '/' && item.href.startsWith('/#')) { isManualNavigationRef.current = true; if (manualNavTimeoutRef.current) { clearTimeout(manualNavTimeoutRef.current); } setActiveSection(item.id); const targetElement = document.getElementById(item.id); if (targetElement) { const checkScrollComplete = () => { const targetPosition = targetElement.offsetTop; const currentPosition = window.scrollY; const threshold = 100; if (Math.abs(currentPosition - targetPosition) < threshold) { manualNavTimeoutRef.current = setTimeout(() => { isManualNavigationRef.current = false; }, 500); } else { requestAnimationFrame(checkScrollComplete); } }; requestAnimationFrame(checkScrollComplete); } else { manualNavTimeoutRef.current = setTimeout(() => { isManualNavigationRef.current = false; }, 2000); } } setIsOpen(false); }, [pathname]); const isActive = useCallback((item: NavigationItem) => { if (pathname === '/') { return activeSection === item.id; } const navPath = item.href.split('#')[0]; return pathname === navPath || pathname.startsWith(navPath + '/'); }, [pathname, activeSection]); const navigationItems = useMemo(() => NAVIGATION, []); return ( <>
{COMPANY_INFO.name}
{isOpen && (
setIsOpen(false)} aria-hidden="true" /> )} ); }