UNPKG

@mantine/carousel

Version:
267 lines (264 loc) 8.49 kB
'use client'; import { jsxs, jsx } from 'react/jsx-runtime'; import { useState, useCallback, useEffect, Children, createElement } from 'react'; import useEmblaCarousel from 'embla-carousel-react'; import { createVarsResolver, getSpacing, rem, factory, useProps, useStyles, useRandomClassName, useDirection, UnstyledButton, Box, AccordionChevron } from '@mantine/core'; import { clamp } from '@mantine/hooks'; import { CarouselProvider } from './Carousel.context.mjs'; import classes from './Carousel.module.css.mjs'; import { CarouselSlide } from './CarouselSlide/CarouselSlide.mjs'; import { CarouselContainerVariables, CarouselVariables } from './CarouselVariables/CarouselVariables.mjs'; import { getChevronRotation } from './get-chevron-rotation.mjs'; const defaultProps = { controlSize: 26, controlsOffset: "sm", slideSize: "100%", slideGap: 0, orientation: "horizontal", includeGapInSize: true, initialSlide: 0, withControls: true, withIndicators: false, withKeyboardEvents: true, type: "media" }; const defaultEmblaOptions = { align: "center", loop: false, slidesToScroll: 1, dragFree: false, inViewThreshold: 0, skipSnaps: false, containScroll: "trimSnaps" }; const varsResolver = createVarsResolver( (_, { height, controlSize, controlsOffset }) => ({ root: { "--carousel-height": rem(height), "--carousel-control-size": rem(controlSize), "--carousel-controls-offset": getSpacing(controlsOffset) } }) ); const Carousel = factory((_props, ref) => { const props = useProps("Carousel", defaultProps, _props); const { classNames, className, style, styles, unstyled, vars, children, getEmblaApi, onNextSlide, onPreviousSlide, onSlideChange, nextControlProps, previousControlProps, controlSize, controlsOffset, slideSize, slideGap, orientation, height, includeGapInSize, draggable, initialSlide, withControls, withIndicators, plugins, nextControlIcon, previousControlIcon, withKeyboardEvents, mod, type, emblaOptions, attributes, ...others } = props; const getStyles = useStyles({ name: "Carousel", classes, props, className, style, classNames, styles, unstyled, attributes, vars, varsResolver }); const responsiveClassName = useRandomClassName(); const { dir } = useDirection(); const [emblaRefElement, embla] = useEmblaCarousel( { axis: orientation === "horizontal" ? "x" : "y", direction: orientation === "horizontal" ? dir : void 0, startIndex: initialSlide, ...defaultEmblaOptions, ...emblaOptions }, plugins ); const [selected, setSelected] = useState(0); const [slidesCount, setSlidesCount] = useState(0); const handleScroll = useCallback((index) => embla && embla.scrollTo(index), [embla]); const handleSelect = useCallback(() => { if (!embla) { return; } const slide = embla.selectedScrollSnap(); setSelected(slide); slide !== selected && onSlideChange?.(slide); }, [embla, setSelected, onSlideChange, selected]); const handlePrevious = useCallback(() => { embla?.scrollPrev(); onPreviousSlide?.(); }, [embla]); const handleNext = useCallback(() => { embla?.scrollNext(); onNextSlide?.(); }, [embla]); const handleKeydown = useCallback( (event) => { if (withKeyboardEvents) { if (event.key === "ArrowRight") { event.preventDefault(); handleNext(); } if (event.key === "ArrowLeft") { event.preventDefault(); handlePrevious(); } } }, [embla] ); useEffect(() => { if (embla) { getEmblaApi?.(embla); handleSelect(); setSlidesCount(embla.scrollSnapList().length); embla.on("select", handleSelect); return () => { embla.off("select", handleSelect); }; } return void 0; }, [embla, emblaOptions?.slidesToScroll, handleSelect]); useEffect(() => { if (embla) { embla.reInit(); setSlidesCount(embla.scrollSnapList().length); setSelected( (currentSelected) => clamp(currentSelected, 0, Children.toArray(children).length - 1) ); } }, [Children.toArray(children).length, emblaOptions?.slidesToScroll]); const canScrollPrev = embla?.canScrollPrev() || false; const canScrollNext = embla?.canScrollNext() || false; const indicators = Array(slidesCount).fill(0).map((_, index) => /* @__PURE__ */ createElement( UnstyledButton, { ...getStyles("indicator"), key: index, "data-active": index === selected || void 0, "aria-hidden": true, tabIndex: -1, onClick: () => handleScroll(index), "data-orientation": orientation, onMouseDown: (event) => event.preventDefault() } )); return /* @__PURE__ */ jsxs(CarouselProvider, { value: { getStyles, orientation }, children: [ type === "container" ? /* @__PURE__ */ jsx(CarouselContainerVariables, { ...props, selector: `.${responsiveClassName}` }) : /* @__PURE__ */ jsx(CarouselVariables, { ...props, selector: `.${responsiveClassName}` }), /* @__PURE__ */ jsxs( Box, { ref, ...getStyles("root", { className: "responsiveClassName" }), ...others, mod: [{ orientation, "include-gap-in-size": includeGapInSize }, mod], onKeyDownCapture: handleKeydown, children: [ withControls && /* @__PURE__ */ jsxs("div", { ...getStyles("controls"), "data-orientation": orientation, children: [ /* @__PURE__ */ jsx( UnstyledButton, { ...previousControlProps, ...getStyles("control", { className: previousControlProps?.className, style: previousControlProps?.style }), onClick: (event) => { handlePrevious(); previousControlProps?.onClick?.(event); }, "data-inactive": !canScrollPrev || void 0, "data-type": "previous", tabIndex: canScrollPrev ? 0 : -1, children: typeof previousControlIcon !== "undefined" ? previousControlIcon : /* @__PURE__ */ jsx( AccordionChevron, { style: { transform: `rotate(${getChevronRotation({ dir, orientation, direction: "previous" })}deg)` } } ) } ), /* @__PURE__ */ jsx( UnstyledButton, { ...getStyles("control", { className: nextControlProps?.className, style: nextControlProps?.style }), ...nextControlProps, onClick: (event) => { handleNext(); nextControlProps?.onClick?.(event); }, "data-inactive": !canScrollNext || void 0, "data-type": "next", tabIndex: canScrollNext ? 0 : -1, children: typeof nextControlIcon !== "undefined" ? nextControlIcon : /* @__PURE__ */ jsx( AccordionChevron, { style: { transform: `rotate(${getChevronRotation({ dir, orientation, direction: "next" })}deg)` } } ) } ) ] }), /* @__PURE__ */ jsx("div", { ...getStyles("viewport"), ref: emblaRefElement, "data-type": type, children: /* @__PURE__ */ jsx( "div", { ...getStyles("container", { className: responsiveClassName }), "data-orientation": orientation, children } ) }), withIndicators && /* @__PURE__ */ jsx("div", { ...getStyles("indicators"), "data-orientation": orientation, children: indicators }) ] } ) ] }); }); Carousel.classes = classes; Carousel.displayName = "@mantine/carousel/Carousel"; Carousel.Slide = CarouselSlide; export { Carousel }; //# sourceMappingURL=Carousel.mjs.map