improved site header, added contact form, content card carousel, footer, and more photos
This commit is contained in:
147
components/site-header.tsx
Normal file
147
components/site-header.tsx
Normal file
@@ -0,0 +1,147 @@
|
||||
'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: 'Plans', href: '/plans' },
|
||||
{ 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>
|
||||
)
|
||||
}
|
||||
Reference in New Issue
Block a user