feat(SW-3695): use svg icons instead of font icons * feat(icons): use svg instead of font icons * feat(icons): use webpack/svgr for inlined svgs. Now support for isFilled again * Merge master * Remove old font icon Approved-by: Joakim Jäderberg
120 lines
3.2 KiB
TypeScript
120 lines
3.2 KiB
TypeScript
"use client"
|
|
import { AnimatePresence, motion } from "motion/react"
|
|
import { useEffect, useState } from "react"
|
|
import { Dialog, Modal, ModalOverlay } from "react-aria-components"
|
|
|
|
import usePopStateHandler from "@scandic-hotels/common/hooks/usePopStateHandler"
|
|
|
|
import { FullView } from "./FullView"
|
|
import { Gallery } from "./Gallery"
|
|
|
|
import styles from "./lightbox.module.css"
|
|
|
|
export type LightboxImage = {
|
|
src: string
|
|
alt: string
|
|
caption?: string | null
|
|
}
|
|
|
|
type LightboxProps = {
|
|
images: LightboxImage[]
|
|
dialogTitle: string /* Accessible title for dialog screen readers */
|
|
onClose: () => void
|
|
isOpen: boolean
|
|
activeIndex?: number
|
|
hideLabel?: boolean
|
|
}
|
|
|
|
export default function Lightbox({
|
|
images,
|
|
dialogTitle,
|
|
onClose,
|
|
isOpen,
|
|
activeIndex = 0,
|
|
hideLabel,
|
|
}: LightboxProps) {
|
|
const [selectedImageIndex, setSelectedImageIndex] = useState(activeIndex)
|
|
const [isFullView, setIsFullView] = useState(false)
|
|
|
|
function handleClose(moveBack = false) {
|
|
setSelectedImageIndex(0)
|
|
if (moveBack) {
|
|
window.history.back()
|
|
} else {
|
|
onClose()
|
|
}
|
|
}
|
|
|
|
usePopStateHandler(() => handleClose(), isOpen)
|
|
|
|
useEffect(() => {
|
|
if (isOpen) {
|
|
window.history.pushState(null, "", window.location.href)
|
|
}
|
|
}, [isOpen])
|
|
|
|
useEffect(() => {
|
|
setSelectedImageIndex(activeIndex)
|
|
}, [activeIndex])
|
|
|
|
function handleNext() {
|
|
setSelectedImageIndex((prevIndex) => (prevIndex + 1) % images.length)
|
|
}
|
|
|
|
function handlePrev() {
|
|
setSelectedImageIndex(
|
|
(prevIndex) => (prevIndex - 1 + images.length) % images.length
|
|
)
|
|
}
|
|
|
|
return (
|
|
<ModalOverlay
|
|
isOpen={isOpen}
|
|
onOpenChange={() => handleClose(true)}
|
|
className={styles.overlay}
|
|
isDismissable
|
|
>
|
|
<Modal>
|
|
<AnimatePresence>
|
|
<Dialog aria-label={dialogTitle}>
|
|
<motion.div
|
|
className={`${styles.content} ${
|
|
isFullView ? styles.fullViewContent : styles.galleryContent
|
|
}`}
|
|
initial={{ opacity: 0, scale: 0.95, x: "-50%", y: "-50%" }}
|
|
animate={{ opacity: 1, scale: 1 }}
|
|
exit={{ opacity: 0, scale: 0.95 }}
|
|
transition={{ duration: 0.2 }}
|
|
>
|
|
{isFullView ? (
|
|
<FullView
|
|
image={images[selectedImageIndex]}
|
|
onClose={() => setIsFullView(false)}
|
|
onNext={handleNext}
|
|
onPrev={handlePrev}
|
|
currentIndex={selectedImageIndex}
|
|
totalImages={images.length}
|
|
hideLabel={hideLabel}
|
|
/>
|
|
) : (
|
|
<Gallery
|
|
images={images}
|
|
onClose={handleClose}
|
|
onSelectImage={(image) => {
|
|
setSelectedImageIndex(
|
|
images.findIndex((img) => img === image)
|
|
)
|
|
}}
|
|
onImageClick={() => setIsFullView(true)}
|
|
selectedImage={images[selectedImageIndex]}
|
|
hideLabel={hideLabel}
|
|
/>
|
|
)}
|
|
</motion.div>
|
|
</Dialog>
|
|
</AnimatePresence>
|
|
</Modal>
|
|
</ModalOverlay>
|
|
)
|
|
}
|