Files
reui-blocks/blocks/gallery34.tsx
T

199 lines
6.2 KiB
TypeScript

"use client";
import { motion } from "framer-motion";
import {
AudioLines,
Globe,
Heart,
TrophyIcon,
type LucideIcon,
} from "lucide-react";
import React, { useState } from "react";
import { cn } from "@/lib/utils";
interface FeatureChip {
icon: LucideIcon;
value: string;
label: string;
}
interface FeatureData {
id: number;
img: string;
link: string;
title: string;
description: string;
chips: FeatureChip[];
}
interface Gallery34Props {
featureData?: FeatureData[];
className?: string;
}
const formatCount = (value: string) => {
const match = value.match(/^(\d+)(.*)$/);
if (!match) return value;
return `${new Intl.NumberFormat("fa-IR").format(Number(match[1]))}${match[2]}`;
};
const defaultFeatureData: FeatureData[] = [
{
id: 1,
img: "/block-media/shadcnblocks/block/guri3/img7.jpeg",
link: "#",
title: "مجموعه گرید چیدمان تیر نسخه ۲",
description: "#مجموعه-بلاک-تیر",
chips: [
{ icon: TrophyIcon, value: "50+", label: "جایزه" },
{ icon: Globe, value: "50+", label: "بازار جهانی" },
{ icon: AudioLines, value: "50+", label: "محتوای صوتی" },
],
},
{
id: 2,
img: "/block-media/shadcnblocks/block/guri3/img6.jpeg",
link: "#",
title: "بسته کامپوننت‌های رابط مدرن",
description: "#کامپوننت-مدرن",
chips: [
{ icon: TrophyIcon, value: "75+", label: "جایزه" },
{ icon: Globe, value: "60+", label: "بازار جهانی" },
{ icon: AudioLines, value: "45+", label: "محتوای صوتی" },
],
},
{
id: 3,
img: "/block-media/shadcnblocks/block/guri3/img14.jpeg",
link: "#",
title: "سیستم طراحی واکنش‌گرا",
description: "#طراحی-واکنش‌گرا",
chips: [
{ icon: TrophyIcon, value: "90+", label: "جایزه" },
{ icon: Globe, value: "80+", label: "بازار جهانی" },
{ icon: AudioLines, value: "65+", label: "محتوای صوتی" },
],
},
{
id: 4,
img: "/block-media/shadcnblocks/block/guri3/img25.jpeg",
link: "#",
title: "کیت انیمیشن تعاملی",
description: "#انیمیشن",
chips: [
{ icon: TrophyIcon, value: "40+", label: "جایزه" },
{ icon: Globe, value: "35+", label: "بازار جهانی" },
{ icon: AudioLines, value: "55+", label: "محتوای صوتی" },
],
},
{
id: 5,
img: "/block-media/shadcnblocks/block/guri3/img11.jpeg",
link: "#",
title: "کامپوننت‌های حالت تیره",
description: "#حالت-تیره",
chips: [
{ icon: TrophyIcon, value: "65+", label: "جایزه" },
{ icon: Globe, value: "70+", label: "بازار جهانی" },
{ icon: AudioLines, value: "50+", label: "محتوای صوتی" },
],
},
{
id: 6,
img: "/block-media/shadcnblocks/block/guri3/img12.jpeg",
link: "#",
title: "بسته قالب فروشگاه آنلاین",
description: "#فروشگاه-آنلاین",
chips: [
{ icon: TrophyIcon, value: "85+", label: "جایزه" },
{ icon: Globe, value: "90+", label: "بازار جهانی" },
{ icon: AudioLines, value: "75+", label: "محتوای صوتی" },
],
},
];
const Gallery34 = ({
featureData = defaultFeatureData,
className,
}: Gallery34Props) => {
const [hoveredIndex, setHoveredIndex] = useState<number | null>(null);
return (
<section
dir="rtl"
lang="fa"
className={cn("h-full overflow-hidden py-32", className)}
>
<div className="container">
<div className="mx-auto grid w-full max-w-6xl grid-cols-1 gap-6 md:grid-cols-2 lg:grid-cols-3">
{featureData.map((item, index) => (
<a
key={item.id}
href={item.link}
onMouseEnter={() => setHoveredIndex(index)}
onMouseLeave={() => setHoveredIndex(null)}
className="relative block overflow-hidden rounded-3xl bg-muted p-2 transition-colors hover:bg-muted/80"
aria-label={item.title}
>
<motion.img
transition={{
duration: 0.2,
ease: "easeOut",
delay: index * 0.1 + 0.5,
}}
animate={{
filter:
hoveredIndex !== null && hoveredIndex !== index
? "blur(10px)"
: "blur(0px)",
transition: {
duration: 0.3,
ease: "easeOut",
delay: 0,
},
scale: hoveredIndex === index ? 1.02 : 1,
}}
src={item.img}
className="pointer-events-none h-72 w-full rounded-2xl object-cover"
alt={item.title}
/>
<div className="absolute top-4 inset-e-4 z-10">
<Heart
className="size-6 fill-background/20 text-background"
aria-hidden
/>
</div>
<div className="mt-2 p-2 text-right">
<h2 className="text-xl font-semibold tracking-tight">
{item.title}
</h2>
<p className="mt-2 text-sm text-muted-foreground" dir="rtl">
{item.description}
</p>
<div className="mt-4 flex flex-wrap items-center justify-end gap-2">
{item.chips.map((chip, chipIndex) => {
const IconComponent = chip.icon;
return (
<span
key={chipIndex}
aria-label={`${chip.label}: ${formatCount(chip.value)}`}
className="inline-flex items-center justify-center gap-2 rounded-full bg-background px-2 py-1 text-sm"
>
<IconComponent className="size-4 shrink-0" />
<span dir="rtl">{formatCount(chip.value)}</span>
</span>
);
})}
</div>
</div>
</a>
))}
</div>
</div>
</section>
);
};
export { Gallery34 };