feat: added RoomCard and CardContainer component to HotelPage

This commit is contained in:
Erik Tiekstra
2024-07-04 10:35:23 +02:00
committed by Chuma McPhoy
parent 6b5606fc8b
commit 68f40a144e
16 changed files with 294 additions and 0 deletions

View File

@@ -0,0 +1,15 @@
.cardContainer {
display: grid;
gap: var(--Spacing-x3);
grid-template-columns: repeat(auto-fill, minmax(300px, 1fr));
}
.cardContainer.twoColumns {
grid-template-columns: repeat(2, 1fr);
}
.cardContainer.threeColumns {
grid-template-columns: repeat(3, 1fr);
}
.cardContainer.fourColumns {
grid-template-columns: repeat(4, 1fr);
}

View File

@@ -0,0 +1,7 @@
import { VariantProps } from "class-variance-authority"
import { cardContainerVariants } from "./variants"
export interface CardContainerProps
extends React.PropsWithChildren<React.HTMLAttributes<HTMLElement>>,
VariantProps<typeof cardContainerVariants> {}

View File

@@ -0,0 +1,18 @@
import { CardContainerProps } from "./cardContainer"
import { cardContainerVariants } from "./variants"
export function CardContainer({
children,
className,
columns,
...props
}: CardContainerProps) {
return (
<section
className={cardContainerVariants({ className, columns })}
{...props}
>
{children}
</section>
)
}

View File

@@ -0,0 +1,16 @@
import { cva } from "class-variance-authority"
import styles from "./cardContainer.module.css"
export const cardContainerVariants = cva(styles.cardContainer, {
variants: {
columns: {
2: styles.twoColumns,
3: styles.threeColumns,
4: styles.fourColumns,
},
},
defaultVariants: {
columns: 3,
},
})

View File

@@ -0,0 +1,52 @@
"use client"
import { ImageIcon } from "@/components/Icons"
import Image from "@/components/Image"
import Body from "@/components/TempDesignSystem/Text/Body"
import Title from "@/components/TempDesignSystem/Text/Title"
import { RoomCardProps } from "./roomCard"
import styles from "./roomCard.module.css"
export function RoomCard({
badgeText,
title,
subtitle,
cta,
image,
imageCount,
imageClick,
}: RoomCardProps) {
return (
<article className={styles.roomCard}>
<button className={styles.imageWrapper} onClick={imageClick}>
{badgeText && <span className={styles.badge}>{badgeText}</span>}
{imageCount && (
<span className={styles.imageCount}>
<ImageIcon color="white" />
{imageCount}
</span>
)}
<Image
className={styles.image}
src={image.src}
alt={image.alt}
height={image.height}
width={image.width}
/>
</button>
<div className={styles.content}>
<div className={styles.innerContent}>
<Title as="h4" level="h3" textTransform="capitalize" color="black">
{title}
</Title>
<Body color="grey">{subtitle}</Body>
</div>
<button className={styles.cta} onClick={cta.callback}>
{cta.text}
</button>
</div>
</article>
)
}

View File

@@ -0,0 +1,90 @@
.roomCard {
border-radius: var(--Corner-radius-Medium);
background-color: var(--Base-Surface-Primary-Normal);
border: 1px solid var(--Base-Border-Subtle);
display: grid;
}
.badge {
position: absolute;
top: var(--Spacing-x1);
left: var(--Spacing-x1);
background-color: var(--Scandic-Blue-100);
padding: var(--Spacing-x-half) var(--Spacing-x1);
border-radius: var(--Corner-radius-Medium);
color: var(--Tertiary-Dark-On-Surface-Text, #fff0c2);
text-transform: uppercase;
font-family: var(--typography-Title-5-fontFamily);
font-size: var(--typography-Footnote-Regular-fontSize);
font-weight: var(--typography-Footnote-Regular-fontWeight);
}
.imageCount {
position: absolute;
right: var(--Spacing-x1);
bottom: var(--Spacing-x1);
display: flex;
gap: var(--Spacing-x-half);
align-items: center;
background-color: color-mix(
in srgb,
var(--Scandic-Beige-80) 80%,
transparent
);
color: white;
padding: var(--Spacing-x-half) var(--Spacing-x1);
border-radius: var(--Corner-radius-Medium);
}
.content {
display: grid;
justify-items: center;
gap: var(--Spacing-x-one-and-half);
padding: var(--Spacing-x2);
}
.innerContent {
display: grid;
justify-items: center;
gap: var(--Spacing-x1);
}
.imageWrapper {
position: relative;
background-color: transparent;
border-width: 0;
cursor: pointer;
margin: 0;
padding: 0;
display: flex;
}
.image {
width: 100%;
object-fit: cover;
border-top-left-radius: var(--Corner-radius-Medium);
border-top-right-radius: var(--Corner-radius-Medium);
}
.subtitle {
color: var(--UI-Text-Placeholder, #787472);
}
.cta {
background-color: transparent;
border-width: 0;
cursor: pointer;
margin: 0;
padding: 0;
display: flex;
align-items: center;
gap: var(--Spacing-x-half);
color: var(--Base-Text-Medium-contrast, #8f4350);
font-family: var(--typography-Body-Bold-fontFamily);
font-size: var(--typography-Body-Bold-fontSize);
font-weight: 600;
text-decoration: underline;
}
.cta:hover {
color: var(--Base-Text-High-contrast, #4d001b);
}

View File

@@ -0,0 +1,16 @@
import { ImageProps } from "next/image"
interface RoomCardCtaProps {
text: string
callback: () => void
}
export interface RoomCardProps {
image: ImageProps
imageCount: number
imageClick: () => void
title: string
subtitle: string
cta: RoomCardCtaProps
badgeText?: string
}

View File

@@ -0,0 +1,23 @@
import { iconVariants } from "./variants"
import type { IconProps } from "@/types/components/icon"
export default function CameraIcon({ className, color, ...props }: IconProps) {
const classNames = iconVariants({ className, color })
return (
<svg
className={classNames}
xmlns="http://www.w3.org/2000/svg"
width="20"
height="18"
viewBox="0 0 20 18"
fill="none"
{...props}
>
<path
d="M10 14.375C11.225 14.375 12.2625 13.95 13.1125 13.1C13.9625 12.25 14.3875 11.2125 14.3875 9.9875C14.3875 8.7625 13.9625 7.725 13.1125 6.875C12.2625 6.025 11.225 5.6 10 5.6C8.775 5.6 7.7375 6.025 6.8875 6.875C6.0375 7.725 5.6125 8.7625 5.6125 9.9875C5.6125 11.2125 6.0375 12.25 6.8875 13.1C7.7375 13.95 8.775 14.375 10 14.375ZM9.9949 12.5C9.28997 12.5 8.69583 12.2583 8.2125 11.775C7.72917 11.2917 7.4875 10.6975 7.4875 9.9926C7.4875 9.28767 7.72917 8.69183 8.2125 8.2051C8.69583 7.71837 9.28997 7.475 9.9949 7.475C10.6998 7.475 11.2957 7.71837 11.7824 8.2051C12.2691 8.69183 12.5125 9.28767 12.5125 9.9926C12.5125 10.6975 12.2691 11.2917 11.7824 11.775C11.2957 12.2583 10.6998 12.5 9.9949 12.5ZM2.125 17.75C1.60937 17.75 1.16796 17.5664 0.800775 17.1992C0.433592 16.832 0.25 16.3906 0.25 15.875V4.1125C0.25 3.59687 0.433592 3.15546 0.800775 2.78828C1.16796 2.42109 1.60937 2.2375 2.125 2.2375H5.275L6.5125 0.875C6.68652 0.6827 6.89613 0.530459 7.14133 0.418275C7.38651 0.306092 7.64357 0.25 7.9125 0.25H12.0875C12.3564 0.25 12.6135 0.306092 12.8587 0.418275C13.1039 0.530459 13.3135 0.6827 13.4875 0.875L14.725 2.2375H17.875C18.3906 2.2375 18.832 2.42109 19.1992 2.78828C19.5664 3.15546 19.75 3.59687 19.75 4.1125V15.875C19.75 16.3906 19.5664 16.832 19.1992 17.1992C18.832 17.5664 18.3906 17.75 17.875 17.75H2.125ZM2.125 15.875H17.875V4.1125H13.8853L12.0875 2.125H7.9162L6.125 4.1125H2.125V15.875Z"
fill="#26201E"
/>
</svg>
)
}

View File

@@ -0,0 +1,36 @@
import { iconVariants } from "./variants"
import type { IconProps } from "@/types/components/icon"
export default function ImageIcon({ className, color, ...props }: IconProps) {
const classNames = iconVariants({ className, color })
return (
<svg
className={classNames}
xmlns="http://www.w3.org/2000/svg"
width="24"
height="24"
viewBox="0 0 24 24"
fill="none"
{...props}
>
<mask
id="mask0_69_3274"
style={{ maskType: "alpha" }}
maskUnits="userSpaceOnUse"
x="0"
y="0"
width="24"
height="24"
>
<rect width="24" height="24" fill="#D9D9D9" />
</mask>
<g mask="url(#mask0_69_3274)">
<path
d="M5.09998 20.775C4.58434 20.775 4.14293 20.5914 3.77575 20.2242C3.40857 19.857 3.22498 19.4156 3.22498 18.9V5.09998C3.22498 4.58434 3.40857 4.14293 3.77575 3.77575C4.14293 3.40857 4.58434 3.22498 5.09998 3.22498H18.9C19.4156 3.22498 19.857 3.40857 20.2242 3.77575C20.5914 4.14293 20.775 4.58434 20.775 5.09998V18.9C20.775 19.4156 20.5914 19.857 20.2242 20.2242C19.857 20.5914 19.4156 20.775 18.9 20.775H5.09998ZM5.09998 18.9H18.9V5.09998H5.09998V18.9ZM7.13748 16.925H16.8826C17.0775 16.925 17.2208 16.8396 17.3125 16.6687C17.4041 16.4979 17.3875 16.3333 17.2625 16.175L14.575 12.6C14.4807 12.475 14.3551 12.4125 14.1981 12.4125C14.041 12.4125 13.9166 12.475 13.825 12.6L11.25 16.025L9.42498 13.6125C9.33074 13.4875 9.2051 13.425 9.04805 13.425C8.891 13.425 8.76664 13.4875 8.67498 13.6125L6.76588 16.1732C6.63861 16.3327 6.62185 16.4979 6.7156 16.6687C6.80935 16.8396 6.94998 16.925 7.13748 16.925ZM8.5007 9.93748C8.90022 9.93748 9.23956 9.79765 9.51873 9.518C9.79789 9.23833 9.93748 8.89875 9.93748 8.49925C9.93748 8.09973 9.79765 7.76039 9.518 7.48123C9.23833 7.20206 8.89875 7.06248 8.49925 7.06248C8.09973 7.06248 7.76039 7.2023 7.48123 7.48195C7.20206 7.76162 7.06248 8.1012 7.06248 8.5007C7.06248 8.90022 7.2023 9.23956 7.48195 9.51873C7.76162 9.79789 8.1012 9.93748 8.5007 9.93748Z"
fill="#26201E"
/>
</g>
</svg>
)
}

View File

@@ -7,6 +7,7 @@ import {
BarIcon,
BikingIcon,
CalendarIcon,
CameraIcon,
CellphoneIcon,
CheckCircleIcon,
CheckIcon,
@@ -21,6 +22,7 @@ import {
FitnessIcon,
GlobeIcon,
HouseIcon,
ImageIcon,
InfoCircleIcon,
LocationIcon,
LockIcon,
@@ -51,6 +53,8 @@ export function getIconByIconName(icon?: IconName): FC<IconProps> | null {
return BikingIcon
case IconName.Calendar:
return CalendarIcon
case IconName.Camera:
return CameraIcon
case IconName.Cellphone:
return CellphoneIcon
case IconName.Check:
@@ -79,6 +83,8 @@ export function getIconByIconName(icon?: IconName): FC<IconProps> | null {
return GlobeIcon
case IconName.House:
return HouseIcon
case IconName.Image:
return ImageIcon
case IconName.InfoCircle:
return InfoCircleIcon
case IconName.Location:

View File

@@ -41,3 +41,8 @@
.red * {
fill: var(--Scandic-Brand-Scandic-Red);
}
.white,
.white * {
fill: var(--Scandic-Opacity-White-100);
}

View File

@@ -4,6 +4,7 @@ export { default as ArrowRightIcon } from "./ArrowRight"
export { default as BarIcon } from "./Bar"
export { default as BikingIcon } from "./Biking"
export { default as CalendarIcon } from "./Calendar"
export { default as CameraIcon } from "./Camera"
export { default as CellphoneIcon } from "./Cellphone"
export { default as CheckIcon } from "./Check"
export { default as CheckCircleIcon } from "./CheckCircle"
@@ -18,6 +19,7 @@ export { default as EmailIcon } from "./Email"
export { default as FitnessIcon } from "./Fitness"
export { default as GlobeIcon } from "./Globe"
export { default as HouseIcon } from "./House"
export { default as ImageIcon } from "./Image"
export { default as InfoCircleIcon } from "./InfoCircle"
export { default as LocationIcon } from "./Location"
export { default as LockIcon } from "./Lock"

View File

@@ -13,6 +13,7 @@ const config = {
primaryLightOnSurfaceAccent: styles.plosa,
red: styles.red,
green: styles.green,
white: styles.white,
},
},
defaultVariants: {

View File

@@ -50,6 +50,10 @@
color: var(--Scandic-Brand-Burgundy);
}
.grey {
color: var(--UI-Text-Placeholder, #787472);
}
.pale {
color: var(--Scandic-Brand-Pale-Peach);
}

View File

@@ -7,6 +7,7 @@ const config = {
color: {
black: styles.black,
burgundy: styles.burgundy,
grey: styles.grey,
pale: styles.pale,
red: styles.red,
textMediumContrast: styles.textMediumContrast,

View File

@@ -13,6 +13,7 @@ export enum IconName {
Bar = "Bar",
Biking = "Biking",
Calendar = "Calendar",
Camera = "Camera",
Cellphone = "Cellphone",
Check = "Check",
CheckCircle = "CheckCircle",
@@ -27,6 +28,7 @@ export enum IconName {
Fitness = "Fitness",
Globe = "Globe",
House = "House",
Image = "Image",
InfoCircle = "InfoCircle",
Location = "Location",
Lock = "Lock",