'use client' import React, { useState, useEffect, useRef, useCallback } from 'react' import type { Header as HeaderType, Page } from '@/payload-types' import Link from 'next/link' import { usePathname } from 'next/navigation' import { MenuIcon, XIcon, ChevronDownIcon, ChevronRightIcon } from 'lucide-react' import { SocialIconsRow } from '@/components/SocialIcons' import type { SocialLinkData } from '@/components/SocialIcons' type NavChild = NonNullable[number]['children']>[number]> type NavItem = NonNullable[number] function resolveHref(item: { type?: string | null url?: string | null reference?: { relationTo?: string; value?: number | Page | null } | null }): string { if (item.type === 'reference' && item.reference?.value) { const page = item.reference.value if (typeof page === 'object' && page !== null) { return page.slug === 'home' || page.slug === 'startsida' ? '/' : `/${(page as Page).slug}` } return '#' } return item.url || '#' } const SwedishFlag = () => ( ) function groupChildren(children: NavChild[]): { heading: string | null; links: NavChild[] }[] { const main: NavChild[] = [] const groups: Record = {} const groupOrder: string[] = [] for (const child of children) { if (!child.group) { main.push(child) } else { if (!groups[child.group]) { groups[child.group] = [] groupOrder.push(child.group) } groups[child.group].push(child) } } const result: { heading: string | null; links: NavChild[] }[] = [] if (main.length > 0) result.push({ heading: null, links: main }) for (const g of groupOrder) result.push({ heading: g, links: groups[g] }) return result } function useFocusTrap(containerRef: React.RefObject, active: boolean) { useEffect(() => { if (!active || !containerRef.current) return const container = containerRef.current const focusableSelector = 'a[href], button:not([disabled]), input:not([disabled]), select:not([disabled]), textarea:not([disabled]), [tabindex]:not([tabindex="-1"])' const getFocusableElements = () => Array.from(container.querySelectorAll(focusableSelector)) const elements = getFocusableElements() if (elements.length > 0) { setTimeout(() => elements[0]?.focus(), 100) } const handleKeyDown = (e: KeyboardEvent) => { if (e.key !== 'Tab') return const focusable = getFocusableElements() if (focusable.length === 0) return const first = focusable[0] const last = focusable[focusable.length - 1] if (e.shiftKey) { if (document.activeElement === first) { e.preventDefault() last.focus() } } else { if (document.activeElement === last) { e.preventDefault() first.focus() } } } document.addEventListener('keydown', handleKeyDown) return () => document.removeEventListener('keydown', handleKeyDown) }, [active, containerRef]) } function isActiveSection(item: NavItem, pathname: string | null): boolean { if (!pathname || !item.children?.length) { const href = resolveHref(item) if (href === '/') return pathname === '/' return pathname?.startsWith(href) || false } return item.children.some((child) => { const childHref = resolveHref(child) if (childHref === '/' || childHref === '#') return false return pathname?.startsWith(childHref) || false }) } const MegaMenuPanel: React.FC<{ item: NavItem headerRef: React.RefObject panelRef: React.RefObject onClose: () => void }> = ({ item, headerRef, panelRef, onClose }) => { const [top, setTop] = useState(72) useEffect(() => { const update = () => { if (headerRef.current) { const rect = headerRef.current.getBoundingClientRect() setTop(rect.bottom) } } update() window.addEventListener('scroll', update, { passive: true }) window.addEventListener('resize', update) return () => { window.removeEventListener('scroll', update) window.removeEventListener('resize', update) } }, [headerRef]) const columns = groupChildren(item.children || []) return ( <> {/* Blur backdrop */}
{/* Panel */}
{/* Left: category title */}

{item.label}

{/* Right: columns */}
{columns.map((col, ci) => (
{col.heading && (

{col.heading}

)}
    {col.links.map((link, li) => (
  • {link.label}
  • ))}
))}
) } export const HeaderNav: React.FC<{ data: HeaderType; socialLinks?: SocialLinkData[] }> = ({ data, socialLinks = [], }) => { const navItems = (data?.navItems || []) as NavItem[] const [mobileOpen, setMobileOpen] = useState(false) const [openDropdown, setOpenDropdown] = useState(null) const [mobileOpenSection, setMobileOpenSection] = useState(null) const pathname = usePathname() const navRef = useRef(null) const headerRef = useRef(null) const megaMenuRef = useRef(null) const mobileMenuRef = useRef(null) useFocusTrap(mobileMenuRef, mobileOpen) useEffect(() => { if (navRef.current) { headerRef.current = navRef.current.closest('header') } }, []) useEffect(() => { setMobileOpen(false) setMobileOpenSection(null) setOpenDropdown(null) }, [pathname]) useEffect(() => { document.body.style.overflow = mobileOpen ? 'hidden' : '' return () => { document.body.style.overflow = '' } }, [mobileOpen]) useEffect(() => { const handler = (e: MouseEvent) => { if ( navRef.current && !navRef.current.contains(e.target as Node) && megaMenuRef.current && !megaMenuRef.current.contains(e.target as Node) ) { setOpenDropdown(null) } } document.addEventListener('mousedown', handler) return () => document.removeEventListener('mousedown', handler) }, []) useEffect(() => { const handler = (e: KeyboardEvent) => { if (e.key === 'Escape') { setOpenDropdown(null) setMobileOpen(false) } } document.addEventListener('keydown', handler) return () => document.removeEventListener('keydown', handler) }, []) const closeMobile = () => { setMobileOpen(false) setMobileOpenSection(null) } const activeItem = navItems.find((item) => item.label === openDropdown) return ( <> {/* ── Desktop nav — shows at lg (1024px+) ── */} {/* ── Mega menu panel ── */} {activeItem?.megaMenu && activeItem.children?.length && ( setOpenDropdown(null)} /> )} {/* ── Hamburger — shows below lg (mobile + tablet) ── */} {/* ── Tablet backdrop — blur overlay left of panel on md+ ── */}