Files
web/packages/design-system/lib/components/ImageGallery/index.tsx
Matilda Landström fc857ad58f Merged in SW-2064-lightbox-updates (pull request #3347)
feat(SW-2064): Lightbox updates

* chore(SW-2064): add opacity to not selected images

* chore(SW-2064): set main image as the first image in thumbnail

* chore(SW-2064): disable navigation buttons on first and last image

* fix(SW-2064): use cx

* Revert "chore(SW-2064): disable navigation buttons on first and last image"

This reverts commit 9c5acd8a02b83740f35d1cfa88bfba6b006ba947.

* refactor(SW-2064): create ImageCounter component

* refactor(SW-2064)

* chore(SW-2064): add enter keu down on main image


Approved-by: Erik Tiekstra
2026-01-08 12:10:03 +00:00

105 lines
2.5 KiB
TypeScript

"use client"
import { memo, useState } from "react"
import { Button as ButtonRAC } from "react-aria-components"
import { useIntl } from "react-intl"
import { MaterialIcon } from "../Icons/MaterialIcon"
import Image from "../Image"
import ImageFallback from "../ImageFallback"
import Lightbox from "../Lightbox"
import { Typography } from "../Typography"
import styles from "./imageGallery.module.css"
import { ImageCounter } from "../ImageCounter"
import { cx } from "class-variance-authority"
export interface GalleryImage {
src: string
alt: string
caption?: string | null
}
type ImageGalleryProps = {
images?: GalleryImage[]
title: string
fill?: boolean
width?: number
height?: number
sizes?: string
hideLabel?: boolean
imageCountPosition?: "top" | "bottom"
}
function ImageGallery({
images,
title,
fill,
height = 280,
sizes,
hideLabel,
imageCountPosition = "bottom",
}: ImageGalleryProps) {
const intl = useIntl()
const [isOpen, setIsOpen] = useState(false)
const [imageError, setImageError] = useState(false)
const imageProps = fill
? {
fill,
sizes:
sizes ?? "auto, (max-width: 400px) 100vw, (min-width: 401px) 500px",
}
: { height, width: height * 1.5 }
if (!images?.length || imageError) {
return <ImageFallback />
}
const firstImage = images[0]
return (
<>
<div className={styles.wrapper}>
<Image
className={styles.image}
src={firstImage.src}
alt={firstImage.alt}
onError={() => setImageError(true)}
{...imageProps}
/>
<ImageCounter
className={cx(styles.imageCount, {
[styles.imageCountTop]: imageCountPosition === "top",
[styles.imageCountBottom]: imageCountPosition === "bottom",
})}
number={images.length}
size="Large"
leadingIcon
/>
<ButtonRAC
className={styles.triggerArea}
onPress={() => setIsOpen(true)}
aria-label={intl.formatMessage({
id: "imageGallery.openImageGallery",
defaultMessage: "Open image gallery",
})}
/>
</div>
<Lightbox
images={images}
dialogTitle={title}
onClose={() => setIsOpen(false)}
isOpen={isOpen}
hideLabel={hideLabel}
/>
</>
)
}
const ImageGalleryComponent = memo(ImageGallery)
export default ImageGalleryComponent as React.MemoExoticComponent<
(props: ImageGalleryProps) => React.ReactElement
>