130 lines
4.4 KiB
TypeScript
130 lines
4.4 KiB
TypeScript
import { useState, useCallback, useMemo, useEffect } from "react";
|
|
import { LazyMotion, domAnimation, m } from "framer-motion";
|
|
import Section from "@/components/Section";
|
|
import { Image } from "@unpic/react";
|
|
|
|
const categories = [
|
|
"Bars",
|
|
"Barks",
|
|
"Pralines",
|
|
"Spreads",
|
|
"Dragees",
|
|
"Gelatos",
|
|
];
|
|
|
|
const categoryLinks = [
|
|
"https://shop.mozimo.in/#bar",
|
|
"https://shop.mozimo.in/#barks",
|
|
"https://shop.mozimo.in/#pralines",
|
|
"https://shop.mozimo.in/#spreads",
|
|
"https://shop.mozimo.in/#dragees",
|
|
"https://shop.mozimo.in/",
|
|
];
|
|
const categoryImages = [
|
|
"/categories/c1.svg",
|
|
"/categories/c2.svg",
|
|
"/categories/c3.svg",
|
|
"/categories/c4.svg",
|
|
"/categories/c5.svg",
|
|
"/categories/c6.svg",
|
|
];
|
|
|
|
export default function ProductCategory() {
|
|
const [currentCategory, setCurrentCategory] = useState(0);
|
|
|
|
// Instant category change with no debouncing
|
|
const handleCategoryChange = useCallback((idx: number) => {
|
|
setCurrentCategory(idx);
|
|
}, []);
|
|
|
|
// Preload only neighbor images ✅
|
|
useEffect(() => {
|
|
const next = (currentCategory + 1) % categoryImages.length;
|
|
const prev = (currentCategory - 1 + categoryImages.length) % categoryImages.length;
|
|
[next, prev].forEach((i) => {
|
|
const img = new window.Image();
|
|
img.src = categoryImages[i];
|
|
});
|
|
}, [currentCategory]);
|
|
|
|
const categoryButtons = useMemo(() => {
|
|
return categories.map((category, idx) => {
|
|
const isActive = idx === currentCategory;
|
|
|
|
return (
|
|
<li key={category}>
|
|
<a
|
|
href={categoryLinks[idx]}
|
|
target="_blank"
|
|
rel="noopener noreferrer"
|
|
className={`group flex items-center gap-4 w-full text-left transition-opacity duration-150
|
|
${isActive ? "opacity-100" : "opacity-70 hover:opacity-90"}
|
|
`}
|
|
style={{
|
|
fontFamily: "MonetaSans-Regular",
|
|
fontWeight: isActive ? 400 : 300,
|
|
fontSize: isActive ? "clamp(22px, 5vw, 64px)" : "clamp(18px, 4vw, 56px)",
|
|
lineHeight: "120%",
|
|
transform: isActive ? "translateX(4px)" : "translateX(0)",
|
|
color: "#703133",
|
|
}}
|
|
onClick={() => handleCategoryChange(idx)}
|
|
onMouseEnter={() => handleCategoryChange(idx)}
|
|
onTouchStart={() => handleCategoryChange(idx)}
|
|
>
|
|
{/* Mobile thumbnail */}
|
|
<div className="block lg:hidden w-16 h-16 rounded-full overflow-hidden shrink-0">
|
|
<Image
|
|
src={categoryImages[idx]}
|
|
alt={category}
|
|
width={64}
|
|
height={64}
|
|
className={`w-full h-full object-cover transition-transform duration-150 ${isActive ? "scale-110" : "scale-100"}`}
|
|
draggable={false}
|
|
layout="fixed"
|
|
/>
|
|
</div>
|
|
|
|
<span>{category}</span>
|
|
</a>
|
|
</li>
|
|
);
|
|
});
|
|
}, [currentCategory, handleCategoryChange]);
|
|
|
|
return (
|
|
<Section background="white" id="categories">
|
|
<LazyMotion features={domAnimation}>
|
|
<div className="grid grid-cols-1 lg:grid-cols-2 gap-0 items-stretch min-h-[400px] md:min-h-[500px]">
|
|
{/* Left */}
|
|
<div className="flex flex-col justify-center h-full bg-white px-4 md:px-8 py-6 md:py-12 order-2 lg:order-1">
|
|
<ul className="space-y-4 md:space-y-6">{categoryButtons}</ul>
|
|
</div>
|
|
|
|
{/* Right (render ONLY active image) */}
|
|
<div className="relative hidden lg:flex items-center justify-center h-full min-h-[300px] md:min-h-[400px] bg-white order-1 lg:order-2 p-4">
|
|
<div className="relative w-full h-full overflow-hidden rounded-2xl">
|
|
<m.div
|
|
key={currentCategory}
|
|
initial={{ opacity: 0, scale: 0.985 }}
|
|
animate={{ opacity: 1, scale: 1 }}
|
|
exit={{ opacity: 0 }}
|
|
transition={{ duration: 0.18, ease: [0.23, 1, 0.32, 1] }}
|
|
className="absolute inset-0"
|
|
>
|
|
<Image
|
|
src={categoryImages[currentCategory]}
|
|
alt={categories[currentCategory]}
|
|
layout="fullWidth"
|
|
className="w-full h-full object-cover"
|
|
draggable={false}
|
|
/>
|
|
</m.div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</LazyMotion>
|
|
</Section>
|
|
);
|
|
}
|