@hitachivantara/uikit-react-core
Version:
UI Kit Core React components.
183 lines (182 loc) • 6.57 kB
JavaScript
import { HvTypography } from "../Typography/Typography.js";
import { HvIcon } from "../icons.js";
import { HvButton } from "../Button/Button.js";
import { useLabels } from "../hooks/useLabels.js";
import { HvIconButton } from "../IconButton/IconButton.js";
import { HvContainer } from "../Container/Container.js";
import { useClasses } from "./Carousel.styles.js";
import { HvCarouselControls } from "./CarouselControls.js";
import { HvCarouselThumbnails } from "./CarouselThumbnails.js";
import { clamp, useDefaultProps } from "@hitachivantara/uikit-react-utils";
import { Children, forwardRef, useCallback, useEffect, useRef, useState } from "react";
import { jsx, jsxs } from "react/jsx-runtime";
import useCarousel from "embla-carousel-react";
//#region src/Carousel/Carousel.tsx
var DEFAULT_LABELS = {
close: "Close",
fullscreen: "Fullscreen",
backwards: "Backwards",
forwards: "Forwards"
};
/**
A Carousel is used to browse content—commonly images, but also text, video, or charts. It highlights one item at a time while giving users a sense of the total content available.
*/
var HvCarousel = forwardRef(function HvCarousel(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 { classes, cx } = useClasses(classesProp);
const labels = useLabels(DEFAULT_LABELS, labelsProps);
const thumbnailsRef = useRef(null);
const [isFullscreen, setIsFullscreen] = useState(false);
const controlsPosition = controlsPositionProp ?? "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 showThumbnails = !(hideThumbnailsProp ?? (xs && !isFullscreen)) && !!renderThumbnail;
const controls = /* @__PURE__ */ jsx(HvCarouselControls, {
classes,
showDots: showDotsProp ?? numSlides <= 5,
page: selectedIndex,
pages: numSlides,
canPrevious: canPrev,
canNext,
onPreviousClick: handlePrevious,
onNextClick: handleNext,
actions: /* @__PURE__ */ jsxs("div", {
className: classes.actions,
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" })
})]
}),
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
}),
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
]
});
});
//#endregion
export { HvCarousel };