UNPKG

@hitachivantara/uikit-react-core

Version:

Core React components for the NEXT Design System.

227 lines (226 loc) 7.83 kB
import { jsxs, jsx } from "react/jsx-runtime"; import { forwardRef, useRef, useState, Children, useCallback, useEffect } from "react"; import useCarousel from "embla-carousel-react"; import { useDefaultProps, useTheme, clamp } from "@hitachivantara/uikit-react-utils"; import { useLabels } from "../hooks/useLabels.js"; import { HvIcon } from "../icons.js"; import { useClasses } from "./Carousel.styles.js"; import { staticClasses } from "./Carousel.styles.js"; import { HvCarouselControls } from "./CarouselControls.js"; import { HvCarouselThumbnails } from "./CarouselThumbnails.js"; import { HvContainer } from "../Container/Container.js"; import { HvButton } from "../Button/Button.js"; import { HvIconButton } from "../IconButton/IconButton.js"; import { HvTypography } from "../Typography/Typography.js"; const DEFAULT_LABELS = { close: "Close", fullscreen: "Fullscreen", backwards: "Backwards", forwards: "Forwards" }; const HvCarousel = forwardRef(function HvCarousel2(props, ref) { const { className, classes: classesProp, height: heightProp = "auto", thumbnailWidth = 90, title, children, actions: actionsProp, xs, showDots: showDotsProp, showCounter: showCounterProp, showSlideControls, showFullscreen: showFullscreenProp, hideThumbnails: hideThumbnailsProp, controlsPosition: controlsPositionProp, thumbnailsPosition: thumbnailsPositionProp, labels: labelsProps, carouselOptions, renderThumbnail, onChange, onFullscreen, ...others } = useDefaultProps("HvCarousel", props); const { activeTheme } = useTheme(); const { classes, css, cx } = useClasses(classesProp); const labels = useLabels(DEFAULT_LABELS, labelsProps); const thumbnailsRef = useRef(null); const [isFullscreen, setIsFullscreen] = useState(false); const isDs3 = activeTheme?.base === "ds3"; const actionsPosition = isDs3 ? "header" : "controls"; const controlsPosition = controlsPositionProp ?? (isDs3 ? "bottom" : "top"); const thumbnailsPosition = thumbnailsPositionProp ?? "bottom"; const [containerRef, controller] = useCarousel({ align: "start", loop: true, ...carouselOptions }); const [selectedIndex, setSelectedIndex] = useState( carouselOptions?.startIndex ?? 0 ); const numSlides = Children.count(children); const handlePrevious = useCallback(() => { controller?.scrollPrev(); }, [controller]); const handleNext = useCallback(() => { controller?.scrollNext(); }, [controller]); const handleScroll = (index) => { controller?.scrollTo(index); }; const handleSelect = useCallback(() => { if (!controller) return; const slideIndex = controller.selectedScrollSnap(); setSelectedIndex(slideIndex); thumbnailsRef.current?.querySelectorAll("button")?.[slideIndex]?.scrollIntoView({ behavior: "smooth", block: "nearest" }); onChange?.(slideIndex); }, [controller, onChange]); useEffect(() => { if (!controller) return; controller.on("select", handleSelect); return () => { controller.off("select", handleSelect); }; }, [controller, handleSelect]); useEffect(() => { if (!controller) return; controller.reInit(); setSelectedIndex((currentIndex) => clamp(currentIndex, numSlides)); }, [numSlides, controller]); const handleFullscreen = (event) => { onFullscreen?.(event, !isFullscreen); setIsFullscreen((curr) => !curr); }; const canPrev = controller?.canScrollPrev() ?? false; const canNext = controller?.canScrollNext() ?? false; const showTitle = !!title && (!xs || isFullscreen); const showFullscreen = showFullscreenProp ?? xs; const height = isFullscreen ? "100%" : heightProp ?? "auto"; const showCounter = xs; const hideThumbnails = hideThumbnailsProp ?? (xs && !isFullscreen); const showThumbnails = !hideThumbnails && !!renderThumbnail; const showDots = showDotsProp ?? numSlides <= 5; const actions = /* @__PURE__ */ jsxs( "div", { className: cx( classes.actions, actionsPosition === "header" ? css({ position: "relative", top: -40, height: 0 }) : css({ position: "absolute" }) ), children: [ actionsProp, showFullscreen && /* @__PURE__ */ jsx( HvIconButton, { title: isFullscreen ? labels.close : labels.fullscreen, onClick: handleFullscreen, className: classes.closeButton, children: /* @__PURE__ */ jsx(HvIcon, { name: isFullscreen ? "Close" : "Fullscreen" }) } ) ] } ); const controls = /* @__PURE__ */ jsx( HvCarouselControls, { classes, showDots, page: selectedIndex, pages: numSlides, canPrevious: canPrev, canNext, onPreviousClick: handlePrevious, onNextClick: handleNext, actions: actionsPosition === "controls" && actions, labels: { backwards: labels.backwards, forwards: labels.forwards } } ); const thumbnails = showThumbnails && /* @__PURE__ */ jsx( HvCarouselThumbnails, { classes, ref: thumbnailsRef, page: selectedIndex, pages: numSlides, width: thumbnailWidth, onThumbnailClick: (evt, i) => handleScroll(i), renderThumbnail } ); return /* @__PURE__ */ jsxs( HvContainer, { ref, className: cx(classes.root, className, { [classes.xs]: xs, [classes.fullscreen]: isFullscreen }), ...others, children: [ showTitle && /* @__PURE__ */ jsx(HvTypography, { variant: "title2", className: classes.title, children: title }), actionsPosition === "header" && actions, thumbnailsPosition === "top" && thumbnails, controlsPosition === "top" && controls, /* @__PURE__ */ jsxs( "div", { className: cx(classes.main, { [classes.mainXs]: xs, [classes.mainFullscreen]: isFullscreen }), children: [ showCounter && /* @__PURE__ */ jsx("div", { className: classes.counterContainer, children: /* @__PURE__ */ jsx("span", { className: classes.counter, children: `${selectedIndex + 1}/${numSlides}` }) }), showSlideControls && /* @__PURE__ */ jsxs("div", { className: classes.slideControls, children: [ /* @__PURE__ */ jsx( HvButton, { icon: true, disabled: !canPrev, variant: "secondarySubtle", "aria-label": labels.backwards, onClick: handlePrevious, children: /* @__PURE__ */ jsx(HvIcon, { name: "Backwards", size: "xs" }) } ), /* @__PURE__ */ jsx( HvButton, { icon: true, disabled: !canNext, variant: "secondarySubtle", "aria-label": labels.forwards, onClick: handleNext, children: /* @__PURE__ */ jsx(HvIcon, { name: "Forwards", size: "xs" }) } ) ] }), /* @__PURE__ */ jsx( "div", { ref: containerRef, style: { height }, className: classes.slidesViewport, children: /* @__PURE__ */ jsx("div", { className: classes.slidesContainer, children }) } ) ] } ), controlsPosition === "bottom" && controls, thumbnailsPosition === "bottom" && thumbnails ] } ); }); export { HvCarousel, staticClasses as carouselClasses };