import { isValidElement, memo, useMemo } from "react"; import { Flex } from "../flex"; import { Icons } from "../icons"; import { Link } from "../link"; import { IconButton } from "../icon-button"; import { Text } from "../text"; import { styled } from "../../theme"; import { BaseCarousel } from "./base-carousel"; import { useCarousel } from "./use-carousel"; import type { UrlObject } from "url"; import type { motion } from "framer-motion"; import type { Grid } from "../grid"; const StyledNavButtonContainer = styled(Flex, { ml: "auto", gap: "$4", }); interface Props extends Omit, "columns" | "onChange"> { /** * The items of the grid */ children: Array; /** * The number of rows in the grid */ rows: number; /** * The number of columns in the grid */ columns: number; /** * The number of columns that will be shifted when using internal navigation buttons */ stepAmount?: number; /** * The controlled value for the current "step" of the carousel. Step 0 represents the initial state of the carousel */ step?: number; /** * This prop only has effect when `step` is set. * If `true` the carousel will not scroll over the first and last item in the grid even if `step` is set to a value * that would otherwise cause this. */ clampStep?: boolean; /** * Customise the grid movement transition value */ transition?: React.ComponentProps["transition"]; /** * ARIA label for the next button */ nextAriaLabel?: string; /** * ARIA label for the carousel container. */ carouselAriaLabel?: string; /** * ARIA label for the previous button. */ previousAriaLabel?: string; /** * Toggle the internal navigation buttons */ navigation?: boolean; /** * Optional Link Header */ header?: | { title: string; href: string; linkProps?: React.ComponentProps } | React.ReactElement<{ href: string | UrlObject; children: string; style?: React.CSSProperties; }>; /** * Event handler called when `value` of the carousel changes */ onStepChange?: (currentStep: number) => void; /** If `true` disables drag functionality */ disableDrag?: boolean; /** * Base element class name */ className?: string; } // eslint-disable-next-line max-lines-per-function const CarouselComponent: React.FC = ({ children, rows, columns, stepAmount, step, clampStep = true, nextAriaLabel, previousAriaLabel, carouselAriaLabel, navigation = true, header, onStepChange, disableDrag = false, className, ...gridProps }) => { const { gridX, viewWindowWidth, viewWindowHeight, gridWidth, disableNext, disablePrevious, firstElementRef, increment, decrement, onItemFocus, handleDragEnd, onMouseDown, onMouseUp, } = useCarousel({ gridProps, children, columns, rows, stepAmount, step, clampStep, onStepChange, }); const baseGrid = useMemo( () => ( {children} ), [ children, rows, gridX, gridProps, gridWidth, viewWindowWidth, viewWindowHeight, firstElementRef, disableDrag, onItemFocus, handleDragEnd, onMouseUp, onMouseDown, ] ); return !navigation && !header ? ( baseGrid ) : ( {header && ( {isValidElement(header) ? ( header ) : ( {header.title} )} )} {navigation && ( )} {baseGrid} ); }; /** * An x by y grid carousel component with swipe gesture support. */ export const Carousel = memo(CarouselComponent); export const CarouselNavContainerSelector = StyledNavButtonContainer.toString();