@grafana/ui
Version:
Grafana Components Library
197 lines (194 loc) • 5.78 kB
JavaScript
import { jsx, jsxs } from 'react/jsx-runtime';
import { cx, css } from '@emotion/css';
import { useState, useEffect } from 'react';
import '@grafana/data';
import { useStyles2 } from '../../themes/ThemeContext.mjs';
import 'micro-memoize';
import '@emotion/react';
import 'tinycolor2';
import '../../utils/skeleton.mjs';
import { t } from '../../utils/i18n.mjs';
import { Alert } from '../Alert/Alert.mjs';
import { IconButton } from '../IconButton/IconButton.mjs';
const Carousel = ({ images }) => {
const [selectedIndex, setSelectedIndex] = useState(null);
const [imageErrors, setImageErrors] = useState({});
const [validImages, setValidImages] = useState(images);
const styles = useStyles2(getStyles());
const handleImageError = (path) => {
setImageErrors((prev) => ({
...prev,
[path]: true
}));
};
useEffect(() => {
const filteredImages = images.filter((image) => !imageErrors[image.path]);
setValidImages(filteredImages);
}, [imageErrors, images]);
const openPreview = (index) => {
setSelectedIndex(index);
};
const closePreview = () => {
setSelectedIndex(null);
};
const goToNext = () => {
if (selectedIndex !== null && validImages.length > 0) {
setSelectedIndex((selectedIndex + 1) % validImages.length);
}
};
const goToPrevious = () => {
if (selectedIndex !== null && validImages.length > 0) {
setSelectedIndex((selectedIndex - 1 + validImages.length) % validImages.length);
}
};
const handleKeyDown = (event) => {
if (selectedIndex === null) {
return;
}
switch (event.key) {
case "ArrowRight":
goToNext();
break;
case "ArrowLeft":
goToPrevious();
break;
case "Escape":
closePreview();
break;
}
};
if (validImages.length === 0) {
return /* @__PURE__ */ jsx(
Alert,
{
title: t("carousel.error", "Something went wrong loading images"),
severity: "warning",
"data-testid": "alert-warning"
}
);
}
return /* @__PURE__ */ jsxs("div", { onKeyDown: handleKeyDown, tabIndex: 0, children: [
/* @__PURE__ */ jsx("div", { className: cx(styles.imageGrid), children: validImages.map((image, index) => /* @__PURE__ */ jsxs("div", { onClick: () => openPreview(index), style: { cursor: "pointer" }, children: [
/* @__PURE__ */ jsx("img", { src: image.path, alt: image.name, onError: () => handleImageError(image.path) }),
/* @__PURE__ */ jsx("p", { children: image.name })
] }, image.path)) }),
selectedIndex !== null && /* @__PURE__ */ jsxs("div", { className: cx(styles.fullScreenDiv), onClick: closePreview, "data-testid": "carousel-full-screen", children: [
/* @__PURE__ */ jsx(
IconButton,
{
name: "times",
"aria-label": t("carousel.close", "Close"),
size: "xl",
onClick: closePreview,
className: cx(styles.closeButton)
}
),
/* @__PURE__ */ jsx(
IconButton,
{
size: "xl",
name: "angle-left",
"aria-label": t("carousel.previous", "Previous"),
onClick: (e) => {
e.stopPropagation();
goToPrevious();
},
className: cx(styles.navigationButton, styles.previousButton),
"data-testid": "previous-button"
}
),
/* @__PURE__ */ jsx(
"div",
{
style: { position: "relative", maxWidth: "90%", maxHeight: "90%" },
onClick: (e) => e.stopPropagation(),
"data-testid": "carousel-full-image",
children: /* @__PURE__ */ jsx(
"img",
{
src: validImages[selectedIndex].path,
alt: validImages[selectedIndex].name,
onError: () => handleImageError(validImages[selectedIndex].path)
}
)
}
),
/* @__PURE__ */ jsx(
IconButton,
{
size: "xl",
name: "angle-right",
"aria-label": t("carousel.next", "Next"),
onClick: (e) => {
e.stopPropagation();
goToNext();
},
className: cx(styles.navigationButton, styles.nextButton),
"data-testid": "next-button"
}
)
] })
] });
};
const getStyles = () => (theme) => ({
imageGrid: css({
display: "grid",
gridTemplateColumns: `repeat(auto-fill, minmax(200px, 1fr))`,
gap: "16px",
marginBottom: "20px",
"& img": {
width: "100%",
height: "150px",
objectFit: "cover",
border: theme.colors.border.strong,
borderRadius: theme.shape.radius.default,
boxShadow: theme.shadows.z1
},
"& p": {
margin: "4px 0",
fontWeight: theme.typography.fontWeightMedium,
color: theme.colors.text.primary
}
}),
fullScreenDiv: css({
position: "fixed",
zIndex: theme.zIndex.modalBackdrop,
top: 0,
right: 0,
bottom: 0,
left: 0,
backgroundColor: theme.components.overlay.background,
alignItems: "center",
justifyContent: "center",
display: "flex",
"& img": {
maxWidth: "100%",
maxHeight: "80vh",
objectFit: "contain"
}
}),
closeButton: css({
position: "absolute",
top: "20px",
right: "20px",
backgroundColor: "transparent",
color: theme.colors.text.primary
}),
navigationButton: css({
position: "absolute",
backgroundColor: "transparent",
color: theme.colors.text.primary,
cursor: "pointer",
display: "flex",
alignItems: "center",
justifyContent: "center"
}),
nextButton: css({
right: "20px"
}),
previousButton: css({
left: "20px"
})
});
export { Carousel };
//# sourceMappingURL=Carousel.mjs.map