148 lines
4.7 KiB
TypeScript
148 lines
4.7 KiB
TypeScript
'use client'
|
|
|
|
import { useEffect, useRef, useState, type MouseEvent } from 'react'
|
|
import Link from 'next/link'
|
|
import { usePathname } from 'next/navigation'
|
|
import saws from '../assets/saws.png'
|
|
import SocialMediaLinks from './social-media-links'
|
|
import './site-header.css'
|
|
|
|
const menuLinks = [
|
|
{ label: 'Home', href: '/' },
|
|
{ label: 'About', href: '/about-us' },
|
|
{ label: 'Services', href: '/services' },
|
|
{ label: 'Contact', href: '/contact' },
|
|
] as const
|
|
|
|
const ctaHref = '/contact'
|
|
|
|
function isActivePath(pathname: string, href: string) {
|
|
if (href === '/') return pathname === '/'
|
|
return pathname === href || pathname.startsWith(`${href}/`)
|
|
}
|
|
|
|
export default function SiteHeader() {
|
|
const pathname = usePathname()
|
|
const menuToggleRef = useRef<HTMLInputElement>(null)
|
|
const [scrolled, setScrolled] = useState(false)
|
|
const [pendingPath, setPendingPath] = useState<string | null>(null)
|
|
|
|
const activePath = pendingPath ?? pathname
|
|
|
|
useEffect(() => {
|
|
setPendingPath(null)
|
|
}, [pathname])
|
|
|
|
useEffect(() => {
|
|
const onScroll = () => setScrolled(window.scrollY > 24)
|
|
onScroll()
|
|
window.addEventListener('scroll', onScroll, { passive: true })
|
|
return () => window.removeEventListener('scroll', onScroll)
|
|
}, [])
|
|
|
|
const closeMobileMenu = () => {
|
|
if (menuToggleRef.current) {
|
|
menuToggleRef.current.checked = false
|
|
}
|
|
}
|
|
|
|
const handleNavClick =
|
|
(href: string) => (event: MouseEvent<HTMLAnchorElement>) => {
|
|
setPendingPath(href)
|
|
closeMobileMenu()
|
|
event.currentTarget.blur()
|
|
}
|
|
|
|
return (
|
|
<header className="site-header-wrapper">
|
|
<nav
|
|
className={`site-header${scrolled ? ' site-header--scrolled' : ''}`}
|
|
aria-label="Main navigation"
|
|
>
|
|
<div className="site-header-content">
|
|
<Link href="/" className="site-header-logo" onClick={handleNavClick('/')}>
|
|
<img src={saws.src} alt="Sculpted Arc" className="site-header-logo-img" />
|
|
</Link>
|
|
|
|
<div className="site-header-nav desktop-menu">
|
|
{menuLinks.map((link) => {
|
|
const active = isActivePath(activePath, link.href)
|
|
return (
|
|
<Link
|
|
key={link.label}
|
|
href={link.href}
|
|
className={`menu-item${active ? ' menu-item--active' : ''}`}
|
|
aria-current={active ? 'page' : undefined}
|
|
onClick={handleNavClick(link.href)}
|
|
>
|
|
{link.label}
|
|
</Link>
|
|
)
|
|
})}
|
|
</div>
|
|
|
|
<div className="site-header-actions desktop-actions">
|
|
<SocialMediaLinks
|
|
className="site-header-social"
|
|
linkClassName="site-header-social-link"
|
|
/>
|
|
<Link href={ctaHref} className="site-header-cta">
|
|
Get a quote
|
|
</Link>
|
|
</div>
|
|
|
|
<div className="mobile-menu">
|
|
<input
|
|
ref={menuToggleRef}
|
|
type="checkbox"
|
|
id="mobile-nav-toggle"
|
|
className="mobile-menu-checkbox"
|
|
/>
|
|
<label
|
|
htmlFor="mobile-nav-toggle"
|
|
className="burger-icon"
|
|
aria-controls="mobile-nav-menu"
|
|
>
|
|
<span className="burger-icon-menu" aria-hidden="true">
|
|
☰
|
|
</span>
|
|
<span className="burger-icon-close" aria-hidden="true">
|
|
×
|
|
</span>
|
|
</label>
|
|
<div id="mobile-nav-menu" className="mobile-dropdown">
|
|
<div className="mobile-dropdown-inner">
|
|
{menuLinks.map((link) => {
|
|
const active = isActivePath(activePath, link.href)
|
|
return (
|
|
<Link
|
|
key={link.label}
|
|
href={link.href}
|
|
className={`menu-item mobile-dropdown-item${active ? ' menu-item--active' : ''}`}
|
|
aria-current={active ? 'page' : undefined}
|
|
onClick={handleNavClick(link.href)}
|
|
>
|
|
{link.label}
|
|
</Link>
|
|
)
|
|
})}
|
|
<Link
|
|
href={ctaHref}
|
|
className="site-header-cta site-header-cta--mobile"
|
|
onClick={handleNavClick(ctaHref)}
|
|
>
|
|
Get a quote
|
|
</Link>
|
|
<SocialMediaLinks
|
|
className="site-header-social site-header-social--mobile"
|
|
linkClassName="site-header-social-link"
|
|
/>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</nav>
|
|
</header>
|
|
)
|
|
}
|