Files
reui-blocks/blocks/gallery10.tsx
T

222 lines
7.8 KiB
TypeScript

"use client";
import { ArrowLeft, ArrowRight } from "lucide-react";
import { useEffect, useState } from "react";
import { Badge } from "@/components/ui/badge";
import { Button } from "@/components/ui/button";
import type { CarouselApi } from "@/components/ui/carousel";
import {
Carousel,
CarouselContent,
CarouselItem,
} from "@/components/ui/carousel";
import { cn } from "@/lib/utils";
interface DataItem {
id: string;
username: string;
quote: string;
author: string;
image: string;
bgColor: string;
}
const DATA: DataItem[] = [
{
id: "item-1",
username: "@نوآور_تکنولوژی",
quote:
"تیم آن‌ها دیدگاه ما را به اپلیکیشنی روان و کاربرپسند تبدیل کرد. توجه به جزئیات و تعهد به کیفیت واقعاً بی‌نظیر است!",
author: "سارا",
image:
"/block-media/shadcnblocks/block/placeholder-4.svg",
bgColor: "bg-green-300",
},
{
id: "item-2",
username: "@بنیان‌گذار_استارتاپ",
quote:
"از ایده تا استقرار، راهکاری مقاوم تحویل دادند که با رشد کسب‌وکار ما مقیاس گرفت. واقعاً شریک مسیر رشد ما بودند.",
author: "امیر",
image:
"/block-media/shadcnblocks/block/placeholder-5.svg",
bgColor: "bg-orange-300",
},
{
id: "item-3",
username: "@مدیر_سازمانی",
quote:
"تخصص آن‌ها در راهکارهای ابری به ما کمک کرد عملیات را بهینه کنیم و هزینه‌ها را به‌شکل چشمگیری کاهش دهیم. خدماتشان را به‌شدت توصیه می‌کنم!",
author: "نیلوفر",
image:
"/block-media/shadcnblocks/block/placeholder-3.svg",
bgColor: "bg-purple-300",
},
{
id: "item-4",
username: "@مدیر_محصول",
quote:
"توانایی تیم در درک نیازهای پیچیده و تحویل به‌موقع فوق‌العاده است. فرآیند توسعه را برای ما بدون استرس کردند.",
author: "میکائیل",
image:
"/block-media/shadcnblocks/block/placeholder-2.svg",
bgColor: "bg-green-300",
},
{
id: "item-5",
username: "@مدیر_فنی",
quote:
"رویکرد نوآورانه آن‌ها در توسعه نرم‌افزار برای سازمان ما تحول‌آفرین بود. از نتیجه کار بسیار راضی هستیم!",
author: "لیلا",
image:
"/block-media/shadcnblocks/block/placeholder-1.svg",
bgColor: "bg-orange-300",
},
];
interface Gallery10Props {
className?: string;
}
const Gallery10 = ({ className }: Gallery10Props) => {
const [carouselApi, setCarouselApi] = useState<CarouselApi>();
const [canScrollPrev, setCanScrollPrev] = useState(false);
const [canScrollNext, setCanScrollNext] = useState(true);
const [currentIndex, setCurrentIndex] = useState(0);
useEffect(() => {
if (!carouselApi) {
return;
}
const updateSelection = () => {
setCanScrollPrev(carouselApi.canScrollPrev());
setCanScrollNext(carouselApi.canScrollNext());
setCurrentIndex(carouselApi.selectedScrollSnap());
};
updateSelection();
carouselApi.on("select", updateSelection);
return () => {
carouselApi.off("select", updateSelection);
};
}, [carouselApi]);
useEffect(() => {
if (!carouselApi) {
return;
}
carouselApi.reInit();
}, [carouselApi]);
const progressWidth = 240;
const progressIndicatorWidth = progressWidth / DATA.length;
const progressOffset = currentIndex * progressIndicatorWidth;
return (
<section dir="rtl" lang="fa" className={cn("py-32", className)}>
<div className="container">
<div className="flex flex-col items-start justify-between gap-6 px-4 lg:flex-row lg:px-10">
<div className="flex flex-col justify-between text-start lg:h-[460px] lg:w-[445px] lg:pe-10">
<div className="flex flex-col gap-4">
<h2 className="text-3xl font-semibold lg:text-xl">
ساخت آینده، خط به خط کد
</h2>
<p className="text-lg text-muted-foreground">
از استارتاپ‌ها تا سازمان‌های بزرگ، کسب‌وکارها را با راهکارهای
نرم‌افزاری پیشرفته توانمند می‌کنیم. نظر مشتریان راضی ما را
بخوانید.
</p>
</div>
<div className="hidden justify-start gap-4 lg:flex">
<Button
size="icon"
className="rounded-full"
variant="outline"
onClick={() => carouselApi?.scrollPrev()}
disabled={!canScrollPrev}
>
<ArrowRight className="size-4" />
<span className="sr-only">اسلاید قبلی</span>
</Button>
<Button
size="icon"
variant="outline"
className="rounded-full"
onClick={() => carouselApi?.scrollNext()}
disabled={!canScrollNext}
>
<ArrowLeft className="size-4" />
<span className="sr-only">اسلاید بعدی</span>
</Button>
</div>
</div>
<div className="relative w-full overflow-hidden pb-12 lg:flex-1">
<Carousel
dir="rtl"
setApi={setCarouselApi}
opts={{
align: "start",
direction: "rtl",
dragFree: true,
}}
>
<CarouselContent className="-ms-4 me-0">
{DATA.map((testimonial) => (
<CarouselItem
key={testimonial.id}
className="min-w-[800px] flex-1 ps-4 pe-0"
>
<div className="flex gap-2">
<div className="h-[460px] w-[400px]">
<img
src={testimonial.image}
alt={testimonial.author}
className="aspect-square h-full w-full rounded-2xl object-cover"
/>
</div>
<div
className={`relative flex h-[460px] w-[400px] flex-col items-start justify-end rounded-2xl p-8 text-start ${testimonial.bgColor}`}
>
<Badge className="mb-auto bg-background px-4 py-2 text-black">
{testimonial.username}
</Badge>
<span className="-rotate-[4deg] text-2xll leading-none">
«
</span>
<p className="text-xl font-semibold">
{testimonial.quote}
</p>
<p className="mt-4 text-lg font-medium">
{testimonial.author}
</p>
</div>
</div>
</CarouselItem>
))}
</CarouselContent>
</Carousel>
<div className="absolute bottom-4 start-1/2 h-0.5 w-60 -translate-x-1/2 rounded bg-gray-200 rtl:translate-x-1/2">
<div
className="absolute top-0 h-0.5 rounded bg-primary transition-[inset-inline-start] duration-300 ease-out"
style={{
width: `${progressIndicatorWidth}px`,
insetInlineStart: `${progressOffset}px`,
}}
/>
</div>
</div>
</div>
</div>
</section>
);
};
export { Gallery10 };