@hitachivantara/uikit-react-core
Version:
Core React components for the NEXT Design System.
227 lines (226 loc) • 7.83 kB
JavaScript
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
};