135 lines
4.5 KiB
TypeScript
135 lines
4.5 KiB
TypeScript
"use client";
|
|
|
|
import Link from "next/link";
|
|
import { useEffect, useState } from "react";
|
|
import { CATEGORIES, slugifyCategory } from "./categories";
|
|
import { useSession } from "next-auth/react";
|
|
|
|
type HeaderProps = {
|
|
forceScrolled?: boolean;
|
|
};
|
|
|
|
export default function MobileHeader({ forceScrolled = false }: HeaderProps) {
|
|
const [isShrunk, setIsShrunk] = useState(forceScrolled);
|
|
const [isHidden, setIsHidden] = useState(false);
|
|
const [touchStartY, setTouchStartY] = useState<number | null>(null);
|
|
useSession();
|
|
|
|
const categoryIcons: Record<string, string> = {
|
|
Interior: "/categories/cat-interior.png",
|
|
Tools: "/categories/cat-tools.png",
|
|
Exterior: "/categories/cat-exterior.png",
|
|
Drivetrain: "/categories/cat-drivetrain.png",
|
|
Lighting: "/categories/cat-lighting.png",
|
|
Suspension: "/categories/cat-suspension.png",
|
|
Audio: "/categories/cat-audio.png",
|
|
Performance: "/categories/cat-performance.png",
|
|
};
|
|
|
|
useEffect(() => {
|
|
if (forceScrolled) {
|
|
setIsShrunk(true);
|
|
setIsHidden(false);
|
|
return;
|
|
}
|
|
|
|
const SHRINK_AT = 140;
|
|
const EXPAND_AT = 80;
|
|
const onScroll = () => {
|
|
const y = window.scrollY;
|
|
setIsShrunk((prev) => (prev ? y > EXPAND_AT : y > SHRINK_AT));
|
|
setIsHidden((prev) => (y > SHRINK_AT ? prev : false));
|
|
};
|
|
onScroll();
|
|
window.addEventListener("scroll", onScroll, { passive: true });
|
|
return () => window.removeEventListener("scroll", onScroll);
|
|
}, [forceScrolled]);
|
|
|
|
const handleTouchStart = (e: React.TouchEvent) => {
|
|
setTouchStartY(e.touches[0]?.clientY ?? null);
|
|
};
|
|
|
|
const handleTouchEnd = (e: React.TouchEvent) => {
|
|
if (touchStartY === null) return;
|
|
const endY = e.changedTouches[0]?.clientY ?? touchStartY;
|
|
const delta = endY - touchStartY;
|
|
const SWIPE_THRESHOLD = 40;
|
|
if (isShrunk && !isHidden && delta < -SWIPE_THRESHOLD) {
|
|
setIsHidden(true);
|
|
} else if (isHidden && delta > SWIPE_THRESHOLD) {
|
|
setIsHidden(false);
|
|
}
|
|
setTouchStartY(null);
|
|
};
|
|
|
|
return (
|
|
<>
|
|
<header
|
|
className={`so-header so-header--mobile${isShrunk ? " so-header--shrunk" : ""}${
|
|
isHidden ? " so-header--hidden" : ""
|
|
}`}
|
|
onTouchStart={handleTouchStart}
|
|
onTouchEnd={handleTouchEnd}
|
|
>
|
|
<div className="container so-header__mobile">
|
|
<div className="so-header__top">
|
|
<Link href="/" className="so-header__logo-wrap">
|
|
<img
|
|
src="/icons/logo.png"
|
|
className="so-header__logo"
|
|
alt="Shifted Offroad"
|
|
/>
|
|
</Link>
|
|
</div>
|
|
|
|
<div className="so-header__categories">
|
|
<nav className="so-header__nav so-header__nav--mobile">
|
|
{CATEGORIES.map((c) => (
|
|
<Link
|
|
key={c}
|
|
href={`/category/${slugifyCategory(c)}`}
|
|
className="so-navitem"
|
|
style={{
|
|
backgroundImage: `url(${categoryIcons[c] || ""})`,
|
|
}}
|
|
>
|
|
<span className="so-navitem__label">{c}</span>
|
|
</Link>
|
|
))}
|
|
</nav>
|
|
<Link href="/cart" className="so-mobile-corner so-mobile-corner--home" aria-label="Cart">
|
|
<span className="so-mobile-corner__icon so-mobile-corner__icon--cart" aria-hidden="true" />
|
|
</Link>
|
|
<Link href="/" className="so-mobile-corner so-mobile-corner--cart" aria-label="Home">
|
|
<span className="so-mobile-corner__icon so-mobile-corner__icon--home" aria-hidden="true" />
|
|
</Link>
|
|
<div className="so-header__mobile-tools">
|
|
<input
|
|
className="so-header__search"
|
|
type="search"
|
|
placeholder="Search"
|
|
aria-label="Search"
|
|
/>
|
|
<Link href="/login" className="so-auth-icon" aria-label="Sign in">
|
|
<span className="so-auth-icon__glyph" aria-hidden="true" />
|
|
</Link>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</header>
|
|
<button
|
|
type="button"
|
|
className={`so-header__pulltab so-header__pulltab--visible${
|
|
isHidden ? " so-header__pulltab--hidden" : ""
|
|
}${isShrunk ? " so-header__pulltab--shrunk" : ""}`}
|
|
onClick={() => setIsHidden(false)}
|
|
onTouchStart={handleTouchStart}
|
|
onTouchEnd={handleTouchEnd}
|
|
aria-label={isHidden ? "Show header" : "Hide header"}
|
|
>
|
|
<span className="so-header__pulltab-grip" />
|
|
</button>
|
|
</>
|
|
);
|
|
}
|