Fix/SW-1563 accessibility
* fix(SW-1563): Added new IconButton component to the design system and removed Icon variant inside the Button component * fix(SW-1563): Added buttons around clickable images and changed to design system components * fix(SW-1563): Renamed variants to match Figma * fix(SW-1563): Renamed AriaButton to ButtonRAC Approved-by: Michael Zetterberg Approved-by: Matilda Landström
This commit is contained in:
@@ -1,7 +1,7 @@
|
|||||||
"use client"
|
"use client"
|
||||||
|
|
||||||
import { useCallback, useEffect, useRef } from "react"
|
import { useCallback, useEffect, useRef } from "react"
|
||||||
import { Button as AriaButton } from "react-aria-components"
|
import { Button as ButtonRAC } from "react-aria-components"
|
||||||
import { useIntl } from "react-intl"
|
import { useIntl } from "react-intl"
|
||||||
|
|
||||||
import HotelLogoIcon from "@scandic-hotels/design-system/Icons/HotelLogoIcon"
|
import HotelLogoIcon from "@scandic-hotels/design-system/Icons/HotelLogoIcon"
|
||||||
@@ -94,12 +94,12 @@ export default function HotelListItem(data: DestinationPagesHotelData) {
|
|||||||
<Typography variant="Body/Supporting text (caption)/smRegular">
|
<Typography variant="Body/Supporting text (caption)/smRegular">
|
||||||
<div className={styles.captions}>
|
<div className={styles.captions}>
|
||||||
<Typography variant="Link/sm">
|
<Typography variant="Link/sm">
|
||||||
<AriaButton
|
<ButtonRAC
|
||||||
className={styles.addressButton}
|
className={styles.addressButton}
|
||||||
onPress={() => setActiveMarker(hotel.id)}
|
onPress={() => setActiveMarker(hotel.id)}
|
||||||
>
|
>
|
||||||
{address}
|
{address}
|
||||||
</AriaButton>
|
</ButtonRAC>
|
||||||
</Typography>
|
</Typography>
|
||||||
<Divider variant="vertical" color="beige" />
|
<Divider variant="vertical" color="beige" />
|
||||||
<p>
|
<p>
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
import { useState } from "react"
|
import { useState } from "react"
|
||||||
import { useIntl } from "react-intl"
|
import { useIntl } from "react-intl"
|
||||||
|
|
||||||
import { Button } from "@scandic-hotels/design-system/Button"
|
import { IconButton } from "@scandic-hotels/design-system/IconButton"
|
||||||
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 { Typography } from "@scandic-hotels/design-system/Typography"
|
||||||
|
|
||||||
@@ -48,9 +48,9 @@ export default function HotelMapCard({
|
|||||||
return (
|
return (
|
||||||
<article className={className}>
|
<article className={className}>
|
||||||
<div className={styles.wrapper}>
|
<div className={styles.wrapper}>
|
||||||
<Button
|
<IconButton
|
||||||
size="Medium"
|
theme="Black"
|
||||||
variant="Icon"
|
style="Muted"
|
||||||
className={styles.closeButton}
|
className={styles.closeButton}
|
||||||
onPress={handleClose}
|
onPress={handleClose}
|
||||||
aria-label={intl.formatMessage({
|
aria-label={intl.formatMessage({
|
||||||
@@ -63,7 +63,7 @@ export default function HotelMapCard({
|
|||||||
className={styles.closeIcon}
|
className={styles.closeIcon}
|
||||||
color="CurrentColor"
|
color="CurrentColor"
|
||||||
/>
|
/>
|
||||||
</Button>
|
</IconButton>
|
||||||
{image ? (
|
{image ? (
|
||||||
<DialogImage
|
<DialogImage
|
||||||
image={image.src}
|
image={image.src}
|
||||||
|
|||||||
@@ -3,7 +3,7 @@
|
|||||||
import { useMap } from "@vis.gl/react-google-maps"
|
import { useMap } from "@vis.gl/react-google-maps"
|
||||||
import { cx } from "class-variance-authority"
|
import { cx } from "class-variance-authority"
|
||||||
import { useState } from "react"
|
import { useState } from "react"
|
||||||
import { Button as AriaButton } from "react-aria-components"
|
import { Button as ButtonRAC } from "react-aria-components"
|
||||||
import { useIntl } from "react-intl"
|
import { useIntl } from "react-intl"
|
||||||
|
|
||||||
import { Typography } from "@scandic-hotels/design-system/Typography"
|
import { Typography } from "@scandic-hotels/design-system/Typography"
|
||||||
@@ -136,12 +136,12 @@ export default function Sidebar({
|
|||||||
}`}
|
}`}
|
||||||
>
|
>
|
||||||
<Typography variant="Body/Paragraph/mdBold">
|
<Typography variant="Body/Paragraph/mdBold">
|
||||||
<AriaButton
|
<ButtonRAC
|
||||||
className={styles.sidebarToggle}
|
className={styles.sidebarToggle}
|
||||||
onPress={toggleFullScreenSidebar}
|
onPress={toggleFullScreenSidebar}
|
||||||
>
|
>
|
||||||
{isFullScreenSidebar ? viewAsMapMsg : viewAsListMsg}
|
{isFullScreenSidebar ? viewAsMapMsg : viewAsListMsg}
|
||||||
</AriaButton>
|
</ButtonRAC>
|
||||||
</Typography>
|
</Typography>
|
||||||
<div className={styles.sidebarContent}>
|
<div className={styles.sidebarContent}>
|
||||||
<Typography variant="Title/sm">
|
<Typography variant="Title/sm">
|
||||||
@@ -168,7 +168,7 @@ export default function Sidebar({
|
|||||||
{pois.map((poi) => (
|
{pois.map((poi) => (
|
||||||
<li key={poi.name} className={styles.poiItem}>
|
<li key={poi.name} className={styles.poiItem}>
|
||||||
<Typography variant="Body/Paragraph/mdRegular">
|
<Typography variant="Body/Paragraph/mdRegular">
|
||||||
<AriaButton
|
<ButtonRAC
|
||||||
className={cx(styles.poiButton, {
|
className={cx(styles.poiButton, {
|
||||||
[styles.active]: activePoi === poi.name,
|
[styles.active]: activePoi === poi.name,
|
||||||
})}
|
})}
|
||||||
@@ -188,7 +188,7 @@ export default function Sidebar({
|
|||||||
}
|
}
|
||||||
)}
|
)}
|
||||||
</span>
|
</span>
|
||||||
</AriaButton>
|
</ButtonRAC>
|
||||||
</Typography>
|
</Typography>
|
||||||
</li>
|
</li>
|
||||||
))}
|
))}
|
||||||
|
|||||||
@@ -1,11 +1,13 @@
|
|||||||
"use client"
|
"use client"
|
||||||
|
|
||||||
import { useState } from "react"
|
import { useState } from "react"
|
||||||
|
import { Button as ButtonRAC } from "react-aria-components"
|
||||||
import { useIntl } from "react-intl"
|
import { useIntl } from "react-intl"
|
||||||
|
|
||||||
|
import { Button } from "@scandic-hotels/design-system/Button"
|
||||||
|
|
||||||
import Image from "@/components/Image"
|
import Image from "@/components/Image"
|
||||||
import Lightbox from "@/components/Lightbox/"
|
import Lightbox from "@/components/Lightbox/"
|
||||||
import Button from "@/components/TempDesignSystem/Button"
|
|
||||||
import { mapApiImagesToGalleryImages } from "@/utils/imageGallery"
|
import { mapApiImagesToGalleryImages } from "@/utils/imageGallery"
|
||||||
|
|
||||||
import styles from "./previewImages.module.css"
|
import styles from "./previewImages.module.css"
|
||||||
@@ -17,31 +19,52 @@ export default function PreviewImages({
|
|||||||
hotelName,
|
hotelName,
|
||||||
}: PreviewImagesProps) {
|
}: PreviewImagesProps) {
|
||||||
const intl = useIntl()
|
const intl = useIntl()
|
||||||
const [lightboxIsOpen, setLightboxIsOpen] = useState(false)
|
const [lightboxState, setLightboxState] = useState({
|
||||||
|
activeIndex: 0,
|
||||||
|
isOpen: false,
|
||||||
|
})
|
||||||
|
|
||||||
const lightboxImages = mapApiImagesToGalleryImages(images)
|
const lightboxImages = mapApiImagesToGalleryImages(images)
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className={styles.imageWrapper}>
|
<div className={styles.imageWrapper}>
|
||||||
{images.slice(0, 3).map((image, index) => (
|
{lightboxImages.slice(0, 3).map((image, index) => (
|
||||||
<Image
|
<ButtonRAC
|
||||||
key={index}
|
key={image.src}
|
||||||
src={image.imageSizes.medium}
|
className={styles.imageButton}
|
||||||
alt={image.metaData.altText}
|
aria-label={intl.formatMessage({
|
||||||
title={image.metaData.title}
|
defaultMessage: "See all photos",
|
||||||
width={index === 0 ? 752 : 292}
|
})}
|
||||||
height={index === 0 ? 540 : 266}
|
onPress={() =>
|
||||||
onClick={() => setLightboxIsOpen(true)}
|
setLightboxState({
|
||||||
className={styles.image}
|
activeIndex: index,
|
||||||
/>
|
isOpen: true,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
>
|
||||||
|
<Image
|
||||||
|
src={image.src}
|
||||||
|
alt={image.alt}
|
||||||
|
title={image.caption || ""}
|
||||||
|
width={index === 0 ? 752 : 292}
|
||||||
|
height={index === 0 ? 540 : 266}
|
||||||
|
className={styles.image}
|
||||||
|
/>
|
||||||
|
</ButtonRAC>
|
||||||
))}
|
))}
|
||||||
{images.length > 1 && (
|
{images.length > 1 && (
|
||||||
<>
|
<>
|
||||||
<Button
|
<Button
|
||||||
theme="base"
|
variant="Primary"
|
||||||
intent="inverted"
|
color="Inverted"
|
||||||
size="small"
|
size="Small"
|
||||||
onClick={() => setLightboxIsOpen(true)}
|
onPress={() =>
|
||||||
|
setLightboxState({
|
||||||
|
activeIndex: 0,
|
||||||
|
isOpen: true,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
typography="Body/Supporting text (caption)/smBold"
|
||||||
className={styles.seeAllButton}
|
className={styles.seeAllButton}
|
||||||
>
|
>
|
||||||
{intl.formatMessage({
|
{intl.formatMessage({
|
||||||
@@ -56,8 +79,9 @@ export default function PreviewImages({
|
|||||||
},
|
},
|
||||||
{ title: hotelName }
|
{ title: hotelName }
|
||||||
)}
|
)}
|
||||||
isOpen={lightboxIsOpen}
|
isOpen={lightboxState.isOpen}
|
||||||
onClose={() => setLightboxIsOpen(false)}
|
activeIndex={lightboxState.activeIndex}
|
||||||
|
onClose={() => setLightboxState({ activeIndex: 0, isOpen: false })}
|
||||||
/>
|
/>
|
||||||
</>
|
</>
|
||||||
)}
|
)}
|
||||||
|
|||||||
@@ -3,18 +3,25 @@
|
|||||||
gap: 8px;
|
gap: 8px;
|
||||||
position: relative;
|
position: relative;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
padding: 0 var(--Spacing-x2);
|
|
||||||
z-index: 0;
|
z-index: 0;
|
||||||
|
max-width: var(--max-width-page);
|
||||||
|
margin: 0 auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
.imageButton {
|
||||||
|
padding: 0;
|
||||||
|
border-width: 0;
|
||||||
|
background-color: transparent;
|
||||||
|
cursor: pointer;
|
||||||
|
border-radius: var(--Corner-radius-Small);
|
||||||
|
overflow: hidden;
|
||||||
}
|
}
|
||||||
|
|
||||||
.image {
|
.image {
|
||||||
object-fit: cover;
|
object-fit: cover;
|
||||||
border-radius: var(--Corner-radius-Small);
|
|
||||||
width: 100%;
|
width: 100%;
|
||||||
height: 100%;
|
height: 100%;
|
||||||
max-height: 30vh;
|
max-height: 30vh;
|
||||||
cursor: pointer;
|
|
||||||
max-width: var(--max-width-page);
|
|
||||||
margin: 0 auto;
|
margin: 0 auto;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ import { Fragment } from "react"
|
|||||||
import { useIntl } from "react-intl"
|
import { useIntl } from "react-intl"
|
||||||
|
|
||||||
import { Button } from "@scandic-hotels/design-system/Button"
|
import { Button } from "@scandic-hotels/design-system/Button"
|
||||||
|
import { IconButton } from "@scandic-hotels/design-system/IconButton"
|
||||||
import DiscountIcon from "@scandic-hotels/design-system/Icons/DiscountIcon"
|
import DiscountIcon from "@scandic-hotels/design-system/Icons/DiscountIcon"
|
||||||
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 { Typography } from "@scandic-hotels/design-system/Typography"
|
||||||
@@ -115,17 +116,18 @@ export default function SummaryUI({
|
|||||||
{/* eslint-disable-next-line formatjs/no-literal-string-in-jsx */}
|
{/* eslint-disable-next-line formatjs/no-literal-string-in-jsx */}
|
||||||
{dt(booking.toDate).locale(lang).format("ddd, D MMM")} ({nightsMsg})
|
{dt(booking.toDate).locale(lang).format("ddd, D MMM")} ({nightsMsg})
|
||||||
</Body>
|
</Body>
|
||||||
<Button
|
<IconButton
|
||||||
onPress={handleToggleSummary}
|
onPress={handleToggleSummary}
|
||||||
className={styles.chevronButton}
|
className={styles.chevronButton}
|
||||||
variant="Icon"
|
theme="Black"
|
||||||
|
style="Muted"
|
||||||
>
|
>
|
||||||
<MaterialIcon
|
<MaterialIcon
|
||||||
icon="keyboard_arrow_down"
|
icon="keyboard_arrow_down"
|
||||||
size={20}
|
size={20}
|
||||||
color="CurrentColor"
|
color="CurrentColor"
|
||||||
/>
|
/>
|
||||||
</Button>
|
</IconButton>
|
||||||
</header>
|
</header>
|
||||||
<Divider color="primaryLightSubtle" />
|
<Divider color="primaryLightSubtle" />
|
||||||
{rooms.map(({ room }, idx) => {
|
{rooms.map(({ room }, idx) => {
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ import { Fragment } from "react"
|
|||||||
import { useIntl } from "react-intl"
|
import { useIntl } from "react-intl"
|
||||||
|
|
||||||
import { Button } from "@scandic-hotels/design-system/Button"
|
import { Button } from "@scandic-hotels/design-system/Button"
|
||||||
|
import { IconButton } from "@scandic-hotels/design-system/IconButton"
|
||||||
import { MaterialIcon } from "@scandic-hotels/design-system/Icons/MaterialIcon"
|
import { MaterialIcon } from "@scandic-hotels/design-system/Icons/MaterialIcon"
|
||||||
|
|
||||||
import { dt } from "@/lib/dt"
|
import { dt } from "@/lib/dt"
|
||||||
@@ -88,13 +89,13 @@ export default function Summary({
|
|||||||
{/* eslint-disable-next-line formatjs/no-literal-string-in-jsx */}
|
{/* eslint-disable-next-line formatjs/no-literal-string-in-jsx */}
|
||||||
{dt(booking.toDate).locale(lang).format("ddd, D MMM")} ({nights})
|
{dt(booking.toDate).locale(lang).format("ddd, D MMM")} ({nights})
|
||||||
</Body>
|
</Body>
|
||||||
<Button onPress={toggleSummaryOpen} variant="Icon">
|
<IconButton onPress={toggleSummaryOpen} theme="Black" style="Muted">
|
||||||
<MaterialIcon
|
<MaterialIcon
|
||||||
icon="keyboard_arrow_down"
|
icon="keyboard_arrow_down"
|
||||||
size={20}
|
size={20}
|
||||||
color="CurrentColor"
|
color="CurrentColor"
|
||||||
/>
|
/>
|
||||||
</Button>
|
</IconButton>
|
||||||
</header>
|
</header>
|
||||||
<Divider color="primaryLightSubtle" />
|
<Divider color="primaryLightSubtle" />
|
||||||
{rooms.map((room, idx) => {
|
{rooms.map((room, idx) => {
|
||||||
|
|||||||
@@ -7,8 +7,8 @@ import {
|
|||||||
} from "react-aria-components"
|
} from "react-aria-components"
|
||||||
import { useIntl } from "react-intl"
|
import { useIntl } from "react-intl"
|
||||||
|
|
||||||
import { Button } from "@scandic-hotels/design-system/Button"
|
|
||||||
import { ChipButton } from "@scandic-hotels/design-system/ChipButton"
|
import { ChipButton } from "@scandic-hotels/design-system/ChipButton"
|
||||||
|
import { IconButton } from "@scandic-hotels/design-system/IconButton"
|
||||||
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 { Typography } from "@scandic-hotels/design-system/Typography"
|
||||||
|
|
||||||
@@ -39,9 +39,13 @@ export default function RoomPackageFilterModal() {
|
|||||||
{intl.formatMessage({ defaultMessage: "Special needs" })}
|
{intl.formatMessage({ defaultMessage: "Special needs" })}
|
||||||
</h3>
|
</h3>
|
||||||
</Typography>
|
</Typography>
|
||||||
<Button variant="Icon" onPress={() => setIsOpen(false)}>
|
<IconButton
|
||||||
|
theme="Black"
|
||||||
|
style="Muted"
|
||||||
|
onPress={() => setIsOpen(false)}
|
||||||
|
>
|
||||||
<MaterialIcon icon="close" size={24} color="CurrentColor" />
|
<MaterialIcon icon="close" size={24} color="CurrentColor" />
|
||||||
</Button>
|
</IconButton>
|
||||||
</div>
|
</div>
|
||||||
<Form close={() => setIsOpen(false)} />
|
<Form close={() => setIsOpen(false)} />
|
||||||
</Dialog>
|
</Dialog>
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
"use client"
|
"use client"
|
||||||
import { Button as AriaButton } from "react-aria-components"
|
import { Button as ButtonRAC } from "react-aria-components"
|
||||||
import { useMediaQuery } from "usehooks-ts"
|
import { useMediaQuery } from "usehooks-ts"
|
||||||
|
|
||||||
import { MaterialIcon } from "@scandic-hotels/design-system/Icons/MaterialIcon"
|
import { MaterialIcon } from "@scandic-hotels/design-system/Icons/MaterialIcon"
|
||||||
@@ -69,12 +69,12 @@ export default function RoomPackageFilter() {
|
|||||||
color="CurrentColor"
|
color="CurrentColor"
|
||||||
/>
|
/>
|
||||||
{pkg.description}
|
{pkg.description}
|
||||||
<AriaButton
|
<ButtonRAC
|
||||||
onPress={() => deleteSelectedPackage(pkg.code)}
|
onPress={() => deleteSelectedPackage(pkg.code)}
|
||||||
className={styles.removeButton}
|
className={styles.removeButton}
|
||||||
>
|
>
|
||||||
<MaterialIcon icon="close" size={16} color="CurrentColor" />
|
<MaterialIcon icon="close" size={16} color="CurrentColor" />
|
||||||
</AriaButton>
|
</ButtonRAC>
|
||||||
</span>
|
</span>
|
||||||
</Typography>
|
</Typography>
|
||||||
))}
|
))}
|
||||||
|
|||||||
@@ -0,0 +1,126 @@
|
|||||||
|
.fullViewContainer {
|
||||||
|
background-color: var(--UI-Text-High-contrast);
|
||||||
|
height: 100%;
|
||||||
|
padding: var(--Spacing-x3) var(--Spacing-x2);
|
||||||
|
position: relative;
|
||||||
|
align-items: center;
|
||||||
|
justify-items: center;
|
||||||
|
display: grid;
|
||||||
|
grid-template-rows: auto 1fr auto;
|
||||||
|
grid-template-columns: 1fr;
|
||||||
|
place-content: center;
|
||||||
|
gap: var(--Spacing-x5);
|
||||||
|
}
|
||||||
|
|
||||||
|
.closeButton {
|
||||||
|
position: absolute;
|
||||||
|
top: var(--Space-x2);
|
||||||
|
right: var(--Space-x2);
|
||||||
|
z-index: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
.header {
|
||||||
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.imageCount {
|
||||||
|
background-color: var(--Overlay-90);
|
||||||
|
padding: var(--Space-x025) var(--Space-x05);
|
||||||
|
border-radius: var(--Corner-radius-Small);
|
||||||
|
color: var(--Text-Inverted);
|
||||||
|
}
|
||||||
|
|
||||||
|
.imageContainer {
|
||||||
|
position: relative;
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
max-height: 25rem;
|
||||||
|
margin-bottom: var(--Spacing-x5);
|
||||||
|
}
|
||||||
|
|
||||||
|
.imageWrapper {
|
||||||
|
position: absolute;
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.image {
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
object-fit: cover;
|
||||||
|
}
|
||||||
|
|
||||||
|
.footer {
|
||||||
|
color: var(--Text-Inverted);
|
||||||
|
position: absolute;
|
||||||
|
bottom: calc(-1 * var(--Spacing-x5));
|
||||||
|
}
|
||||||
|
|
||||||
|
@media screen and (max-width: 767px) {
|
||||||
|
.navigationButton {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@media screen and (min-width: 768px) and (max-width: 1366px) {
|
||||||
|
.fullViewContainer {
|
||||||
|
padding: var(--Spacing-x5);
|
||||||
|
}
|
||||||
|
|
||||||
|
.imageContainer {
|
||||||
|
height: 100%;
|
||||||
|
max-height: 560px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@media screen and (min-width: 768px) {
|
||||||
|
.closeButton {
|
||||||
|
position: fixed;
|
||||||
|
top: var(--Spacing-x-one-and-half);
|
||||||
|
right: var(--Spacing-x-half);
|
||||||
|
}
|
||||||
|
|
||||||
|
.fullViewContainer {
|
||||||
|
margin-top: 0;
|
||||||
|
padding: var(--Spacing-x5);
|
||||||
|
grid-template-rows: auto 1fr auto;
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.imageContainer {
|
||||||
|
width: 70%;
|
||||||
|
max-width: 1454px;
|
||||||
|
max-height: 700px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.navigationButton {
|
||||||
|
position: absolute;
|
||||||
|
top: 50%;
|
||||||
|
transform: translateY(-50%);
|
||||||
|
background-color: var(--Component-Button-Inverted-Fill-Default);
|
||||||
|
color: var(--Component-Button-Inverted-On-fill-Default);
|
||||||
|
border-radius: var(--Corner-radius-rounded);
|
||||||
|
padding: 10px;
|
||||||
|
cursor: pointer;
|
||||||
|
border-width: 0;
|
||||||
|
display: flex;
|
||||||
|
z-index: 1;
|
||||||
|
box-shadow: 0px 0px 8px 1px #0000001a;
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
background-color: var(--Component-Button-Inverted-Fill-Hover);
|
||||||
|
color: var(--Component-Button-Inverted-On-fill-Hover);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.fullViewNextButton {
|
||||||
|
right: var(--Spacing-x5);
|
||||||
|
}
|
||||||
|
|
||||||
|
.fullViewPrevButton {
|
||||||
|
left: var(--Spacing-x5);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -2,15 +2,15 @@
|
|||||||
|
|
||||||
import { AnimatePresence, motion } from "framer-motion"
|
import { AnimatePresence, motion } from "framer-motion"
|
||||||
import { useState } from "react"
|
import { useState } from "react"
|
||||||
|
import { useIntl } from "react-intl"
|
||||||
|
|
||||||
|
import { IconButton } from "@scandic-hotels/design-system/IconButton"
|
||||||
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 Image from "@/components/Image"
|
import Image from "@/components/Image"
|
||||||
import Button from "@/components/TempDesignSystem/Button"
|
|
||||||
import Body from "@/components/TempDesignSystem/Text/Body"
|
|
||||||
import Caption from "@/components/TempDesignSystem/Text/Caption"
|
|
||||||
|
|
||||||
import styles from "./Lightbox.module.css"
|
import styles from "./fullView.module.css"
|
||||||
|
|
||||||
import type { FullViewProps } from "@/types/components/lightbox/lightbox"
|
import type { FullViewProps } from "@/types/components/lightbox/lightbox"
|
||||||
|
|
||||||
@@ -23,6 +23,7 @@ export default function FullView({
|
|||||||
totalImages,
|
totalImages,
|
||||||
hideLabel,
|
hideLabel,
|
||||||
}: FullViewProps) {
|
}: FullViewProps) {
|
||||||
|
const intl = useIntl()
|
||||||
const [animateLeft, setAnimateLeft] = useState(true)
|
const [animateLeft, setAnimateLeft] = useState(true)
|
||||||
|
|
||||||
function handleSwipe(offset: number) {
|
function handleSwipe(offset: number) {
|
||||||
@@ -54,29 +55,26 @@ export default function FullView({
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<div className={styles.fullViewContainer}>
|
<div className={styles.fullViewContainer}>
|
||||||
<Button
|
<IconButton
|
||||||
intent="text"
|
theme="Inverted"
|
||||||
size="small"
|
style="Muted"
|
||||||
variant="icon"
|
className={styles.closeButton}
|
||||||
className={styles.fullViewCloseButton}
|
onPress={onClose}
|
||||||
onClick={onClose}
|
aria-label={intl.formatMessage({
|
||||||
|
defaultMessage: "Close",
|
||||||
|
})}
|
||||||
>
|
>
|
||||||
<MaterialIcon
|
<MaterialIcon icon="close" color="CurrentColor" size={24} />
|
||||||
icon="close"
|
</IconButton>
|
||||||
size={32}
|
<div className={styles.header}>
|
||||||
className={styles.fullViewCloseIcon}
|
<Typography variant="Tag/sm">
|
||||||
color="Icon/Inverted"
|
<span className={styles.imageCount}>
|
||||||
/>
|
|
||||||
</Button>
|
|
||||||
<div className={styles.fullViewHeader}>
|
|
||||||
<span className={styles.imagePosition}>
|
|
||||||
<Caption color="white">
|
|
||||||
{/* eslint-disable-next-line formatjs/no-literal-string-in-jsx */}
|
{/* eslint-disable-next-line formatjs/no-literal-string-in-jsx */}
|
||||||
{`${currentIndex + 1} / ${totalImages}`}
|
{`${currentIndex + 1} / ${totalImages}`}
|
||||||
</Caption>
|
</span>
|
||||||
</span>
|
</Typography>
|
||||||
</div>
|
</div>
|
||||||
<div className={styles.fullViewImageContainer}>
|
<div className={styles.imageContainer}>
|
||||||
<AnimatePresence initial={false} custom={animateLeft}>
|
<AnimatePresence initial={false} custom={animateLeft}>
|
||||||
<motion.div
|
<motion.div
|
||||||
key={image.src}
|
key={image.src}
|
||||||
@@ -86,7 +84,7 @@ export default function FullView({
|
|||||||
animate="animate"
|
animate="animate"
|
||||||
exit="exit"
|
exit="exit"
|
||||||
transition={{ duration: 0.3 }}
|
transition={{ duration: 0.3 }}
|
||||||
className={styles.fullViewImage}
|
className={styles.imageWrapper}
|
||||||
drag="x"
|
drag="x"
|
||||||
onDragEnd={(_e, info) => handleSwipe(info.offset.x)}
|
onDragEnd={(_e, info) => handleSwipe(info.offset.x)}
|
||||||
>
|
>
|
||||||
@@ -95,14 +93,14 @@ export default function FullView({
|
|||||||
fill
|
fill
|
||||||
sizes="(min-width: 1500px) 1500px, 100vw"
|
sizes="(min-width: 1500px) 1500px, 100vw"
|
||||||
src={image.src}
|
src={image.src}
|
||||||
style={{ objectFit: "cover" }}
|
className={styles.image}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<div className={styles.fullViewFooter}>
|
{image.caption && !hideLabel ? (
|
||||||
{image.caption && !hideLabel && (
|
<Typography variant="Body/Paragraph/mdRegular">
|
||||||
<Body color="white">{image.caption}</Body>
|
<p className={styles.footer}>{image.caption}</p>
|
||||||
)}
|
</Typography>
|
||||||
</div>
|
) : null}
|
||||||
</motion.div>
|
</motion.div>
|
||||||
</AnimatePresence>
|
</AnimatePresence>
|
||||||
</div>
|
</div>
|
||||||
@@ -112,8 +110,8 @@ export default function FullView({
|
|||||||
onClick={handlePrev}
|
onClick={handlePrev}
|
||||||
>
|
>
|
||||||
<MaterialIcon
|
<MaterialIcon
|
||||||
icon="arrow_forward"
|
icon="arrow_back"
|
||||||
color="Icon/Interactive/Default"
|
color="CurrentColor"
|
||||||
className={styles.leftTransformIcon}
|
className={styles.leftTransformIcon}
|
||||||
/>
|
/>
|
||||||
</motion.button>
|
</motion.button>
|
||||||
@@ -121,7 +119,7 @@ export default function FullView({
|
|||||||
className={`${styles.navigationButton} ${styles.fullViewNextButton}`}
|
className={`${styles.navigationButton} ${styles.fullViewNextButton}`}
|
||||||
onClick={handleNext}
|
onClick={handleNext}
|
||||||
>
|
>
|
||||||
<MaterialIcon icon="arrow_forward" color="Icon/Interactive/Default" />
|
<MaterialIcon icon="arrow_forward" color="CurrentColor" />
|
||||||
</motion.button>
|
</motion.button>
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
160
apps/scandic-web/components/Lightbox/Gallery/gallery.module.css
Normal file
160
apps/scandic-web/components/Lightbox/Gallery/gallery.module.css
Normal file
@@ -0,0 +1,160 @@
|
|||||||
|
.galleryContainer {
|
||||||
|
display: grid;
|
||||||
|
gap: var(--Space-x2);
|
||||||
|
padding: var(--Space-x2);
|
||||||
|
height: 100%;
|
||||||
|
overflow-y: auto;
|
||||||
|
background-color: var(--Base-Background-Primary-Normal);
|
||||||
|
}
|
||||||
|
|
||||||
|
.mobileGallery {
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: 1fr 1fr;
|
||||||
|
gap: var(--Space-x1);
|
||||||
|
padding-bottom: var(--Space-x3);
|
||||||
|
}
|
||||||
|
|
||||||
|
.thumbnailContainer {
|
||||||
|
position: relative;
|
||||||
|
height: 242px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.fullWidthImage {
|
||||||
|
grid-column: 1 / -1;
|
||||||
|
height: 240px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.imageButton {
|
||||||
|
position: relative;
|
||||||
|
height: 100%;
|
||||||
|
width: 100%;
|
||||||
|
padding: 0;
|
||||||
|
border-width: 0;
|
||||||
|
background-color: transparent;
|
||||||
|
cursor: pointer;
|
||||||
|
border-radius: var(--Corner-radius-Medium);
|
||||||
|
overflow: hidden;
|
||||||
|
z-index: 0;
|
||||||
|
|
||||||
|
&:focus-visible {
|
||||||
|
outline-offset: -2px; /* Adjust the outline offset as wrappers uses overflow-hidden */
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.image {
|
||||||
|
transition: opacity 0.3s ease-in-out;
|
||||||
|
object-fit: cover;
|
||||||
|
z-index: -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
@media screen and (max-width: 767px) {
|
||||||
|
.desktopCloseIcon,
|
||||||
|
.desktopGallery {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.closeButton {
|
||||||
|
justify-self: start;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@media screen and (min-width: 768px) {
|
||||||
|
.mobileGallery,
|
||||||
|
.mobileCloseIcon {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.galleryContainer {
|
||||||
|
padding: var(--Spacing-x5) var(--Spacing-x6);
|
||||||
|
}
|
||||||
|
|
||||||
|
.closeButton {
|
||||||
|
position: absolute;
|
||||||
|
top: var(--Space-x2);
|
||||||
|
right: var(--Space-x2);
|
||||||
|
z-index: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
.desktopGallery {
|
||||||
|
display: grid;
|
||||||
|
grid-template-rows: 28px 1fr 7.8125rem;
|
||||||
|
row-gap: var(--Spacing-x-one-and-half);
|
||||||
|
background-color: var(--Base-Background-Primary-Normal);
|
||||||
|
height: 100%;
|
||||||
|
position: relative;
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
|
||||||
|
.galleryHeader {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.imageCaption {
|
||||||
|
background-color: var(--Base-Surface-Subtle-Normal);
|
||||||
|
padding: var(--Spacing-x-half) var(--Spacing-x1);
|
||||||
|
border-radius: var(--Corner-radius-Small);
|
||||||
|
color: var(--Text-Secondary);
|
||||||
|
}
|
||||||
|
|
||||||
|
.mainImageWrapper {
|
||||||
|
position: relative;
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.mainImageContainer {
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
will-change: transform;
|
||||||
|
position: absolute;
|
||||||
|
}
|
||||||
|
|
||||||
|
.desktopThumbnailGrid {
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: repeat(5, 1fr);
|
||||||
|
gap: var(--Spacing-x1);
|
||||||
|
max-height: 7.8125rem;
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
|
||||||
|
.thumbnailContainer {
|
||||||
|
height: 125px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.fullWidthImage {
|
||||||
|
grid-column: auto;
|
||||||
|
height: auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
.thumbnailContainer img {
|
||||||
|
border-radius: var(--Corner-radius-Small);
|
||||||
|
}
|
||||||
|
|
||||||
|
.navigationButton {
|
||||||
|
position: absolute;
|
||||||
|
top: 50%;
|
||||||
|
transform: translateY(-50%);
|
||||||
|
background-color: var(--Component-Button-Inverted-Fill-Default);
|
||||||
|
color: var(--Component-Button-Inverted-On-fill-Default);
|
||||||
|
border-radius: var(--Corner-radius-rounded);
|
||||||
|
padding: 10px;
|
||||||
|
cursor: pointer;
|
||||||
|
border-width: 0;
|
||||||
|
display: flex;
|
||||||
|
z-index: 1;
|
||||||
|
box-shadow: 0px 0px 8px 1px #0000001a;
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
background-color: var(--Component-Button-Inverted-Fill-Hover);
|
||||||
|
color: var(--Component-Button-Inverted-On-fill-Hover);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.galleryPrevButton {
|
||||||
|
left: var(--Spacing-x2);
|
||||||
|
}
|
||||||
|
|
||||||
|
.galleryNextButton {
|
||||||
|
right: var(--Spacing-x2);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,15 +1,16 @@
|
|||||||
"use client"
|
"use client"
|
||||||
import { AnimatePresence, motion } from "framer-motion"
|
import { AnimatePresence, motion } from "framer-motion"
|
||||||
import { useState } from "react"
|
import { useState } from "react"
|
||||||
|
import { Button as ButtonRAC } from "react-aria-components"
|
||||||
import { useIntl } from "react-intl"
|
import { useIntl } from "react-intl"
|
||||||
|
|
||||||
|
import { IconButton } from "@scandic-hotels/design-system/IconButton"
|
||||||
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 Image from "@/components/Image"
|
import Image from "@/components/Image"
|
||||||
import Button from "@/components/TempDesignSystem/Button"
|
|
||||||
import Caption from "@/components/TempDesignSystem/Text/Caption"
|
|
||||||
|
|
||||||
import styles from "./Lightbox.module.css"
|
import styles from "./gallery.module.css"
|
||||||
|
|
||||||
import type { GalleryProps } from "@/types/components/lightbox/lightbox"
|
import type { GalleryProps } from "@/types/components/lightbox/lightbox"
|
||||||
|
|
||||||
@@ -61,36 +62,38 @@ export default function Gallery({
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<div className={styles.galleryContainer}>
|
<div className={styles.galleryContainer}>
|
||||||
<Button
|
<IconButton
|
||||||
intent="text"
|
theme="Black"
|
||||||
size="small"
|
style="Muted"
|
||||||
className={styles.closeButton}
|
className={styles.closeButton}
|
||||||
onClick={onClose}
|
onPress={onClose}
|
||||||
aria-label={intl.formatMessage({
|
aria-label={intl.formatMessage({
|
||||||
defaultMessage: "Close",
|
defaultMessage: "Close",
|
||||||
})}
|
})}
|
||||||
>
|
>
|
||||||
<MaterialIcon
|
<MaterialIcon
|
||||||
icon="chevron_left"
|
icon="chevron_left"
|
||||||
color="Icon/Intense"
|
color="CurrentColor"
|
||||||
size={32}
|
size={24}
|
||||||
className={styles.mobileCloseIcon}
|
className={styles.mobileCloseIcon}
|
||||||
/>
|
/>
|
||||||
<MaterialIcon
|
<MaterialIcon
|
||||||
icon="close"
|
icon="close"
|
||||||
size={32}
|
color="CurrentColor"
|
||||||
|
size={24}
|
||||||
className={styles.desktopCloseIcon}
|
className={styles.desktopCloseIcon}
|
||||||
/>
|
/>
|
||||||
</Button>
|
</IconButton>
|
||||||
|
|
||||||
{/* Desktop Gallery */}
|
{/* Desktop Gallery */}
|
||||||
<div className={styles.desktopGallery}>
|
<div className={styles.desktopGallery}>
|
||||||
<div className={styles.galleryHeader}>
|
<Typography variant="Body/Supporting text (caption)/smRegular">
|
||||||
{mainImage.caption && !hideLabel && (
|
<p className={styles.galleryHeader}>
|
||||||
<div className={styles.imageCaption}>
|
{mainImage.caption && !hideLabel && (
|
||||||
<Caption color="textMediumContrast">{mainImage.caption}</Caption>
|
<span className={styles.imageCaption}>{mainImage.caption}</span>
|
||||||
</div>
|
)}
|
||||||
)}
|
</p>
|
||||||
</div>
|
</Typography>
|
||||||
<div className={styles.mainImageWrapper}>
|
<div className={styles.mainImageWrapper}>
|
||||||
<AnimatePresence initial={false} custom={animateLeft}>
|
<AnimatePresence initial={false} custom={animateLeft}>
|
||||||
<motion.div
|
<motion.div
|
||||||
@@ -103,34 +106,34 @@ export default function Gallery({
|
|||||||
exit="exit"
|
exit="exit"
|
||||||
transition={{ duration: 0.3 }}
|
transition={{ duration: 0.3 }}
|
||||||
>
|
>
|
||||||
<Image
|
<ButtonRAC
|
||||||
src={mainImage.src}
|
onPress={onImageClick}
|
||||||
alt={mainImage.alt}
|
className={styles.imageButton}
|
||||||
fill
|
aria-label={intl.formatMessage({
|
||||||
sizes="(min-width: 1000px) 1000px, 100vw"
|
defaultMessage: "Open image",
|
||||||
className={styles.image}
|
})}
|
||||||
onClick={onImageClick}
|
>
|
||||||
/>
|
<Image
|
||||||
|
src={mainImage.src}
|
||||||
|
alt={mainImage.alt}
|
||||||
|
fill
|
||||||
|
sizes="(min-width: 1000px) 1000px, 100vw"
|
||||||
|
className={styles.image}
|
||||||
|
/>
|
||||||
|
</ButtonRAC>
|
||||||
</motion.div>
|
</motion.div>
|
||||||
</AnimatePresence>
|
</AnimatePresence>
|
||||||
<motion.button
|
<motion.button
|
||||||
className={`${styles.navigationButton} ${styles.galleryPrevButton}`}
|
className={`${styles.navigationButton} ${styles.galleryPrevButton}`}
|
||||||
onClick={handlePrev}
|
onClick={handlePrev}
|
||||||
>
|
>
|
||||||
<MaterialIcon
|
<MaterialIcon icon="arrow_back" color="CurrentColor" />
|
||||||
icon="arrow_forward"
|
|
||||||
color="Icon/Interactive/Default"
|
|
||||||
className={styles.leftTransformIcon}
|
|
||||||
/>
|
|
||||||
</motion.button>
|
</motion.button>
|
||||||
<motion.button
|
<motion.button
|
||||||
className={`${styles.navigationButton} ${styles.galleryNextButton}`}
|
className={`${styles.navigationButton} ${styles.galleryNextButton}`}
|
||||||
onClick={handleNext}
|
onClick={handleNext}
|
||||||
>
|
>
|
||||||
<MaterialIcon
|
<MaterialIcon icon="arrow_forward" color="CurrentColor" />
|
||||||
icon="arrow_forward"
|
|
||||||
color="Icon/Interactive/Default"
|
|
||||||
/>
|
|
||||||
</motion.button>
|
</motion.button>
|
||||||
</div>
|
</div>
|
||||||
<div className={styles.desktopThumbnailGrid}>
|
<div className={styles.desktopThumbnailGrid}>
|
||||||
@@ -139,19 +142,26 @@ export default function Gallery({
|
|||||||
<motion.div
|
<motion.div
|
||||||
key={image.smallSrc || image.src}
|
key={image.smallSrc || image.src}
|
||||||
className={styles.thumbnailContainer}
|
className={styles.thumbnailContainer}
|
||||||
onClick={() => onSelectImage(image)}
|
|
||||||
initial={{ opacity: 0, x: 50 }}
|
initial={{ opacity: 0, x: 50 }}
|
||||||
animate={{ opacity: 1, x: 0 }}
|
animate={{ opacity: 1, x: 0 }}
|
||||||
exit={{ opacity: 0, x: -50 }}
|
exit={{ opacity: 0, x: -50 }}
|
||||||
transition={{ duration: 0.2, delay: index * 0.05 }}
|
transition={{ duration: 0.2, delay: index * 0.05 }}
|
||||||
>
|
>
|
||||||
<Image
|
<ButtonRAC
|
||||||
src={image.smallSrc || image.src}
|
className={styles.imageButton}
|
||||||
alt={image.alt}
|
onPress={() => onSelectImage(image)}
|
||||||
fill
|
aria-label={intl.formatMessage({
|
||||||
sizes="200px"
|
defaultMessage: "Open image",
|
||||||
className={styles.image}
|
})}
|
||||||
/>
|
>
|
||||||
|
<Image
|
||||||
|
src={image.smallSrc || image.src}
|
||||||
|
alt={image.alt}
|
||||||
|
fill
|
||||||
|
sizes="200px"
|
||||||
|
className={styles.image}
|
||||||
|
/>
|
||||||
|
</ButtonRAC>
|
||||||
</motion.div>
|
</motion.div>
|
||||||
))}
|
))}
|
||||||
</AnimatePresence>
|
</AnimatePresence>
|
||||||
@@ -160,31 +170,32 @@ export default function Gallery({
|
|||||||
|
|
||||||
{/* Mobile Gallery */}
|
{/* Mobile Gallery */}
|
||||||
<div className={styles.mobileGallery}>
|
<div className={styles.mobileGallery}>
|
||||||
<div className={styles.mobileGalleryContent}>
|
{images.map((image, index) => (
|
||||||
<div className={styles.thumbnailGrid}>
|
<motion.div
|
||||||
{images.map((image, index) => (
|
key={image.smallSrc || image.src}
|
||||||
<motion.div
|
className={`${styles.thumbnailContainer} ${index % 3 === 0 ? styles.fullWidthImage : ""}`}
|
||||||
key={image.smallSrc || image.src}
|
initial={{ opacity: 0, y: 20 }}
|
||||||
className={`${styles.thumbnailContainer} ${index % 3 === 0 ? styles.fullWidthImage : ""}`}
|
animate={{ opacity: 1, y: 0 }}
|
||||||
onClick={() => {
|
transition={{ duration: 0.3, delay: index * 0.05 }}
|
||||||
onSelectImage(image)
|
>
|
||||||
onImageClick()
|
<ButtonRAC
|
||||||
}}
|
className={styles.imageButton}
|
||||||
initial={{ opacity: 0, y: 20 }}
|
aria-label={intl.formatMessage({ defaultMessage: "Open image" })}
|
||||||
animate={{ opacity: 1, y: 0 }}
|
onPress={() => {
|
||||||
transition={{ duration: 0.3, delay: index * 0.05 }}
|
onSelectImage(image)
|
||||||
>
|
onImageClick()
|
||||||
<Image
|
}}
|
||||||
src={image.smallSrc || image.src}
|
>
|
||||||
alt={image.alt}
|
<Image
|
||||||
fill
|
src={image.smallSrc || image.src}
|
||||||
sizes="100vw"
|
alt={image.alt}
|
||||||
className={styles.image}
|
fill
|
||||||
/>
|
sizes="100vw"
|
||||||
</motion.div>
|
className={styles.image}
|
||||||
))}
|
/>
|
||||||
</div>
|
</ButtonRAC>
|
||||||
</div>
|
</motion.div>
|
||||||
|
))}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
@@ -1,348 +0,0 @@
|
|||||||
@keyframes darken-background {
|
|
||||||
from {
|
|
||||||
background-color: rgba(0, 0, 0, 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
to {
|
|
||||||
background-color: rgba(0, 0, 0, 0.5);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.mobileGallery {
|
|
||||||
height: 100%;
|
|
||||||
position: relative;
|
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
gap: var(--Spacing-x2);
|
|
||||||
}
|
|
||||||
|
|
||||||
.closeButton {
|
|
||||||
justify-content: flex-start;
|
|
||||||
width: fit-content;
|
|
||||||
}
|
|
||||||
.closeButton .desktopCloseIcon {
|
|
||||||
display: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
.mobileGalleryContent {
|
|
||||||
display: block;
|
|
||||||
}
|
|
||||||
|
|
||||||
.fullViewCloseButton {
|
|
||||||
position: absolute;
|
|
||||||
top: var(--Spacing-x-one-and-half);
|
|
||||||
right: var(--Spacing-x-half);
|
|
||||||
z-index: 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
.fullViewCloseButton:hover .fullViewCloseIcon {
|
|
||||||
background-color: var(--UI-Text-Medium-contrast);
|
|
||||||
border-radius: 50%;
|
|
||||||
}
|
|
||||||
|
|
||||||
.leftTransformIcon {
|
|
||||||
transform: scaleX(-1);
|
|
||||||
}
|
|
||||||
|
|
||||||
.content {
|
|
||||||
width: 100%;
|
|
||||||
height: 100%;
|
|
||||||
border-radius: 0;
|
|
||||||
position: fixed;
|
|
||||||
top: 50%;
|
|
||||||
left: 50%;
|
|
||||||
z-index: var(--lightbox-z-index);
|
|
||||||
}
|
|
||||||
|
|
||||||
.overlay {
|
|
||||||
position: fixed;
|
|
||||||
inset: 0;
|
|
||||||
background-color: rgba(0, 0, 0, 0.5);
|
|
||||||
z-index: var(--lightbox-z-index);
|
|
||||||
}
|
|
||||||
|
|
||||||
.overlay[data-entering] {
|
|
||||||
animation: darken-background 0.2s;
|
|
||||||
}
|
|
||||||
|
|
||||||
.overlay[data-exiting] {
|
|
||||||
animation: darken-background 0.2s reverse;
|
|
||||||
}
|
|
||||||
|
|
||||||
.galleryContainer {
|
|
||||||
background-color: var(--Base-Background-Primary-Normal);
|
|
||||||
padding: var(--Spacing-x2);
|
|
||||||
height: 100%;
|
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
position: relative;
|
|
||||||
overflow-y: auto;
|
|
||||||
}
|
|
||||||
|
|
||||||
.galleryHeader {
|
|
||||||
display: flex;
|
|
||||||
justify-content: space-between;
|
|
||||||
align-items: center;
|
|
||||||
height: 1.71875rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
.desktopGallery,
|
|
||||||
.desktopThumbnailGrid,
|
|
||||||
.navigationButton {
|
|
||||||
display: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
.imageCaption {
|
|
||||||
background-color: var(--Base-Surface-Subtle-Normal);
|
|
||||||
padding: var(--Spacing-x-half) var(--Spacing-x1);
|
|
||||||
border-radius: var(--Corner-radius-Small);
|
|
||||||
}
|
|
||||||
|
|
||||||
.mainImageWrapper {
|
|
||||||
position: relative;
|
|
||||||
width: 100%;
|
|
||||||
}
|
|
||||||
|
|
||||||
.mainImageContainer {
|
|
||||||
width: 100%;
|
|
||||||
height: 100%;
|
|
||||||
will-change: transform;
|
|
||||||
overflow: hidden;
|
|
||||||
position: absolute;
|
|
||||||
}
|
|
||||||
|
|
||||||
.mainImageContainer img,
|
|
||||||
.thumbnailContainer img {
|
|
||||||
border-radius: var(--Corner-radius-Small);
|
|
||||||
cursor: pointer;
|
|
||||||
transition: opacity 0.3s ease-in-out;
|
|
||||||
}
|
|
||||||
|
|
||||||
.thumbnailGrid {
|
|
||||||
display: grid;
|
|
||||||
grid-template-columns: 1fr 1fr;
|
|
||||||
gap: var(--Spacing-x1);
|
|
||||||
max-height: none;
|
|
||||||
padding: var(--Spacing-x3) 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
.thumbnailContainer {
|
|
||||||
position: relative;
|
|
||||||
height: 242px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.fullWidthImage {
|
|
||||||
grid-column: 1 / -1;
|
|
||||||
height: 240px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.thumbnailContainer img {
|
|
||||||
border-radius: var(--Corner-radius-Medium);
|
|
||||||
}
|
|
||||||
|
|
||||||
.fullViewContainer {
|
|
||||||
background-color: var(--UI-Text-High-contrast);
|
|
||||||
height: 100%;
|
|
||||||
padding: var(--Spacing-x2);
|
|
||||||
position: relative;
|
|
||||||
align-items: center;
|
|
||||||
justify-items: center;
|
|
||||||
display: grid;
|
|
||||||
grid-template-rows: auto 1fr auto;
|
|
||||||
grid-template-columns: 1fr;
|
|
||||||
place-content: center;
|
|
||||||
gap: var(--Spacing-x5);
|
|
||||||
}
|
|
||||||
|
|
||||||
.fullViewHeader {
|
|
||||||
display: flex;
|
|
||||||
justify-content: center;
|
|
||||||
width: 100%;
|
|
||||||
}
|
|
||||||
|
|
||||||
.fullViewImageContainer {
|
|
||||||
position: relative;
|
|
||||||
width: 100%;
|
|
||||||
height: 100%;
|
|
||||||
max-height: 25rem;
|
|
||||||
margin-bottom: var(--Spacing-x5);
|
|
||||||
}
|
|
||||||
|
|
||||||
.fullViewImage {
|
|
||||||
position: absolute;
|
|
||||||
width: 100%;
|
|
||||||
height: 100%;
|
|
||||||
border-radius: var(--Corner-radius-Medium);
|
|
||||||
}
|
|
||||||
|
|
||||||
.fullViewImageContainer img {
|
|
||||||
border-radius: var(--Corner-radius-Medium);
|
|
||||||
width: 100%;
|
|
||||||
height: 100%;
|
|
||||||
}
|
|
||||||
|
|
||||||
.fullViewFooter {
|
|
||||||
position: absolute;
|
|
||||||
bottom: calc(-1 * var(--Spacing-x5));
|
|
||||||
}
|
|
||||||
|
|
||||||
.imagePosition {
|
|
||||||
background-color: var(--UI-Grey-90);
|
|
||||||
padding: var(--Spacing-x-quarter) var(--Spacing-x-half);
|
|
||||||
border-radius: var(--Corner-radius-Small);
|
|
||||||
}
|
|
||||||
|
|
||||||
.portraitImage {
|
|
||||||
max-width: 548px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.image {
|
|
||||||
object-fit: cover;
|
|
||||||
}
|
|
||||||
@media (min-width: 768px) and (max-width: 1366px) {
|
|
||||||
.fullViewContainer {
|
|
||||||
padding: var(--Spacing-x5);
|
|
||||||
}
|
|
||||||
|
|
||||||
.fullViewImageContainer {
|
|
||||||
height: 100%;
|
|
||||||
max-height: 35rem;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@media (min-width: 768px) {
|
|
||||||
.mobileGallery,
|
|
||||||
.thumbnailGrid {
|
|
||||||
display: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
.content {
|
|
||||||
position: fixed;
|
|
||||||
top: 50%;
|
|
||||||
left: 50%;
|
|
||||||
overflow: hidden;
|
|
||||||
}
|
|
||||||
|
|
||||||
.content:not(.fullViewContent) {
|
|
||||||
border-radius: var(--Corner-radius-Large);
|
|
||||||
}
|
|
||||||
|
|
||||||
.galleryContent {
|
|
||||||
width: 1090px;
|
|
||||||
width: min(var(--max-width-page), 1090px);
|
|
||||||
height: min(725px, 85dvh);
|
|
||||||
}
|
|
||||||
|
|
||||||
.fullViewContent {
|
|
||||||
width: 100vw;
|
|
||||||
height: 100vh;
|
|
||||||
}
|
|
||||||
|
|
||||||
.galleryContainer {
|
|
||||||
padding: var(--Spacing-x5) var(--Spacing-x6);
|
|
||||||
}
|
|
||||||
|
|
||||||
.desktopGallery {
|
|
||||||
display: grid;
|
|
||||||
grid-template-rows: 1.71875rem 1fr 7.8125rem;
|
|
||||||
row-gap: var(--Spacing-x-one-and-half);
|
|
||||||
background-color: var(--Base-Background-Primary-Normal);
|
|
||||||
height: 100%;
|
|
||||||
position: relative;
|
|
||||||
overflow: hidden;
|
|
||||||
}
|
|
||||||
|
|
||||||
.closeButton {
|
|
||||||
display: block;
|
|
||||||
position: absolute;
|
|
||||||
top: var(--Spacing-x-one-and-half);
|
|
||||||
right: var(--Spacing-x1);
|
|
||||||
z-index: 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
.closeButton .mobileCloseIcon {
|
|
||||||
display: none;
|
|
||||||
}
|
|
||||||
.closeButton .desktopCloseIcon {
|
|
||||||
display: block;
|
|
||||||
}
|
|
||||||
|
|
||||||
.closeButton:hover .desktopCloseIcon {
|
|
||||||
background-color: var(--Base-Surface-Primary-light-Hover-alt);
|
|
||||||
border-radius: 50%;
|
|
||||||
}
|
|
||||||
|
|
||||||
.desktopThumbnailGrid {
|
|
||||||
display: grid;
|
|
||||||
grid-template-columns: repeat(5, 1fr);
|
|
||||||
gap: var(--Spacing-x1);
|
|
||||||
max-height: 7.8125rem;
|
|
||||||
overflow: hidden;
|
|
||||||
}
|
|
||||||
|
|
||||||
.thumbnailContainer {
|
|
||||||
height: 125px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.fullViewCloseButton {
|
|
||||||
position: fixed;
|
|
||||||
top: var(--Spacing-x-one-and-half);
|
|
||||||
right: var(--Spacing-x-half);
|
|
||||||
}
|
|
||||||
|
|
||||||
.fullWidthImage {
|
|
||||||
grid-column: auto;
|
|
||||||
height: auto;
|
|
||||||
}
|
|
||||||
|
|
||||||
.thumbnailContainer img {
|
|
||||||
border-radius: var(--Corner-radius-Small);
|
|
||||||
}
|
|
||||||
|
|
||||||
.fullViewContainer {
|
|
||||||
margin-top: 0;
|
|
||||||
padding: var(--Spacing-x5);
|
|
||||||
grid-template-rows: auto 1fr auto;
|
|
||||||
width: 100%;
|
|
||||||
height: 100%;
|
|
||||||
}
|
|
||||||
|
|
||||||
.fullViewImageContainer {
|
|
||||||
width: 70%;
|
|
||||||
max-width: 90.875rem;
|
|
||||||
max-height: 43.75rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
.navigationButton {
|
|
||||||
position: absolute;
|
|
||||||
top: 50%;
|
|
||||||
transform: translateY(-50%);
|
|
||||||
background-color: var(--Base-Button-Inverted-Fill-Normal);
|
|
||||||
border-radius: 50%;
|
|
||||||
padding: var(--Spacing-x1);
|
|
||||||
cursor: pointer;
|
|
||||||
border: none;
|
|
||||||
display: flex;
|
|
||||||
z-index: 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
.galleryPrevButton {
|
|
||||||
left: var(--Spacing-x2);
|
|
||||||
}
|
|
||||||
|
|
||||||
.galleryNextButton {
|
|
||||||
right: var(--Spacing-x2);
|
|
||||||
}
|
|
||||||
|
|
||||||
.fullViewNextButton {
|
|
||||||
right: var(--Spacing-x5);
|
|
||||||
}
|
|
||||||
|
|
||||||
.fullViewPrevButton {
|
|
||||||
left: var(--Spacing-x5);
|
|
||||||
}
|
|
||||||
|
|
||||||
.fullViewFooter {
|
|
||||||
text-align: left;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -6,7 +6,7 @@ import { Dialog, Modal, ModalOverlay } from "react-aria-components"
|
|||||||
import FullView from "./FullView"
|
import FullView from "./FullView"
|
||||||
import Gallery from "./Gallery"
|
import Gallery from "./Gallery"
|
||||||
|
|
||||||
import styles from "./Lightbox.module.css"
|
import styles from "./lightbox.module.css"
|
||||||
|
|
||||||
import type { LightboxProps } from "@/types/components/lightbox/lightbox"
|
import type { LightboxProps } from "@/types/components/lightbox/lightbox"
|
||||||
|
|
||||||
|
|||||||
57
apps/scandic-web/components/Lightbox/lightbox.module.css
Normal file
57
apps/scandic-web/components/Lightbox/lightbox.module.css
Normal file
@@ -0,0 +1,57 @@
|
|||||||
|
.overlay {
|
||||||
|
position: fixed;
|
||||||
|
inset: 0;
|
||||||
|
background-color: rgba(0, 0, 0, 0.5);
|
||||||
|
z-index: var(--lightbox-z-index);
|
||||||
|
|
||||||
|
&[data-entering] {
|
||||||
|
animation: darken-background 0.2s;
|
||||||
|
}
|
||||||
|
|
||||||
|
&[data-exiting] {
|
||||||
|
animation: darken-background 0.2s reverse;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.content {
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
border-radius: 0;
|
||||||
|
position: fixed;
|
||||||
|
top: 50%;
|
||||||
|
left: 50%;
|
||||||
|
z-index: var(--lightbox-z-index);
|
||||||
|
}
|
||||||
|
|
||||||
|
@media screen and (min-width: 768px) {
|
||||||
|
.content {
|
||||||
|
position: fixed;
|
||||||
|
top: 50%;
|
||||||
|
left: 50%;
|
||||||
|
overflow: hidden;
|
||||||
|
|
||||||
|
&:not(.fullViewContent) {
|
||||||
|
border-radius: var(--Corner-radius-Large);
|
||||||
|
}
|
||||||
|
|
||||||
|
&.fullViewContent {
|
||||||
|
width: 100vw;
|
||||||
|
height: 100vh;
|
||||||
|
}
|
||||||
|
|
||||||
|
&.galleryContent {
|
||||||
|
width: min(var(--max-width-page), 1090px);
|
||||||
|
height: min(725px, 85dvh);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes darken-background {
|
||||||
|
from {
|
||||||
|
background-color: rgba(0, 0, 0, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
to {
|
||||||
|
background-color: rgba(0, 0, 0, 0.5);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -36,8 +36,7 @@ const meta: Meta<typeof Button> = {
|
|||||||
control: 'select',
|
control: 'select',
|
||||||
options: Object.keys(buttonConfig.variants.size),
|
options: Object.keys(buttonConfig.variants.size),
|
||||||
type: 'string',
|
type: 'string',
|
||||||
description:
|
description: 'The size of the button. Defaults to `Large`.',
|
||||||
'The size of the button. Defaults to `Large`. This variant does not apply to the `Icon` variant.',
|
|
||||||
},
|
},
|
||||||
wrapping: {
|
wrapping: {
|
||||||
control: 'radio',
|
control: 'radio',
|
||||||
@@ -351,25 +350,3 @@ export const TextWithIconInverted: Story = {
|
|||||||
color: 'Inverted',
|
color: 'Inverted',
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
export const Icon: Story = {
|
|
||||||
args: {
|
|
||||||
onPress: fn(),
|
|
||||||
children: <MaterialIcon icon="favorite" size={24} />,
|
|
||||||
variant: 'Icon',
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
export const IconWithColor: Story = {
|
|
||||||
args: {
|
|
||||||
onPress: fn(),
|
|
||||||
children: (
|
|
||||||
<MaterialIcon
|
|
||||||
icon="check_circle"
|
|
||||||
size={24}
|
|
||||||
color="Icon/Feedback/Success"
|
|
||||||
/>
|
|
||||||
),
|
|
||||||
variant: 'Icon',
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -1,5 +1,3 @@
|
|||||||
'use client'
|
|
||||||
|
|
||||||
import { Button as ButtonRAC } from 'react-aria-components'
|
import { Button as ButtonRAC } from 'react-aria-components'
|
||||||
|
|
||||||
import { variants } from './variants'
|
import { variants } from './variants'
|
||||||
|
|||||||
@@ -6,6 +6,7 @@
|
|||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
|
gap: var(--Space-x05);
|
||||||
|
|
||||||
&:disabled {
|
&:disabled {
|
||||||
cursor: unset;
|
cursor: unset;
|
||||||
@@ -166,11 +167,3 @@
|
|||||||
.variant-text.color-inverted:disabled {
|
.variant-text.color-inverted:disabled {
|
||||||
color: var(--Component-Button-Brand-Secondary-On-fill-Disabled);
|
color: var(--Component-Button-Brand-Secondary-On-fill-Disabled);
|
||||||
}
|
}
|
||||||
|
|
||||||
.variant-icon {
|
|
||||||
background-color: transparent;
|
|
||||||
border-color: transparent;
|
|
||||||
color: inherit;
|
|
||||||
padding: 0;
|
|
||||||
margin: 0;
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -16,7 +16,6 @@ export const config = {
|
|||||||
Tertiary: styles['variant-tertiary'],
|
Tertiary: styles['variant-tertiary'],
|
||||||
Inverted: styles['variant-inverted'],
|
Inverted: styles['variant-inverted'],
|
||||||
Text: styles['variant-text'],
|
Text: styles['variant-text'],
|
||||||
Icon: styles['variant-icon'],
|
|
||||||
},
|
},
|
||||||
color: {
|
color: {
|
||||||
Primary: styles['color-primary'],
|
Primary: styles['color-primary'],
|
||||||
|
|||||||
@@ -0,0 +1,141 @@
|
|||||||
|
import type { Meta, StoryObj } from '@storybook/react'
|
||||||
|
|
||||||
|
import { fn } from '@storybook/test'
|
||||||
|
|
||||||
|
import { MaterialIcon } from '../Icons/MaterialIcon'
|
||||||
|
import { IconButton } from './IconButton'
|
||||||
|
import { config } from './variants'
|
||||||
|
|
||||||
|
const meta: Meta<typeof IconButton> = {
|
||||||
|
title: 'Components/IconButton',
|
||||||
|
component: IconButton,
|
||||||
|
argTypes: {
|
||||||
|
onPress: {
|
||||||
|
table: {
|
||||||
|
disable: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
theme: {
|
||||||
|
control: 'select',
|
||||||
|
options: Object.keys(config.variants.theme),
|
||||||
|
default: 'Primary',
|
||||||
|
},
|
||||||
|
style: {
|
||||||
|
control: 'select',
|
||||||
|
options: Object.keys(config.variants.style),
|
||||||
|
default: 'Normal',
|
||||||
|
type: 'string',
|
||||||
|
description: `The style variant is only applied on certain variants. The examples below shows the possible combinations of variants and style variants.`,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
export default meta
|
||||||
|
|
||||||
|
type Story = StoryObj<typeof IconButton>
|
||||||
|
|
||||||
|
export const PrimaryDefault: Story = {
|
||||||
|
args: {
|
||||||
|
onPress: fn(),
|
||||||
|
children: <MaterialIcon icon="search" size={24} color="CurrentColor" />,
|
||||||
|
theme: 'Primary',
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
export const PrimaryDisabled: Story = {
|
||||||
|
args: {
|
||||||
|
...PrimaryDefault.args,
|
||||||
|
isDisabled: true,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
export const InvertedDefault: Story = {
|
||||||
|
args: {
|
||||||
|
onPress: fn(),
|
||||||
|
children: (
|
||||||
|
<MaterialIcon icon="arrow_forward" size={24} color="CurrentColor" />
|
||||||
|
),
|
||||||
|
theme: 'Inverted',
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
export const InvertedDisabled: Story = {
|
||||||
|
args: {
|
||||||
|
...InvertedDefault.args,
|
||||||
|
isDisabled: true,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
export const InvertedElevated: Story = {
|
||||||
|
args: {
|
||||||
|
...InvertedDefault.args,
|
||||||
|
style: 'Elevated',
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
export const InvertedElevatedDisabled: Story = {
|
||||||
|
args: {
|
||||||
|
...InvertedElevated.args,
|
||||||
|
isDisabled: true,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
export const InvertedMuted: Story = {
|
||||||
|
args: {
|
||||||
|
...InvertedDefault.args,
|
||||||
|
children: <MaterialIcon icon="close" size={24} color="CurrentColor" />,
|
||||||
|
style: 'Muted',
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
export const InvertedMutedDisabled: Story = {
|
||||||
|
args: {
|
||||||
|
...InvertedMuted.args,
|
||||||
|
isDisabled: true,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
export const InvertedFaded: Story = {
|
||||||
|
args: {
|
||||||
|
...InvertedDefault.args,
|
||||||
|
style: 'Faded',
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
export const InvertedFadedDisabled: Story = {
|
||||||
|
args: {
|
||||||
|
...InvertedFaded.args,
|
||||||
|
isDisabled: true,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
export const TertiaryElevated: Story = {
|
||||||
|
args: {
|
||||||
|
onPress: fn(),
|
||||||
|
children: <MaterialIcon icon="arrow_back" size={24} color="CurrentColor" />,
|
||||||
|
theme: 'Tertiary',
|
||||||
|
style: 'Elevated',
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
export const TertiaryDisabled: Story = {
|
||||||
|
args: {
|
||||||
|
...TertiaryElevated.args,
|
||||||
|
isDisabled: true,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
export const BlackMuted: Story = {
|
||||||
|
args: {
|
||||||
|
onPress: fn(),
|
||||||
|
children: <MaterialIcon icon="close" size={24} color="CurrentColor" />,
|
||||||
|
theme: 'Black',
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
export const BlackMutedDisabled: Story = {
|
||||||
|
args: {
|
||||||
|
...BlackMuted.args,
|
||||||
|
isDisabled: true,
|
||||||
|
},
|
||||||
|
}
|
||||||
@@ -0,0 +1,20 @@
|
|||||||
|
import { Button as ButtonRAC } from 'react-aria-components'
|
||||||
|
|
||||||
|
import { variants } from './variants'
|
||||||
|
|
||||||
|
import type { IconButtonProps } from './types'
|
||||||
|
|
||||||
|
export function IconButton({
|
||||||
|
theme,
|
||||||
|
style,
|
||||||
|
className,
|
||||||
|
...props
|
||||||
|
}: IconButtonProps) {
|
||||||
|
const classNames = variants({
|
||||||
|
theme,
|
||||||
|
style,
|
||||||
|
className,
|
||||||
|
})
|
||||||
|
|
||||||
|
return <ButtonRAC {...props} className={classNames} />
|
||||||
|
}
|
||||||
@@ -0,0 +1,102 @@
|
|||||||
|
.iconButton {
|
||||||
|
border-radius: var(--Corner-radius-rounded);
|
||||||
|
border-width: 0;
|
||||||
|
cursor: pointer;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
padding: 10px;
|
||||||
|
|
||||||
|
&:disabled {
|
||||||
|
cursor: unset;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.theme-primary {
|
||||||
|
background-color: var(--Component-Button-Brand-Primary-Fill-Default);
|
||||||
|
color: var(--Component-Button-Brand-Primary-On-fill-Default);
|
||||||
|
|
||||||
|
&:hover:not(:disabled) {
|
||||||
|
background-color: var(--Component-Button-Brand-Primary-Fill-Hover);
|
||||||
|
color: var(--Component-Button-Brand-Primary-On-fill-Hover);
|
||||||
|
}
|
||||||
|
|
||||||
|
&:disabled {
|
||||||
|
background-color: var(--Component-Button-Brand-Primary-Fill-Disabled);
|
||||||
|
color: var(--Component-Button-Brand-Primary-On-fill-Disabled);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.theme-inverted {
|
||||||
|
background-color: var(--Component-Button-Inverted-Fill-Default);
|
||||||
|
color: var(--Component-Button-Inverted-On-fill-Default);
|
||||||
|
|
||||||
|
&:hover:not(:disabled) {
|
||||||
|
background-color: var(--Component-Button-Inverted-Fill-Hover);
|
||||||
|
color: var(--Component-Button-Inverted-On-fill-Hover);
|
||||||
|
}
|
||||||
|
|
||||||
|
&:disabled {
|
||||||
|
background-color: var(--Component-Button-Inverted-Fill-Disabled);
|
||||||
|
color: var(--Component-Button-Inverted-On-fill-Disabled);
|
||||||
|
}
|
||||||
|
|
||||||
|
&.style-muted {
|
||||||
|
color: var(--Component-Button-Muted-On-fill-Inverted);
|
||||||
|
|
||||||
|
&:hover:not(:disabled) {
|
||||||
|
color: var(--Component-Button-Muted-On-fill-Inverted);
|
||||||
|
}
|
||||||
|
|
||||||
|
&:disabled {
|
||||||
|
color: var(--Component-Button-Muted-On-fill-Disabled);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.theme-tertiary {
|
||||||
|
background-color: var(--Component-Button-Brand-Tertiary-Fill-Default);
|
||||||
|
color: var(--Component-Button-Brand-Tertiary-On-fill-Default);
|
||||||
|
|
||||||
|
&:hover:not(:disabled) {
|
||||||
|
background-color: var(--Component-Button-Brand-Tertiary-Fill-Hover);
|
||||||
|
color: var(--Component-Button-Brand-Tertiary-On-fill-Hover);
|
||||||
|
}
|
||||||
|
|
||||||
|
&:disabled {
|
||||||
|
background-color: var(--Component-Button-Brand-Tertiary-Fill-Disabled);
|
||||||
|
color: var(--Component-Button-Brand-Tertiary-On-fill-Disabled);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.theme-black {
|
||||||
|
color: var(--Component-Button-Muted-On-fill-Default);
|
||||||
|
|
||||||
|
&:hover:not(:disabled) {
|
||||||
|
color: var(--Component-Button-Muted-On-fill-Hover-Inverted);
|
||||||
|
}
|
||||||
|
|
||||||
|
&:disabled {
|
||||||
|
color: var(--Component-Button-Muted-On-fill-Disabled);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.style-elevated {
|
||||||
|
box-shadow: 0px 0px 8px 1px #0000001a;
|
||||||
|
}
|
||||||
|
|
||||||
|
.style-faded {
|
||||||
|
background-color: var(--Component-Button-Inverted-Fill-Faded);
|
||||||
|
}
|
||||||
|
|
||||||
|
.style-muted {
|
||||||
|
background-color: var(--Component-Button-Muted-Fill-Default);
|
||||||
|
|
||||||
|
&:hover:not(:disabled) {
|
||||||
|
background-color: var(--Component-Button-Muted-Fill-Hover-inverted);
|
||||||
|
}
|
||||||
|
|
||||||
|
&:disabled {
|
||||||
|
background-color: var(--Component-Button-Muted-Fill-Disabled-inverted);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
export { IconButton } from './IconButton'
|
||||||
10
packages/design-system/lib/components/IconButton/types.ts
Normal file
10
packages/design-system/lib/components/IconButton/types.ts
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
import { Button } from 'react-aria-components'
|
||||||
|
|
||||||
|
import type { VariantProps } from 'class-variance-authority'
|
||||||
|
import type { ComponentProps } from 'react'
|
||||||
|
|
||||||
|
import type { variants } from './variants'
|
||||||
|
|
||||||
|
export interface IconButtonProps
|
||||||
|
extends Omit<ComponentProps<typeof Button>, 'style'>,
|
||||||
|
VariantProps<typeof variants> {}
|
||||||
78
packages/design-system/lib/components/IconButton/variants.ts
Normal file
78
packages/design-system/lib/components/IconButton/variants.ts
Normal file
@@ -0,0 +1,78 @@
|
|||||||
|
import { cva } from 'class-variance-authority'
|
||||||
|
|
||||||
|
import styles from './iconButton.module.css'
|
||||||
|
|
||||||
|
const variantKeys = {
|
||||||
|
theme: {
|
||||||
|
Primary: 'Primary',
|
||||||
|
Tertiary: 'Tertiary',
|
||||||
|
Inverted: 'Inverted',
|
||||||
|
Black: 'Black',
|
||||||
|
},
|
||||||
|
style: {
|
||||||
|
Normal: 'Normal',
|
||||||
|
Muted: 'Muted',
|
||||||
|
Elevated: 'Elevated',
|
||||||
|
Faded: 'Faded',
|
||||||
|
},
|
||||||
|
} as const
|
||||||
|
|
||||||
|
export const config = {
|
||||||
|
variants: {
|
||||||
|
theme: {
|
||||||
|
[variantKeys.theme.Primary]: styles['theme-primary'],
|
||||||
|
[variantKeys.theme.Tertiary]: styles['theme-tertiary'],
|
||||||
|
[variantKeys.theme.Inverted]: styles['theme-inverted'],
|
||||||
|
[variantKeys.theme.Black]: styles['theme-black'],
|
||||||
|
},
|
||||||
|
// Some variants cannot be used in combination with certain style variants.
|
||||||
|
// The style variant will be applied using the compoundVariants.
|
||||||
|
style: {
|
||||||
|
[variantKeys.style.Normal]: '',
|
||||||
|
[variantKeys.style.Muted]: '',
|
||||||
|
[variantKeys.style.Elevated]: '',
|
||||||
|
[variantKeys.style.Faded]: '',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
compoundVariants: [
|
||||||
|
// Primary should only use Normal
|
||||||
|
{ theme: variantKeys.theme.Primary, className: styles['style-normal'] },
|
||||||
|
|
||||||
|
// Tertiary should only use Elevated
|
||||||
|
{
|
||||||
|
theme: variantKeys.theme.Tertiary,
|
||||||
|
className: styles['style-elevated'],
|
||||||
|
},
|
||||||
|
|
||||||
|
// Black should only use Muted
|
||||||
|
{ theme: variantKeys.theme.Black, className: styles['style-muted'] },
|
||||||
|
|
||||||
|
// Inverted can use any style variant
|
||||||
|
{
|
||||||
|
theme: variantKeys.theme.Inverted,
|
||||||
|
style: variantKeys.style.Normal,
|
||||||
|
className: styles['style-normal'],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
theme: variantKeys.theme.Inverted,
|
||||||
|
style: variantKeys.style.Muted,
|
||||||
|
className: styles['style-muted'],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
theme: variantKeys.theme.Inverted,
|
||||||
|
style: variantKeys.style.Elevated,
|
||||||
|
className: styles['style-elevated'],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
theme: variantKeys.theme.Inverted,
|
||||||
|
style: variantKeys.style.Faded,
|
||||||
|
className: styles['style-faded'],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
defaultVariants: {
|
||||||
|
theme: variantKeys.theme.Primary,
|
||||||
|
style: variantKeys.style.Normal,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
export const variants = cva(styles.iconButton, config)
|
||||||
@@ -1,7 +1,7 @@
|
|||||||
import { Typography } from '../../Typography'
|
import { Typography } from '../../Typography'
|
||||||
import { Rate, RateTermDetails } from '../types'
|
import { Rate, RateTermDetails } from '../types'
|
||||||
|
|
||||||
import { Button } from '../../Button'
|
import { IconButton } from '../../IconButton'
|
||||||
import { MaterialIcon } from '../../Icons/MaterialIcon'
|
import { MaterialIcon } from '../../Icons/MaterialIcon'
|
||||||
import Modal from '../Modal'
|
import Modal from '../Modal'
|
||||||
import styles from '../rate-card.module.css'
|
import styles from '../rate-card.module.css'
|
||||||
@@ -67,13 +67,13 @@ export default function CampaignRateCard({
|
|||||||
title={rateTitle}
|
title={rateTitle}
|
||||||
subtitle={paymentTerm}
|
subtitle={paymentTerm}
|
||||||
trigger={
|
trigger={
|
||||||
<Button variant="Icon" size="Small">
|
<IconButton theme="Black" style="Muted">
|
||||||
<MaterialIcon
|
<MaterialIcon
|
||||||
icon="info"
|
icon="info"
|
||||||
size={20}
|
size={20}
|
||||||
color="Icon/Default"
|
color="Icon/Default"
|
||||||
/>
|
/>
|
||||||
</Button>
|
</IconButton>
|
||||||
}
|
}
|
||||||
>
|
>
|
||||||
{rateTermDetails.map((termGroup) => (
|
{rateTermDetails.map((termGroup) => (
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import { Rate, RateTermDetails } from '../types'
|
import { Rate, RateTermDetails } from '../types'
|
||||||
|
|
||||||
import { Button } from '../../Button'
|
import { IconButton } from '../../IconButton'
|
||||||
import { MaterialIcon } from '../../Icons/MaterialIcon'
|
import { MaterialIcon } from '../../Icons/MaterialIcon'
|
||||||
import { Typography } from '../../Typography'
|
import { Typography } from '../../Typography'
|
||||||
import Modal from '../Modal'
|
import Modal from '../Modal'
|
||||||
@@ -63,13 +63,13 @@ export default function CodeRateCard({
|
|||||||
title={rateTitle}
|
title={rateTitle}
|
||||||
subtitle={paymentTerm}
|
subtitle={paymentTerm}
|
||||||
trigger={
|
trigger={
|
||||||
<Button variant="Icon" size="Small">
|
<IconButton theme="Black" style="Muted">
|
||||||
<MaterialIcon
|
<MaterialIcon
|
||||||
icon="info"
|
icon="info"
|
||||||
size={20}
|
size={20}
|
||||||
color="Icon/Default"
|
color="Icon/Default"
|
||||||
/>
|
/>
|
||||||
</Button>
|
</IconButton>
|
||||||
}
|
}
|
||||||
>
|
>
|
||||||
{rateTermDetails.map((termGroup) => (
|
{rateTermDetails.map((termGroup) => (
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import { Button } from '../../Button'
|
import { IconButton } from '../../IconButton'
|
||||||
import { MaterialIcon } from '../../Icons/MaterialIcon'
|
import { MaterialIcon } from '../../Icons/MaterialIcon'
|
||||||
import { Typography } from '../../Typography'
|
import { Typography } from '../../Typography'
|
||||||
import styles from '../rate-card.module.css'
|
import styles from '../rate-card.module.css'
|
||||||
@@ -34,9 +34,9 @@ export default function NoRateAvailableCard({
|
|||||||
<header>
|
<header>
|
||||||
<Typography variant="Tag/sm">
|
<Typography variant="Tag/sm">
|
||||||
<h3 className={`${styles.title} ${styles.textDisabled}`}>
|
<h3 className={`${styles.title} ${styles.textDisabled}`}>
|
||||||
<Button variant="Icon" size="Small">
|
<IconButton theme="Black" style="Muted">
|
||||||
<MaterialIcon icon="info" size={20} color="Icon/Default" />
|
<MaterialIcon icon="info" size={20} color="Icon/Default" />
|
||||||
</Button>
|
</IconButton>
|
||||||
{`${rateTitle} / ${paymentTerm}`}
|
{`${rateTitle} / ${paymentTerm}`}
|
||||||
</h3>
|
</h3>
|
||||||
</Typography>
|
</Typography>
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ import { Typography } from '../../Typography'
|
|||||||
import { RatePointsOption, RateTermDetails } from '../types'
|
import { RatePointsOption, RateTermDetails } from '../types'
|
||||||
|
|
||||||
import { RadioGroup } from 'react-aria-components'
|
import { RadioGroup } from 'react-aria-components'
|
||||||
import { Button } from '../../Button'
|
import { IconButton } from '../../IconButton'
|
||||||
import { MaterialIcon } from '../../Icons/MaterialIcon'
|
import { MaterialIcon } from '../../Icons/MaterialIcon'
|
||||||
import { Radio } from '../../Radio'
|
import { Radio } from '../../Radio'
|
||||||
import Modal from '../Modal'
|
import Modal from '../Modal'
|
||||||
@@ -49,9 +49,9 @@ export default function PointsRateCard({
|
|||||||
title={rateTitle}
|
title={rateTitle}
|
||||||
subtitle={paymentTerm}
|
subtitle={paymentTerm}
|
||||||
trigger={
|
trigger={
|
||||||
<Button variant="Icon" size="Small">
|
<IconButton theme="Black" style="Muted">
|
||||||
<MaterialIcon icon="info" size={20} color="Icon/Default" />
|
<MaterialIcon icon="info" size={20} color="Icon/Default" />
|
||||||
</Button>
|
</IconButton>
|
||||||
}
|
}
|
||||||
>
|
>
|
||||||
{rateTermDetails.map((termGroup) => (
|
{rateTermDetails.map((termGroup) => (
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import { Rate, RateTermDetails } from '../types'
|
import { Rate, RateTermDetails } from '../types'
|
||||||
|
|
||||||
import { Button } from '../../Button'
|
import { IconButton } from '../../IconButton'
|
||||||
import { MaterialIcon } from '../../Icons/MaterialIcon'
|
import { MaterialIcon } from '../../Icons/MaterialIcon'
|
||||||
import { Typography } from '../../Typography'
|
import { Typography } from '../../Typography'
|
||||||
import Modal from '../Modal'
|
import Modal from '../Modal'
|
||||||
@@ -56,13 +56,13 @@ export default function RegularRateCard({
|
|||||||
title={rateTitle}
|
title={rateTitle}
|
||||||
subtitle={paymentTerm}
|
subtitle={paymentTerm}
|
||||||
trigger={
|
trigger={
|
||||||
<Button variant="Icon" size="Small">
|
<IconButton theme="Black" style="Muted">
|
||||||
<MaterialIcon
|
<MaterialIcon
|
||||||
icon="info"
|
icon="info"
|
||||||
size={20}
|
size={20}
|
||||||
color="Icon/Default"
|
color="Icon/Default"
|
||||||
/>
|
/>
|
||||||
</Button>
|
</IconButton>
|
||||||
}
|
}
|
||||||
>
|
>
|
||||||
{rateTermDetails.map((termGroup) => (
|
{rateTermDetails.map((termGroup) => (
|
||||||
|
|||||||
@@ -16,6 +16,7 @@
|
|||||||
"./CodeRateCard": "./dist/components/RateCard/Code/index.js",
|
"./CodeRateCard": "./dist/components/RateCard/Code/index.js",
|
||||||
"./PointsRateCard": "./dist/components/RateCard/Points/index.js",
|
"./PointsRateCard": "./dist/components/RateCard/Points/index.js",
|
||||||
"./NoRateAvailableCard": "./dist/components/RateCard/NoRateAvailable/index.js",
|
"./NoRateAvailableCard": "./dist/components/RateCard/NoRateAvailable/index.js",
|
||||||
|
"./IconButton": "./dist/components/IconButton/index.js",
|
||||||
"./Icons": "./dist/components/Icons/index.js",
|
"./Icons": "./dist/components/Icons/index.js",
|
||||||
"./Icons/BathroomCabinetIcon": "./dist/components/Icons/Nucleo/Amenities_Facilities/bathroom-cabinet-2.js",
|
"./Icons/BathroomCabinetIcon": "./dist/components/Icons/Nucleo/Amenities_Facilities/bathroom-cabinet-2.js",
|
||||||
"./Icons/BedHotelIcon": "./dist/components/Icons/Customised/Amenities_Facilities/BedHotel.js",
|
"./Icons/BedHotelIcon": "./dist/components/Icons/Customised/Amenities_Facilities/BedHotel.js",
|
||||||
|
|||||||
Reference in New Issue
Block a user