Merged in SW-3270-move-interactive-map-to-design-system-or-booking-flow (pull request #2681)
SW-3270 move interactive map to design system or booking flow * wip * wip * merge * wip * add support for locales in design-system * add story for HotelCard * setup alias * . * remove tracking from design-system for hotelcard * pass isUserLoggedIn * export design-system-new-deprecated.css from design-system * Add HotelMarkerByType to Storybook * Add interactive map to Storybook * fix reactintl in vitest * rename env variables * . * fix background colors * add storybook stories for <Link /> * merge * fix tracking for when clicking 'See rooms' in InteractiveMap * Merge branch 'master' of bitbucket.org:scandic-swap/web into SW-3270-move-interactive-map-to-design-system-or-booking-flow * remove deprecated comment Approved-by: Anton Gunnarsson
This commit is contained in:
+32
@@ -0,0 +1,32 @@
|
||||
.pin {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
padding: var(--Spacing-x-half) var(--Spacing-x1) var(--Spacing-x-half)
|
||||
var(--Spacing-x-half);
|
||||
border: 2px solid var(--Base-Surface-Primary-light-Normal);
|
||||
border-radius: var(--Corner-radius-rounded);
|
||||
background-color: var(--Base-Surface-Primary-light-Normal);
|
||||
box-shadow: 0 0 4px 2px rgba(0, 0, 0, 0.1);
|
||||
gap: var(--Spacing-x1);
|
||||
width: max-content;
|
||||
}
|
||||
|
||||
.pin.active {
|
||||
background-color: var(--Primary-Dark-Surface-Normal);
|
||||
color: var(--Base-Surface-Primary-light-Normal);
|
||||
}
|
||||
|
||||
.pinIcon {
|
||||
width: 24px;
|
||||
height: 24px;
|
||||
border-radius: 50%;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
background: var(--Surface-Brand-Primary-2-Default);
|
||||
}
|
||||
|
||||
.pin.active .pinIcon {
|
||||
background: var(--Base-Surface-Primary-light-Normal);
|
||||
}
|
||||
+60
@@ -0,0 +1,60 @@
|
||||
import { useIntl } from 'react-intl'
|
||||
|
||||
import { formatPrice } from '@scandic-hotels/common/utils/numberFormatting'
|
||||
import { MaterialIcon } from '@scandic-hotels/design-system/Icons/MaterialIcon'
|
||||
import { Typography } from '@scandic-hotels/design-system/Typography'
|
||||
|
||||
import HotelMarker from '../../../Markers/HotelMarker'
|
||||
|
||||
import styles from './hotelPin.module.css'
|
||||
|
||||
interface HotelPinProps {
|
||||
isActive: boolean
|
||||
hotelPrice: number | null
|
||||
currency: string
|
||||
hotelAdditionalPrice?: number
|
||||
hotelAdditionalCurrency?: string
|
||||
}
|
||||
|
||||
export function HotelPin({
|
||||
isActive,
|
||||
hotelPrice,
|
||||
currency,
|
||||
hotelAdditionalPrice,
|
||||
hotelAdditionalCurrency,
|
||||
}: HotelPinProps) {
|
||||
const intl = useIntl()
|
||||
const isNotAvailable = !hotelPrice
|
||||
|
||||
return (
|
||||
<div
|
||||
className={`${styles.pin} ${isActive ? styles.active : ''}`}
|
||||
data-hotelpin
|
||||
>
|
||||
<span className={styles.pinIcon}>
|
||||
{isNotAvailable ? (
|
||||
<MaterialIcon
|
||||
icon="calendar_clock"
|
||||
size={16}
|
||||
color={isActive ? 'Icon/Interactive/Default' : 'Icon/Inverted'}
|
||||
/>
|
||||
) : (
|
||||
<HotelMarker width={16} color={isActive ? 'burgundy' : 'white'} />
|
||||
)}
|
||||
</span>
|
||||
<Typography variant="Body/Paragraph/mdRegular">
|
||||
<p>
|
||||
{isNotAvailable
|
||||
? '—'
|
||||
: formatPrice(
|
||||
intl,
|
||||
hotelPrice,
|
||||
currency,
|
||||
hotelAdditionalPrice,
|
||||
hotelAdditionalCurrency
|
||||
)}
|
||||
</p>
|
||||
</Typography>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
+3
@@ -0,0 +1,3 @@
|
||||
.advancedMarker {
|
||||
height: 32px;
|
||||
}
|
||||
+122
@@ -0,0 +1,122 @@
|
||||
import {
|
||||
AdvancedMarker,
|
||||
AdvancedMarkerAnchorPoint,
|
||||
InfoWindow,
|
||||
} from '@vis.gl/react-google-maps'
|
||||
import { useMediaQuery } from 'usehooks-ts'
|
||||
|
||||
import { HotelPin } from './HotelPin'
|
||||
import type { HotelPin as HotelPinType } from '../../types'
|
||||
import styles from './hotelListingMapContent.module.css'
|
||||
import { StandaloneHotelCardDialog } from '../../../HotelCard/HotelDialogCard/StandaloneHotelCardDialog'
|
||||
import { Lang } from '@scandic-hotels/common/constants/language'
|
||||
|
||||
export type HotelListingMapContentProps = {
|
||||
hotelPins: HotelPinType[]
|
||||
activeHotel?: string | null
|
||||
hoveredHotel?: string | null
|
||||
lang: Lang
|
||||
isUserLoggedIn: boolean
|
||||
onClickHotel?: (hotelId: string) => void
|
||||
setActiveHotel?: (args: { hotelName: string; hotelId: string } | null) => void
|
||||
setHoveredHotel?: (
|
||||
args: { hotelName: string; hotelId: string } | null
|
||||
) => void
|
||||
}
|
||||
export function HotelListingMapContent({
|
||||
hotelPins,
|
||||
activeHotel,
|
||||
hoveredHotel,
|
||||
isUserLoggedIn,
|
||||
setActiveHotel,
|
||||
setHoveredHotel,
|
||||
lang,
|
||||
onClickHotel,
|
||||
}: HotelListingMapContentProps) {
|
||||
const isDesktop = useMediaQuery('(min-width: 768px)')
|
||||
|
||||
const toggleActiveHotelPin = (
|
||||
args: { hotelName: string; hotelId: string } | null
|
||||
) => {
|
||||
if (!args) {
|
||||
setActiveHotel?.(null)
|
||||
return
|
||||
}
|
||||
|
||||
setActiveHotel?.({ hotelName: args.hotelName, hotelId: args.hotelId })
|
||||
}
|
||||
|
||||
return (
|
||||
<div>
|
||||
{hotelPins.map((pin) => {
|
||||
const isActiveOrHovered =
|
||||
activeHotel === pin.name || hoveredHotel === pin.name
|
||||
const hotelPrice =
|
||||
pin.memberPrice ??
|
||||
pin.publicPrice ??
|
||||
pin.redemptionPrice ??
|
||||
pin.voucherPrice ??
|
||||
pin.chequePrice?.numberOfCheques ??
|
||||
null
|
||||
|
||||
const hotelAdditionalPrice = pin.chequePrice
|
||||
? pin.chequePrice.additionalPricePerStay
|
||||
: undefined
|
||||
const hotelAdditionalCurrency = pin.chequePrice
|
||||
? pin.chequePrice.currency?.toString()
|
||||
: undefined
|
||||
|
||||
return (
|
||||
<AdvancedMarker
|
||||
key={pin.name}
|
||||
className={styles.advancedMarker}
|
||||
position={pin.coordinates}
|
||||
anchorPoint={AdvancedMarkerAnchorPoint.CENTER}
|
||||
zIndex={isActiveOrHovered ? 2 : 0}
|
||||
onMouseEnter={() => {
|
||||
setHoveredHotel?.({ hotelName: pin.name, hotelId: pin.operaId })
|
||||
}}
|
||||
onMouseLeave={() => {
|
||||
setHoveredHotel?.(null)
|
||||
}}
|
||||
onClick={() =>
|
||||
toggleActiveHotelPin({
|
||||
hotelName: pin.name,
|
||||
hotelId: pin.operaId,
|
||||
})
|
||||
}
|
||||
>
|
||||
{isActiveOrHovered && isDesktop && (
|
||||
<InfoWindow
|
||||
position={pin.coordinates}
|
||||
pixelOffset={[0, -24]}
|
||||
headerDisabled={true}
|
||||
shouldFocus={false}
|
||||
>
|
||||
<StandaloneHotelCardDialog
|
||||
data={pin}
|
||||
lang={lang}
|
||||
isUserLoggedIn={isUserLoggedIn}
|
||||
handleClose={() => {
|
||||
setActiveHotel?.(null)
|
||||
setHoveredHotel?.(null)
|
||||
}}
|
||||
onClick={() => {
|
||||
onClickHotel?.(pin.operaId)
|
||||
}}
|
||||
/>
|
||||
</InfoWindow>
|
||||
)}
|
||||
<HotelPin
|
||||
isActive={isActiveOrHovered}
|
||||
hotelPrice={hotelPrice}
|
||||
currency={pin.currency}
|
||||
hotelAdditionalPrice={hotelAdditionalPrice}
|
||||
hotelAdditionalCurrency={hotelAdditionalCurrency}
|
||||
/>
|
||||
</AdvancedMarker>
|
||||
)
|
||||
})}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
Reference in New Issue
Block a user