From 333be1379cc7f0dcd8bebbfb527d054dbdebea94 Mon Sep 17 00:00:00 2001 From: Pontus Dreij Date: Fri, 22 Nov 2024 17:05:29 +0100 Subject: [PATCH] fix(SW-983): Fixed bug with hotel card in map --- .../HotelReservation/HotelCard/index.tsx | 14 +++-- .../hotelCardDialog.module.css | 7 ++- .../hotelCardDialogListing.module.css | 20 ++++++ .../HotelCardDialogListing/index.tsx | 15 ++++- .../HotelListing/hotelListing.module.css | 19 +----- components/ImageGallery/index.tsx | 6 +- .../HotelListingMapContent/index.tsx | 61 +++++++++++-------- 7 files changed, 88 insertions(+), 54 deletions(-) create mode 100644 components/HotelReservation/HotelCardDialogListing/hotelCardDialogListing.module.css diff --git a/components/HotelReservation/HotelCard/index.tsx b/components/HotelReservation/HotelCard/index.tsx index 916796229..7789f8b2d 100644 --- a/components/HotelReservation/HotelCard/index.tsx +++ b/components/HotelReservation/HotelCard/index.tsx @@ -1,5 +1,6 @@ "use client" import { useParams } from "next/dist/client/components/navigation" +import { memo, useCallback } from "react" import { useIntl } from "react-intl" import { Lang } from "@/constants/languages" @@ -24,7 +25,7 @@ import styles from "./hotelCard.module.css" import { HotelCardListingTypeEnum } from "@/types/components/hotelReservation/selectHotel/hotelCardListingProps" import type { HotelCardProps } from "@/types/components/hotelReservation/selectHotel/hotelCardProps" -export default function HotelCard({ +function HotelCard({ hotel, type = HotelCardListingTypeEnum.PageListing, state = "default", @@ -44,16 +45,17 @@ export default function HotelCard({ state, }) - const handleMouseEnter = () => { + const handleMouseEnter = useCallback(() => { if (onHotelCardHover) { onHotelCardHover(hotelData.name) } - } - const handleMouseLeave = () => { + }, [onHotelCardHover, hotelData.name]) + + const handleMouseLeave = useCallback(() => { if (onHotelCardHover) { onHotelCardHover(null) } - } + }, [onHotelCardHover]) return (
) } + +export default memo(HotelCard) diff --git a/components/HotelReservation/HotelCardDialog/hotelCardDialog.module.css b/components/HotelReservation/HotelCardDialog/hotelCardDialog.module.css index 5c6e6d6bd..7d607d3ad 100644 --- a/components/HotelReservation/HotelCardDialog/hotelCardDialog.module.css +++ b/components/HotelReservation/HotelCardDialog/hotelCardDialog.module.css @@ -1,6 +1,6 @@ .dialog { padding-bottom: var(--Spacing-x1); - bottom: 32px; + bottom: 0; left: 50%; transform: translateX(-50%); border: none; @@ -33,6 +33,8 @@ .imageContainer { position: relative; min-width: 177px; + border-radius: var(--Corner-radius-Medium) 0 0 var(--Corner-radius-Medium); + overflow: hidden; } .imageContainer img { @@ -108,4 +110,7 @@ .memberPrice { display: none; } + .dialog { + bottom: 32px; + } } diff --git a/components/HotelReservation/HotelCardDialogListing/hotelCardDialogListing.module.css b/components/HotelReservation/HotelCardDialogListing/hotelCardDialogListing.module.css new file mode 100644 index 000000000..b507aace1 --- /dev/null +++ b/components/HotelReservation/HotelCardDialogListing/hotelCardDialogListing.module.css @@ -0,0 +1,20 @@ +.hotelCardDialogListing { + display: flex; + flex-direction: row; + gap: var(--Spacing-x1); + align-items: flex-end; +} + +.hotelCardDialogListing dialog { + position: relative; + padding: 0; + margin: 0; +} + +.hotelCardDialogListing > div:first-child { + margin-left: 16px; +} + +.hotelCardDialogListing > div:last-child { + margin-right: 16px; +} diff --git a/components/HotelReservation/HotelCardDialogListing/index.tsx b/components/HotelReservation/HotelCardDialogListing/index.tsx index f13ff0433..123cc4ced 100644 --- a/components/HotelReservation/HotelCardDialogListing/index.tsx +++ b/components/HotelReservation/HotelCardDialogListing/index.tsx @@ -1,10 +1,15 @@ "use client" import { useCallback, useEffect, useRef } from "react" +import { useMediaQuery } from "usehooks-ts" + +import useClickOutside from "@/hooks/useClickOutside" import HotelCardDialog from "../HotelCardDialog" import { getHotelPins } from "./utils" +import styles from "./hotelCardDialogListing.module.css" + import type { HotelCardDialogListingProps } from "@/types/components/hotelReservation/selectHotel/map" export default function HotelCardDialogListing({ @@ -15,6 +20,12 @@ export default function HotelCardDialogListing({ const hotelsPinData = getHotelPins(hotels) const activeCardRef = useRef(null) const observerRef = useRef(null) + const dialogRef = useRef(null) + const isMobile = useMediaQuery("(max-width: 768px)") + + useClickOutside(dialogRef, !!activeCard && isMobile, () => { + onActiveCardChange(null) + }) const handleIntersection = useCallback( (entries: IntersectionObserverEntry[]) => { @@ -65,7 +76,7 @@ export default function HotelCardDialogListing({ }, [activeCard]) return ( - <> +
{hotelsPinData?.length && hotelsPinData.map((data) => { const isActive = data.name === activeCard @@ -83,6 +94,6 @@ export default function HotelCardDialogListing({
) })} - + ) } diff --git a/components/HotelReservation/SelectHotel/SelectHotelMap/HotelListing/hotelListing.module.css b/components/HotelReservation/SelectHotel/SelectHotelMap/HotelListing/hotelListing.module.css index 5fd7e4084..401c7fe3a 100644 --- a/components/HotelReservation/SelectHotel/SelectHotelMap/HotelListing/hotelListing.module.css +++ b/components/HotelReservation/SelectHotel/SelectHotelMap/HotelListing/hotelListing.module.css @@ -4,35 +4,18 @@ .hotelListingMobile { display: none; - align-items: flex-end; overflow-x: auto; position: absolute; - bottom: 0px; + bottom: 32px; left: 0; right: 0; z-index: 10; - height: 100%; - gap: var(--Spacing-x1); } .hotelListingMobile[data-open="true"] { display: flex; } -.hotelListingMobile dialog { - position: relative; - padding: 0; - margin: 0; -} - -.hotelListingMobile > div:first-child { - margin-left: 16px; -} - -.hotelListingMobile > div:last-child { - margin-right: 16px; -} - @media (min-width: 768px) { .hotelListing { display: block; diff --git a/components/ImageGallery/index.tsx b/components/ImageGallery/index.tsx index 3fc10448b..d26ed0007 100644 --- a/components/ImageGallery/index.tsx +++ b/components/ImageGallery/index.tsx @@ -1,6 +1,6 @@ "use client" -import { useState } from "react" +import { memo, useState } from "react" import { useIntl } from "react-intl" import { GalleryIcon } from "@/components/Icons" @@ -12,7 +12,7 @@ import styles from "./imageGallery.module.css" import type { ImageGalleryProps } from "@/types/components/imageGallery" -export default function ImageGallery({ +function ImageGallery({ images, title, fill, @@ -58,3 +58,5 @@ export default function ImageGallery({ ) } + +export default memo(ImageGallery) diff --git a/components/Maps/InteractiveMap/HotelListingMapContent/index.tsx b/components/Maps/InteractiveMap/HotelListingMapContent/index.tsx index c1a3e8c01..9f4c189c0 100644 --- a/components/Maps/InteractiveMap/HotelListingMapContent/index.tsx +++ b/components/Maps/InteractiveMap/HotelListingMapContent/index.tsx @@ -2,11 +2,10 @@ import { AdvancedMarker, AdvancedMarkerAnchorPoint, } from "@vis.gl/react-google-maps" -import { useRef, useState } from "react" +import { memo, useCallback, useState } from "react" import HotelCardDialog from "@/components/HotelReservation/HotelCardDialog" import Body from "@/components/TempDesignSystem/Text/Body" -import useClickOutside from "@/hooks/useClickOutside" import HotelMarker from "../../Markers/HotelMarker" @@ -14,33 +13,45 @@ import styles from "./hotelListingMapContent.module.css" import type { HotelListingMapContentProps } from "@/types/components/hotelReservation/selectHotel/map" -export default function HotelListingMapContent({ +function HotelListingMapContent({ activeHotelPin, hotelPins, onActiveHotelPinChange, }: HotelListingMapContentProps) { const [hoveredHotelPin, setHoveredHotelPin] = useState(null) - const dialogRef = useRef(null) - function toggleActiveHotelPin(pinName: string | null) { - if (onActiveHotelPinChange) { - onActiveHotelPinChange(activeHotelPin === pinName ? null : pinName) - setHoveredHotelPin(null) - } - } + const toggleActiveHotelPin = useCallback( + (pinName: string | null) => { + if (onActiveHotelPinChange) { + const newActivePin = activeHotelPin === pinName ? null : pinName + onActiveHotelPinChange(newActivePin) + if (newActivePin === null) { + setHoveredHotelPin(null) + } + } + }, + [activeHotelPin, onActiveHotelPinChange] + ) - function isPinActiveOrHovered(pinName: string) { - return activeHotelPin === pinName || hoveredHotelPin === pinName - } - - useClickOutside(dialogRef, isPinActiveOrHovered(activeHotelPin ?? ""), () => { - toggleActiveHotelPin(null) - }) + const handleHover = useCallback( + (pinName: string | null) => { + if (pinName !== null && activeHotelPin !== pinName) { + setHoveredHotelPin(pinName) + if (activeHotelPin && onActiveHotelPinChange) { + onActiveHotelPinChange(null) + } + } else if (pinName === null) { + setHoveredHotelPin(null) + } + }, + [activeHotelPin, onActiveHotelPinChange] + ) return (
{hotelPins.map((pin) => { - const isActiveOrHovered = isPinActiveOrHovered(pin.name) + const isActiveOrHovered = + activeHotelPin === pin.name || hoveredHotelPin === pin.name return ( setHoveredHotelPin(pin.name)} - onMouseLeave={() => setHoveredHotelPin(null)} - onClick={() => { - toggleActiveHotelPin( - activeHotelPin === pin.name ? null : pin.name - ) - }} + onMouseEnter={() => handleHover(pin.name)} + onMouseLeave={() => handleHover(null)} + onClick={() => toggleActiveHotelPin(pin.name)} > -
+
void }) => { @@ -92,3 +99,5 @@ export default function HotelListingMapContent({
) } + +export default memo(HotelListingMapContent)