UNPKG

@grafana/ui

Version:
1 lines 12.6 kB
{"version":3,"file":"Carousel.mjs","sources":["../../../../src/components/Carousel/Carousel.tsx"],"sourcesContent":["import { css, cx } from '@emotion/css';\nimport { useDialog } from '@react-aria/dialog';\nimport { FocusScope } from '@react-aria/focus';\nimport { OverlayContainer, useOverlay } from '@react-aria/overlays';\nimport { useState, useEffect, useRef } from 'react';\n\nimport { GrafanaTheme2 } from '@grafana/data';\nimport { t } from '@grafana/i18n';\n\nimport { useStyles2 } from '../../themes/ThemeContext';\nimport { Alert } from '../Alert/Alert';\nimport { clearButtonStyles } from '../Button/Button';\nimport { IconButton } from '../IconButton/IconButton';\n\n// Define the image item interface\nexport interface CarouselImage {\n path: string;\n name: string;\n}\n\nexport interface CarouselProps {\n images: CarouselImage[];\n}\n\nexport const Carousel: React.FC<CarouselProps> = ({ images }) => {\n const [selectedIndex, setSelectedIndex] = useState<number | null>(null);\n const [imageErrors, setImageErrors] = useState<Record<string, boolean>>({});\n const [validImages, setValidImages] = useState<CarouselImage[]>(images);\n\n const styles = useStyles2(getStyles);\n const resetButtonStyles = useStyles2(clearButtonStyles);\n\n const handleImageError = (path: string) => {\n setImageErrors((prev) => ({\n ...prev,\n [path]: true,\n }));\n };\n\n useEffect(() => {\n const filteredImages = images.filter((image) => !imageErrors[image.path]);\n setValidImages(filteredImages);\n }, [imageErrors, images]);\n\n const openPreview = (index: number) => {\n setSelectedIndex(index);\n };\n\n const closePreview = () => {\n setSelectedIndex(null);\n };\n\n const goToNext = () => {\n if (selectedIndex !== null && validImages.length > 0) {\n setSelectedIndex((selectedIndex + 1) % validImages.length);\n }\n };\n\n const goToPrevious = () => {\n if (selectedIndex !== null && validImages.length > 0) {\n setSelectedIndex((selectedIndex - 1 + validImages.length) % validImages.length);\n }\n };\n\n const handleKeyDown = (event: React.KeyboardEvent) => {\n if (selectedIndex === null) {\n return;\n }\n\n switch (event.key) {\n case 'ArrowRight':\n goToNext();\n break;\n case 'ArrowLeft':\n goToPrevious();\n break;\n case 'Escape':\n closePreview();\n break;\n default:\n break;\n }\n };\n\n const ref = useRef<HTMLDivElement>(null);\n\n const { overlayProps, underlayProps } = useOverlay({ isOpen: selectedIndex !== null, onClose: closePreview }, ref);\n const { dialogProps } = useDialog({}, ref);\n\n if (validImages.length === 0) {\n return (\n <Alert\n title={t('carousel.error', 'Something went wrong loading images')}\n severity=\"warning\"\n data-testid=\"alert-warning\"\n />\n );\n }\n\n return (\n <>\n <div className={cx(styles.imageGrid)}>\n {validImages.map((image, index) => (\n <button\n type=\"button\"\n key={image.path}\n onClick={() => openPreview(index)}\n className={cx(resetButtonStyles, styles.imageButton)}\n >\n <img src={image.path} alt={image.name} onError={() => handleImageError(image.path)} />\n <p>{image.name}</p>\n </button>\n ))}\n </div>\n\n {selectedIndex !== null && (\n <OverlayContainer>\n <div role=\"presentation\" className={styles.underlay} onClick={closePreview} {...underlayProps} />\n <FocusScope contain autoFocus restoreFocus>\n {/* convenience method for keyboard users */}\n {/* eslint-disable-next-line jsx-a11y/no-static-element-interactions */}\n <div\n data-testid=\"carousel-full-screen\"\n ref={ref}\n {...overlayProps}\n {...dialogProps}\n onKeyDown={handleKeyDown}\n className={styles.overlay}\n >\n <IconButton\n name=\"times\"\n aria-label={t('carousel.close', 'Close')}\n size=\"xl\"\n onClick={closePreview}\n className={cx(styles.closeButton)}\n />\n\n <IconButton\n size=\"xl\"\n name=\"angle-left\"\n aria-label={t('carousel.previous', 'Previous')}\n onClick={goToPrevious}\n data-testid=\"previous-button\"\n />\n\n <div className={styles.imageContainer} data-testid=\"carousel-full-image\">\n <img\n className={styles.imagePreview}\n src={validImages[selectedIndex].path}\n alt={validImages[selectedIndex].name}\n onError={() => handleImageError(validImages[selectedIndex].path)}\n />\n </div>\n\n <IconButton\n size=\"xl\"\n name=\"angle-right\"\n aria-label={t('carousel.next', 'Next')}\n onClick={goToNext}\n data-testid=\"next-button\"\n />\n </div>\n </FocusScope>\n </OverlayContainer>\n )}\n </>\n );\n};\n\nconst getStyles = (theme: GrafanaTheme2) => ({\n imageButton: css({\n textAlign: 'left',\n }),\n imageContainer: css({\n display: 'flex',\n justifyContent: 'center',\n flex: 1,\n }),\n imagePreview: css({\n maxWidth: '100%',\n maxHeight: '80vh',\n objectFit: 'contain',\n }),\n imageGrid: css({\n display: 'grid',\n gridTemplateColumns: `repeat(auto-fill, minmax(200px, 1fr))`,\n gap: theme.spacing(2),\n marginBottom: '20px',\n\n '& img': {\n width: '100%',\n height: '150px',\n objectFit: 'cover',\n border: theme.colors.border.strong,\n borderRadius: theme.shape.radius.default,\n boxShadow: theme.shadows.z1,\n },\n '& p': {\n margin: theme.spacing(0.5, 0),\n fontWeight: theme.typography.fontWeightMedium,\n color: theme.colors.text.primary,\n },\n }),\n underlay: css({\n position: 'fixed',\n zIndex: theme.zIndex.modalBackdrop,\n inset: 0,\n backgroundColor: theme.components.overlay.background,\n }),\n overlay: css({\n alignItems: 'center',\n display: 'flex',\n gap: theme.spacing(1),\n height: 'fit-content',\n marginBottom: 'auto',\n marginTop: 'auto',\n padding: theme.spacing(2),\n position: 'fixed',\n inset: 0,\n zIndex: theme.zIndex.modal,\n }),\n closeButton: css({\n color: theme.colors.text.primary,\n position: 'fixed',\n top: theme.spacing(2),\n right: theme.spacing(2),\n }),\n});\n"],"names":[],"mappings":"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"}