feat(SW-340): Added scroll to top button
This commit is contained in:
@@ -12,6 +12,10 @@
|
|||||||
width: 100%;
|
width: 100%;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.card.active {
|
||||||
|
border: 1px solid var(--Base-Border-Hover);
|
||||||
|
}
|
||||||
|
|
||||||
.imageContainer {
|
.imageContainer {
|
||||||
grid-area: image;
|
grid-area: image;
|
||||||
position: relative;
|
position: relative;
|
||||||
|
|||||||
@@ -17,12 +17,13 @@ import { hotelCardVariants } from "./variants"
|
|||||||
|
|
||||||
import styles from "./hotelCard.module.css"
|
import styles from "./hotelCard.module.css"
|
||||||
|
|
||||||
import { HotelCardListingType } from "@/types/components/hotelReservation/selectHotel/hotelCardListingProps"
|
import { HotelCardListingTypeEnum } from "@/types/components/hotelReservation/selectHotel/hotelCardListingProps"
|
||||||
import type { HotelCardProps } from "@/types/components/hotelReservation/selectHotel/hotelCardProps"
|
import type { HotelCardProps } from "@/types/components/hotelReservation/selectHotel/hotelCardProps"
|
||||||
|
|
||||||
export default function HotelCard({
|
export default function HotelCard({
|
||||||
hotel,
|
hotel,
|
||||||
type = HotelCardListingType.PageListing,
|
type = HotelCardListingTypeEnum.PageListing,
|
||||||
|
state = "default",
|
||||||
}: HotelCardProps) {
|
}: HotelCardProps) {
|
||||||
const intl = useIntl()
|
const intl = useIntl()
|
||||||
|
|
||||||
@@ -33,6 +34,7 @@ export default function HotelCard({
|
|||||||
|
|
||||||
const classNames = hotelCardVariants({
|
const classNames = hotelCardVariants({
|
||||||
type,
|
type,
|
||||||
|
state,
|
||||||
})
|
})
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
|||||||
@@ -8,8 +8,13 @@ export const hotelCardVariants = cva(styles.card, {
|
|||||||
pageListing: styles.pageListing,
|
pageListing: styles.pageListing,
|
||||||
mapListing: styles.mapListing,
|
mapListing: styles.mapListing,
|
||||||
},
|
},
|
||||||
|
state: {
|
||||||
|
active: styles.active,
|
||||||
|
default: styles.default,
|
||||||
|
},
|
||||||
},
|
},
|
||||||
defaultVariants: {
|
defaultVariants: {
|
||||||
type: "pageListing",
|
type: "pageListing",
|
||||||
|
state: "default",
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -10,30 +10,39 @@
|
|||||||
.dialogContainer {
|
.dialogContainer {
|
||||||
border: 1px solid var(--Base-Border-Subtle);
|
border: 1px solid var(--Base-Border-Subtle);
|
||||||
border-radius: var(--Corner-radius-Medium);
|
border-radius: var(--Corner-radius-Medium);
|
||||||
width: 402px;
|
min-width: 334px;
|
||||||
min-height: 227px;
|
|
||||||
background: var(--Base-Surface-Primary-light-Normal);
|
background: var(--Base-Surface-Primary-light-Normal);
|
||||||
box-shadow: 0px 0px 8px 3px rgba(0, 0, 0, 0.1);
|
box-shadow: 0px 0px 8px 3px rgba(0, 0, 0, 0.1);
|
||||||
flex-direction: row;
|
flex-direction: row;
|
||||||
display: flex;
|
display: flex;
|
||||||
|
position: relative;
|
||||||
|
}
|
||||||
|
|
||||||
|
.closeIcon {
|
||||||
|
position: absolute;
|
||||||
|
top: 7px;
|
||||||
|
right: 7px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.imageContainer {
|
.imageContainer {
|
||||||
position: relative;
|
position: relative;
|
||||||
min-height: 227px;
|
min-width: 177px;
|
||||||
width: 177px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.tripAdvisor {
|
|
||||||
display: none;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.imageContainer img {
|
.imageContainer img {
|
||||||
object-fit: cover;
|
object-fit: cover;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.tripAdvisor {
|
||||||
|
position: absolute;
|
||||||
|
display: block;
|
||||||
|
left: 7px;
|
||||||
|
top: 7px;
|
||||||
|
}
|
||||||
|
|
||||||
.content {
|
.content {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
|
min-width: 201px;
|
||||||
padding: var(--Spacing-x-one-and-half);
|
padding: var(--Spacing-x-one-and-half);
|
||||||
gap: var(--Spacing-x1);
|
gap: var(--Spacing-x1);
|
||||||
display: flex;
|
display: flex;
|
||||||
@@ -65,6 +74,13 @@
|
|||||||
color: var(--Base-Text-Subtle-light-Normal);
|
color: var(--Base-Text-Subtle-light-Normal);
|
||||||
}
|
}
|
||||||
|
|
||||||
.button {
|
.content .button {
|
||||||
margin-top: auto;
|
margin-top: auto;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@media (min-width: 768px) {
|
||||||
|
.facilities,
|
||||||
|
.memberPrice {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -3,6 +3,7 @@
|
|||||||
import { useIntl } from "react-intl"
|
import { useIntl } from "react-intl"
|
||||||
|
|
||||||
import { mapFacilityToIcon } from "@/components/ContentType/HotelPage/data"
|
import { mapFacilityToIcon } from "@/components/ContentType/HotelPage/data"
|
||||||
|
import { CloseLargeIcon } from "@/components/Icons"
|
||||||
import TripAdvisorIcon from "@/components/Icons/TripAdvisor"
|
import TripAdvisorIcon from "@/components/Icons/TripAdvisor"
|
||||||
import Image from "@/components/Image"
|
import Image from "@/components/Image"
|
||||||
import Button from "@/components/TempDesignSystem/Button"
|
import Button from "@/components/TempDesignSystem/Button"
|
||||||
@@ -13,17 +14,19 @@ import Subtitle from "@/components/TempDesignSystem/Text/Subtitle"
|
|||||||
|
|
||||||
import styles from "./hotelCardDialog.module.css"
|
import styles from "./hotelCardDialog.module.css"
|
||||||
|
|
||||||
import { HotelPin } from "@/types/components/hotelReservation/selectHotel/map"
|
import { HotelCardDialogProps } from "@/types/components/hotelReservation/selectHotel/map"
|
||||||
|
|
||||||
export default function HotelCardDialog({
|
export default function HotelCardDialog({
|
||||||
pin,
|
pin,
|
||||||
isOpen,
|
isOpen,
|
||||||
}: {
|
handleClose,
|
||||||
isOpen: boolean
|
}: HotelCardDialogProps) {
|
||||||
pin: HotelPin
|
|
||||||
}) {
|
|
||||||
const intl = useIntl()
|
const intl = useIntl()
|
||||||
|
|
||||||
|
if (!pin) {
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
|
||||||
const {
|
const {
|
||||||
name,
|
name,
|
||||||
publicPrice,
|
publicPrice,
|
||||||
@@ -34,15 +37,20 @@ export default function HotelCardDialog({
|
|||||||
ratings,
|
ratings,
|
||||||
} = pin
|
} = pin
|
||||||
|
|
||||||
|
const firstImage = images[0]?.imageSizes?.small
|
||||||
|
const altText = images[0]?.metaData?.altText
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<dialog open={isOpen} className={styles.dialog}>
|
<dialog open={isOpen} className={styles.dialog}>
|
||||||
<div className={styles.dialogContainer}>
|
<div className={styles.dialogContainer}>
|
||||||
|
<CloseLargeIcon
|
||||||
|
onClick={handleClose}
|
||||||
|
className={styles.closeIcon}
|
||||||
|
width={16}
|
||||||
|
height={16}
|
||||||
|
/>
|
||||||
<div className={styles.imageContainer}>
|
<div className={styles.imageContainer}>
|
||||||
<Image
|
<Image src={firstImage} alt={altText} fill />
|
||||||
src={images[0].imageSizes.small}
|
|
||||||
alt={images[0].metaData.altText}
|
|
||||||
fill
|
|
||||||
/>
|
|
||||||
<div className={styles.tripAdvisor}>
|
<div className={styles.tripAdvisor}>
|
||||||
<Chip intent="primary" className={styles.tripAdvisor}>
|
<Chip intent="primary" className={styles.tripAdvisor}>
|
||||||
<TripAdvisorIcon color="white" />
|
<TripAdvisorIcon color="white" />
|
||||||
@@ -58,7 +66,9 @@ export default function HotelCardDialog({
|
|||||||
return (
|
return (
|
||||||
<div className={styles.facilitiesItem} key={facility.id}>
|
<div className={styles.facilitiesItem} key={facility.id}>
|
||||||
{IconComponent && <IconComponent color="grey80" />}
|
{IconComponent && <IconComponent color="grey80" />}
|
||||||
<Caption color="textMediumContrast">{facility.name}</Caption>
|
<Caption color="uiTextMediumContrast">
|
||||||
|
{facility.name}
|
||||||
|
</Caption>
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
})}
|
})}
|
||||||
@@ -72,7 +82,7 @@ export default function HotelCardDialog({
|
|||||||
</Body>
|
</Body>
|
||||||
</Subtitle>
|
</Subtitle>
|
||||||
{memberPrice && (
|
{memberPrice && (
|
||||||
<Subtitle type="two" color="red">
|
<Subtitle type="two" color="red" className={styles.memberPrice}>
|
||||||
{memberPrice} {currency}
|
{memberPrice} {currency}
|
||||||
<Body asChild color="red">
|
<Body asChild color="red">
|
||||||
<span>/{intl.formatMessage({ id: "night" })}</span>
|
<span>/{intl.formatMessage({ id: "night" })}</span>
|
||||||
|
|||||||
@@ -9,13 +9,14 @@ import HotelCard from "../HotelCard"
|
|||||||
import styles from "./hotelCardListing.module.css"
|
import styles from "./hotelCardListing.module.css"
|
||||||
|
|
||||||
import {
|
import {
|
||||||
HotelCardListingProps,
|
type HotelCardListingProps,
|
||||||
HotelCardListingType,
|
HotelCardListingTypeEnum,
|
||||||
} from "@/types/components/hotelReservation/selectHotel/hotelCardListingProps"
|
} from "@/types/components/hotelReservation/selectHotel/hotelCardListingProps"
|
||||||
|
|
||||||
export default function HotelCardListing({
|
export default function HotelCardListing({
|
||||||
hotelData,
|
hotelData,
|
||||||
type = HotelCardListingType.PageListing,
|
type = HotelCardListingTypeEnum.PageListing,
|
||||||
|
state = "default",
|
||||||
}: HotelCardListingProps) {
|
}: HotelCardListingProps) {
|
||||||
const searchParams = useSearchParams()
|
const searchParams = useSearchParams()
|
||||||
|
|
||||||
@@ -36,7 +37,12 @@ export default function HotelCardListing({
|
|||||||
<section className={styles.hotelCards}>
|
<section className={styles.hotelCards}>
|
||||||
{hotels?.length ? (
|
{hotels?.length ? (
|
||||||
hotels.map((hotel) => (
|
hotels.map((hotel) => (
|
||||||
<HotelCard key={hotel.hotelData.name} hotel={hotel} type={type} />
|
<HotelCard
|
||||||
|
key={hotel.hotelData.name}
|
||||||
|
hotel={hotel}
|
||||||
|
type={type}
|
||||||
|
state={state}
|
||||||
|
/>
|
||||||
))
|
))
|
||||||
) : (
|
) : (
|
||||||
<Title>No hotels found</Title>
|
<Title>No hotels found</Title>
|
||||||
|
|||||||
@@ -5,8 +5,8 @@
|
|||||||
@media (min-width: 768px) {
|
@media (min-width: 768px) {
|
||||||
.hotelListing {
|
.hotelListing {
|
||||||
display: block;
|
display: block;
|
||||||
width: 420px;
|
width: 100%;
|
||||||
padding: var(--Spacing-x3) var(--Spacing-x4);
|
|
||||||
overflow-y: auto;
|
overflow-y: auto;
|
||||||
|
padding-top: var(--Spacing-x2);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,15 +4,19 @@ import HotelCardListing from "@/components/HotelReservation/HotelCardListing"
|
|||||||
|
|
||||||
import styles from "./hotelListing.module.css"
|
import styles from "./hotelListing.module.css"
|
||||||
|
|
||||||
import { HotelCardListingType } from "@/types/components/hotelReservation/selectHotel/hotelCardListingProps"
|
import { HotelCardListingTypeEnum } from "@/types/components/hotelReservation/selectHotel/hotelCardListingProps"
|
||||||
import type { HotelListingProps } from "@/types/components/hotelReservation/selectHotel/map"
|
import type { HotelListingProps } from "@/types/components/hotelReservation/selectHotel/map"
|
||||||
|
|
||||||
export default function HotelListing({ hotels }: HotelListingProps) {
|
export default function HotelListing({
|
||||||
|
hotels,
|
||||||
|
cardState = "default",
|
||||||
|
}: HotelListingProps) {
|
||||||
return (
|
return (
|
||||||
<div className={styles.hotelListing}>
|
<div className={styles.hotelListing}>
|
||||||
<HotelCardListing
|
<HotelCardListing
|
||||||
hotelData={hotels}
|
hotelData={hotels}
|
||||||
type={HotelCardListingType.MapListing}
|
type={HotelCardListingTypeEnum.MapListing}
|
||||||
|
state={cardState}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
"use client"
|
"use client"
|
||||||
import { APIProvider } from "@vis.gl/react-google-maps"
|
import { APIProvider } from "@vis.gl/react-google-maps"
|
||||||
import { useRouter, useSearchParams } from "next/navigation"
|
import { useRouter, useSearchParams } from "next/navigation"
|
||||||
import { useState } from "react"
|
import { useEffect, useState } from "react"
|
||||||
import { useIntl } from "react-intl"
|
import { useIntl } from "react-intl"
|
||||||
|
|
||||||
import { selectHotel } from "@/constants/routes/hotelReservation"
|
import { selectHotel } from "@/constants/routes/hotelReservation"
|
||||||
@@ -30,6 +30,29 @@ export default function SelectHotelMap({
|
|||||||
const lang = useLang()
|
const lang = useLang()
|
||||||
const intl = useIntl()
|
const intl = useIntl()
|
||||||
const [activeHotelPin, setActiveHotelPin] = useState<string | null>(null)
|
const [activeHotelPin, setActiveHotelPin] = useState<string | null>(null)
|
||||||
|
const [showBackToTop, setShowBackToTop] = useState<boolean>(false)
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
const hotelListingElement = document.querySelector(
|
||||||
|
`.${styles.listingContainer}`
|
||||||
|
)
|
||||||
|
if (!hotelListingElement) return
|
||||||
|
|
||||||
|
const handleScroll = () => {
|
||||||
|
const hasScrolledPast = hotelListingElement.scrollTop > 490
|
||||||
|
setShowBackToTop(hasScrolledPast)
|
||||||
|
}
|
||||||
|
|
||||||
|
hotelListingElement.addEventListener("scroll", handleScroll)
|
||||||
|
return () => hotelListingElement.removeEventListener("scroll", handleScroll)
|
||||||
|
}, [])
|
||||||
|
|
||||||
|
function scrollToTop() {
|
||||||
|
const hotelListingElement = document.querySelector(
|
||||||
|
`.${styles.listingContainer}`
|
||||||
|
)
|
||||||
|
hotelListingElement?.scrollTo({ top: 0, behavior: "smooth" })
|
||||||
|
}
|
||||||
|
|
||||||
function handleModalDismiss() {
|
function handleModalDismiss() {
|
||||||
router.back()
|
router.back()
|
||||||
@@ -54,25 +77,41 @@ export default function SelectHotelMap({
|
|||||||
return (
|
return (
|
||||||
<APIProvider apiKey={apiKey}>
|
<APIProvider apiKey={apiKey}>
|
||||||
<div className={styles.container}>
|
<div className={styles.container}>
|
||||||
<div className={styles.filterContainer}>
|
<div className={styles.listingContainer}>
|
||||||
<Button
|
<div className={styles.filterContainer}>
|
||||||
intent="text"
|
<Button
|
||||||
size="small"
|
intent="text"
|
||||||
variant="icon"
|
size="small"
|
||||||
wrapping
|
variant="icon"
|
||||||
onClick={isModal ? handleModalDismiss : handlePageRedirect}
|
wrapping
|
||||||
>
|
onClick={isModal ? handleModalDismiss : handlePageRedirect}
|
||||||
<CloseLargeIcon />
|
className={styles.filterContainerCloseButton}
|
||||||
</Button>
|
>
|
||||||
<span>Filter and sort</span>
|
<CloseLargeIcon />
|
||||||
{/* TODO: Add filter and sort button */}
|
</Button>
|
||||||
|
<span>Filter and sort</span>
|
||||||
|
{/* TODO: Add filter and sort button */}
|
||||||
|
</div>
|
||||||
|
<HotelListing
|
||||||
|
hotels={hotels}
|
||||||
|
cardState={activeHotelPin ? "active" : "default"}
|
||||||
|
/>
|
||||||
|
{showBackToTop && (
|
||||||
|
<Button
|
||||||
|
intent="inverted"
|
||||||
|
size="small"
|
||||||
|
theme="base"
|
||||||
|
className={styles.backToTopButton}
|
||||||
|
onClick={scrollToTop}
|
||||||
|
>
|
||||||
|
{intl.formatMessage({ id: "Back to top" })}
|
||||||
|
</Button>
|
||||||
|
)}
|
||||||
</div>
|
</div>
|
||||||
<HotelListing hotels={hotels} />
|
|
||||||
<InteractiveMap
|
<InteractiveMap
|
||||||
closeButton={closeButton}
|
closeButton={closeButton}
|
||||||
coordinates={coordinates}
|
coordinates={coordinates}
|
||||||
hotelPins={hotelPins}
|
hotelPins={hotelPins}
|
||||||
hotels={hotels}
|
|
||||||
activeHotelPin={activeHotelPin}
|
activeHotelPin={activeHotelPin}
|
||||||
onActiveHotelPinChange={setActiveHotelPin}
|
onActiveHotelPinChange={setActiveHotelPin}
|
||||||
mapId={mapId}
|
mapId={mapId}
|
||||||
|
|||||||
@@ -23,16 +23,29 @@
|
|||||||
height: 44px;
|
height: 44px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.filterContainer .closeButton {
|
.backToTopButton {
|
||||||
color: var(--UI-Text-High-Contrast);
|
display: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
@media (min-width: 768px) {
|
@media (min-width: 768px) {
|
||||||
.closeButton {
|
.closeButton {
|
||||||
display: flex !important;
|
display: flex !important;
|
||||||
}
|
}
|
||||||
.filterContainer {
|
.filterContainerCloseButton {
|
||||||
display: none;
|
display: none !important;
|
||||||
|
}
|
||||||
|
.backToTopButton {
|
||||||
|
position: fixed;
|
||||||
|
bottom: 24px;
|
||||||
|
left: 32px;
|
||||||
|
display: flex;
|
||||||
|
}
|
||||||
|
.listingContainer {
|
||||||
|
background-color: var(--Base-Surface-Secondary-light-Normal);
|
||||||
|
padding: var(--Spacing-x3) var(--Spacing-x4);
|
||||||
|
overflow-y: auto;
|
||||||
|
min-width: 420px;
|
||||||
|
position: relative;
|
||||||
}
|
}
|
||||||
.container {
|
.container {
|
||||||
display: flex;
|
display: flex;
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ import {
|
|||||||
AdvancedMarker,
|
AdvancedMarker,
|
||||||
AdvancedMarkerAnchorPoint,
|
AdvancedMarkerAnchorPoint,
|
||||||
} from "@vis.gl/react-google-maps"
|
} from "@vis.gl/react-google-maps"
|
||||||
|
import { useState } from "react"
|
||||||
|
|
||||||
import HotelCardDialog from "@/components/HotelReservation/HotelCardDialog"
|
import HotelCardDialog from "@/components/HotelReservation/HotelCardDialog"
|
||||||
import Body from "@/components/TempDesignSystem/Text/Body"
|
import Body from "@/components/TempDesignSystem/Text/Body"
|
||||||
@@ -10,59 +11,77 @@ import HotelMarker from "../../Markers/HotelMarker"
|
|||||||
|
|
||||||
import styles from "./hotelListingMapContent.module.css"
|
import styles from "./hotelListingMapContent.module.css"
|
||||||
|
|
||||||
import { HotelData } from "@/types/components/hotelReservation/selectHotel/hotelCardListingProps"
|
import type { HotelListingMapContentProps } from "@/types/components/hotelReservation/selectHotel/map"
|
||||||
import { HotelPin } from "@/types/components/hotelReservation/selectHotel/map"
|
|
||||||
|
|
||||||
export default function HotelListingMapContent({
|
export default function HotelListingMapContent({
|
||||||
activeHotelPin,
|
activeHotelPin,
|
||||||
hotelPins,
|
hotelPins,
|
||||||
hotels,
|
|
||||||
onActiveHotelPinChange,
|
onActiveHotelPinChange,
|
||||||
}: {
|
}: HotelListingMapContentProps) {
|
||||||
activeHotelPin?: HotelPin["name"] | null
|
const [hoveredHotelPin, setHoveredHotelPin] = useState<string | null>(null)
|
||||||
hotelPins: HotelPin[]
|
|
||||||
hotels?: HotelData[]
|
function toggleActiveHotelPin(pinName: string | null) {
|
||||||
onActiveHotelPinChange?: (pinName: string | null) => void
|
if (onActiveHotelPinChange) {
|
||||||
}) {
|
onActiveHotelPinChange(activeHotelPin === pinName ? null : pinName)
|
||||||
function toggleActiveHotelPin(pinName: string) {
|
setHoveredHotelPin(null)
|
||||||
onActiveHotelPinChange?.(activeHotelPin === pinName ? null : pinName)
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function isPinActiveOrHovered(pinName: string) {
|
||||||
|
return activeHotelPin === pinName || hoveredHotelPin === pinName
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div>
|
<div>
|
||||||
{hotelPins.map((pin) => (
|
{hotelPins.map((pin) => {
|
||||||
<AdvancedMarker
|
const isActiveOrHovered = isPinActiveOrHovered(pin.name)
|
||||||
key={pin.name}
|
return (
|
||||||
className={styles.advancedMarker}
|
<AdvancedMarker
|
||||||
position={pin.coordinates}
|
key={pin.name}
|
||||||
anchorPoint={AdvancedMarkerAnchorPoint.CENTER}
|
className={styles.advancedMarker}
|
||||||
zIndex={activeHotelPin === pin.name ? 2 : 0}
|
position={pin.coordinates}
|
||||||
onMouseEnter={() => onActiveHotelPinChange?.(pin.name)}
|
anchorPoint={AdvancedMarkerAnchorPoint.CENTER}
|
||||||
onMouseLeave={() => onActiveHotelPinChange?.(null)}
|
zIndex={isActiveOrHovered ? 2 : 0}
|
||||||
onClick={() => toggleActiveHotelPin(pin.name)}
|
onMouseEnter={() => setHoveredHotelPin(pin.name)}
|
||||||
>
|
onMouseLeave={() => setHoveredHotelPin(null)}
|
||||||
<HotelCardDialog isOpen={activeHotelPin === pin.name} pin={pin} />
|
onClick={() =>
|
||||||
|
toggleActiveHotelPin(
|
||||||
<span
|
activeHotelPin === pin.name ? null : pin.name
|
||||||
className={`${styles.pin} ${activeHotelPin === pin.name ? styles.active : ""}`}
|
)
|
||||||
|
}
|
||||||
>
|
>
|
||||||
<span className={styles.pinIcon}>
|
<HotelCardDialog
|
||||||
<HotelMarker
|
isOpen={isActiveOrHovered}
|
||||||
width={16}
|
handleClose={(event: { stopPropagation: () => void }) => {
|
||||||
color={activeHotelPin === pin.name ? "burgundy" : "white"}
|
event.stopPropagation()
|
||||||
/>
|
if (activeHotelPin === pin.name) {
|
||||||
</span>
|
toggleActiveHotelPin(null)
|
||||||
<Body
|
}
|
||||||
asChild
|
}}
|
||||||
color={activeHotelPin === pin.name ? "white" : "textHighContrast"}
|
pin={pin}
|
||||||
|
/>
|
||||||
|
|
||||||
|
<span
|
||||||
|
className={`${styles.pin} ${isActiveOrHovered ? styles.active : ""}`}
|
||||||
>
|
>
|
||||||
<span>
|
<span className={styles.pinIcon}>
|
||||||
{pin.memberPrice} {pin.currency}
|
<HotelMarker
|
||||||
|
width={16}
|
||||||
|
color={isActiveOrHovered ? "burgundy" : "white"}
|
||||||
|
/>
|
||||||
</span>
|
</span>
|
||||||
</Body>
|
<Body
|
||||||
</span>
|
asChild
|
||||||
</AdvancedMarker>
|
color={isActiveOrHovered ? "white" : "textHighContrast"}
|
||||||
))}
|
>
|
||||||
|
<span>
|
||||||
|
{pin.memberPrice} {pin.currency}
|
||||||
|
</span>
|
||||||
|
</Body>
|
||||||
|
</span>
|
||||||
|
</AdvancedMarker>
|
||||||
|
)
|
||||||
|
})}
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -18,7 +18,6 @@ export default function InteractiveMap({
|
|||||||
activePoi,
|
activePoi,
|
||||||
hotelPins,
|
hotelPins,
|
||||||
activeHotelPin,
|
activeHotelPin,
|
||||||
hotels,
|
|
||||||
mapId,
|
mapId,
|
||||||
closeButton,
|
closeButton,
|
||||||
onActivePoiChange,
|
onActivePoiChange,
|
||||||
@@ -56,7 +55,6 @@ export default function InteractiveMap({
|
|||||||
activeHotelPin={activeHotelPin}
|
activeHotelPin={activeHotelPin}
|
||||||
hotelPins={hotelPins}
|
hotelPins={hotelPins}
|
||||||
onActiveHotelPinChange={onActiveHotelPinChange}
|
onActiveHotelPinChange={onActiveHotelPinChange}
|
||||||
hotels={hotels}
|
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
{pointsOfInterest && (
|
{pointsOfInterest && (
|
||||||
|
|||||||
@@ -36,6 +36,7 @@
|
|||||||
"At the hotel": "På hotellet",
|
"At the hotel": "På hotellet",
|
||||||
"Attractions": "Attraktioner",
|
"Attractions": "Attraktioner",
|
||||||
"Back to scandichotels.com": "Tilbage til scandichotels.com",
|
"Back to scandichotels.com": "Tilbage til scandichotels.com",
|
||||||
|
"Back to top": "Tilbage til top",
|
||||||
"Bar": "Bar",
|
"Bar": "Bar",
|
||||||
"Based on availability": "Baseret på tilgængelighed",
|
"Based on availability": "Baseret på tilgængelighed",
|
||||||
"Bed type": "Seng type",
|
"Bed type": "Seng type",
|
||||||
|
|||||||
@@ -36,6 +36,7 @@
|
|||||||
"At the hotel": "Im Hotel",
|
"At the hotel": "Im Hotel",
|
||||||
"Attraction": "Attraktion",
|
"Attraction": "Attraktion",
|
||||||
"Back to scandichotels.com": "Zurück zu scandichotels.com",
|
"Back to scandichotels.com": "Zurück zu scandichotels.com",
|
||||||
|
"Back to top": "Zurück zur Spitze",
|
||||||
"Bar": "Bar",
|
"Bar": "Bar",
|
||||||
"Based on availability": "Je nach Verfügbarkeit",
|
"Based on availability": "Je nach Verfügbarkeit",
|
||||||
"Bed type": "Bettentyp",
|
"Bed type": "Bettentyp",
|
||||||
|
|||||||
@@ -38,6 +38,7 @@
|
|||||||
"At the hotel": "At the hotel",
|
"At the hotel": "At the hotel",
|
||||||
"Attractions": "Attractions",
|
"Attractions": "Attractions",
|
||||||
"Back to scandichotels.com": "Back to scandichotels.com",
|
"Back to scandichotels.com": "Back to scandichotels.com",
|
||||||
|
"Back to top": "Back to top",
|
||||||
"Bar": "Bar",
|
"Bar": "Bar",
|
||||||
"Based on availability": "Based on availability",
|
"Based on availability": "Based on availability",
|
||||||
"Bed": "Bed",
|
"Bed": "Bed",
|
||||||
|
|||||||
@@ -36,6 +36,7 @@
|
|||||||
"At the hotel": "Hotellissa",
|
"At the hotel": "Hotellissa",
|
||||||
"Attractions": "Nähtävyydet",
|
"Attractions": "Nähtävyydet",
|
||||||
"Back to scandichotels.com": "Takaisin scandichotels.com",
|
"Back to scandichotels.com": "Takaisin scandichotels.com",
|
||||||
|
"Back to top": "Takaisin ylös",
|
||||||
"Bar": "Bar",
|
"Bar": "Bar",
|
||||||
"Based on availability": "Saatavuuden mukaan",
|
"Based on availability": "Saatavuuden mukaan",
|
||||||
"Bed type": "Vuodetyyppi",
|
"Bed type": "Vuodetyyppi",
|
||||||
|
|||||||
@@ -36,6 +36,7 @@
|
|||||||
"At the hotel": "På hotellet",
|
"At the hotel": "På hotellet",
|
||||||
"Attractions": "Attraksjoner",
|
"Attractions": "Attraksjoner",
|
||||||
"Back to scandichotels.com": "Tilbake til scandichotels.com",
|
"Back to scandichotels.com": "Tilbake til scandichotels.com",
|
||||||
|
"Back to top": "Tilbake til toppen",
|
||||||
"Bar": "Bar",
|
"Bar": "Bar",
|
||||||
"Based on availability": "Basert på tilgjengelighet",
|
"Based on availability": "Basert på tilgjengelighet",
|
||||||
"Bed type": "Seng type",
|
"Bed type": "Seng type",
|
||||||
|
|||||||
@@ -36,6 +36,7 @@
|
|||||||
"At the hotel": "På hotellet",
|
"At the hotel": "På hotellet",
|
||||||
"Attractions": "Sevärdheter",
|
"Attractions": "Sevärdheter",
|
||||||
"Back to scandichotels.com": "Tillbaka till scandichotels.com",
|
"Back to scandichotels.com": "Tillbaka till scandichotels.com",
|
||||||
|
"Back to top": "Tillbaka till toppen",
|
||||||
"Bar": "Bar",
|
"Bar": "Bar",
|
||||||
"Based on availability": "Baserat på tillgänglighet",
|
"Based on availability": "Baserat på tillgänglighet",
|
||||||
"Bed type": "Sängtyp",
|
"Bed type": "Sängtyp",
|
||||||
|
|||||||
@@ -12,7 +12,6 @@ export interface InteractiveMapProps {
|
|||||||
activePoi?: PointOfInterest["name"] | null
|
activePoi?: PointOfInterest["name"] | null
|
||||||
hotelPins?: HotelPin[]
|
hotelPins?: HotelPin[]
|
||||||
activeHotelPin?: HotelPin["name"] | null
|
activeHotelPin?: HotelPin["name"] | null
|
||||||
hotels?: HotelData[]
|
|
||||||
mapId: string
|
mapId: string
|
||||||
closeButton: ReactElement
|
closeButton: ReactElement
|
||||||
onActivePoiChange?: (poi: PointOfInterest["name"] | null) => void
|
onActivePoiChange?: (poi: PointOfInterest["name"] | null) => void
|
||||||
|
|||||||
@@ -2,14 +2,15 @@ import { HotelsAvailabilityPrices } from "@/server/routers/hotels/output"
|
|||||||
|
|
||||||
import { Hotel } from "@/types/hotel"
|
import { Hotel } from "@/types/hotel"
|
||||||
|
|
||||||
export enum HotelCardListingType {
|
export enum HotelCardListingTypeEnum {
|
||||||
MapListing = "mapListing",
|
MapListing = "mapListing",
|
||||||
PageListing = "pageListing",
|
PageListing = "pageListing",
|
||||||
}
|
}
|
||||||
|
|
||||||
export type HotelCardListingProps = {
|
export type HotelCardListingProps = {
|
||||||
hotelData: HotelData[]
|
hotelData: HotelData[]
|
||||||
type?: HotelCardListingType
|
type?: HotelCardListingTypeEnum
|
||||||
|
state?: "default" | "active"
|
||||||
}
|
}
|
||||||
|
|
||||||
export type HotelData = {
|
export type HotelData = {
|
||||||
|
|||||||
@@ -1,6 +1,10 @@
|
|||||||
import { HotelCardListingType, HotelData } from "./hotelCardListingProps"
|
import {
|
||||||
|
HotelCardListingTypeEnum,
|
||||||
|
type HotelData,
|
||||||
|
} from "./hotelCardListingProps"
|
||||||
|
|
||||||
export type HotelCardProps = {
|
export type HotelCardProps = {
|
||||||
hotel: HotelData
|
hotel: HotelData
|
||||||
type?: HotelCardListingType
|
type?: HotelCardListingTypeEnum
|
||||||
|
state?: "default" | "active"
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -12,6 +12,7 @@ import type { Coordinates } from "@/types/components/maps/coordinates"
|
|||||||
|
|
||||||
export interface HotelListingProps {
|
export interface HotelListingProps {
|
||||||
hotels: HotelData[]
|
hotels: HotelData[]
|
||||||
|
cardState?: "default" | "active"
|
||||||
// pointsOfInterest: PointOfInterest[]
|
// pointsOfInterest: PointOfInterest[]
|
||||||
// activePoi: PointOfInterest["name"] | null
|
// activePoi: PointOfInterest["name"] | null
|
||||||
// onActivePoiChange: (poi: PointOfInterest["name"] | null) => void
|
// onActivePoiChange: (poi: PointOfInterest["name"] | null) => void
|
||||||
@@ -42,3 +43,15 @@ export type HotelPin = {
|
|||||||
amenities: Filter[]
|
amenities: Filter[]
|
||||||
ratings: number | null
|
ratings: number | null
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export interface HotelListingMapContentProps {
|
||||||
|
activeHotelPin?: HotelPin["name"] | null
|
||||||
|
hotelPins: HotelPin[]
|
||||||
|
onActiveHotelPinChange?: (pinName: string | null) => void
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface HotelCardDialogProps {
|
||||||
|
isOpen: boolean
|
||||||
|
pin: HotelPin
|
||||||
|
handleClose: (event: { stopPropagation: () => void }) => void
|
||||||
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user