ldc-ovoteam/resources/ts/front/Components/Base/Carousel/CarouselContent.tsx

162 lines
4.9 KiB
TypeScript

/** REACT */
import { useMemo, useRef, useState } from "react";
/** SWIPER PACKAGE */
import { Swiper as SwiperType } from "swiper";
import "swiper/css";
import "swiper/css/navigation";
import "swiper/css/pagination";
import { Pagination } from "swiper/modules";
import { Swiper, SwiperClass, SwiperSlide } from "swiper/react";
import { PaginationOptions } from "swiper/types";
/** THEME */
import EggArrowIcon from "$/uwil/Icons/EggArrowIcon";
import ItemCard from "$/uwil/ItemCard";
import { bgColors, imageShapes } from "@/front/lib/constants";
import { cn } from "@/lib/utils";
import {
CarouselBreakpointsProps,
CarouselPropsWithNavigation,
SwiperButtonProps,
} from "@/types/front";
function NextButton({ swiperRef, disabled, icon }: SwiperButtonProps) {
return (
<button
type="button"
onClick={() => swiperRef.current?.slideNext()}
disabled={disabled}
className="hidden md:block disabled:opacity-0 z-10 hover:scale-105 transition-transform"
>
{icon ?? <EggArrowIcon direction="right" />}
</button>
);
}
function PrevButton({ swiperRef, disabled, icon }: SwiperButtonProps) {
return (
<button
type="button"
onClick={() => swiperRef.current?.slidePrev()}
disabled={disabled}
className="hidden md:block disabled:opacity-0 z-10 hover:scale-105 transition-transform"
>
{icon ?? <EggArrowIcon direction="left" />}
</button>
);
}
const defaultSlidesPerView: Record<number, number> = {
768: 2,
1024: 3,
1248: 4,
};
export default function CarouselContent(props: CarouselPropsWithNavigation) {
const {
items,
carousel,
variant = bgColors.PRIMARY,
prevBtn,
nextBtn,
shape = imageShapes.ROUNDED,
} = props;
const [isBeginning, setIsBeginning] = useState(true);
const [isEnd, setIsEnd] = useState(false);
const swiperRef = useRef<SwiperType | null>(null);
const spaceBetween = carousel?.spaceBetween ?? 50;
const slidesPerView = carousel?.slidesPerView ?? 1;
const defaultPagination = useMemo((): PaginationOptions => {
const bulletClass =
variant === bgColors.PRIMARY
? "inline-block w-3 h-3 rounded-full bg-background/90 ring-1 ring-primary-foreground/20 mx-1"
: "inline-block w-3 h-3 rounded-full bg-primary mx-1";
return {
clickable: true,
bulletClass,
bulletActiveClass: "!bg-primary-foreground w-6 h-6",
};
}, [variant]);
const breakpoints = useMemo(() => {
return Object.keys({
...defaultSlidesPerView,
...carousel?.breakpoints,
}).reduce((acc, key) => {
const numKey = Number(key);
acc[numKey] = {
...carousel?.breakpoints?.[numKey],
slidesPerView:
carousel?.breakpoints?.[numKey]?.slidesPerView ??
defaultSlidesPerView[numKey],
};
return acc;
}, {} as CarouselBreakpointsProps);
}, [carousel?.breakpoints]);
const pagination = useMemo(() => {
if (carousel?.pagination === false) return false;
return {
...defaultPagination,
...(typeof carousel?.pagination === "object" ? carousel.pagination : {}),
};
}, [carousel?.pagination, defaultPagination]);
const handleSwiper = (swiper: SwiperClass) => {
swiperRef.current = swiper;
carousel?.onSwiper?.(swiper);
};
return (
<div className="relative flex justify-center items-center w-full ">
<PrevButton icon={prevBtn} swiperRef={swiperRef} disabled={isBeginning} />
<Swiper
modules={[Pagination]}
centeredSlides={carousel?.centeredSlides}
spaceBetween={spaceBetween}
slidesPerView={slidesPerView}
onSlideChange={(swiper) => {
carousel?.onSlideChange?.(swiper);
setIsBeginning(swiper.isBeginning);
setIsEnd(swiper.isEnd || swiper.isLocked);
}}
onAfterInit={(swiper) => {
setIsBeginning(swiper.isBeginning);
setIsEnd(swiper.isEnd || swiper.isLocked);
}}
watchOverflow={carousel?.watchOverflow}
onSwiper={handleSwiper}
autoplay={carousel?.autoplay}
pagination={pagination}
navigation={carousel?.navigation}
breakpoints={breakpoints}
wrapperClass={cn("pb-20", carousel?.wrapperClass)}
className="w-full"
>
{items.map((item) => {
const slideKey = `${item.image.src}-${item.title?.text ?? item.button?.href ?? "slide"}`;
return (
<SwiperSlide key={slideKey} className="justify-center !flex">
<ItemCard
image={{ lazyload: true, ...item.image }}
shape={shape}
title={item.title}
subtitle={item.subtitle}
description={item.description}
button={item.button}
variant={variant}
/>
</SwiperSlide>
);
})}
</Swiper>
<NextButton icon={nextBtn} swiperRef={swiperRef} disabled={isEnd} />
</div>
);
}