132 lines
4.0 KiB
TypeScript
132 lines
4.0 KiB
TypeScript
"use client"
|
|
|
|
import { cx } from "class-variance-authority"
|
|
import { useRef, useState } from "react"
|
|
import { useIntl } from "react-intl"
|
|
import { useMediaQuery } from "usehooks-ts"
|
|
|
|
import { Button } from "@scandic-hotels/design-system/Button"
|
|
import {
|
|
MaterialIcon,
|
|
type MaterialIconProps,
|
|
} from "@scandic-hotels/design-system/Icons/MaterialIcon"
|
|
import { Typography } from "@scandic-hotels/design-system/Typography"
|
|
|
|
import HotelListingItem from "./HotelListingItem"
|
|
|
|
import styles from "./campaignHotelListing.module.css"
|
|
|
|
import type { HotelDataWithUrl } from "@scandic-hotels/trpc/types/hotel"
|
|
|
|
interface CampaignHotelListingClientProps {
|
|
heading: string
|
|
preamble?: string | null
|
|
hotels: HotelDataWithUrl[]
|
|
visibleCountMobile?: 3 | 6
|
|
visibleCountDesktop?: 3 | 6
|
|
}
|
|
|
|
export default function CampaignHotelListingClient({
|
|
heading,
|
|
preamble,
|
|
hotels,
|
|
visibleCountMobile = 3,
|
|
visibleCountDesktop = 6,
|
|
}: CampaignHotelListingClientProps) {
|
|
const intl = useIntl()
|
|
const isMobile = useMediaQuery("(max-width: 767px)")
|
|
const scrollRef = useRef<HTMLElement>(null)
|
|
|
|
const initialCount = isMobile ? visibleCountMobile : visibleCountDesktop // Initial number of hotels to show
|
|
const thresholdCount = initialCount + 3 // This is the threshold at which we start showing the "Show More" button
|
|
const showAllThreshold = initialCount * 3 // This is the threshold at which we show the "Show All" button
|
|
const incrementCount = initialCount // Number of hotels to increment when the button is clicked
|
|
|
|
const [visibleCount, setVisibleCount] = useState(() =>
|
|
// Set initial visible count based on the number of hotels and the threshold
|
|
hotels.length <= thresholdCount ? hotels.length : initialCount
|
|
)
|
|
|
|
// Only show the show more/less button if the length of hotels exceeds the threshold count
|
|
const showButton = hotels.length > thresholdCount
|
|
|
|
// Determine if we are at the stage where the user can click to show all hotels
|
|
const canShowAll =
|
|
hotels.length > visibleCount &&
|
|
(visibleCount + incrementCount > showAllThreshold ||
|
|
visibleCount + incrementCount >= hotels.length)
|
|
|
|
function handleButtonClick() {
|
|
if (visibleCount < hotels.length) {
|
|
if (canShowAll) {
|
|
setVisibleCount(hotels.length)
|
|
} else {
|
|
setVisibleCount((prev) =>
|
|
Math.min(prev + incrementCount, hotels.length)
|
|
)
|
|
}
|
|
} else {
|
|
setVisibleCount(initialCount)
|
|
if (scrollRef.current) {
|
|
scrollRef.current.scrollIntoView({ behavior: "smooth" })
|
|
}
|
|
}
|
|
}
|
|
|
|
let buttonText = intl.formatMessage({
|
|
defaultMessage: "Show more",
|
|
})
|
|
let iconDirection: MaterialIconProps["icon"] = "keyboard_arrow_down"
|
|
|
|
if (visibleCount === hotels.length) {
|
|
buttonText = intl.formatMessage({
|
|
defaultMessage: "Show less",
|
|
})
|
|
iconDirection = "keyboard_arrow_up"
|
|
} else if (canShowAll) {
|
|
buttonText = intl.formatMessage({
|
|
defaultMessage: "Show all",
|
|
})
|
|
}
|
|
|
|
return (
|
|
<section className={styles.hotelListingSection} ref={scrollRef}>
|
|
<header className={styles.header}>
|
|
<Typography variant="Title/Subtitle/lg">
|
|
<h3>{heading}</h3>
|
|
</Typography>
|
|
{preamble ? (
|
|
<Typography variant="Body/Paragraph/mdRegular">
|
|
<p>{preamble}</p>
|
|
</Typography>
|
|
) : null}
|
|
</header>
|
|
<ul className={styles.list}>
|
|
{hotels.map(({ hotel, url }, index) => (
|
|
<li
|
|
key={hotel.id}
|
|
className={cx(styles.listItem, {
|
|
[styles.hidden]: index >= visibleCount,
|
|
})}
|
|
>
|
|
<HotelListingItem hotel={hotel} url={url} />
|
|
</li>
|
|
))}
|
|
</ul>
|
|
|
|
{showButton ? (
|
|
<Button
|
|
variant="Text"
|
|
color="Primary"
|
|
size="Medium"
|
|
typography="Body/Paragraph/mdBold"
|
|
onPress={handleButtonClick}
|
|
>
|
|
<MaterialIcon icon={iconDirection} color="CurrentColor" />
|
|
{buttonText}
|
|
</Button>
|
|
) : null}
|
|
</section>
|
|
)
|
|
}
|