feat(SW-344): Hotel list in mobile
This commit is contained in:
@@ -17,13 +17,13 @@ import styles from "./hotelCardDialog.module.css"
|
||||
import type { HotelCardDialogProps } from "@/types/components/hotelReservation/selectHotel/map"
|
||||
|
||||
export default function HotelCardDialog({
|
||||
pin,
|
||||
data,
|
||||
isOpen,
|
||||
handleClose,
|
||||
}: HotelCardDialogProps) {
|
||||
const intl = useIntl()
|
||||
|
||||
if (!pin) {
|
||||
if (!data) {
|
||||
return null
|
||||
}
|
||||
|
||||
@@ -35,7 +35,7 @@ export default function HotelCardDialog({
|
||||
amenities,
|
||||
images,
|
||||
ratings,
|
||||
} = pin
|
||||
} = data
|
||||
|
||||
const firstImage = images[0]?.imageSizes?.small
|
||||
const altText = images[0]?.metaData?.altText
|
||||
|
||||
47
components/HotelReservation/HotelCardDialogListing/index.tsx
Normal file
47
components/HotelReservation/HotelCardDialogListing/index.tsx
Normal file
@@ -0,0 +1,47 @@
|
||||
"use client"
|
||||
|
||||
import { useEffect, useRef } from "react"
|
||||
|
||||
import HotelCardDialog from "../HotelCardDialog"
|
||||
import { getHotelPins } from "./utils"
|
||||
|
||||
import type { HotelData } from "@/types/components/hotelReservation/selectHotel/hotelCardListingProps"
|
||||
|
||||
export default function HotelCardDialogListing({
|
||||
hotels,
|
||||
activeCard,
|
||||
}: {
|
||||
hotels: HotelData[]
|
||||
activeCard: string | null | undefined
|
||||
}) {
|
||||
const hotelsPinData = getHotelPins(hotels)
|
||||
const activeCardRef = useRef<HTMLDivElement | null>(null)
|
||||
|
||||
useEffect(() => {
|
||||
if (activeCardRef.current) {
|
||||
activeCardRef.current.scrollIntoView({
|
||||
behavior: "smooth",
|
||||
block: "nearest",
|
||||
inline: "center",
|
||||
})
|
||||
}
|
||||
}, [activeCard])
|
||||
|
||||
return (
|
||||
<>
|
||||
{hotelsPinData?.length &&
|
||||
hotelsPinData.map((data) => {
|
||||
const isActive = data.name === activeCard
|
||||
return (
|
||||
<div key={data.name} ref={isActive ? activeCardRef : null}>
|
||||
<HotelCardDialog
|
||||
data={data}
|
||||
isOpen={!!activeCard}
|
||||
handleClose={() => {}}
|
||||
/>
|
||||
</div>
|
||||
)
|
||||
})}
|
||||
</>
|
||||
)
|
||||
}
|
||||
21
components/HotelReservation/HotelCardDialogListing/utils.ts
Normal file
21
components/HotelReservation/HotelCardDialogListing/utils.ts
Normal file
@@ -0,0 +1,21 @@
|
||||
import type { HotelData } from "@/types/components/hotelReservation/selectHotel/hotelCardListingProps"
|
||||
import type { HotelPin } from "@/types/components/hotelReservation/selectHotel/map"
|
||||
|
||||
export function getHotelPins(hotels: HotelData[]): HotelPin[] {
|
||||
return hotels.map((hotel) => ({
|
||||
coordinates: {
|
||||
lat: hotel.hotelData.location.latitude,
|
||||
lng: hotel.hotelData.location.longitude,
|
||||
},
|
||||
name: hotel.hotelData.name,
|
||||
publicPrice: hotel.price?.regularAmount ?? null,
|
||||
memberPrice: hotel.price?.memberAmount ?? null,
|
||||
currency: hotel.price?.currency || null,
|
||||
images: [
|
||||
hotel.hotelData.hotelContent.images,
|
||||
...(hotel.hotelData.gallery?.heroImages ?? []),
|
||||
],
|
||||
amenities: hotel.hotelData.detailedFacilities.slice(0, 3),
|
||||
ratings: hotel.hotelData.ratings?.tripAdvisor.rating ?? null,
|
||||
}))
|
||||
}
|
||||
@@ -2,6 +2,37 @@
|
||||
display: none;
|
||||
}
|
||||
|
||||
.hotelListingMobile {
|
||||
display: none;
|
||||
align-items: flex-end;
|
||||
overflow-x: auto;
|
||||
position: absolute;
|
||||
bottom: 0px;
|
||||
left: 0;
|
||||
right: 0;
|
||||
z-index: 10;
|
||||
height: 350px;
|
||||
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;
|
||||
@@ -9,4 +40,9 @@
|
||||
overflow-y: auto;
|
||||
padding-top: var(--Spacing-x2);
|
||||
}
|
||||
|
||||
.hotelListingMobile,
|
||||
.hotelListingMobile[data-open="true"] {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
"use client"
|
||||
|
||||
import HotelCardDialogListing from "@/components/HotelReservation/HotelCardDialogListing"
|
||||
import HotelCardListing from "@/components/HotelReservation/HotelCardListing"
|
||||
|
||||
import styles from "./hotelListing.module.css"
|
||||
@@ -13,13 +14,18 @@ export default function HotelListing({
|
||||
onHotelCardHover,
|
||||
}: HotelListingProps) {
|
||||
return (
|
||||
<div className={styles.hotelListing}>
|
||||
<HotelCardListing
|
||||
hotelData={hotels}
|
||||
type={HotelCardListingTypeEnum.MapListing}
|
||||
activeCard={activeHotelPin}
|
||||
onHotelCardHover={onHotelCardHover}
|
||||
/>
|
||||
</div>
|
||||
<>
|
||||
<div className={styles.hotelListing}>
|
||||
<HotelCardListing
|
||||
hotelData={hotels}
|
||||
type={HotelCardListingTypeEnum.MapListing}
|
||||
activeCard={activeHotelPin}
|
||||
onHotelCardHover={onHotelCardHover}
|
||||
/>
|
||||
</div>
|
||||
<div className={styles.hotelListingMobile} data-open={!!activeHotelPin}>
|
||||
<HotelCardDialogListing hotels={hotels} activeCard={activeHotelPin} />
|
||||
</div>
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
@@ -8,6 +8,10 @@
|
||||
min-width: 109px !important; /* Overwriting the default width of the @vis.gl/react-google-maps AdvancedMarker */
|
||||
}
|
||||
|
||||
.dialogContainer {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.pin {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
@@ -67,3 +71,9 @@
|
||||
.card.active {
|
||||
display: block;
|
||||
}
|
||||
|
||||
@media (min-width: 768px) {
|
||||
.dialogContainer {
|
||||
display: block;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -50,17 +50,18 @@ export default function HotelListingMapContent({
|
||||
)
|
||||
}
|
||||
>
|
||||
<HotelCardDialog
|
||||
isOpen={isActiveOrHovered}
|
||||
handleClose={(event: { stopPropagation: () => void }) => {
|
||||
event.stopPropagation()
|
||||
if (activeHotelPin === pin.name) {
|
||||
toggleActiveHotelPin(null)
|
||||
}
|
||||
}}
|
||||
pin={pin}
|
||||
/>
|
||||
|
||||
<div className={styles.dialogContainer}>
|
||||
<HotelCardDialog
|
||||
isOpen={isActiveOrHovered}
|
||||
handleClose={(event: { stopPropagation: () => void }) => {
|
||||
event.stopPropagation()
|
||||
if (activeHotelPin === pin.name) {
|
||||
toggleActiveHotelPin(null)
|
||||
}
|
||||
}}
|
||||
data={pin}
|
||||
/>
|
||||
</div>
|
||||
<span
|
||||
className={`${styles.pin} ${isActiveOrHovered ? styles.active : ""}`}
|
||||
>
|
||||
|
||||
Reference in New Issue
Block a user