Initial import of 1407 block sources under blocks/
Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -0,0 +1,194 @@
|
||||
"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 };
|
||||
Reference in New Issue
Block a user