feat(SW-2144): added back-to-top button on destination map views
Approved-by: Matilda Landström
This commit is contained in:
@@ -66,9 +66,9 @@ export default function CityListing() {
|
|||||||
</li>
|
</li>
|
||||||
))}
|
))}
|
||||||
</ul>
|
</ul>
|
||||||
{showBackToTop && (
|
{showBackToTop ? (
|
||||||
<BackToTopButton position="center" onClick={scrollToTop} />
|
<BackToTopButton position="right" onClick={scrollToTop} />
|
||||||
)}
|
) : null}
|
||||||
</>
|
</>
|
||||||
)}
|
)}
|
||||||
</section>
|
</section>
|
||||||
|
|||||||
@@ -11,8 +11,7 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
.hotelList {
|
.hotelList {
|
||||||
display: flex;
|
display: grid;
|
||||||
flex-direction: column;
|
|
||||||
gap: var(--Spacing-x3);
|
gap: var(--Spacing-x3);
|
||||||
list-style: none;
|
list-style: none;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -140,6 +140,7 @@ export default function HotelListItem(data: DestinationPagesHotelData) {
|
|||||||
variant="Tertiary"
|
variant="Tertiary"
|
||||||
color="Primary"
|
color="Primary"
|
||||||
size="Small"
|
size="Small"
|
||||||
|
typography="Body/Paragraph/mdBold"
|
||||||
>
|
>
|
||||||
{intl.formatMessage({
|
{intl.formatMessage({
|
||||||
defaultMessage: "See hotel details",
|
defaultMessage: "See hotel details",
|
||||||
|
|||||||
@@ -132,6 +132,7 @@ export default function HotelListingItem(data: DestinationPagesHotelData) {
|
|||||||
color="Primary"
|
color="Primary"
|
||||||
size="Medium"
|
size="Medium"
|
||||||
wrapping={false}
|
wrapping={false}
|
||||||
|
typography="Body/Paragraph/mdBold"
|
||||||
onClick={() => setActiveMarker(hotel.id)}
|
onClick={() => setActiveMarker(hotel.id)}
|
||||||
>
|
>
|
||||||
{intl.formatMessage({
|
{intl.formatMessage({
|
||||||
@@ -150,6 +151,7 @@ export default function HotelListingItem(data: DestinationPagesHotelData) {
|
|||||||
variant="Tertiary"
|
variant="Tertiary"
|
||||||
color="Primary"
|
color="Primary"
|
||||||
size="Small"
|
size="Small"
|
||||||
|
typography="Body/Paragraph/mdBold"
|
||||||
>
|
>
|
||||||
{intl.formatMessage({
|
{intl.formatMessage({
|
||||||
defaultMessage: "See hotel details",
|
defaultMessage: "See hotel details",
|
||||||
|
|||||||
@@ -97,9 +97,9 @@ export default function HotelListing() {
|
|||||||
</li>
|
</li>
|
||||||
))}
|
))}
|
||||||
</ul>
|
</ul>
|
||||||
{showBackToTop && (
|
{showBackToTop ? (
|
||||||
<BackToTopButton position="center" onClick={scrollToTop} />
|
<BackToTopButton position="right" onClick={scrollToTop} />
|
||||||
)}
|
) : null}
|
||||||
</>
|
</>
|
||||||
)}
|
)}
|
||||||
</section>
|
</section>
|
||||||
|
|||||||
@@ -17,7 +17,9 @@ import { useDestinationDataStore } from "@/stores/destination-data"
|
|||||||
import { useDestinationPageHotelsMapStore } from "@/stores/destination-page-hotels-map"
|
import { useDestinationPageHotelsMapStore } from "@/stores/destination-page-hotels-map"
|
||||||
|
|
||||||
import DestinationFilterAndSort from "@/components/DestinationFilterAndSort"
|
import DestinationFilterAndSort from "@/components/DestinationFilterAndSort"
|
||||||
|
import { BackToTopButton } from "@/components/TempDesignSystem/BackToTopButton"
|
||||||
import Button from "@/components/TempDesignSystem/Button"
|
import Button from "@/components/TempDesignSystem/Button"
|
||||||
|
import { useScrollToTop } from "@/hooks/useScrollToTop"
|
||||||
import { debounce } from "@/utils/debounce"
|
import { debounce } from "@/utils/debounce"
|
||||||
|
|
||||||
import DynamicMap from "./DynamicMap"
|
import DynamicMap from "./DynamicMap"
|
||||||
@@ -52,6 +54,12 @@ export default function Map({
|
|||||||
const activeHotel = hotels.find(({ hotel }) => hotel.id === activeHotelId)
|
const activeHotel = hotels.find(({ hotel }) => hotel.id === activeHotelId)
|
||||||
const rootDiv = useRef<HTMLDivElement | null>(null)
|
const rootDiv = useRef<HTMLDivElement | null>(null)
|
||||||
const [mapHeight, setMapHeight] = useState("100dvh")
|
const [mapHeight, setMapHeight] = useState("100dvh")
|
||||||
|
const scrollRef = useRef<HTMLElement>(null)
|
||||||
|
const { showBackToTop, scrollToTop } = useScrollToTop({
|
||||||
|
threshold: 550,
|
||||||
|
elementRef: scrollRef,
|
||||||
|
refScrollable: true,
|
||||||
|
})
|
||||||
|
|
||||||
const intl = useIntl()
|
const intl = useIntl()
|
||||||
|
|
||||||
@@ -145,7 +153,12 @@ export default function Map({
|
|||||||
listType={pageType === "city" ? "hotel" : "city"}
|
listType={pageType === "city" ? "hotel" : "city"}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<aside className={styles.sidebar}>{children}</aside>
|
<aside className={styles.sidebar} ref={scrollRef}>
|
||||||
|
{children}
|
||||||
|
{showBackToTop ? (
|
||||||
|
<BackToTopButton position="left" onClick={scrollToTop} />
|
||||||
|
) : null}
|
||||||
|
</aside>
|
||||||
<DynamicMap
|
<DynamicMap
|
||||||
markers={markers}
|
markers={markers}
|
||||||
mapId={mapId}
|
mapId={mapId}
|
||||||
|
|||||||
@@ -1,38 +1,33 @@
|
|||||||
.backToTopButton {
|
.backToTopButton {
|
||||||
border-radius: var(--Corner-radius-rounded);
|
display: inline-flex;
|
||||||
|
padding: var(--Space-x1);
|
||||||
|
justify-content: center;
|
||||||
|
align-items: center;
|
||||||
|
gap: var(--Space-x05);
|
||||||
|
width: max-content;
|
||||||
|
color: var(--Component-Button-Brand-Secondary-On-fill-Default);
|
||||||
|
background-color: var(--Component-Button-Brand-Secondary-Fill-Inverted);
|
||||||
|
border: 2px solid var(--Component-Button-Brand-Secondary-Border-Default);
|
||||||
|
border-radius: var(--Corner-radius-Rounded);
|
||||||
|
box-shadow: 0px 0px 8px 3px rgba(0, 0, 0, 0.1);
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
display: flex;
|
position: sticky;
|
||||||
align-items: flex-end;
|
bottom: var(--Space-x2);
|
||||||
position: fixed;
|
|
||||||
bottom: 20px;
|
|
||||||
z-index: var(--back-to-top-button);
|
|
||||||
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.084px;
|
|
||||||
text-decoration: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
.backToTopButtonText {
|
&:hover {
|
||||||
display: none;
|
color: var(--Component-Button-Brand-Secondary-On-fill-Inverted);
|
||||||
|
background-color: var(
|
||||||
|
--Component-Button-Brand-Secondary-Fill-Hover-Inverted
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.left {
|
.left {
|
||||||
left: 32px;
|
left: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
.right {
|
.right {
|
||||||
right: 32px;
|
left: 100%;
|
||||||
}
|
}
|
||||||
|
|
||||||
.center {
|
.center {
|
||||||
@@ -40,18 +35,14 @@
|
|||||||
transform: translateX(-50%);
|
transform: translateX(-50%);
|
||||||
}
|
}
|
||||||
|
|
||||||
@media (min-width: 768px) {
|
@media screen and (max-width: 767px) {
|
||||||
.backToTopButtonText {
|
.text {
|
||||||
display: initial;
|
display: none;
|
||||||
}
|
}
|
||||||
.backToTopButton:hover {
|
}
|
||||||
background-color: var(--Base-Button-Tertiary-Fill-Normal);
|
|
||||||
color: var(--Base-Button-Tertiary-On-Fill-Hover);
|
@media screen and (min-width: 768px) {
|
||||||
}
|
.backToTopButton {
|
||||||
.backToTopButton:hover > svg * {
|
padding: 10px var(--Space-x2);
|
||||||
fill: var(--Base-Button-Tertiary-On-Fill-Hover);
|
|
||||||
}
|
|
||||||
.backToTopButton {
|
|
||||||
padding: calc(var(--Spacing-x1) + 2px) var(--Spacing-x2);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,9 @@
|
|||||||
|
import type { VariantProps } from "class-variance-authority"
|
||||||
|
import type { ComponentProps } from "react"
|
||||||
|
import type { Button } from "react-aria-components"
|
||||||
|
|
||||||
|
import type { backToTopButtonVariants } from "./variants"
|
||||||
|
|
||||||
|
export interface BackToTopButtonProps
|
||||||
|
extends ComponentProps<typeof Button>,
|
||||||
|
VariantProps<typeof backToTopButtonVariants> {}
|
||||||
@@ -4,30 +4,32 @@ import { Button as ButtonRAC } from "react-aria-components"
|
|||||||
import { useIntl } from "react-intl"
|
import { useIntl } from "react-intl"
|
||||||
|
|
||||||
import { MaterialIcon } from "@scandic-hotels/design-system/Icons/MaterialIcon"
|
import { MaterialIcon } from "@scandic-hotels/design-system/Icons/MaterialIcon"
|
||||||
|
import { Typography } from "@scandic-hotels/design-system/Typography"
|
||||||
|
|
||||||
import { backToTopButtonVariants } from "./variants"
|
import { backToTopButtonVariants } from "./variants"
|
||||||
|
|
||||||
import styles from "./backToTopButton.module.css"
|
import styles from "./backToTopButton.module.css"
|
||||||
|
|
||||||
export function BackToTopButton({
|
import type { BackToTopButtonProps } from "./backToTopButton"
|
||||||
onClick,
|
|
||||||
position,
|
export function BackToTopButton({ position, ...props }: BackToTopButtonProps) {
|
||||||
}: {
|
|
||||||
onClick: () => void
|
|
||||||
position: "left" | "right" | "center"
|
|
||||||
}) {
|
|
||||||
const intl = useIntl()
|
const intl = useIntl()
|
||||||
return (
|
return (
|
||||||
<ButtonRAC
|
<Typography variant="Body/Supporting text (caption)/smBold">
|
||||||
className={backToTopButtonVariants({ position })}
|
<ButtonRAC
|
||||||
onPress={onClick}
|
className={backToTopButtonVariants({ position })}
|
||||||
>
|
aria-label={intl.formatMessage({
|
||||||
<MaterialIcon icon="arrow_upward" color="CurrentColor" />
|
|
||||||
<span className={styles.backToTopButtonText}>
|
|
||||||
{intl.formatMessage({
|
|
||||||
defaultMessage: "Back to top",
|
defaultMessage: "Back to top",
|
||||||
})}
|
})}
|
||||||
</span>
|
{...props}
|
||||||
</ButtonRAC>
|
>
|
||||||
|
<MaterialIcon icon="arrow_upward" color="CurrentColor" size={20} />
|
||||||
|
<span className={styles.text}>
|
||||||
|
{intl.formatMessage({
|
||||||
|
defaultMessage: "Back to top",
|
||||||
|
})}
|
||||||
|
</span>
|
||||||
|
</ButtonRAC>
|
||||||
|
</Typography>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user