From 962760ae1bef5e131e0b9c3a2eca81c3ed26bc56 Mon Sep 17 00:00:00 2001 From: Erik Tiekstra Date: Tue, 12 Nov 2024 10:39:42 +0100 Subject: [PATCH] feat(SW-842): Added lightbox to roomcard --- .../HotelPage/PreviewImages/index.tsx | 68 +++++---- .../HotelPage/Rooms/RoomCard/index.tsx | 38 +++-- .../SelectRate/ImageGallery/index.tsx | 25 +++- components/Lightbox/index.tsx | 133 +++++++----------- i18n/dictionaries/da.json | 4 +- i18n/dictionaries/de.json | 4 +- i18n/dictionaries/en.json | 4 +- i18n/dictionaries/fi.json | 4 +- i18n/dictionaries/no.json | 4 +- i18n/dictionaries/sv.json | 4 +- types/components/lightbox/lightbox.ts | 4 +- 11 files changed, 150 insertions(+), 142 deletions(-) diff --git a/components/ContentType/HotelPage/PreviewImages/index.tsx b/components/ContentType/HotelPage/PreviewImages/index.tsx index 2a0074c5c..307d77a65 100644 --- a/components/ContentType/HotelPage/PreviewImages/index.tsx +++ b/components/ContentType/HotelPage/PreviewImages/index.tsx @@ -1,44 +1,54 @@ +"use client" + +import { useState } from "react" +import { useIntl } from "react-intl" + import Image from "@/components/Image" import Lightbox from "@/components/Lightbox/" import Button from "@/components/TempDesignSystem/Button" -import { getIntl } from "@/i18n" import styles from "./previewImages.module.css" import type { PreviewImagesProps } from "@/types/components/hotelPage/previewImages" -export default async function PreviewImages({ +export default function PreviewImages({ images, hotelName, }: PreviewImagesProps) { - const intl = await getIntl() - const imageGalleryText = intl.formatMessage({ id: "Image gallery" }) - const dialogTitle = `${hotelName} - ${imageGalleryText}` + const intl = useIntl() + const [lightboxIsOpen, setLightboxIsOpen] = useState(false) return ( - -
- {images.slice(0, 3).map((image, index) => ( - {image.metaData.altText} - ))} - -
-
+
+ {images.slice(0, 3).map((image, index) => ( + {image.metaData.altText} + ))} + + setLightboxIsOpen(false)} + /> +
) } diff --git a/components/ContentType/HotelPage/Rooms/RoomCard/index.tsx b/components/ContentType/HotelPage/Rooms/RoomCard/index.tsx index 2b0620885..2acb1c9a3 100644 --- a/components/ContentType/HotelPage/Rooms/RoomCard/index.tsx +++ b/components/ContentType/HotelPage/Rooms/RoomCard/index.tsx @@ -1,9 +1,11 @@ "use client" +import { useState } from "react" import { useIntl } from "react-intl" import { GalleryIcon } from "@/components/Icons" import Image from "@/components/Image" +import Lightbox from "@/components/Lightbox" import Body from "@/components/TempDesignSystem/Text/Body" 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) { const { images, name, roomSize, occupancy, id } = room const intl = useIntl() + const [lightboxIsOpen, setLightboxIsOpen] = useState(false) const mainImage = images[0] const size = @@ -23,21 +26,12 @@ export function RoomCard({ hotelId, room }: RoomCardProps) { ? `${roomSize.min} m²` : `${roomSize.min} - ${roomSize.max} m²` - 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 (
- + setLightboxIsOpen(false)} + />
{name} - {subtitle} + + {intl.formatMessage( + { id: "hotelPages.rooms.roomCard.persons" }, + { size, totalOccupancy: occupancy.total } + )} +
+ } return ( - -
+ <> +
setLightboxIsOpen(true)} + > {images[0].metaData.altText}
-
+ setLightboxIsOpen(false)} + /> + ) } diff --git a/components/Lightbox/index.tsx b/components/Lightbox/index.tsx index e9e5d8fa1..3b651c907 100644 --- a/components/Lightbox/index.tsx +++ b/components/Lightbox/index.tsx @@ -1,6 +1,6 @@ "use client" 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 FullView from "./FullView" @@ -12,24 +12,19 @@ import type { LightboxProps } from "@/types/components/lightbox/lightbox" export default function Lightbox({ images, - children, dialogTitle, + onClose, + isOpen, }: LightboxProps) { - const [isOpen, setIsOpen] = useState(false) const [selectedImageIndex, setSelectedImageIndex] = useState(0) const [isFullView, setIsFullView] = useState(false) - function handleOpenChange(open: boolean) { - if (!open) { - setTimeout(() => { - setIsOpen(false) - setSelectedImageIndex(0) - setIsFullView(false) - }, 300) // 300ms delay - } else { - setIsOpen(true) + useEffect(() => { + if (isOpen) { + setSelectedImageIndex(0) + setIsFullView(false) } - } + }, [isOpen]) function handleNext() { 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) - } else if (child.props.children) { - return React.cloneElement(child, { - children: React.Children.map(child.props.children, mapChild), - } as React.HTMLAttributes) - } - } - return child - } - ) - return ( - <> - {triggerElement} - - - + + + + {isOpen && ( - - - {isFullView ? ( - setIsFullView(false)} - onNext={handleNext} - onPrev={handlePrev} - currentIndex={selectedImageIndex} - totalImages={images.length} - /> - ) : ( - setIsOpen(false)} - onSelectImage={(image) => { - setSelectedImageIndex( - images.findIndex((img) => img === image) - ) - }} - onImageClick={() => setIsFullView(true)} - selectedImage={images[selectedImageIndex]} - /> - )} - - + + {isFullView ? ( + setIsFullView(false)} + onNext={handleNext} + onPrev={handlePrev} + currentIndex={selectedImageIndex} + totalImages={images.length} + /> + ) : ( + { + setSelectedImageIndex( + images.findIndex((img) => img === image) + ) + }} + onImageClick={() => setIsFullView(true)} + selectedImage={images[selectedImageIndex]} + /> + )} + )} - - - - + + + + ) } diff --git a/i18n/dictionaries/da.json b/i18n/dictionaries/da.json index 6ead0af95..6610d66a5 100644 --- a/i18n/dictionaries/da.json +++ b/i18n/dictionaries/da.json @@ -144,6 +144,7 @@ "Highest level": "Højeste niveau", "Hospital": "Hospital", "Hotel": "Hotel", + "Hotel Image gallery": "{hotel} - Billedgalleri", "Hotel facilities": "Hotel faciliteter", "Hotel surroundings": "Hotel omgivelser", "Hotels": "Hoteller", @@ -151,7 +152,6 @@ "How it works": "Hvordan det virker", "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", - "Image gallery": "Billedgalleri", "In adults bed": "i de voksnes seng", "In crib": "i tremmeseng", "In extra bed": "i ekstra seng", @@ -409,7 +409,7 @@ "guaranteeing": "garanti", "guest": "gæst", "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", "km to city center": "km til byens centrum", "lowercase letter": "lille bogstav", diff --git a/i18n/dictionaries/de.json b/i18n/dictionaries/de.json index 8a1a463d1..ae5a29025 100644 --- a/i18n/dictionaries/de.json +++ b/i18n/dictionaries/de.json @@ -144,6 +144,7 @@ "Highest level": "Höchstes Level", "Hospital": "Krankenhaus", "Hotel": "Hotel", + "Hotel Image gallery": "{hotel} - Bildergalerie", "Hotel facilities": "Hotel-Infos", "Hotel surroundings": "Umgebung des Hotels", "Hotels": "Hotels", @@ -151,7 +152,6 @@ "How it works": "Wie es funktioniert", "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", - "Image gallery": "Bildergalerie", "In adults bed": "Im Bett der Eltern", "In crib": "im Kinderbett", "In extra bed": "im zusätzlichen Bett", @@ -408,7 +408,7 @@ "guaranteeing": "garantiert", "guest": "gast", "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", "km to city center": "km bis zum Stadtzentrum", "lowercase letter": "Kleinbuchstabe", diff --git a/i18n/dictionaries/en.json b/i18n/dictionaries/en.json index 0f28e3231..7ca27c217 100644 --- a/i18n/dictionaries/en.json +++ b/i18n/dictionaries/en.json @@ -156,6 +156,7 @@ "Highest level": "Highest level", "Hospital": "Hospital", "Hotel": "Hotel", + "Hotel Image gallery": "{hotel} - Image gallery", "Hotel facilities": "Hotel facilities", "Hotel surroundings": "Hotel surroundings", "Hotels": "Hotels", @@ -163,7 +164,6 @@ "How it works": "How it works", "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", - "Image gallery": "Image gallery", "In adults bed": "In adults bed", "In crib": "In crib", "In extra bed": "In extra bed", @@ -446,7 +446,7 @@ "guest": "guest", "guest.paid": "{amount} {currency} has been paid", "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", "km to city center": "km to city center", "lowercase letter": "lowercase letter", diff --git a/i18n/dictionaries/fi.json b/i18n/dictionaries/fi.json index 24325460c..544f52e8d 100644 --- a/i18n/dictionaries/fi.json +++ b/i18n/dictionaries/fi.json @@ -144,6 +144,7 @@ "Highest level": "Korkein taso", "Hospital": "Sairaala", "Hotel": "Hotelli", + "Hotel Image gallery": "{hotel} - Kuvagalleria", "Hotel facilities": "Hotellin palvelut", "Hotel surroundings": "Hotellin ympäristö", "Hotels": "Hotellit", @@ -151,7 +152,6 @@ "How it works": "Kuinka se toimii", "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ä", - "Image gallery": "Kuvagalleria", "In adults bed": "Aikuisten vuoteessa", "In crib": "Pinnasängyssä", "In extra bed": "Oma vuodepaikka", @@ -408,7 +408,7 @@ "guaranteeing": "varmistetaan", "guest": "Vieras", "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", "km to city center": "km keskustaan", "lowercase letter": "pien kirjain", diff --git a/i18n/dictionaries/no.json b/i18n/dictionaries/no.json index 5a7dfe511..810664462 100644 --- a/i18n/dictionaries/no.json +++ b/i18n/dictionaries/no.json @@ -143,13 +143,13 @@ "Highest level": "Høyeste nivå", "Hospital": "Sykehus", "Hotel": "Hotel", + "Hotel Image gallery": "{hotel} - Bildegalleri", "Hotel facilities": "Hotelfaciliteter", "Hotel surroundings": "Hotellomgivelser", "Hotels": "Hoteller", "How do you want to sleep?": "Hvordan vil du sove?", "How it works": "Hvordan det fungerer", "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 crib": "i sprinkelseng", "In extra bed": "i ekstraseng", @@ -406,7 +406,7 @@ "guaranteeing": "garantiert", "guest": "gjest", "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", "km to city center": "km til sentrum", "lowercase letter": "liten bokstav", diff --git a/i18n/dictionaries/sv.json b/i18n/dictionaries/sv.json index 9aeced109..fc796030e 100644 --- a/i18n/dictionaries/sv.json +++ b/i18n/dictionaries/sv.json @@ -143,13 +143,13 @@ "Highest level": "Högsta nivå", "Hospital": "Sjukhus", "Hotel": "Hotell", + "Hotel Image gallery": "{hotel} - Bildgalleri", "Hotel facilities": "Hotellfaciliteter", "Hotel surroundings": "Hotellomgivning", "Hotels": "Hotell", "How do you want to sleep?": "Hur vill du sova?", "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!", - "Image gallery": "Bildgalleri", "In adults bed": "I vuxens säng", "In crib": "I spjälsäng", "In extra bed": "Egen sängplats", @@ -407,7 +407,7 @@ "guaranteeing": "garanterar", "guest": "gäst", "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", "km to city center": "km till stadens centrum", "lowercase letter": "liten bokstav", diff --git a/types/components/lightbox/lightbox.ts b/types/components/lightbox/lightbox.ts index af592bca6..3c0b7db16 100644 --- a/types/components/lightbox/lightbox.ts +++ b/types/components/lightbox/lightbox.ts @@ -3,12 +3,12 @@ import type { GalleryImage } from "@/types/hotel" export interface LightboxProps { images: GalleryImage[] dialogTitle: string /* Accessible title for dialog screen readers */ - children: React.ReactNode + onClose: () => void + isOpen: boolean } export interface GalleryProps { images: GalleryImage[] - dialogTitle: string onClose: () => void onSelectImage: (image: GalleryImage) => void onImageClick: () => void