Merged in feat/SW-904-add-back-to-top-button (pull request #923)
Feat/SW-904 add back to top button on hotel list page * feat(SW-904): Added back to top button * fix: removed alert on hotel listing page * Remove console.log Approved-by: Niclas Edenvin
This commit is contained in:
committed by
Niclas Edenvin
parent
bd0720dc0f
commit
60f1d268a9
@@ -7,7 +7,6 @@ import { selectHotelMap, selectRate } from "@/constants/routes/hotelReservation"
|
||||
|
||||
import { mapFacilityToIcon } from "@/components/ContentType/HotelPage/data"
|
||||
import ImageGallery from "@/components/ImageGallery"
|
||||
import Alert from "@/components/TempDesignSystem/Alert"
|
||||
import Button from "@/components/TempDesignSystem/Button"
|
||||
import Divider from "@/components/TempDesignSystem/Divider"
|
||||
import Link from "@/components/TempDesignSystem/Link"
|
||||
@@ -133,13 +132,6 @@ export default function HotelCard({
|
||||
hotel={hotelData}
|
||||
showCTA={true}
|
||||
/>
|
||||
{hotelData.specialAlerts.length > 0 && (
|
||||
<div className={styles.specialAlerts}>
|
||||
{hotelData.specialAlerts.map((alert) => (
|
||||
<Alert key={alert.id} type={alert.type} text={alert.text} />
|
||||
))}
|
||||
</div>
|
||||
)}
|
||||
</section>
|
||||
<HotelPriceList price={price} hotelId={hotel.hotelData.operaId} />
|
||||
</div>
|
||||
|
||||
@@ -1,9 +1,12 @@
|
||||
"use client"
|
||||
import { useSearchParams } from "next/navigation"
|
||||
import { useMemo } from "react"
|
||||
import { useEffect, useMemo, useState } from "react"
|
||||
import { useIntl } from "react-intl"
|
||||
|
||||
import { useHotelFilterStore } from "@/stores/hotel-filters"
|
||||
|
||||
import { BackToTopButton } from "@/components/TempDesignSystem/BackToTopButton"
|
||||
|
||||
import HotelCard from "../HotelCard"
|
||||
import { DEFAULT_SORT } from "../SelectHotel/HotelSorter"
|
||||
|
||||
@@ -22,9 +25,11 @@ export default function HotelCardListing({
|
||||
activeCard,
|
||||
onHotelCardHover,
|
||||
}: HotelCardListingProps) {
|
||||
const intl = useIntl()
|
||||
const searchParams = useSearchParams()
|
||||
const activeFilters = useHotelFilterStore((state) => state.activeFilters)
|
||||
const setResultCount = useHotelFilterStore((state) => state.setResultCount)
|
||||
const [showBackToTop, setShowBackToTop] = useState<boolean>(false)
|
||||
|
||||
const sortBy = useMemo(
|
||||
() => searchParams.get("sort") ?? DEFAULT_SORT,
|
||||
@@ -82,6 +87,20 @@ export default function HotelCardListing({
|
||||
return filteredHotels
|
||||
}, [activeFilters, sortedHotels, setResultCount])
|
||||
|
||||
useEffect(() => {
|
||||
const handleScroll = () => {
|
||||
const hasScrolledPast = window.scrollY > 490
|
||||
setShowBackToTop(hasScrolledPast)
|
||||
}
|
||||
|
||||
window.addEventListener("scroll", handleScroll, { passive: true })
|
||||
return () => window.removeEventListener("scroll", handleScroll)
|
||||
}, [])
|
||||
|
||||
function scrollToTop() {
|
||||
window.scrollTo({ top: 0, behavior: "smooth" })
|
||||
}
|
||||
|
||||
return (
|
||||
<section className={styles.hotelCards}>
|
||||
{hotels?.length
|
||||
@@ -95,6 +114,7 @@ export default function HotelCardListing({
|
||||
/>
|
||||
))
|
||||
: null}
|
||||
{showBackToTop && <BackToTopButton onClick={scrollToTop} />}
|
||||
</section>
|
||||
)
|
||||
}
|
||||
|
||||
@@ -7,8 +7,9 @@ import { useMediaQuery } from "usehooks-ts"
|
||||
|
||||
import { selectHotel } from "@/constants/routes/hotelReservation"
|
||||
|
||||
import { CloseIcon, CloseLargeIcon } from "@/components/Icons"
|
||||
import { ArrowUpIcon, CloseIcon, CloseLargeIcon } from "@/components/Icons"
|
||||
import InteractiveMap from "@/components/Maps/InteractiveMap"
|
||||
import { BackToTopButton } from "@/components/TempDesignSystem/BackToTopButton"
|
||||
import Button from "@/components/TempDesignSystem/Button"
|
||||
import useLang from "@/hooks/useLang"
|
||||
|
||||
@@ -109,17 +110,7 @@ export default function SelectHotelMap({
|
||||
activeHotelPin={activeHotelPin}
|
||||
setActiveHotelPin={setActiveHotelPin}
|
||||
/>
|
||||
{showBackToTop && (
|
||||
<Button
|
||||
intent="inverted"
|
||||
size="small"
|
||||
theme="base"
|
||||
className={styles.backToTopButton}
|
||||
onClick={scrollToTop}
|
||||
>
|
||||
{intl.formatMessage({ id: "Back to top" })}
|
||||
</Button>
|
||||
)}
|
||||
{showBackToTop && <BackToTopButton onClick={scrollToTop} />}
|
||||
</div>
|
||||
<InteractiveMap
|
||||
closeButton={closeButton}
|
||||
|
||||
@@ -23,10 +23,6 @@
|
||||
height: 44px;
|
||||
}
|
||||
|
||||
.backToTopButton {
|
||||
display: none;
|
||||
}
|
||||
|
||||
@media (min-width: 768px) {
|
||||
.container .closeButton {
|
||||
display: flex;
|
||||
@@ -34,12 +30,7 @@
|
||||
.container .listingContainer .filterContainer .filterContainerCloseButton {
|
||||
display: none;
|
||||
}
|
||||
.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);
|
||||
|
||||
33
components/Icons/ArrowUp.tsx
Normal file
33
components/Icons/ArrowUp.tsx
Normal file
@@ -0,0 +1,33 @@
|
||||
import { iconVariants } from "./variants"
|
||||
|
||||
import type { IconProps } from "@/types/components/icon"
|
||||
|
||||
export default function ArrowUpIcon({ className, color, ...props }: IconProps) {
|
||||
const classNames = iconVariants({ className, color })
|
||||
return (
|
||||
<svg
|
||||
className={classNames}
|
||||
fill="none"
|
||||
height="20"
|
||||
viewBox="0 0 20 20"
|
||||
width="20"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
{...props}
|
||||
>
|
||||
<mask
|
||||
id="a"
|
||||
width="20"
|
||||
height="20"
|
||||
x="0"
|
||||
y="0"
|
||||
maskUnits="userSpaceOnUse"
|
||||
>
|
||||
<path fill="#D9D9D9" d="M0 0h20v20H0z" />
|
||||
</mask>
|
||||
<path
|
||||
fill="#4D001B"
|
||||
d="m9.219 6.541-4.021 4.021a.74.74 0 0 1-.552.235.778.778 0 0 1-.552-.245.796.796 0 0 1-.235-.552.74.74 0 0 1 .235-.552l5.354-5.355a.77.77 0 0 1 .849-.171.77.77 0 0 1 .255.171l5.354 5.355a.782.782 0 0 1 0 1.104.764.764 0 0 1-1.114 0l-4.01-4.01v9.135c0 .215-.077.4-.23.552a.752.752 0 0 1-.552.229.752.752 0 0 1-.552-.23.752.752 0 0 1-.23-.551V6.54Z"
|
||||
/>
|
||||
</svg>
|
||||
)
|
||||
}
|
||||
@@ -6,6 +6,7 @@ export { default as AirIcon } from "./Air"
|
||||
export { default as AirplaneIcon } from "./Airplane"
|
||||
export { default as AllergyIcon } from "./Allergy"
|
||||
export { default as ArrowRightIcon } from "./ArrowRight"
|
||||
export { default as ArrowUpIcon } from "./ArrowUp"
|
||||
export { default as BarIcon } from "./Bar"
|
||||
export { default as BathtubIcon } from "./Bathtub"
|
||||
export { default as BedDoubleIcon } from "./BedDouble"
|
||||
|
||||
@@ -0,0 +1,45 @@
|
||||
.backToTopButton {
|
||||
border-radius: var(--Corner-radius-Rounded);
|
||||
cursor: pointer;
|
||||
display: flex;
|
||||
align-items: flex-end;
|
||||
position: fixed;
|
||||
bottom: 20px;
|
||||
right: 20px;
|
||||
z-index: 1000;
|
||||
background-color: var(--Base-Surface-Primary-light-Normal);
|
||||
color: var(--Base-Button-Secondary-On-Fill-Normal);
|
||||
border: 2px solid var(--Base-Button-Secondary-On-Fill-Normal);
|
||||
gap: var(--Spacing-x-half);
|
||||
padding: var(--Spacing-x1);
|
||||
text-align: center;
|
||||
transition:
|
||||
background-color 300ms ease,
|
||||
color 300ms ease;
|
||||
font-family: var(--typography-Body-Bold-fontFamily);
|
||||
font-weight: 500;
|
||||
font-size: var(--typography-Caption-Bold-fontSize);
|
||||
line-height: var(--typography-Caption-Bold-lineHeight);
|
||||
letter-spacing: 0.6%;
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
.backToTopButtonText {
|
||||
display: none;
|
||||
}
|
||||
|
||||
@media (min-width: 768px) {
|
||||
.backToTopButtonText {
|
||||
display: initial;
|
||||
}
|
||||
.backToTopButton:hover {
|
||||
background-color: var(--Base-Button-Tertiary-Fill-Normal);
|
||||
color: var(--Base-Button-Tertiary-On-Fill-Hover);
|
||||
}
|
||||
.backToTopButton:hover > svg * {
|
||||
fill: var(--Base-Button-Tertiary-On-Fill-Hover);
|
||||
}
|
||||
.backToTopButton {
|
||||
padding: calc(var(--Spacing-x1) + 2px) var(--Spacing-x2);
|
||||
}
|
||||
}
|
||||
20
components/TempDesignSystem/BackToTopButton/index.tsx
Normal file
20
components/TempDesignSystem/BackToTopButton/index.tsx
Normal file
@@ -0,0 +1,20 @@
|
||||
"use client"
|
||||
|
||||
import { Button as ButtonRAC } from "react-aria-components"
|
||||
import { useIntl } from "react-intl"
|
||||
|
||||
import { ArrowUpIcon } from "@/components/Icons"
|
||||
|
||||
import styles from "./backToTopButton.module.css"
|
||||
|
||||
export function BackToTopButton({ onClick }: { onClick: () => void }) {
|
||||
const intl = useIntl()
|
||||
return (
|
||||
<ButtonRAC className={styles.backToTopButton} onPress={onClick}>
|
||||
<ArrowUpIcon color="burgundy" />
|
||||
<span className={styles.backToTopButtonText}>
|
||||
{intl.formatMessage({ id: "Back to top" })}
|
||||
</span>
|
||||
</ButtonRAC>
|
||||
)
|
||||
}
|
||||
Reference in New Issue
Block a user