Merge branch 'develop' into feature/sw-561-design-fixes
This commit is contained in:
@@ -0,0 +1,107 @@
|
||||
"use client"
|
||||
|
||||
import {
|
||||
flexRender,
|
||||
getCoreRowModel,
|
||||
getPaginationRowModel,
|
||||
useReactTable,
|
||||
} from "@tanstack/react-table"
|
||||
import { useState } from "react"
|
||||
|
||||
import SectionContainer from "@/components/Section/Container"
|
||||
import SectionHeader from "@/components/Section/Header"
|
||||
import ScrollWrapper from "@/components/TempDesignSystem/ScrollWrapper"
|
||||
import ShowMoreButton from "@/components/TempDesignSystem/ShowMoreButton"
|
||||
import Table from "@/components/TempDesignSystem/Table"
|
||||
|
||||
import styles from "./table.module.css"
|
||||
|
||||
import type { TableBlockProps } from "@/types/components/blocks/table"
|
||||
|
||||
export default function TableBlock({ data }: TableBlockProps) {
|
||||
const { columns, rows, totalWidth, heading, preamble } = data
|
||||
const initialPageSize = 5
|
||||
const [pageSize, setPageSize] = useState(initialPageSize)
|
||||
const showMoreVisible = rows.length > initialPageSize
|
||||
const showLessVisible = pageSize >= rows.length
|
||||
const columnDefs = columns.map((col) => ({
|
||||
accessorKey: col.id,
|
||||
header: col.header,
|
||||
size: col.width,
|
||||
}))
|
||||
|
||||
const table = useReactTable({
|
||||
columns: columnDefs,
|
||||
data: rows,
|
||||
getCoreRowModel: getCoreRowModel(),
|
||||
getPaginationRowModel: getPaginationRowModel(),
|
||||
state: {
|
||||
pagination: {
|
||||
pageIndex: 0,
|
||||
pageSize,
|
||||
},
|
||||
},
|
||||
})
|
||||
|
||||
function handleShowMore() {
|
||||
setPageSize(showLessVisible ? initialPageSize : rows.length)
|
||||
}
|
||||
|
||||
return (
|
||||
<SectionContainer>
|
||||
{heading ? (
|
||||
<SectionHeader preamble={data.preamble} title={heading} />
|
||||
) : null}
|
||||
<div className={styles.tableWrapper}>
|
||||
<ScrollWrapper>
|
||||
<Table
|
||||
width={`${totalWidth}%`}
|
||||
variant="content"
|
||||
intent="striped"
|
||||
layout="fixed"
|
||||
borderRadius="none"
|
||||
>
|
||||
<Table.THead>
|
||||
{table.getHeaderGroups().map((headerGroup) => (
|
||||
<Table.TR key={headerGroup.id}>
|
||||
{headerGroup.headers.map((header) => (
|
||||
<Table.TH
|
||||
key={header.id}
|
||||
width={`${header.column.columnDef.size}%`}
|
||||
>
|
||||
{flexRender(
|
||||
header.column.columnDef.header,
|
||||
header.getContext()
|
||||
)}
|
||||
</Table.TH>
|
||||
))}
|
||||
</Table.TR>
|
||||
))}
|
||||
</Table.THead>
|
||||
<Table.TBody>
|
||||
{table.getRowModel().rows.map((row) => (
|
||||
<Table.TR key={row.id}>
|
||||
{row.getVisibleCells().map((cell) => (
|
||||
<Table.TD key={cell.id}>
|
||||
{flexRender(
|
||||
cell.column.columnDef.cell,
|
||||
cell.getContext()
|
||||
)}
|
||||
</Table.TD>
|
||||
))}
|
||||
</Table.TR>
|
||||
))}
|
||||
</Table.TBody>
|
||||
</Table>
|
||||
</ScrollWrapper>
|
||||
{showMoreVisible ? (
|
||||
<ShowMoreButton
|
||||
loadMoreData={handleShowMore}
|
||||
showLess={showLessVisible}
|
||||
intent="table"
|
||||
/>
|
||||
) : null}
|
||||
</div>
|
||||
</SectionContainer>
|
||||
)
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
.tableWrapper {
|
||||
display: grid;
|
||||
border: 1px solid var(--Base-Border-Subtle);
|
||||
border-radius: var(--Corner-radius-Small);
|
||||
overflow: hidden;
|
||||
}
|
||||
@@ -5,6 +5,8 @@ import TextCols from "@/components/Blocks/TextCols"
|
||||
import UspGrid from "@/components/Blocks/UspGrid"
|
||||
import JsonToHtml from "@/components/JsonToHtml"
|
||||
|
||||
import Table from "./Table"
|
||||
|
||||
import type { BlocksProps } from "@/types/components/blocks"
|
||||
import { BlocksEnums } from "@/types/enums/blocks"
|
||||
|
||||
@@ -47,6 +49,8 @@ export default function Blocks({ blocks }: BlocksProps) {
|
||||
title={block.shortcuts.title}
|
||||
/>
|
||||
)
|
||||
case BlocksEnums.block.Table:
|
||||
return <Table data={block.table} />
|
||||
case BlocksEnums.block.TextCols:
|
||||
return <TextCols text_cols={block.text_cols} />
|
||||
case BlocksEnums.block.TextContent:
|
||||
|
||||
@@ -42,7 +42,7 @@
|
||||
box-shadow: 0px 4px 24px 0px rgba(0, 0, 0, 0.05);
|
||||
position: sticky;
|
||||
top: 0;
|
||||
z-index: 10000;
|
||||
z-index: 9;
|
||||
background-color: var(--Base-Surface-Primary-light-Normal);
|
||||
}
|
||||
|
||||
|
||||
@@ -40,6 +40,8 @@
|
||||
}
|
||||
|
||||
.mainContent {
|
||||
display: grid;
|
||||
gap: var(--Spacing-x4);
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
|
||||
@@ -5,7 +5,7 @@ import { FormProvider, useForm } from "react-hook-form"
|
||||
import { useIntl } from "react-intl"
|
||||
|
||||
import { KingBedIcon } from "@/components/Icons"
|
||||
import RadioCard from "@/components/TempDesignSystem/Form/Card/Radio"
|
||||
import RadioCard from "@/components/TempDesignSystem/Form/ChoiceCard/Radio"
|
||||
|
||||
import { bedTypeSchema } from "./schema"
|
||||
|
||||
@@ -24,9 +24,7 @@ export default function BedType() {
|
||||
reValidateMode: "onChange",
|
||||
})
|
||||
|
||||
// @ts-expect-error - Types mismatch docs as this is
|
||||
// a pattern that is allowed https://formatjs.io/docs/react-intl/api#usage
|
||||
const text = intl.formatMessage(
|
||||
const text = intl.formatMessage<React.ReactNode>(
|
||||
{ id: "<b>Included</b> (based on availability)" },
|
||||
{ b: (str) => <b>{str}</b> }
|
||||
)
|
||||
|
||||
@@ -5,7 +5,7 @@ import { FormProvider, useForm } from "react-hook-form"
|
||||
import { useIntl } from "react-intl"
|
||||
|
||||
import { BreakfastIcon, NoBreakfastIcon } from "@/components/Icons"
|
||||
import RadioCard from "@/components/TempDesignSystem/Form/Card/Radio"
|
||||
import RadioCard from "@/components/TempDesignSystem/Form/ChoiceCard/Radio"
|
||||
|
||||
import { breakfastSchema } from "./schema"
|
||||
|
||||
@@ -31,9 +31,7 @@ export default function Breakfast() {
|
||||
Icon={BreakfastIcon}
|
||||
id={breakfastEnum.BREAKFAST}
|
||||
name="breakfast"
|
||||
// @ts-expect-error - Types mismatch docs as this is
|
||||
// a pattern that is allowed https://formatjs.io/docs/react-intl/api#usage
|
||||
subtitle={intl.formatMessage(
|
||||
subtitle={intl.formatMessage<React.ReactNode>(
|
||||
{ id: "<b>{amount} {currency}</b>/night per adult" },
|
||||
{
|
||||
amount: "150",
|
||||
|
||||
@@ -4,7 +4,7 @@ import { FormProvider, useForm } from "react-hook-form"
|
||||
import { useIntl } from "react-intl"
|
||||
|
||||
import Button from "@/components/TempDesignSystem/Button"
|
||||
import CheckboxCard from "@/components/TempDesignSystem/Form/Card/Checkbox"
|
||||
import CheckboxCard from "@/components/TempDesignSystem/Form/ChoiceCard/Checkbox"
|
||||
import CountrySelect from "@/components/TempDesignSystem/Form/Country"
|
||||
import Input from "@/components/TempDesignSystem/Form/Input"
|
||||
import Phone from "@/components/TempDesignSystem/Form/Phone"
|
||||
@@ -88,8 +88,9 @@ export default function Details({ user }: DetailsProps) {
|
||||
<footer className={styles.footer}>
|
||||
{user ? null : (
|
||||
<CheckboxCard
|
||||
highlightSubtitle
|
||||
list={list}
|
||||
saving
|
||||
name="join"
|
||||
subtitle={intl.formatMessage(
|
||||
{
|
||||
id: "{difference}{amount} {currency}",
|
||||
|
||||
@@ -0,0 +1,77 @@
|
||||
"use client"
|
||||
|
||||
import { useIntl } from "react-intl"
|
||||
|
||||
import { EditIcon, ImageIcon } from "@/components/Icons"
|
||||
import Button from "@/components/TempDesignSystem/Button"
|
||||
import Link from "@/components/TempDesignSystem/Link"
|
||||
import Footnote from "@/components/TempDesignSystem/Text/Footnote"
|
||||
import Subtitle from "@/components/TempDesignSystem/Text/Subtitle"
|
||||
|
||||
import styles from "./selectedRoom.module.css"
|
||||
|
||||
export default function SelectedRoom() {
|
||||
const intl = useIntl()
|
||||
return (
|
||||
<article className={styles.container}>
|
||||
<div className={styles.tempImage}>
|
||||
<ImageIcon
|
||||
color="baseButtonTertiaryOnFillNormal"
|
||||
height={60}
|
||||
width={60}
|
||||
/>
|
||||
</div>
|
||||
<div className={styles.content}>
|
||||
<div className={styles.textContainer}>
|
||||
<Footnote
|
||||
className={styles.label}
|
||||
color="uiTextPlaceholder"
|
||||
textTransform="uppercase"
|
||||
>
|
||||
{intl.formatMessage({ id: "Your room" })}
|
||||
</Footnote>
|
||||
<div className={styles.text}>
|
||||
{/**
|
||||
* [TEMP]
|
||||
* No translation on Subtitles as they will be derived
|
||||
* from Room selection.
|
||||
*/}
|
||||
<Subtitle
|
||||
className={styles.room}
|
||||
color="uiTextHighContrast"
|
||||
type="two"
|
||||
>
|
||||
Cozy cabin
|
||||
</Subtitle>
|
||||
<Subtitle
|
||||
className={styles.invertFontWeight}
|
||||
color="uiTextMediumContrast"
|
||||
type="two"
|
||||
>
|
||||
Free rebooking
|
||||
</Subtitle>
|
||||
<Subtitle
|
||||
className={styles.invertFontWeight}
|
||||
color="uiTextMediumContrast"
|
||||
type="two"
|
||||
>
|
||||
Pay now
|
||||
</Subtitle>
|
||||
</div>
|
||||
</div>
|
||||
<Button
|
||||
asChild
|
||||
intent="tertiary"
|
||||
size="small"
|
||||
theme="base"
|
||||
variant="icon"
|
||||
>
|
||||
<Link href="#">
|
||||
<EditIcon color="baseButtonTertiaryOnFillNormal" />
|
||||
{intl.formatMessage({ id: "Modify" })}
|
||||
</Link>
|
||||
</Button>
|
||||
</div>
|
||||
</article>
|
||||
)
|
||||
}
|
||||
@@ -0,0 +1,51 @@
|
||||
.container {
|
||||
background-color: var(--Base-Surface-Primary-light-Normal);
|
||||
border-radius: var(--Corner-radius-Large);
|
||||
display: grid;
|
||||
grid-template-columns: 144px 1fr;
|
||||
gap: var(--Spacing-x3);
|
||||
padding: var(--Spacing-x2) var(--Spacing-x4) var(--Spacing-x2)
|
||||
var(--Spacing-x2);
|
||||
}
|
||||
|
||||
.tempImage {
|
||||
align-items: center;
|
||||
background-color: lightgray;
|
||||
border-radius: var(--Corner-radius-Medium);
|
||||
display: flex;
|
||||
height: auto;
|
||||
justify-content: center;
|
||||
min-height: 80px;
|
||||
}
|
||||
|
||||
.content {
|
||||
align-items: center;
|
||||
display: grid;
|
||||
gap: var(--Spacing-x3);
|
||||
grid-template-columns: 1fr auto;
|
||||
}
|
||||
|
||||
.textContainer {
|
||||
display: grid;
|
||||
}
|
||||
|
||||
.label {
|
||||
grid-column: 1 / -1;
|
||||
}
|
||||
|
||||
.text {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
gap: var(--Spacing-x1);
|
||||
}
|
||||
|
||||
p.invertFontWeight {
|
||||
font-weight: 400;
|
||||
}
|
||||
|
||||
.invertFontWeight:not(:last-of-type)::after,
|
||||
.room::after {
|
||||
color: var(--UI-Text-Medium-contrast);
|
||||
content: "∙";
|
||||
padding-left: var(--Spacing-x1);
|
||||
}
|
||||
@@ -1,10 +1,13 @@
|
||||
.hotelSelectionHeader {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
background-color: var(--Base-Surface-Subtle-Normal);
|
||||
padding: var(--Spacing-x3) var(--Spacing-x2);
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.hotelSelectionHeaderWrapper {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: var(--Spacing-x3);
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.titleContainer {
|
||||
@@ -33,9 +36,15 @@
|
||||
|
||||
@media (min-width: 768px) {
|
||||
.hotelSelectionHeader {
|
||||
flex-direction: row;
|
||||
padding: var(--Spacing-x4) var(--Spacing-x5);
|
||||
}
|
||||
|
||||
.hotelSelectionHeaderWrapper {
|
||||
flex-direction: row;
|
||||
gap: var(--Spacing-x6);
|
||||
max-width: var(--max-width-navigation);
|
||||
margin: 0 auto;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.titleContainer > h1 {
|
||||
|
||||
@@ -19,35 +19,35 @@ export default function HotelSelectionHeader({
|
||||
|
||||
return (
|
||||
<header className={styles.hotelSelectionHeader}>
|
||||
<div className={styles.titleContainer}>
|
||||
<Title as="h3" level="h1">
|
||||
{hotel.name}
|
||||
</Title>
|
||||
<address className={styles.address}>
|
||||
<Caption color="textMediumContrast">
|
||||
{hotel.address.streetAddress}, {hotel.address.city}
|
||||
</Caption>
|
||||
<div>
|
||||
<Divider variant="vertical" color="subtle" />
|
||||
</div>
|
||||
<Caption color="textMediumContrast">
|
||||
{intl.formatMessage(
|
||||
{
|
||||
id: "Distance to city centre",
|
||||
},
|
||||
{ number: hotel.location.distanceToCentre }
|
||||
)}
|
||||
</Caption>
|
||||
</address>
|
||||
</div>
|
||||
<div className={styles.dividerContainer}>
|
||||
<Divider variant="vertical" color="subtle" />
|
||||
</div>
|
||||
<div className={styles.descriptionContainer}>
|
||||
<Body color="textHighContrast">
|
||||
{hotel.hotelContent.texts.descriptions.short}
|
||||
</Body>
|
||||
<HotelDetailSidePeek />
|
||||
<div className={styles.hotelSelectionHeaderWrapper}>
|
||||
<div className={styles.titleContainer}>
|
||||
<Title as="h3" level="h1">
|
||||
{hotel.name}
|
||||
</Title>
|
||||
<address className={styles.address}>
|
||||
<Caption color="textMediumContrast">
|
||||
{hotel.address.streetAddress}, {hotel.address.city}
|
||||
</Caption>
|
||||
<div>
|
||||
<Divider variant="vertical" color="subtle" />
|
||||
</div>
|
||||
<Caption color="textMediumContrast">
|
||||
{intl.formatMessage(
|
||||
{ id: "Distance to city centre" },
|
||||
{ number: hotel.location.distanceToCentre }
|
||||
)}
|
||||
</Caption>
|
||||
</address>
|
||||
</div>
|
||||
<div className={styles.dividerContainer}>
|
||||
<Divider variant="vertical" color="subtle" />
|
||||
</div>
|
||||
<div className={styles.descriptionContainer}>
|
||||
<Body color="textHighContrast">
|
||||
{hotel.hotelContent.texts.descriptions.short}
|
||||
</Body>
|
||||
<HotelDetailSidePeek />
|
||||
</div>
|
||||
</div>
|
||||
</header>
|
||||
)
|
||||
|
||||
@@ -129,7 +129,7 @@ export default function Payment({ hotel }: PaymentProps) {
|
||||
name="payment-method"
|
||||
id="card"
|
||||
value="card"
|
||||
checked={selectedPaymentMethod === "card"}
|
||||
defaultChecked={selectedPaymentMethod === "card"}
|
||||
/>
|
||||
<label htmlFor="card">card</label>
|
||||
</button>
|
||||
@@ -145,7 +145,7 @@ export default function Payment({ hotel }: PaymentProps) {
|
||||
name="payment-method"
|
||||
id={paymentOption}
|
||||
value={paymentOption}
|
||||
checked={selectedPaymentMethod === paymentOption}
|
||||
defaultChecked={selectedPaymentMethod === paymentOption}
|
||||
/>
|
||||
<label htmlFor={paymentOption}>{paymentOption}</label>
|
||||
</button>
|
||||
|
||||
@@ -9,17 +9,31 @@ import styles from "./flexibilityOption.module.css"
|
||||
import { FlexibilityOptionProps } from "@/types/components/hotelReservation/selectRate/flexibilityOption"
|
||||
|
||||
export default function FlexibilityOption({
|
||||
currency,
|
||||
standardPrice,
|
||||
memberPrice,
|
||||
product,
|
||||
name,
|
||||
value,
|
||||
paymentTerm,
|
||||
}: FlexibilityOptionProps) {
|
||||
const intl = useIntl()
|
||||
|
||||
if (!product) {
|
||||
// TODO: Implement empty state when this rate can't be booked
|
||||
return <div>TBI: Rate not available</div>
|
||||
}
|
||||
|
||||
const { productType } = product
|
||||
const { public: publicPrice, member: memberPrice } = productType
|
||||
const { localPrice: publicLocalPrice, requestedPrice: publicRequestedPrice } =
|
||||
publicPrice
|
||||
const { localPrice: memberLocalPrice, requestedPrice: memberRequestedPrice } =
|
||||
memberPrice
|
||||
|
||||
return (
|
||||
<label>
|
||||
<input type="radio" name="flexibility" value={value} />
|
||||
<input
|
||||
type="radio"
|
||||
name="rateCode"
|
||||
value={product.productType.public.rateCode}
|
||||
/>
|
||||
<div className={styles.card}>
|
||||
<div className={styles.header}>
|
||||
<Body>{name}</Body>
|
||||
@@ -29,15 +43,27 @@ export default function FlexibilityOption({
|
||||
<div>
|
||||
<dt>{intl.formatMessage({ id: "Standard price" })}</dt>
|
||||
<dd>
|
||||
{standardPrice} {currency}
|
||||
{publicLocalPrice.pricePerNight} {publicLocalPrice.currency}/
|
||||
{intl.formatMessage({ id: "night" })}
|
||||
</dd>
|
||||
</div>
|
||||
<div>
|
||||
<dt>{intl.formatMessage({ id: "Member price" })}</dt>
|
||||
<dd>
|
||||
{memberPrice} {currency}
|
||||
{memberLocalPrice.pricePerNight} {memberLocalPrice.currency}/
|
||||
{intl.formatMessage({ id: "night" })}
|
||||
</dd>
|
||||
</div>
|
||||
{publicRequestedPrice && memberRequestedPrice && (
|
||||
<div>
|
||||
<dt>{intl.formatMessage({ id: "Approx." })}</dt>
|
||||
<dd>
|
||||
{publicRequestedPrice.pricePerNight}/
|
||||
{memberRequestedPrice.pricePerNight}{" "}
|
||||
{publicRequestedPrice.currency}
|
||||
</dd>
|
||||
</div>
|
||||
)}
|
||||
</dl>
|
||||
</div>
|
||||
</label>
|
||||
|
||||
@@ -11,20 +11,54 @@ import styles from "./roomCard.module.css"
|
||||
import { RoomCardProps } from "@/types/components/hotelReservation/selectRate/roomCard"
|
||||
|
||||
export default function RoomCard({
|
||||
room,
|
||||
nrOfAdults,
|
||||
nrOfNights,
|
||||
breakfastIncluded,
|
||||
rateDefinitions,
|
||||
roomConfiguration,
|
||||
}: RoomCardProps) {
|
||||
const intl = useIntl()
|
||||
|
||||
const saveRate = rateDefinitions.find(
|
||||
// TODO: Update string when API has decided
|
||||
(rate) => rate.cancellationRule === "NonCancellable"
|
||||
)
|
||||
const changeRate = rateDefinitions.find(
|
||||
// TODO: Update string when API has decided
|
||||
(rate) => rate.cancellationRule === "Modifiable"
|
||||
)
|
||||
const flexRate = rateDefinitions.find(
|
||||
// TODO: Update string when API has decided
|
||||
(rate) => rate.cancellationRule === "CancellableBefore6PM"
|
||||
)
|
||||
|
||||
const saveProduct = saveRate
|
||||
? roomConfiguration.products.find(
|
||||
(product) =>
|
||||
product.productType.public.rateCode === saveRate.rateCode ||
|
||||
product.productType.member.rateCode === saveRate.rateCode
|
||||
)
|
||||
: undefined
|
||||
const changeProduct = changeRate
|
||||
? roomConfiguration.products.find(
|
||||
(product) =>
|
||||
product.productType.public.rateCode === changeRate.rateCode ||
|
||||
product.productType.member.rateCode === changeRate.rateCode
|
||||
)
|
||||
: undefined
|
||||
const flexProduct = flexRate
|
||||
? roomConfiguration.products.find(
|
||||
(product) =>
|
||||
product.productType.public.rateCode === flexRate.rateCode ||
|
||||
product.productType.member.rateCode === flexRate.rateCode
|
||||
)
|
||||
: undefined
|
||||
|
||||
return (
|
||||
<div className={styles.card}>
|
||||
<div className={styles.cardBody}>
|
||||
<div className={styles.specification}>
|
||||
<Subtitle className={styles.name} type="two">
|
||||
{room.name}
|
||||
{roomConfiguration.roomType}
|
||||
</Subtitle>
|
||||
<Caption>{room.size}</Caption>
|
||||
<Caption>Room size TBI</Caption>
|
||||
<Button intent="text" type="button" size="small" theme="base">
|
||||
{intl.formatMessage({ id: "See room details" })}
|
||||
</Button>
|
||||
@@ -32,20 +66,15 @@ export default function RoomCard({
|
||||
{/*TODO: Handle pluralisation*/}
|
||||
{intl.formatMessage(
|
||||
{
|
||||
id: "Nr night, nr adult",
|
||||
defaultMessage:
|
||||
"{nights, number} night, {adults, number} adult",
|
||||
id: "Max {nrOfGuests} guests",
|
||||
defaultMessage: "Max {nrOfGuests} guests",
|
||||
},
|
||||
{ nights: nrOfNights, adults: nrOfAdults }
|
||||
// TODO: Correct number
|
||||
{ nrOfGuests: 2 }
|
||||
)}
|
||||
{" | "}
|
||||
{breakfastIncluded
|
||||
? intl.formatMessage({
|
||||
id: "Breakfast included",
|
||||
})
|
||||
: intl.formatMessage({
|
||||
id: "Breakfast excluded",
|
||||
})}
|
||||
{intl.formatMessage({
|
||||
id: "Breakfast included",
|
||||
})}
|
||||
</Caption>
|
||||
</div>
|
||||
|
||||
@@ -53,25 +82,19 @@ export default function RoomCard({
|
||||
name={intl.formatMessage({ id: "Non-refundable" })}
|
||||
value="non-refundable"
|
||||
paymentTerm={intl.formatMessage({ id: "Pay now" })}
|
||||
standardPrice={room.prices.nonRefundable.standard}
|
||||
memberPrice={room.prices.nonRefundable.member}
|
||||
currency={room.prices.currency}
|
||||
product={saveProduct}
|
||||
/>
|
||||
<FlexibilityOption
|
||||
name={intl.formatMessage({ id: "Free rebooking" })}
|
||||
value="free-rebooking"
|
||||
paymentTerm={intl.formatMessage({ id: "Pay now" })}
|
||||
standardPrice={room.prices.freeRebooking.standard}
|
||||
memberPrice={room.prices.freeRebooking.member}
|
||||
currency={room.prices.currency}
|
||||
product={changeProduct}
|
||||
/>
|
||||
<FlexibilityOption
|
||||
name={intl.formatMessage({ id: "Free cancellation" })}
|
||||
value="free-cancellation"
|
||||
paymentTerm={intl.formatMessage({ id: "Pay later" })}
|
||||
standardPrice={room.prices.freeCancellation.standard}
|
||||
memberPrice={room.prices.freeCancellation.member}
|
||||
currency={room.prices.currency}
|
||||
product={flexProduct}
|
||||
/>
|
||||
|
||||
<Button
|
||||
@@ -87,7 +110,8 @@ export default function RoomCard({
|
||||
{/* eslint-disable-next-line @next/next/no-img-element */}
|
||||
<img
|
||||
alt={intl.formatMessage({ id: "A photo of the room" })}
|
||||
src={room.imageSrc}
|
||||
// TODO: Correct image URL
|
||||
src="https://www.scandichotels.se/imageVault/publishedmedia/xnmqnmz6mz0uhuat0917/scandic-helsinki-hub-room-standard-KR-7.jpg"
|
||||
/>
|
||||
</div>
|
||||
)
|
||||
|
||||
@@ -1,5 +1,8 @@
|
||||
"use client"
|
||||
import { useRouter, useSearchParams } from "next/navigation"
|
||||
import { useIntl } from "react-intl"
|
||||
|
||||
import Button from "@/components/TempDesignSystem/Button"
|
||||
|
||||
import RoomCard from "./RoomCard"
|
||||
|
||||
@@ -8,12 +11,11 @@ import styles from "./roomSelection.module.css"
|
||||
import { RoomSelectionProps } from "@/types/components/hotelReservation/selectRate/roomSelection"
|
||||
|
||||
export default function RoomSelection({
|
||||
rates,
|
||||
nrOfNights,
|
||||
nrOfAdults,
|
||||
roomConfigurations,
|
||||
}: RoomSelectionProps) {
|
||||
const router = useRouter()
|
||||
const searchParams = useSearchParams()
|
||||
const intl = useIntl()
|
||||
|
||||
function handleSubmit(e: React.FormEvent<HTMLFormElement>) {
|
||||
e.preventDefault()
|
||||
@@ -25,31 +27,28 @@ export default function RoomSelection({
|
||||
|
||||
return (
|
||||
<div className={styles.wrapper}>
|
||||
<ul className={styles.roomList}>
|
||||
{rates.map((room) => (
|
||||
<li key={room.id}>
|
||||
<form
|
||||
method="GET"
|
||||
action={`select-bed?${searchParams}`}
|
||||
onSubmit={handleSubmit}
|
||||
>
|
||||
<input
|
||||
type="hidden"
|
||||
name="roomClass"
|
||||
value={room.id}
|
||||
id={`room-${room.id}`}
|
||||
/>
|
||||
<form
|
||||
method="GET"
|
||||
action={`select-bed?${searchParams}`}
|
||||
onSubmit={handleSubmit}
|
||||
>
|
||||
<ul className={styles.roomList}>
|
||||
{roomConfigurations.roomConfigurations.map((roomConfiguration) => (
|
||||
<li key={roomConfiguration.roomType}>
|
||||
<RoomCard
|
||||
room={room}
|
||||
nrOfAdults={nrOfAdults}
|
||||
nrOfNights={nrOfNights}
|
||||
breakfastIncluded={room.breakfastIncluded}
|
||||
rateDefinitions={roomConfigurations.rateDefinitions}
|
||||
roomConfiguration={roomConfiguration}
|
||||
/>
|
||||
</form>
|
||||
</li>
|
||||
))}
|
||||
</ul>
|
||||
<div className={styles.summary}>This is summary</div>
|
||||
</li>
|
||||
))}
|
||||
</ul>
|
||||
<div className={styles.summary}>
|
||||
This is summary
|
||||
<Button type="submit" size="small" theme="primaryDark">
|
||||
{intl.formatMessage({ id: "Choose room" })}
|
||||
</Button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
@@ -27,4 +27,6 @@
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
background-color: white;
|
||||
padding: var(--Spacing-x3) var(--Spacing-x7) var(--Spacing-x5);
|
||||
}
|
||||
|
||||
@@ -40,7 +40,7 @@
|
||||
.iconWrapper {
|
||||
position: relative;
|
||||
top: var(--Spacing-x1);
|
||||
z-index: 10;
|
||||
z-index: 2;
|
||||
}
|
||||
|
||||
.circle {
|
||||
|
||||
@@ -66,3 +66,8 @@
|
||||
.blue * {
|
||||
fill: var(--UI-Input-Controls-Fill-Selected);
|
||||
}
|
||||
|
||||
.baseButtonTertiaryOnFillNormal,
|
||||
.baseButtonTertiaryOnFillNormal * {
|
||||
fill: var(--Base-Button-Tertiary-On-Fill-Normal);
|
||||
}
|
||||
|
||||
@@ -5,19 +5,20 @@ import styles from "./icon.module.css"
|
||||
const config = {
|
||||
variants: {
|
||||
color: {
|
||||
baseButtonTertiaryOnFillNormal: styles.baseButtonTertiaryOnFillNormal,
|
||||
baseIconLowContrast: styles.baseIconLowContrast,
|
||||
black: styles.black,
|
||||
blue: styles.blue,
|
||||
burgundy: styles.burgundy,
|
||||
green: styles.green,
|
||||
grey80: styles.grey80,
|
||||
pale: styles.pale,
|
||||
peach80: styles.peach80,
|
||||
primaryLightOnSurfaceAccent: styles.plosa,
|
||||
red: styles.red,
|
||||
green: styles.green,
|
||||
white: styles.white,
|
||||
uiTextHighContrast: styles.uiTextHighContrast,
|
||||
uiTextMediumContrast: styles.uiTextMediumContrast,
|
||||
blue: styles.blue,
|
||||
},
|
||||
},
|
||||
defaultVariants: {
|
||||
|
||||
@@ -49,10 +49,10 @@ export default function FullView({
|
||||
className={styles.fullViewImage}
|
||||
>
|
||||
<Image
|
||||
src={image.url}
|
||||
alt={image.alt}
|
||||
layout="fill"
|
||||
objectFit="cover"
|
||||
fill
|
||||
src={image.url}
|
||||
style={{ objectFit: "cover" }}
|
||||
/>
|
||||
|
||||
<div className={styles.fullViewFooter}>
|
||||
|
||||
@@ -64,10 +64,10 @@ a.default {
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.icon {
|
||||
.btn.icon:is(.small, .medium, .large) {
|
||||
align-items: center;
|
||||
display: flex;
|
||||
gap: var(--Spacing-x-half);
|
||||
gap: var(--Spacing-x1);
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
@@ -76,7 +76,8 @@ a.default {
|
||||
font-size: var(--typography-Caption-Bold-fontSize);
|
||||
line-height: var(--typography-Caption-Bold-lineHeight);
|
||||
gap: var(--Spacing-x-quarter);
|
||||
padding: calc(var(--Spacing-x1) + 2px) var(--Spacing-x2); /* Special case padding to adjust the missing border */
|
||||
padding: calc(var(--Spacing-x1) + 2px) var(--Spacing-x2);
|
||||
/* Special case padding to adjust the missing border */
|
||||
}
|
||||
|
||||
.btn.small.secondary {
|
||||
@@ -85,7 +86,8 @@ a.default {
|
||||
|
||||
.btn.medium {
|
||||
gap: var(--Spacing-x-half);
|
||||
padding: calc(var(--Spacing-x-one-and-half) + 2px) var(--Spacing-x2); /* Special case padding to adjust the missing border */
|
||||
padding: calc(var(--Spacing-x-one-and-half) + 2px) var(--Spacing-x2);
|
||||
/* Special case padding to adjust the missing border */
|
||||
}
|
||||
|
||||
.medium.secondary {
|
||||
@@ -94,7 +96,8 @@ a.default {
|
||||
|
||||
.btn.large {
|
||||
gap: var(--Spacing-x-half);
|
||||
padding: calc(var(--Spacing-x2) + 2px) var(--Spacing-x3); /* Special case padding to adjust the missing border */
|
||||
padding: calc(var(--Spacing-x2) + 2px) var(--Spacing-x3);
|
||||
/* Special case padding to adjust the missing border */
|
||||
}
|
||||
|
||||
.large.secondary {
|
||||
|
||||
+2
-2
@@ -1,6 +1,6 @@
|
||||
import Card from "."
|
||||
import Card from "./_Card"
|
||||
|
||||
import type { CheckboxProps } from "./card"
|
||||
import type { CheckboxProps } from "./_Card/card"
|
||||
|
||||
export default function CheckboxCard(props: CheckboxProps) {
|
||||
return <Card {...props} type="checkbox" />
|
||||
+2
-2
@@ -1,6 +1,6 @@
|
||||
import Card from "."
|
||||
import Card from "./_Card"
|
||||
|
||||
import type { RadioProps } from "./card"
|
||||
import type { RadioProps } from "./_Card/card"
|
||||
|
||||
export default function RadioCard(props: RadioProps) {
|
||||
return <Card {...props} type="radio" />
|
||||
+7
-6
@@ -1,14 +1,15 @@
|
||||
import type { IconProps } from "@/types/components/icon"
|
||||
|
||||
interface BaseCardProps extends React.LabelHTMLAttributes<HTMLLabelElement> {
|
||||
interface BaseCardProps
|
||||
extends Omit<React.LabelHTMLAttributes<HTMLLabelElement>, "title"> {
|
||||
Icon?: (props: IconProps) => JSX.Element
|
||||
declined?: boolean
|
||||
highlightSubtitle?: boolean
|
||||
iconHeight?: number
|
||||
iconWidth?: number
|
||||
name?: string
|
||||
saving?: boolean
|
||||
subtitle?: string
|
||||
title: string
|
||||
name: string
|
||||
subtitle?: React.ReactNode
|
||||
title: React.ReactNode
|
||||
type: "checkbox" | "radio"
|
||||
value?: string
|
||||
}
|
||||
@@ -22,7 +23,7 @@ interface ListCardProps extends BaseCardProps {
|
||||
|
||||
interface TextCardProps extends BaseCardProps {
|
||||
list?: never
|
||||
text: string
|
||||
text: React.ReactNode
|
||||
}
|
||||
|
||||
export type CardProps = ListCardProps | TextCardProps
|
||||
+3
-3
@@ -13,10 +13,10 @@ export default function Card({
|
||||
iconHeight = 32,
|
||||
iconWidth = 32,
|
||||
declined = false,
|
||||
highlightSubtitle = false,
|
||||
id,
|
||||
list,
|
||||
name = "join",
|
||||
saving = false,
|
||||
name,
|
||||
subtitle,
|
||||
text,
|
||||
title,
|
||||
@@ -31,7 +31,7 @@ export default function Card({
|
||||
{subtitle ? (
|
||||
<Caption
|
||||
className={styles.subtitle}
|
||||
color={saving ? "baseTextAccent" : "uiTextHighContrast"}
|
||||
color={highlightSubtitle ? "baseTextAccent" : "uiTextHighContrast"}
|
||||
textTransform="bold"
|
||||
>
|
||||
{subtitle}
|
||||
@@ -0,0 +1,36 @@
|
||||
"use client"
|
||||
|
||||
import { useMemo } from "react"
|
||||
|
||||
import useScrollShadows from "@/hooks/useScrollShadows"
|
||||
|
||||
import styles from "./scrollWrapper.module.css"
|
||||
|
||||
import type { ScrollWrapperProps } from "./scrollWrapper"
|
||||
|
||||
export default function ScrollWrapper({
|
||||
className,
|
||||
children,
|
||||
}: ScrollWrapperProps) {
|
||||
const { containerRef, showLeftShadow, showRightShadow } =
|
||||
useScrollShadows<HTMLDivElement>()
|
||||
|
||||
const classNames = useMemo(() => {
|
||||
const cls = [styles.scrollWrapper, className]
|
||||
if (showLeftShadow) {
|
||||
cls.push(styles.leftShadow)
|
||||
}
|
||||
if (showRightShadow) {
|
||||
cls.push(styles.rightShadow)
|
||||
}
|
||||
return cls.join(" ")
|
||||
}, [showLeftShadow, showRightShadow, className])
|
||||
|
||||
return (
|
||||
<div className={classNames}>
|
||||
<div className={styles.content} ref={containerRef}>
|
||||
{children}
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
@@ -0,0 +1,33 @@
|
||||
.scrollWrapper {
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.scrollWrapper::before,
|
||||
.scrollWrapper::after {
|
||||
content: "";
|
||||
position: absolute;
|
||||
top: 0;
|
||||
height: 100%;
|
||||
pointer-events: none;
|
||||
z-index: 1;
|
||||
transition: opacity 0.2s ease;
|
||||
opacity: 0;
|
||||
width: 50px;
|
||||
}
|
||||
|
||||
.scrollWrapper.leftShadow::before {
|
||||
left: 0;
|
||||
background: linear-gradient(to right, rgba(0, 0, 0, 0.3), transparent);
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
.scrollWrapper.rightShadow::after {
|
||||
right: 0;
|
||||
background: linear-gradient(to left, rgba(0, 0, 0, 0.3), transparent);
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
.content {
|
||||
overflow-x: auto;
|
||||
}
|
||||
@@ -0,0 +1,2 @@
|
||||
export interface ScrollWrapperProps
|
||||
extends React.PropsWithChildren<React.HTMLAttributes<HTMLDivElement>> {}
|
||||
@@ -1,4 +0,0 @@
|
||||
.container {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
}
|
||||
@@ -5,18 +5,29 @@ import { useIntl } from "react-intl"
|
||||
import { ChevronDownIcon } from "@/components/Icons"
|
||||
import Button from "@/components/TempDesignSystem/Button"
|
||||
|
||||
import styles from "./button.module.css"
|
||||
import { showMoreButtonVariants } from "./variants"
|
||||
|
||||
import type { ShowMoreButtonParams } from "@/types/components/myPages/stays/button"
|
||||
import styles from "./showMoreButton.module.css"
|
||||
|
||||
import type { ShowMoreButtonProps } from "./showMoreButton"
|
||||
|
||||
export default function ShowMoreButton({
|
||||
className,
|
||||
intent,
|
||||
disabled,
|
||||
showLess,
|
||||
loadMoreData,
|
||||
}: ShowMoreButtonParams) {
|
||||
const { formatMessage } = useIntl()
|
||||
}: ShowMoreButtonProps) {
|
||||
const intl = useIntl()
|
||||
const classNames = showMoreButtonVariants({
|
||||
className,
|
||||
intent,
|
||||
})
|
||||
|
||||
return (
|
||||
<div className={styles.container}>
|
||||
<div className={`${classNames} ${showLess ? styles.showLess : ""}`}>
|
||||
<Button
|
||||
className={styles.button}
|
||||
disabled={disabled}
|
||||
onClick={loadMoreData}
|
||||
variant="icon"
|
||||
@@ -24,8 +35,8 @@ export default function ShowMoreButton({
|
||||
theme="base"
|
||||
intent="text"
|
||||
>
|
||||
<ChevronDownIcon />
|
||||
{formatMessage({ id: "Show more" })}
|
||||
<ChevronDownIcon className={styles.icon} />
|
||||
{intl.formatMessage({ id: showLess ? "Show less" : "Show more" })}
|
||||
</Button>
|
||||
</div>
|
||||
)
|
||||
|
||||
@@ -0,0 +1,23 @@
|
||||
.container {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.table {
|
||||
display: grid;
|
||||
justify-content: stretch;
|
||||
border-top: 1px solid var(--Base-Border-Subtle);
|
||||
background-color: var(--Base-Surface-Primary-light-Normal);
|
||||
}
|
||||
|
||||
.table .button {
|
||||
border-radius: 0;
|
||||
}
|
||||
|
||||
.icon {
|
||||
transition: transform 0.3s;
|
||||
}
|
||||
|
||||
.showLess .icon {
|
||||
transform: rotate(180deg);
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
import { showMoreButtonVariants } from "./variants"
|
||||
|
||||
import type { VariantProps } from "class-variance-authority"
|
||||
|
||||
export interface ShowMoreButtonProps
|
||||
extends React.PropsWithChildren<React.HTMLAttributes<HTMLDivElement>>,
|
||||
VariantProps<typeof showMoreButtonVariants> {
|
||||
disabled?: boolean
|
||||
showLess?: boolean
|
||||
loadMoreData: () => void
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
import { cva } from "class-variance-authority"
|
||||
|
||||
import styles from "./showMoreButton.module.css"
|
||||
|
||||
export const showMoreButtonVariants = cva(styles.container, {
|
||||
variants: {
|
||||
intent: {
|
||||
table: styles.table,
|
||||
},
|
||||
},
|
||||
})
|
||||
@@ -1,7 +1,13 @@
|
||||
import styles from "./table.module.css"
|
||||
|
||||
function TH({ children }: React.PropsWithChildren) {
|
||||
return <th className={styles.th}>{children}</th>
|
||||
import type { THeadProps } from "./table"
|
||||
|
||||
function TH({ children, width = "auto", ...props }: THeadProps) {
|
||||
return (
|
||||
<th className={styles.th} style={{ width }} {...props}>
|
||||
{children}
|
||||
</th>
|
||||
)
|
||||
}
|
||||
|
||||
export default TH
|
||||
|
||||
@@ -3,11 +3,33 @@ import TD from "./TD"
|
||||
import TH from "./TH"
|
||||
import THead from "./THead"
|
||||
import TR from "./TR"
|
||||
import { tableVariants } from "./variants"
|
||||
|
||||
import styles from "./table.module.css"
|
||||
import type { TableProps } from "./table"
|
||||
|
||||
function Table({ children }: React.PropsWithChildren) {
|
||||
return <table className={styles.table}>{children}</table>
|
||||
function Table({
|
||||
className,
|
||||
intent,
|
||||
borderRadius,
|
||||
variant,
|
||||
layout,
|
||||
width = "100%",
|
||||
children,
|
||||
...props
|
||||
}: TableProps) {
|
||||
const classNames = tableVariants({
|
||||
className,
|
||||
borderRadius,
|
||||
intent,
|
||||
layout,
|
||||
variant,
|
||||
})
|
||||
|
||||
return (
|
||||
<table className={classNames} style={{ width }} {...props}>
|
||||
{children}
|
||||
</table>
|
||||
)
|
||||
}
|
||||
|
||||
Table.THead = THead
|
||||
|
||||
@@ -1,20 +1,20 @@
|
||||
.table {
|
||||
border-radius: var(--Corner-radius-Medium);
|
||||
border-collapse: collapse;
|
||||
overflow: hidden;
|
||||
width: 100%;
|
||||
min-width: 100%;
|
||||
}
|
||||
|
||||
.thead {
|
||||
background-color: var(--Base-Background-Secondary-Normal, #f7e1d5);
|
||||
color: var(--Base-Text-High-contrast);
|
||||
background-color: var(--Base-Surface-Primary-dark-Normal);
|
||||
}
|
||||
|
||||
.tbody {
|
||||
background-color: var(--Base-Surface-Primary-light-Normal, #fff);
|
||||
background-color: var(--Base-Surface-Primary-light-Normal);
|
||||
}
|
||||
|
||||
.tr:not(:last-of-type) {
|
||||
border-bottom: 1px solid var(--Primary-Light-On-Surface-Divider, #f0c1b6);
|
||||
border-bottom: 1px solid var(--Base-Border-Subtle);
|
||||
}
|
||||
|
||||
.th {
|
||||
@@ -28,6 +28,35 @@
|
||||
padding: var(--Spacing-x2);
|
||||
}
|
||||
|
||||
.fixed {
|
||||
table-layout: fixed;
|
||||
}
|
||||
|
||||
.smallRadius {
|
||||
border-radius: var(--Corner-radius-Small);
|
||||
}
|
||||
.mediumRadius {
|
||||
border-radius: var(--Corner-radius-Medium);
|
||||
}
|
||||
.largeRadius {
|
||||
border-radius: var(--Corner-radius-Large);
|
||||
}
|
||||
|
||||
.content .thead {
|
||||
background-color: var(--Base-Surface-Subtle-Hover);
|
||||
}
|
||||
|
||||
.content .tbody {
|
||||
background-color: var(--Base-Background-Primary-Normal);
|
||||
}
|
||||
|
||||
.content.striped .tbody .tr:nth-child(odd) {
|
||||
background-color: var(--Base-Surface-Subtle-Normal);
|
||||
}
|
||||
.content.striped .tbody .tr:nth-child(even) {
|
||||
background-color: var(--Base-Background-Primary-Normal);
|
||||
}
|
||||
|
||||
@media screen and (min-width: 768px) {
|
||||
.th {
|
||||
padding: var(--Spacing-x2) var(--Spacing-x3);
|
||||
|
||||
@@ -0,0 +1,14 @@
|
||||
import { tableVariants } from "./variants"
|
||||
|
||||
import type { VariantProps } from "class-variance-authority"
|
||||
|
||||
export interface TableProps
|
||||
extends React.PropsWithChildren<React.HTMLAttributes<HTMLTableElement>>,
|
||||
VariantProps<typeof tableVariants> {
|
||||
width?: string
|
||||
}
|
||||
|
||||
export interface THeadProps
|
||||
extends React.PropsWithChildren<React.HTMLAttributes<HTMLTableCellElement>> {
|
||||
width?: string
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
import { cva } from "class-variance-authority"
|
||||
|
||||
import styles from "./table.module.css"
|
||||
|
||||
export const tableVariants = cva(styles.table, {
|
||||
variants: {
|
||||
intent: {
|
||||
light: styles.light,
|
||||
striped: styles.striped,
|
||||
},
|
||||
variant: {
|
||||
content: styles.content,
|
||||
},
|
||||
borderRadius: {
|
||||
none: "",
|
||||
small: styles.smallRadius,
|
||||
medium: styles.mediumRadius,
|
||||
large: styles.largeRadius,
|
||||
},
|
||||
layout: {
|
||||
fixed: styles.fixed,
|
||||
},
|
||||
},
|
||||
defaultVariants: {
|
||||
borderRadius: "medium",
|
||||
},
|
||||
})
|
||||
@@ -10,7 +10,7 @@
|
||||
0.3vw + 15px,
|
||||
var(--typography-Subtitle-1-Desktop-fontSize)
|
||||
);
|
||||
font-weight: 600;
|
||||
font-weight: 500;
|
||||
letter-spacing: var(--typography-Subtitle-1-letterSpacing);
|
||||
line-height: var(--typography-Subtitle-1-lineHeight);
|
||||
}
|
||||
@@ -22,7 +22,7 @@
|
||||
0.3vw + 15px,
|
||||
var(--typography-Subtitle-2-Desktop-fontSize)
|
||||
);
|
||||
font-weight: 600;
|
||||
font-weight: 500;
|
||||
letter-spacing: var(--typography-Subtitle-2-letterSpacing);
|
||||
line-height: var(--typography-Subtitle-2-lineHeight);
|
||||
}
|
||||
@@ -62,3 +62,7 @@
|
||||
.uiTextHighContrast {
|
||||
color: var(--UI-Text-High-contrast);
|
||||
}
|
||||
|
||||
.uiTextMediumContrast {
|
||||
color: var(--UI-Text-Medium-contrast);
|
||||
}
|
||||
|
||||
@@ -9,6 +9,7 @@ const config = {
|
||||
burgundy: styles.burgundy,
|
||||
pale: styles.pale,
|
||||
uiTextHighContrast: styles.uiTextHighContrast,
|
||||
uiTextMediumContrast: styles.uiTextMediumContrast,
|
||||
},
|
||||
textAlign: {
|
||||
center: styles.center,
|
||||
|
||||
Reference in New Issue
Block a user