"use client"; import cx from "classnames"; import type { HTMLAttributes, ReactElement, ReactNode } from "react"; import React from "react"; import { useStatic } from "@/utils/hooks"; import { Controls } from "../Controls"; import CarouselStatic from "./Carousel.static"; import { CarouselItem } from "./CarouselItem"; import { CLASS_BLEED_RIGHT_NO_LEFT, CLASS_CONTROLS, CLASS_DOTS, CLASS_NAVIGATION, CLASS_NEXT, CLASS_PREV, CLASS_ROOT, CLASS_SCROLLBAR, CLASS_SCROLLBAR_DRAG, CLASS_TRACK, CLASS_VIEWPORT, CLASS_VIEWPORT_WRAPPER, } from "./constants"; type CarouselRenderItem = (item: T, index: number) => ReactNode; interface CarouselProps extends HTMLAttributes { /** Swiper.js (https://swiperjs.com/) options object */ swiperOptions?: Record; /** Carousel items */ items: T[]; /** Render carousel item content */ renderItems?: CarouselRenderItem; /** Always show scrollbar and hide dots */ showScrollbar?: boolean; /** Make carousel bleed to the right edge of screen while keeping left aligned with container */ bleedRight?: boolean; /** Make carousel bleed right from its root edge without bleeding left */ bleedRightNoLeft?: boolean; className?: string; } const isCarouselItemElement = ( item: ReactNode, ): item is ReactElement> => React.isValidElement(item) && item.type === CarouselItem; const Carousel = ({ className, swiperOptions, items, renderItems, showScrollbar = false, bleedRight = false, bleedRightNoLeft = false, ...other }: CarouselProps) => { const [carouselRef] = useStatic(CarouselStatic); const { ["data-swiper-options"]: _dataSwiperOptionsAttr, ...domProps } = other as HTMLAttributes & { "data-swiper-options"?: string; }; const carouselItems = items.map((item, i) => { const content = renderItems ? renderItems(item, i) : (item as ReactNode); if (isCarouselItemElement(content)) { return React.cloneElement(content, { key: content.key ?? i.toString(), }); } return {content}; }); const classes = cx(CLASS_ROOT, className, { [`${CLASS_ROOT}--scrollbar`]: showScrollbar, [`${CLASS_ROOT}--bleed-right`]: bleedRight, [CLASS_BLEED_RIGHT_NO_LEFT]: bleedRightNoLeft, }); const elementClasses = { navigation: cx(CLASS_NAVIGATION), controls: cx(CLASS_CONTROLS), prev: cx(CLASS_PREV), next: cx(CLASS_NEXT), dots: cx(CLASS_DOTS), }; const customSwiperOptions = showScrollbar ? { pagination: { enabled: false, }, ...swiperOptions, } : swiperOptions; return (
{carouselItems}
{!showScrollbar && (
)} {showScrollbar && (
)}
); }; Carousel.displayName = "Carousel"; export type { CarouselProps }; export { Carousel };