Files
web/apps/scandic-web/components/Blocks/CampaignHotelListing/Client.tsx
Anton Gunnarsson 002d093af4 Merged in feat/sw-2863-move-contentstack-router-to-trpc-package (pull request #2389)
feat(SW-2863): Move contentstack router to trpc package

* Add exports to packages and lint rule to prevent relative imports

* Add env to trpc package

* Add eslint to trpc package

* Apply lint rules

* Use direct imports from trpc package

* Add lint-staged config to trpc

* Move lang enum to common

* Restructure trpc package folder structure

* WIP first step

* update internal imports in trpc

* Fix most errors in scandic-web

Just 100 left...

* Move Props type out of trpc

* Fix CategorizedFilters types

* Move more schemas in hotel router

* Fix deps

* fix getNonContentstackUrls

* Fix import error

* Fix entry error handling

* Fix generateMetadata metrics

* Fix alertType enum

* Fix duplicated types

* lint:fix

* Merge branch 'master' into feat/sw-2863-move-contentstack-router-to-trpc-package

* Fix broken imports

* Merge branch 'master' into feat/sw-2863-move-contentstack-router-to-trpc-package


Approved-by: Linus Flood
2025-06-26 07:53:01 +00:00

125 lines
3.8 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
hotels: HotelDataWithUrl[]
visibleCountMobile?: 3 | 6
visibleCountDesktop?: 3 | 6
}
export default function CampaignHotelListingClient({
heading,
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>
</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>
)
}