238 lines
7.2 KiB
TypeScript
238 lines
7.2 KiB
TypeScript
"use client";
|
|
|
|
import "swiper/css/effect-coverflow";
|
|
import "swiper/css/pagination";
|
|
import "swiper/css/navigation";
|
|
import "swiper/css";
|
|
import "swiper/css/effect-cards";
|
|
|
|
import { ChevronLeft, ChevronRight } from "lucide-react";
|
|
import { useEffect, useRef, useState } from "react";
|
|
import { FaXTwitter } from "react-icons/fa6";
|
|
import type { Swiper as SwiperType } from "swiper";
|
|
import { Autoplay, EffectCoverflow } from "swiper/modules";
|
|
import { Swiper, SwiperSlide } from "swiper/react";
|
|
|
|
import { cn } from "@/lib/utils";
|
|
|
|
const TEAM = [
|
|
{
|
|
src: "/block-media/shadcnblocks/block/lummi/person2.jpg",
|
|
alt: "پرتره سارا احمدی در فضای شهری",
|
|
name: "سارا احمدی",
|
|
role: "سرپرست طراحی",
|
|
url: "#",
|
|
},
|
|
{
|
|
src: "/block-media/shadcnblocks/block/lummi/person1.jpg",
|
|
alt: "پرتره مریم رضایی در نور طبیعی",
|
|
name: "مریم رضایی",
|
|
role: "مدیر خلاق",
|
|
url: "#",
|
|
},
|
|
{
|
|
src: "/block-media/shadcnblocks/block/lummi/person2.jpg",
|
|
alt: "پرتره نیلوفر کریمی در استودیو",
|
|
name: "نیلوفر کریمی",
|
|
role: "مدیر محصول",
|
|
url: "#",
|
|
},
|
|
{
|
|
src: "/block-media/shadcnblocks/block/lummi/person3.jpg",
|
|
alt: "پرتره امیر حسینی",
|
|
name: "امیر حسینی",
|
|
role: "سرپرست طراحی",
|
|
url: "#",
|
|
},
|
|
{
|
|
src: "/block-media/shadcnblocks/block/lummi/person1.jpg",
|
|
alt: "پرتره لیلا محمدی در نور طبیعی",
|
|
name: "لیلا محمدی",
|
|
role: "مدیر خلاق",
|
|
url: "#",
|
|
},
|
|
{
|
|
src: "/block-media/shadcnblocks/block/lummi/random11.jpg",
|
|
alt: "پرتره کامران نظری در استودیو",
|
|
name: "کامران نظری",
|
|
role: "مدیر محصول",
|
|
url: "#",
|
|
},
|
|
];
|
|
|
|
const css = `
|
|
.mySwiper22 {
|
|
position: relative;
|
|
z-index: 2;
|
|
width: 100%;
|
|
height: 350px;
|
|
}
|
|
|
|
.mySwiper22 .swiper-slide {
|
|
width: 200px;
|
|
height: 300px;
|
|
border-radius: 0;
|
|
opacity: 0.25;
|
|
transition: opacity 0.3s ease-in-out;
|
|
}
|
|
|
|
.mySwiper22 .swiper-slide-active {
|
|
opacity: 1;
|
|
z-index: 10;
|
|
}
|
|
|
|
.mySwiper22 .swiper-slide img {
|
|
display: block;
|
|
width: 100%;
|
|
height: 100%;
|
|
object-fit: cover;
|
|
}
|
|
|
|
.mySwiper22.swiper-3d .swiper-slide-shadow-left,
|
|
.mySwiper22.swiper-3d .swiper-slide-shadow-right {
|
|
background: none;
|
|
}
|
|
`;
|
|
|
|
interface Gallery22Props {
|
|
className?: string;
|
|
}
|
|
|
|
const Gallery22 = ({ className }: Gallery22Props) => {
|
|
const [activeIndex, setActiveIndex] = useState(0);
|
|
const swiperRef = useRef<SwiperType | null>(null);
|
|
const swiperContainerRef = useRef<HTMLDivElement>(null);
|
|
const [swiperReady, setSwiperReady] = useState(false);
|
|
|
|
useEffect(() => {
|
|
const swiper = swiperRef.current;
|
|
if (!swiper || !swiperReady) {
|
|
return;
|
|
}
|
|
|
|
const updateSwiper = () => {
|
|
swiper.update();
|
|
swiper.slideToLoop(swiper.realIndex, 0);
|
|
};
|
|
|
|
updateSwiper();
|
|
|
|
const container = swiperContainerRef.current;
|
|
if (!container) {
|
|
return;
|
|
}
|
|
|
|
const resizeObserver = new ResizeObserver(updateSwiper);
|
|
resizeObserver.observe(container);
|
|
|
|
return () => {
|
|
resizeObserver.disconnect();
|
|
};
|
|
}, [swiperReady]);
|
|
|
|
return (
|
|
<section dir="rtl" lang="fa" className={cn("py-32", className)}>
|
|
<style>{css}</style>
|
|
<div className="container">
|
|
<div
|
|
ref={swiperContainerRef}
|
|
dir="rtl"
|
|
className="relative mx-auto w-full max-w-3xl"
|
|
>
|
|
<div className="pointer-events-none absolute inset-y-0 start-0 z-20 w-16 bg-gradient-to-r from-background to-transparent sm:w-24 lg:w-42" />
|
|
<div className="pointer-events-none absolute inset-y-0 end-0 z-20 w-16 bg-gradient-to-l from-background to-transparent sm:w-24 lg:w-42" />
|
|
|
|
<div className="relative flex w-full justify-center">
|
|
<Swiper
|
|
onSwiper={(instance) => {
|
|
swiperRef.current = instance;
|
|
setSwiperReady(true);
|
|
}}
|
|
autoplay={{
|
|
delay: 1500,
|
|
disableOnInteraction: false,
|
|
}}
|
|
breakpoints={{
|
|
0: { spaceBetween: 40 },
|
|
640: { spaceBetween: 70 },
|
|
1024: { spaceBetween: 100 },
|
|
}}
|
|
effect="coverflow"
|
|
grabCursor={true}
|
|
centeredSlides={true}
|
|
loop={true}
|
|
slidesPerView="auto"
|
|
coverflowEffect={{
|
|
rotate: 0,
|
|
stretch: 0,
|
|
depth: 80,
|
|
modifier: 2.5,
|
|
}}
|
|
className="mySwiper22"
|
|
modules={[EffectCoverflow, Autoplay]}
|
|
onSlideChange={(instance) => setActiveIndex(instance.realIndex)}
|
|
>
|
|
{TEAM.map((member, index) => (
|
|
<SwiperSlide key={`${member.name}-${index}`} className="relative">
|
|
<img
|
|
className="mt-4 h-full w-full object-cover shadow-lg"
|
|
src={member.src}
|
|
alt={member.alt}
|
|
/>
|
|
<a
|
|
href={member.url}
|
|
className="absolute top-1 -left-2.5 flex size-10 items-center justify-center bg-foreground transition-all duration-300 hover:scale-90 hover:rotate-12"
|
|
>
|
|
<FaXTwitter className="size-5 text-background" />
|
|
<span className="sr-only">{member.name}</span>
|
|
</a>
|
|
</SwiperSlide>
|
|
))}
|
|
</Swiper>
|
|
</div>
|
|
|
|
<div
|
|
dir="rtl"
|
|
className="pointer-events-none absolute top-0 left-1/2 z-[1] flex h-[450px] w-[230px] -translate-x-1/2 flex-col justify-end border border-border p-4 text-right"
|
|
>
|
|
<div className="pointer-events-auto relative z-20 w-full text-right">
|
|
<h3 className="mb-2 text-xl leading-7 font-semibold tracking-tighter">
|
|
{TEAM[activeIndex].name}
|
|
</h3>
|
|
<p className="text-sm text-muted-foreground">
|
|
{TEAM[activeIndex].role}
|
|
</p>
|
|
</div>
|
|
<div className="pointer-events-auto absolute right-0 -bottom-12 z-20 flex gap-2 md:-right-20 md:bottom-0">
|
|
<button
|
|
type="button"
|
|
className="bg-muted p-2 transition-colors hover:bg-muted/80 active:scale-95"
|
|
aria-label="اسلاید قبلی"
|
|
onClick={(e) => {
|
|
e.preventDefault();
|
|
swiperRef.current?.slidePrev();
|
|
}}
|
|
>
|
|
<ChevronRight className="size-4" />
|
|
</button>
|
|
<button
|
|
type="button"
|
|
className="bg-muted p-2 transition-colors hover:bg-muted/80 active:scale-95"
|
|
aria-label="اسلاید بعدی"
|
|
onClick={(e) => {
|
|
e.preventDefault();
|
|
swiperRef.current?.slideNext();
|
|
}}
|
|
>
|
|
<ChevronLeft className="size-4" />
|
|
</button>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</section>
|
|
);
|
|
};
|
|
|
|
export { Gallery22 };
|