Files
reui-blocks/blocks/projects7.tsx
T

195 lines
6.8 KiB
TypeScript

"use client";
import { AnimatePresence, motion } from "framer-motion";
import { useState } from "react";
import { cn } from "@/lib/utils";
interface Projects7Type {
id: number;
title: string;
category: string;
description: string;
image: string;
tags: string[];
}
const categoryLabels: Record<string, string> = {
MINIMAL: "مینیمال",
ABSTRACT: "انتزاعی",
EDITORIAL: "ادیتوریال",
"STUDIO SHOT": "عکاسی استودیو",
};
const filterCategories = [
{ key: "ALL", label: "همه" },
{ key: "MINIMAL", label: "مینیمال" },
{ key: "ABSTRACT", label: "انتزاعی" },
{ key: "EDITORIAL", label: "ادیتوریال" },
{ key: "STUDIO SHOT", label: "عکاسی استودیو" },
] as const;
const Projects7Data: Projects7Type[] = [
{
id: 1,
title: "هندسه مینیمال",
category: "MINIMAL",
description:
"ترکیبی تمیز با تمرکز بر شکل‌های ساده و فضای منفی که آرامش و وضوح را برمی‌انگیزد.",
image: "/block-media/shadcnblocks/block/lummi/random1.jpeg",
tags: ["minimal", "geometry"],
},
{
id: 2,
title: "جریان رنگ انتزاعی",
category: "ABSTRACT",
description:
"رنگ‌های زنده در الگویی انتزاعی می‌آمیزند و حس حرکت و انرژی می‌آفرینند.",
image: "/block-media/shadcnblocks/block/lummi/random2.jpeg",
tags: ["abstract", "color"],
},
{
id: 3,
title: "پرتره ادیتوریال",
category: "EDITORIAL",
description:
"پرتره‌ای ادیتوریال چشمگیر با نورپردازی دراماتیک و تمرکز بر بیان و حس.",
image: "/block-media/shadcnblocks/block/lummi/random3.jpeg",
tags: ["editorial", "portrait"],
},
{
id: 4,
title: "طبیعت بی‌جان استودیو",
category: "STUDIO SHOT",
description:
"عکاسی استودیویی با چیدمانی دقیق از اشیای روزمره که بافت و فرم را برجسته می‌کند.",
image: "/block-media/shadcnblocks/block/lummi/random4.jpeg",
tags: ["studio", "still-life"],
},
{
id: 5,
title: "سایه‌های مینیمال",
category: "MINIMAL",
description:
"سایه‌های نرم و گرادیان‌های ظریف، صحنه‌ای آرام و مینیمال با حس مدرن می‌سازند.",
image: "/block-media/shadcnblocks/block/lummi/random5.jpeg",
tags: ["minimal", "shadows"],
},
{
id: 6,
title: "استودیوی انتزاعی",
category: "ABSTRACT",
description:
"نور استودیو با فرم‌های انتزاعی در این ترکیب‌بندی آزمایشی می‌آمیزد و هنر و عکاسی را در هم می‌کشد.",
image: "/block-media/shadcnblocks/block/lummi/random6.jpeg",
tags: ["abstract", "studio"],
},
];
interface Projects7Props {
className?: string;
}
const Projects7 = ({ className }: Projects7Props) => {
const [activeFilter, setActiveFilter] = useState<string>("ALL");
const [filteredItems, setFilteredItems] = useState(Projects7Data);
const handleFilterChange = (category: string) => {
setActiveFilter(category);
if (category === "ALL") {
setFilteredItems(Projects7Data);
} else {
const filtered = Projects7Data.filter(
(item) =>
item.category === category ||
item.tags.includes(category.toLowerCase().replace(" ", "-")),
);
setFilteredItems(filtered);
}
};
const activeFilterLabel =
filterCategories.find((item) => item.key === activeFilter)?.label ??
activeFilter;
return (
<section dir="rtl" lang="fa" className={cn("py-32", className)}>
<div className="container">
<div className="mx-auto max-w-7xl">
<div className="mb-16 flex flex-wrap justify-center gap-8">
{filterCategories.map(({ key, label }) => (
<button
key={key}
onClick={() => handleFilterChange(key)}
className={`text-sm font-medium tracking-wider transition-colors duration-300 hover:text-foreground ${
activeFilter === key
? "border-b-2 border-border pb-1 text-foreground"
: "text-gray-500"
}`}
>
{label}
</button>
))}
</div>
<motion.div layout className="grid grid-cols-1 gap-8 md:grid-cols-2">
<AnimatePresence mode="wait">
{filteredItems.map((item) => (
<motion.div
key={item.id}
layout
initial={{ opacity: 0, scale: 0.9 }}
animate={{ opacity: 1, scale: 1 }}
exit={{ opacity: 0, scale: 0.9 }}
transition={{
duration: 0.2,
ease: "easeInOut",
}}
className="group relative cursor-pointer overflow-hidden rounded-lg"
>
<div className="relative aspect-[4/3] h-96 w-full">
<img
src={item.image}
alt={item.title}
className="object-cover transition-transform duration-300"
/>
<motion.div className="absolute inset-0 flex flex-col items-center justify-center bg-black/60 p-8 text-white opacity-0 transition-opacity duration-200 group-hover:opacity-100">
<motion.div className="translate-y-5 text-center opacity-0 transition-all duration-300 group-hover:translate-y-0 group-hover:opacity-100">
<p className="mb-4 text-xs font-medium tracking-widest text-gray-300">
{categoryLabels[item.category] ?? item.category}
</p>
<h3 className="mb-4 text-2xl font-light tracking-wide">
{item.title}
</h3>
<p className="max-w-xs text-sm leading-relaxed text-gray-200">
{item.description}
</p>
</motion.div>
</motion.div>
</div>
</motion.div>
))}
</AnimatePresence>
</motion.div>
{filteredItems.length === 0 && (
<motion.div
initial={{ opacity: 0 }}
animate={{ opacity: 1 }}
className="py-16 text-center"
>
<p className="text-lg text-gray-500">
موردی در دسته «{activeFilterLabel}» یافت نشد.
</p>
</motion.div>
)}
</div>
</div>
</section>
);
};
export { Projects7 };