flowbite-react
Version:
Official React components built for Flowbite and Tailwind CSS
159 lines (155 loc) • 6.39 kB
JavaScript
'use client';
'use strict';
var jsxRuntime = require('react/jsx-runtime');
var React = require('react');
var hi = require('react-icons/hi');
var tailwindMerge = require('tailwind-merge');
var index$1 = require('../../helpers/drag-scroll/index.cjs');
var isClient = require('../../helpers/is-client.cjs');
var mergeDeep = require('../../helpers/merge-deep.cjs');
var index = require('../../theme-store/index.cjs');
const Carousel = ({
children,
indicators = true,
leftControl,
rightControl,
slide = true,
draggable = true,
slideInterval,
className,
theme: customTheme = {},
onSlideChange = null,
pauseOnHover = false,
...props
}) => {
const theme = mergeDeep.mergeDeep(index.getTheme().carousel, customTheme);
const isDeviceMobile = isClient.isClient() && navigator.userAgent.indexOf("IEMobile") !== -1;
const carouselContainer = React.useRef(null);
const [activeItem, setActiveItem] = React.useState(0);
const [isDragging, setIsDragging] = React.useState(false);
const [isHovering, setIsHovering] = React.useState(false);
const didMountRef = React.useRef(false);
const items = React.useMemo(
() => React.Children.map(
children,
(child) => React.cloneElement(child, {
className: tailwindMerge.twMerge(theme.item.base, child.props.className)
})
),
[children, theme.item.base]
);
const navigateTo = React.useCallback(
(item) => () => {
if (!items)
return;
item = (item + items.length) % items.length;
if (carouselContainer.current) {
carouselContainer.current.scrollLeft = carouselContainer.current.clientWidth * item;
}
setActiveItem(item);
},
[items]
);
React.useEffect(() => {
if (carouselContainer.current && !isDragging && carouselContainer.current.scrollLeft !== 0) {
setActiveItem(Math.round(carouselContainer.current.scrollLeft / carouselContainer.current.clientWidth));
}
}, [isDragging]);
React.useEffect(() => {
if (slide && !(pauseOnHover && isHovering)) {
const intervalId = setInterval(() => !isDragging && navigateTo(activeItem + 1)(), slideInterval ?? 3e3);
return () => clearInterval(intervalId);
}
}, [activeItem, isDragging, navigateTo, slide, slideInterval, pauseOnHover, isHovering]);
React.useEffect(() => {
if (didMountRef.current) {
onSlideChange && onSlideChange(activeItem);
} else {
didMountRef.current = true;
}
}, [onSlideChange, activeItem]);
const handleDragging = (dragging) => () => setIsDragging(dragging);
const setHoveringTrue = React.useCallback(() => setIsHovering(true), [setIsHovering]);
const setHoveringFalse = React.useCallback(() => setIsHovering(false), [setIsHovering]);
return /* @__PURE__ */ jsxRuntime.jsxs(
"div",
{
className: tailwindMerge.twMerge(theme.root.base, className),
"data-testid": "carousel",
onMouseEnter: setHoveringTrue,
onMouseLeave: setHoveringFalse,
onTouchStart: setHoveringTrue,
onTouchEnd: setHoveringFalse,
...props,
children: [
/* @__PURE__ */ jsxRuntime.jsx(
index$1,
{
className: tailwindMerge.twMerge(theme.scrollContainer.base, (isDeviceMobile || !isDragging) && theme.scrollContainer.snap),
draggingClassName: "cursor-grab",
innerRef: carouselContainer,
onEndScroll: handleDragging(false),
onStartScroll: handleDragging(draggable),
vertical: false,
horizontal: draggable,
children: items?.map((item, index) => /* @__PURE__ */ jsxRuntime.jsx(
"div",
{
className: theme.item.wrapper[draggable ? "on" : "off"],
"data-active": activeItem === index,
"data-testid": "carousel-item",
children: item
},
index
))
}
),
indicators && /* @__PURE__ */ jsxRuntime.jsx("div", { className: theme.indicators.wrapper, children: items?.map((_, index) => /* @__PURE__ */ jsxRuntime.jsx(
"button",
{
className: tailwindMerge.twMerge(theme.indicators.base, theme.indicators.active[index === activeItem ? "on" : "off"]),
onClick: navigateTo(index),
"data-testid": "carousel-indicator",
"aria-label": `Slide ${index + 1}`
},
index
)) }),
items && /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
/* @__PURE__ */ jsxRuntime.jsx("div", { className: theme.root.leftControl, children: /* @__PURE__ */ jsxRuntime.jsx(
"button",
{
className: "group",
"data-testid": "carousel-left-control",
onClick: navigateTo(activeItem - 1),
type: "button",
"aria-label": "Previous slide",
children: leftControl ? leftControl : /* @__PURE__ */ jsxRuntime.jsx(DefaultLeftControl, { theme: customTheme })
}
) }),
/* @__PURE__ */ jsxRuntime.jsx("div", { className: theme.root.rightControl, children: /* @__PURE__ */ jsxRuntime.jsx(
"button",
{
className: "group",
"data-testid": "carousel-right-control",
onClick: navigateTo(activeItem + 1),
type: "button",
"aria-label": "Next slide",
children: rightControl ? rightControl : /* @__PURE__ */ jsxRuntime.jsx(DefaultRightControl, { theme: customTheme })
}
) })
] })
]
}
);
};
const DefaultLeftControl = ({ theme: customTheme = {} }) => {
const theme = mergeDeep.mergeDeep(index.getTheme().carousel, customTheme);
return /* @__PURE__ */ jsxRuntime.jsx("span", { className: theme.control.base, children: /* @__PURE__ */ jsxRuntime.jsx(hi.HiOutlineChevronLeft, { className: theme.control.icon }) });
};
const DefaultRightControl = ({ theme: customTheme = {} }) => {
const theme = mergeDeep.mergeDeep(index.getTheme().carousel, customTheme);
return /* @__PURE__ */ jsxRuntime.jsx("span", { className: theme.control.base, children: /* @__PURE__ */ jsxRuntime.jsx(hi.HiOutlineChevronRight, { className: theme.control.icon }) });
};
Carousel.displayName = "Carousel";
exports.Carousel = Carousel;
//# sourceMappingURL=Carousel.cjs.map