feat(SW-842): Added lightbox to roomcard

This commit is contained in:
Erik Tiekstra
2024-11-12 10:39:42 +01:00
parent d732138696
commit 962760ae1b
11 changed files with 150 additions and 142 deletions

View File

@@ -1,44 +1,54 @@
"use client"
import { useState } from "react"
import { useIntl } from "react-intl"
import Image from "@/components/Image" import Image from "@/components/Image"
import Lightbox from "@/components/Lightbox/" import Lightbox from "@/components/Lightbox/"
import Button from "@/components/TempDesignSystem/Button" import Button from "@/components/TempDesignSystem/Button"
import { getIntl } from "@/i18n"
import styles from "./previewImages.module.css" import styles from "./previewImages.module.css"
import type { PreviewImagesProps } from "@/types/components/hotelPage/previewImages" import type { PreviewImagesProps } from "@/types/components/hotelPage/previewImages"
export default async function PreviewImages({ export default function PreviewImages({
images, images,
hotelName, hotelName,
}: PreviewImagesProps) { }: PreviewImagesProps) {
const intl = await getIntl() const intl = useIntl()
const imageGalleryText = intl.formatMessage({ id: "Image gallery" }) const [lightboxIsOpen, setLightboxIsOpen] = useState(false)
const dialogTitle = `${hotelName} - ${imageGalleryText}`
return ( return (
<Lightbox images={images} dialogTitle={dialogTitle}> <div className={styles.imageWrapper}>
<div className={styles.imageWrapper}> {images.slice(0, 3).map((image, index) => (
{images.slice(0, 3).map((image, index) => ( <Image
<Image key={index}
key={index} src={image.imageSizes.medium}
src={image.imageSizes.medium} alt={image.metaData.altText}
alt={image.metaData.altText} title={image.metaData.title}
title={image.metaData.title} width={index === 0 ? 752 : 292}
width={index === 0 ? 752 : 292} height={index === 0 ? 540 : 266}
height={index === 0 ? 540 : 266} className={styles.image}
className={styles.image} />
/> ))}
))} <Button
<Button theme="base"
theme="base" intent="inverted"
intent="inverted" size="small"
size="small" onClick={() => setLightboxIsOpen(true)}
id="lightboxTrigger" className={styles.seeAllButton}
className={styles.seeAllButton} >
> {intl.formatMessage({ id: "See all photos" })}
{intl.formatMessage({ id: "See all photos" })} </Button>
</Button> <Lightbox
</div> images={images}
</Lightbox> dialogTitle={intl.formatMessage(
{ id: "Hotel Image gallery" },
{ hotel: hotelName }
)}
isOpen={lightboxIsOpen}
onClose={() => setLightboxIsOpen(false)}
/>
</div>
) )
} }

View File

@@ -1,9 +1,11 @@
"use client" "use client"
import { useState } from "react"
import { useIntl } from "react-intl" import { useIntl } from "react-intl"
import { GalleryIcon } from "@/components/Icons" import { GalleryIcon } from "@/components/Icons"
import Image from "@/components/Image" import Image from "@/components/Image"
import Lightbox from "@/components/Lightbox"
import Body from "@/components/TempDesignSystem/Text/Body" import Body from "@/components/TempDesignSystem/Text/Body"
import Subtitle from "@/components/TempDesignSystem/Text/Subtitle" import Subtitle from "@/components/TempDesignSystem/Text/Subtitle"
@@ -16,6 +18,7 @@ import type { RoomCardProps } from "@/types/components/hotelPage/room"
export function RoomCard({ hotelId, room }: RoomCardProps) { export function RoomCard({ hotelId, room }: RoomCardProps) {
const { images, name, roomSize, occupancy, id } = room const { images, name, roomSize, occupancy, id } = room
const intl = useIntl() const intl = useIntl()
const [lightboxIsOpen, setLightboxIsOpen] = useState(false)
const mainImage = images[0] const mainImage = images[0]
const size = const size =
@@ -23,21 +26,12 @@ export function RoomCard({ hotelId, room }: RoomCardProps) {
? `${roomSize.min}` ? `${roomSize.min}`
: `${roomSize.min} - ${roomSize.max}` : `${roomSize.min} - ${roomSize.max}`
const personLabel = intl.formatMessage(
{ id: "hotelPages.rooms.roomCard.persons" },
{ totalOccupancy: occupancy.total }
)
const subtitle = `${size} (${personLabel})`
function handleImageClick() {
// TODO: Implement opening of a model with carousel
console.log("Image clicked: ", id)
}
return ( return (
<article className={styles.roomCard}> <article className={styles.roomCard}>
<button className={styles.imageWrapper} onClick={handleImageClick}> <button
className={styles.imageWrapper}
onClick={() => setLightboxIsOpen(true)}
>
{/* TODO: re-enable once we have support for badge text from API team. */} {/* TODO: re-enable once we have support for badge text from API team. */}
{/* {badgeTextTransKey && ( */} {/* {badgeTextTransKey && ( */}
{/* <span className={styles.badge}> */} {/* <span className={styles.badge}> */}
@@ -52,12 +46,21 @@ export function RoomCard({ hotelId, room }: RoomCardProps) {
which can't be accessed unless on Scandic's Wifi or using Citrix. */} which can't be accessed unless on Scandic's Wifi or using Citrix. */}
<Image <Image
className={styles.image} className={styles.image}
src={mainImage.imageSizes.large} src={mainImage.imageSizes.small}
alt={mainImage.metaData.altText} alt={mainImage.metaData.altText}
height={200} height={200}
width={300} width={300}
/> />
</button> </button>
<Lightbox
images={images}
dialogTitle={intl.formatMessage(
{ id: "Hotel Image gallery" },
{ hotel: name }
)}
isOpen={lightboxIsOpen}
onClose={() => setLightboxIsOpen(false)}
/>
<div className={styles.content}> <div className={styles.content}>
<div className={styles.innerContent}> <div className={styles.innerContent}>
<Subtitle <Subtitle
@@ -69,7 +72,12 @@ export function RoomCard({ hotelId, room }: RoomCardProps) {
> >
{name} {name}
</Subtitle> </Subtitle>
<Body color="grey">{subtitle}</Body> <Body color="grey">
{intl.formatMessage(
{ id: "hotelPages.rooms.roomCard.persons" },
{ size, totalOccupancy: occupancy.total }
)}
</Body>
</div> </div>
<RoomDetailsButton <RoomDetailsButton
hotelId={hotelId} hotelId={hotelId}

View File

@@ -1,3 +1,7 @@
"use client"
import { useState } from "react"
import { GalleryIcon } from "@/components/Icons" import { GalleryIcon } from "@/components/Icons"
import Image from "@/components/Image" import Image from "@/components/Image"
import Lightbox from "@/components/Lightbox" import Lightbox from "@/components/Lightbox"
@@ -8,12 +12,19 @@ import styles from "./imageGallery.module.css"
import type { ImageGalleryProps } from "@/types/components/hotelReservation/selectRate/imageGallery" import type { ImageGalleryProps } from "@/types/components/hotelReservation/selectRate/imageGallery"
export default function ImageGallery({ images, title }: ImageGalleryProps) { export default function ImageGallery({ images, title }: ImageGalleryProps) {
if (!images || images.length === 0) const [lightboxIsOpen, setLightboxIsOpen] = useState(false)
if (!images || images.length === 0) {
return <div className={styles.imagePlaceholder} /> return <div className={styles.imagePlaceholder} />
}
return ( return (
<Lightbox images={images} dialogTitle={title}> <>
<div className={styles.triggerArea} id="lightboxTrigger"> <div
className={styles.triggerArea}
role="button"
onClick={() => setLightboxIsOpen(true)}
>
<Image <Image
src={images[0].imageSizes.medium} src={images[0].imageSizes.medium}
alt={images[0].metaData.altText} alt={images[0].metaData.altText}
@@ -26,6 +37,12 @@ export default function ImageGallery({ images, title }: ImageGalleryProps) {
</Footnote> </Footnote>
</div> </div>
</div> </div>
</Lightbox> <Lightbox
images={images}
dialogTitle={title}
isOpen={lightboxIsOpen}
onClose={() => setLightboxIsOpen(false)}
/>
</>
) )
} }

View File

@@ -1,6 +1,6 @@
"use client" "use client"
import { AnimatePresence, motion } from "framer-motion" import { AnimatePresence, motion } from "framer-motion"
import React, { useState } from "react" import { useEffect, useState } from "react"
import { Dialog, Modal, ModalOverlay } from "react-aria-components" import { Dialog, Modal, ModalOverlay } from "react-aria-components"
import FullView from "./FullView" import FullView from "./FullView"
@@ -12,24 +12,19 @@ import type { LightboxProps } from "@/types/components/lightbox/lightbox"
export default function Lightbox({ export default function Lightbox({
images, images,
children,
dialogTitle, dialogTitle,
onClose,
isOpen,
}: LightboxProps) { }: LightboxProps) {
const [isOpen, setIsOpen] = useState(false)
const [selectedImageIndex, setSelectedImageIndex] = useState(0) const [selectedImageIndex, setSelectedImageIndex] = useState(0)
const [isFullView, setIsFullView] = useState(false) const [isFullView, setIsFullView] = useState(false)
function handleOpenChange(open: boolean) { useEffect(() => {
if (!open) { if (isOpen) {
setTimeout(() => { setSelectedImageIndex(0)
setIsOpen(false) setIsFullView(false)
setSelectedImageIndex(0)
setIsFullView(false)
}, 300) // 300ms delay
} else {
setIsOpen(true)
} }
} }, [isOpen])
function handleNext() { function handleNext() {
setSelectedImageIndex((prevIndex) => (prevIndex + 1) % images.length) setSelectedImageIndex((prevIndex) => (prevIndex + 1) % images.length)
@@ -41,75 +36,53 @@ export default function Lightbox({
) )
} }
const triggerElement = React.Children.map(
children,
function mapChild(child): React.ReactNode {
if (React.isValidElement(child)) {
if (child.props.id === "lightboxTrigger") {
return React.cloneElement(child, {
onClick: () => setIsOpen(true),
} as React.HTMLAttributes<HTMLElement>)
} else if (child.props.children) {
return React.cloneElement(child, {
children: React.Children.map(child.props.children, mapChild),
} as React.HTMLAttributes<HTMLElement>)
}
}
return child
}
)
return ( return (
<> <ModalOverlay
{triggerElement} isOpen={isOpen}
<ModalOverlay onOpenChange={onClose}
isOpen={isOpen} className={styles.overlay}
onOpenChange={handleOpenChange} isDismissable
className={styles.overlay} >
isDismissable <Modal>
> <AnimatePresence>
<Modal> <Dialog aria-label={dialogTitle}>
<AnimatePresence>
{isOpen && ( {isOpen && (
<Dialog> <motion.div
<motion.div className={`${styles.content} ${
className={`${styles.content} ${ isFullView ? styles.fullViewContent : styles.galleryContent
isFullView ? styles.fullViewContent : styles.galleryContent }`}
}`} initial={{ opacity: 0, scale: 0.95 }}
initial={{ opacity: 0, scale: 0.95 }} animate={{ opacity: 1, scale: 1, x: "-50%", y: "-50%" }}
animate={{ opacity: 1, scale: 1, x: "-50%", y: "-50%" }} exit={{ opacity: 0, scale: 0.95 }}
exit={{ opacity: 0, scale: 0.95 }} transition={{ duration: 0.2 }}
transition={{ duration: 0.2 }} >
> {isFullView ? (
{isFullView ? ( <FullView
<FullView image={images[selectedImageIndex]}
image={images[selectedImageIndex]} onClose={() => setIsFullView(false)}
onClose={() => setIsFullView(false)} onNext={handleNext}
onNext={handleNext} onPrev={handlePrev}
onPrev={handlePrev} currentIndex={selectedImageIndex}
currentIndex={selectedImageIndex} totalImages={images.length}
totalImages={images.length} />
/> ) : (
) : ( <Gallery
<Gallery images={images}
images={images} onClose={onClose}
dialogTitle={dialogTitle} onSelectImage={(image) => {
onClose={() => setIsOpen(false)} setSelectedImageIndex(
onSelectImage={(image) => { images.findIndex((img) => img === image)
setSelectedImageIndex( )
images.findIndex((img) => img === image) }}
) onImageClick={() => setIsFullView(true)}
}} selectedImage={images[selectedImageIndex]}
onImageClick={() => setIsFullView(true)} />
selectedImage={images[selectedImageIndex]} )}
/> </motion.div>
)}
</motion.div>
</Dialog>
)} )}
</AnimatePresence> </Dialog>
</Modal> </AnimatePresence>
</ModalOverlay> </Modal>
</> </ModalOverlay>
) )
} }

View File

@@ -144,6 +144,7 @@
"Highest level": "Højeste niveau", "Highest level": "Højeste niveau",
"Hospital": "Hospital", "Hospital": "Hospital",
"Hotel": "Hotel", "Hotel": "Hotel",
"Hotel Image gallery": "{hotel} - Billedgalleri",
"Hotel facilities": "Hotel faciliteter", "Hotel facilities": "Hotel faciliteter",
"Hotel surroundings": "Hotel omgivelser", "Hotel surroundings": "Hotel omgivelser",
"Hotels": "Hoteller", "Hotels": "Hoteller",
@@ -151,7 +152,6 @@
"How it works": "Hvordan det virker", "How it works": "Hvordan det virker",
"Hurry up and use them before they expire!": "Skynd dig og brug dem, før de udløber!", "Hurry up and use them before they expire!": "Skynd dig og brug dem, før de udløber!",
"I would like to get my booking confirmation via sms": "Jeg vil gerne få min booking bekræftelse via SMS", "I would like to get my booking confirmation via sms": "Jeg vil gerne få min booking bekræftelse via SMS",
"Image gallery": "Billedgalleri",
"In adults bed": "i de voksnes seng", "In adults bed": "i de voksnes seng",
"In crib": "i tremmeseng", "In crib": "i tremmeseng",
"In extra bed": "i ekstra seng", "In extra bed": "i ekstra seng",
@@ -409,7 +409,7 @@
"guaranteeing": "garanti", "guaranteeing": "garanti",
"guest": "gæst", "guest": "gæst",
"guests": "gæster", "guests": "gæster",
"hotelPages.rooms.roomCard.persons": "{totalOccupancy, plural, one {# person} other {# personer}}", "hotelPages.rooms.roomCard.persons": "{size} ({totalOccupancy, plural, one {# person} other {# personer}})",
"hotelPages.rooms.roomCard.seeRoomDetails": "Se værelsesdetaljer", "hotelPages.rooms.roomCard.seeRoomDetails": "Se værelsesdetaljer",
"km to city center": "km til byens centrum", "km to city center": "km til byens centrum",
"lowercase letter": "lille bogstav", "lowercase letter": "lille bogstav",

View File

@@ -144,6 +144,7 @@
"Highest level": "Höchstes Level", "Highest level": "Höchstes Level",
"Hospital": "Krankenhaus", "Hospital": "Krankenhaus",
"Hotel": "Hotel", "Hotel": "Hotel",
"Hotel Image gallery": "{hotel} - Bildergalerie",
"Hotel facilities": "Hotel-Infos", "Hotel facilities": "Hotel-Infos",
"Hotel surroundings": "Umgebung des Hotels", "Hotel surroundings": "Umgebung des Hotels",
"Hotels": "Hotels", "Hotels": "Hotels",
@@ -151,7 +152,6 @@
"How it works": "Wie es funktioniert", "How it works": "Wie es funktioniert",
"Hurry up and use them before they expire!": "Beeilen Sie sich und nutzen Sie sie, bevor sie ablaufen!", "Hurry up and use them before they expire!": "Beeilen Sie sich und nutzen Sie sie, bevor sie ablaufen!",
"I would like to get my booking confirmation via sms": "Ich möchte meine Buchungsbestätigung per SMS erhalten", "I would like to get my booking confirmation via sms": "Ich möchte meine Buchungsbestätigung per SMS erhalten",
"Image gallery": "Bildergalerie",
"In adults bed": "Im Bett der Eltern", "In adults bed": "Im Bett der Eltern",
"In crib": "im Kinderbett", "In crib": "im Kinderbett",
"In extra bed": "im zusätzlichen Bett", "In extra bed": "im zusätzlichen Bett",
@@ -408,7 +408,7 @@
"guaranteeing": "garantiert", "guaranteeing": "garantiert",
"guest": "gast", "guest": "gast",
"guests": "gäste", "guests": "gäste",
"hotelPages.rooms.roomCard.persons": "{totalOccupancy, plural, one {# person} other {# personen}}", "hotelPages.rooms.roomCard.persons": "{size} ({totalOccupancy, plural, one {# person} other {# personen}})",
"hotelPages.rooms.roomCard.seeRoomDetails": "Zimmerdetails ansehen", "hotelPages.rooms.roomCard.seeRoomDetails": "Zimmerdetails ansehen",
"km to city center": "km bis zum Stadtzentrum", "km to city center": "km bis zum Stadtzentrum",
"lowercase letter": "Kleinbuchstabe", "lowercase letter": "Kleinbuchstabe",

View File

@@ -156,6 +156,7 @@
"Highest level": "Highest level", "Highest level": "Highest level",
"Hospital": "Hospital", "Hospital": "Hospital",
"Hotel": "Hotel", "Hotel": "Hotel",
"Hotel Image gallery": "{hotel} - Image gallery",
"Hotel facilities": "Hotel facilities", "Hotel facilities": "Hotel facilities",
"Hotel surroundings": "Hotel surroundings", "Hotel surroundings": "Hotel surroundings",
"Hotels": "Hotels", "Hotels": "Hotels",
@@ -163,7 +164,6 @@
"How it works": "How it works", "How it works": "How it works",
"Hurry up and use them before they expire!": "Hurry up and use them before they expire!", "Hurry up and use them before they expire!": "Hurry up and use them before they expire!",
"I would like to get my booking confirmation via sms": "I would like to get my booking confirmation via sms", "I would like to get my booking confirmation via sms": "I would like to get my booking confirmation via sms",
"Image gallery": "Image gallery",
"In adults bed": "In adults bed", "In adults bed": "In adults bed",
"In crib": "In crib", "In crib": "In crib",
"In extra bed": "In extra bed", "In extra bed": "In extra bed",
@@ -446,7 +446,7 @@
"guest": "guest", "guest": "guest",
"guest.paid": "{amount} {currency} has been paid", "guest.paid": "{amount} {currency} has been paid",
"guests": "guests", "guests": "guests",
"hotelPages.rooms.roomCard.persons": "{totalOccupancy, plural, one {# person} other {# persons}}", "hotelPages.rooms.roomCard.persons": "{size} ({totalOccupancy, plural, one {# person} other {# persons}})",
"hotelPages.rooms.roomCard.seeRoomDetails": "See room details", "hotelPages.rooms.roomCard.seeRoomDetails": "See room details",
"km to city center": "km to city center", "km to city center": "km to city center",
"lowercase letter": "lowercase letter", "lowercase letter": "lowercase letter",

View File

@@ -144,6 +144,7 @@
"Highest level": "Korkein taso", "Highest level": "Korkein taso",
"Hospital": "Sairaala", "Hospital": "Sairaala",
"Hotel": "Hotelli", "Hotel": "Hotelli",
"Hotel Image gallery": "{hotel} - Kuvagalleria",
"Hotel facilities": "Hotellin palvelut", "Hotel facilities": "Hotellin palvelut",
"Hotel surroundings": "Hotellin ympäristö", "Hotel surroundings": "Hotellin ympäristö",
"Hotels": "Hotellit", "Hotels": "Hotellit",
@@ -151,7 +152,6 @@
"How it works": "Kuinka se toimii", "How it works": "Kuinka se toimii",
"Hurry up and use them before they expire!": "Ole nopea ja käytä ne ennen kuin ne vanhenevat!", "Hurry up and use them before they expire!": "Ole nopea ja käytä ne ennen kuin ne vanhenevat!",
"I would like to get my booking confirmation via sms": "Haluan saada varauksen vahvistuksen SMS-viestillä", "I would like to get my booking confirmation via sms": "Haluan saada varauksen vahvistuksen SMS-viestillä",
"Image gallery": "Kuvagalleria",
"In adults bed": "Aikuisten vuoteessa", "In adults bed": "Aikuisten vuoteessa",
"In crib": "Pinnasängyssä", "In crib": "Pinnasängyssä",
"In extra bed": "Oma vuodepaikka", "In extra bed": "Oma vuodepaikka",
@@ -408,7 +408,7 @@
"guaranteeing": "varmistetaan", "guaranteeing": "varmistetaan",
"guest": "Vieras", "guest": "Vieras",
"guests": "Vieraita", "guests": "Vieraita",
"hotelPages.rooms.roomCard.persons": "{totalOccupancy, plural, one {# henkilö} other {# Henkilöä}}", "hotelPages.rooms.roomCard.persons": "{size} ({totalOccupancy, plural, one {# henkilö} other {# Henkilöä}})",
"hotelPages.rooms.roomCard.seeRoomDetails": "Katso huoneen tiedot", "hotelPages.rooms.roomCard.seeRoomDetails": "Katso huoneen tiedot",
"km to city center": "km keskustaan", "km to city center": "km keskustaan",
"lowercase letter": "pien kirjain", "lowercase letter": "pien kirjain",

View File

@@ -143,13 +143,13 @@
"Highest level": "Høyeste nivå", "Highest level": "Høyeste nivå",
"Hospital": "Sykehus", "Hospital": "Sykehus",
"Hotel": "Hotel", "Hotel": "Hotel",
"Hotel Image gallery": "{hotel} - Bildegalleri",
"Hotel facilities": "Hotelfaciliteter", "Hotel facilities": "Hotelfaciliteter",
"Hotel surroundings": "Hotellomgivelser", "Hotel surroundings": "Hotellomgivelser",
"Hotels": "Hoteller", "Hotels": "Hoteller",
"How do you want to sleep?": "Hvordan vil du sove?", "How do you want to sleep?": "Hvordan vil du sove?",
"How it works": "Hvordan det fungerer", "How it works": "Hvordan det fungerer",
"Hurry up and use them before they expire!": "Skynd deg og bruk dem før de utløper!", "Hurry up and use them before they expire!": "Skynd deg og bruk dem før de utløper!",
"Image gallery": "Bildegalleri",
"In adults bed": "i voksnes seng", "In adults bed": "i voksnes seng",
"In crib": "i sprinkelseng", "In crib": "i sprinkelseng",
"In extra bed": "i ekstraseng", "In extra bed": "i ekstraseng",
@@ -406,7 +406,7 @@
"guaranteeing": "garantiert", "guaranteeing": "garantiert",
"guest": "gjest", "guest": "gjest",
"guests": "gjester", "guests": "gjester",
"hotelPages.rooms.roomCard.persons": "{totalOccupancy, plural, one {# person} other {# personer}}", "hotelPages.rooms.roomCard.persons": "{size} ({totalOccupancy, plural, one {# person} other {# personer}})",
"hotelPages.rooms.roomCard.seeRoomDetails": "Se detaljer om rommet", "hotelPages.rooms.roomCard.seeRoomDetails": "Se detaljer om rommet",
"km to city center": "km til sentrum", "km to city center": "km til sentrum",
"lowercase letter": "liten bokstav", "lowercase letter": "liten bokstav",

View File

@@ -143,13 +143,13 @@
"Highest level": "Högsta nivå", "Highest level": "Högsta nivå",
"Hospital": "Sjukhus", "Hospital": "Sjukhus",
"Hotel": "Hotell", "Hotel": "Hotell",
"Hotel Image gallery": "{hotel} - Bildgalleri",
"Hotel facilities": "Hotellfaciliteter", "Hotel facilities": "Hotellfaciliteter",
"Hotel surroundings": "Hotellomgivning", "Hotel surroundings": "Hotellomgivning",
"Hotels": "Hotell", "Hotels": "Hotell",
"How do you want to sleep?": "Hur vill du sova?", "How do you want to sleep?": "Hur vill du sova?",
"How it works": "Hur det fungerar", "How it works": "Hur det fungerar",
"Hurry up and use them before they expire!": "Skynda dig och använd dem innan de går ut!", "Hurry up and use them before they expire!": "Skynda dig och använd dem innan de går ut!",
"Image gallery": "Bildgalleri",
"In adults bed": "I vuxens säng", "In adults bed": "I vuxens säng",
"In crib": "I spjälsäng", "In crib": "I spjälsäng",
"In extra bed": "Egen sängplats", "In extra bed": "Egen sängplats",
@@ -407,7 +407,7 @@
"guaranteeing": "garanterar", "guaranteeing": "garanterar",
"guest": "gäst", "guest": "gäst",
"guests": "gäster", "guests": "gäster",
"hotelPages.rooms.roomCard.persons": "{totalOccupancy, plural, one {# person} other {# personer}}", "hotelPages.rooms.roomCard.persons": "{size} ({totalOccupancy, plural, one {# person} other {# personer}})",
"hotelPages.rooms.roomCard.seeRoomDetails": "Se information om rummet", "hotelPages.rooms.roomCard.seeRoomDetails": "Se information om rummet",
"km to city center": "km till stadens centrum", "km to city center": "km till stadens centrum",
"lowercase letter": "liten bokstav", "lowercase letter": "liten bokstav",

View File

@@ -3,12 +3,12 @@ import type { GalleryImage } from "@/types/hotel"
export interface LightboxProps { export interface LightboxProps {
images: GalleryImage[] images: GalleryImage[]
dialogTitle: string /* Accessible title for dialog screen readers */ dialogTitle: string /* Accessible title for dialog screen readers */
children: React.ReactNode onClose: () => void
isOpen: boolean
} }
export interface GalleryProps { export interface GalleryProps {
images: GalleryImage[] images: GalleryImage[]
dialogTitle: string
onClose: () => void onClose: () => void
onSelectImage: (image: GalleryImage) => void onSelectImage: (image: GalleryImage) => void
onImageClick: () => void onImageClick: () => void