diff --git a/apps/scandic-web/components/Breadcrumbs/index.tsx b/apps/scandic-web/components/Breadcrumbs/index.tsx index 228c892c0..e9a616561 100644 --- a/apps/scandic-web/components/Breadcrumbs/index.tsx +++ b/apps/scandic-web/components/Breadcrumbs/index.tsx @@ -3,6 +3,7 @@ import { serverClient } from "@/lib/trpc/server" import BreadcrumbsComp from "@/components/TempDesignSystem/Breadcrumbs" import { generateBreadcrumbsSchema } from "@/utils/jsonSchemas" +import type { Breadcrumbs } from "@/types/trpc/routers/contentstack/breadcrumbs" import type { BreadcrumbsProps } from "@/components/TempDesignSystem/Breadcrumbs/breadcrumbs" export default async function Breadcrumbs({ diff --git a/apps/scandic-web/components/ContentType/HotelPage/Map/MobileMapToggle/mobileToggle.module.css b/apps/scandic-web/components/ContentType/HotelPage/Map/MobileMapToggle/mobileToggle.module.css index 2765d6958..afbc001d8 100644 --- a/apps/scandic-web/components/ContentType/HotelPage/Map/MobileMapToggle/mobileToggle.module.css +++ b/apps/scandic-web/components/ContentType/HotelPage/Map/MobileMapToggle/mobileToggle.module.css @@ -1,5 +1,7 @@ .mobileToggle { - position: sticky; + position: fixed; + left: 50%; + transform: translate(-50%, 0); bottom: var(--Spacing-x5); z-index: var(--hotel-mobile-map-toggle-button-z-index); margin: 0 auto; diff --git a/apps/scandic-web/components/TempDesignSystem/Breadcrumbs/breadcrumbs.module.css b/apps/scandic-web/components/TempDesignSystem/Breadcrumbs/breadcrumbs.module.css index bc607a43c..08a7149fe 100644 --- a/apps/scandic-web/components/TempDesignSystem/Breadcrumbs/breadcrumbs.module.css +++ b/apps/scandic-web/components/TempDesignSystem/Breadcrumbs/breadcrumbs.module.css @@ -1,5 +1,4 @@ .breadcrumbs { - display: block; padding: var(--Spacing-x4) 0 var(--Spacing-x3); margin: 0 auto; width: 100%; @@ -23,21 +22,91 @@ } .list { - align-items: center; display: flex; gap: var(--Spacing-x-quarter); - justify-content: flex-start; - list-style: none; - margin: 0 auto; - max-width: var(--max-width-page); + padding-inline-start: 0; } .listItem { align-items: center; display: flex; gap: var(--Spacing-x-quarter); + flex-shrink: 0; + flex-grow: 0; } -.homeLink { +.listItem > a { display: flex; } + +.listItem > svg { + flex-shrink: 0; +} + +.last { + flex: 1; + max-width: 100%; + min-width: 0; +} + +.last > button { + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; + text-align: left; +} + +.button { + border: none; + background: transparent; + height: 100%; + /* a bit of a hack of centering the popover and its arrow */ + padding: 0 10px; + margin: 0 -10px; +} + +.dialog { + background-color: var(--Base-Surface-Primary-light-Normal); + padding: var(--Spacing-x1); + display: flex; + flex-direction: column; + gap: var(--Spacing-x-half); + border-radius: var(--Corner-radius-Medium); + min-width: 169px; + outline: none; + box-shadow: 0 0 4px 2px rgba(0, 0, 0, 0.1); +} + +.dialogLink { + display: block; + border-radius: var(--Corner-radius-Medium); + padding: var(--Spacing-x1); +} + +.dialogLink:focus, +.dialogLink:hover { + background-color: var(--Base-Surface-Primary-light-Hover); +} + +@media screen and (max-width: 767px) { + .desktop { + display: none; + } +} + +@media screen and (min-width: 768px) { + .mobile { + display: none; + } + .desktop { + display: flex; + } +} + +.tooltip { + background-color: var(--Surface-UI-Fill-Intense); + padding: var(--Spacing-x-half) var(--Spacing-x1); + border-radius: var(--Corner-radius-sm); + color: var(--Text-Inverted); + outline: none; +} diff --git a/apps/scandic-web/components/TempDesignSystem/Breadcrumbs/breadcrumbs.ts b/apps/scandic-web/components/TempDesignSystem/Breadcrumbs/breadcrumbs.ts index 99ec65a6f..25cf14250 100644 --- a/apps/scandic-web/components/TempDesignSystem/Breadcrumbs/breadcrumbs.ts +++ b/apps/scandic-web/components/TempDesignSystem/Breadcrumbs/breadcrumbs.ts @@ -2,7 +2,7 @@ import type { VariantProps } from "class-variance-authority" import type { breadcrumbsVariants } from "./variants" -type Breadcrumb = { +export type Breadcrumb = { title: string uid: string href?: string diff --git a/apps/scandic-web/components/TempDesignSystem/Breadcrumbs/index.tsx b/apps/scandic-web/components/TempDesignSystem/Breadcrumbs/index.tsx index adfbc170b..e73049e59 100644 --- a/apps/scandic-web/components/TempDesignSystem/Breadcrumbs/index.tsx +++ b/apps/scandic-web/components/TempDesignSystem/Breadcrumbs/index.tsx @@ -1,18 +1,61 @@ +"use client" + +import { cx } from "class-variance-authority" +import { type PropsWithChildren, useEffect, useState } from "react" +import { + Breadcrumb as AriaBreadcrumb, + Breadcrumbs as AriaBreadCrumbs, + Button, + Dialog, + DialogTrigger, + OverlayArrow, + Popover, +} from "react-aria-components" + import { MaterialIcon } from "@scandic-hotels/design-system/Icons" +import { Typography } from "@scandic-hotels/design-system/Typography" import Link from "@/components/TempDesignSystem/Link" import Footnote from "@/components/TempDesignSystem/Text/Footnote" +import { debounce } from "@/utils/debounce" +import { Arrow } from "../Popover/Arrow" import { breadcrumbsVariants } from "./variants" import styles from "./breadcrumbs.module.css" -import type { BreadcrumbsProps } from "@/components/TempDesignSystem/Breadcrumbs/breadcrumbs" +import type { + Breadcrumb, + BreadcrumbsProps, +} from "@/components/TempDesignSystem/Breadcrumbs/breadcrumbs" export default function Breadcrumbs({ breadcrumbs, variant, }: BreadcrumbsProps) { + // using a ref instead to detect when the element is available and forcing a render + const [element, attachRef] = useState(null) + const [isTooltipDisabled, setIsTooltipDisabled] = useState(false) + + useEffect(() => { + const observer = new ResizeObserver( + debounce(([entry]) => { + const el = entry.target + setIsTooltipDisabled(el.clientWidth >= el.scrollWidth) + }, 200) + ) + + if (element) { + observer.observe(element) + } + + return () => { + if (element) { + observer.unobserve(element) + } + } + }, [element]) + if (!breadcrumbs?.length) { return null } @@ -21,60 +64,137 @@ export default function Breadcrumbs({ variant, }) - const homeBreadcrumb = breadcrumbs.shift() + const [homeBreadcrumb, remainingBreadcrumbs, lastBreadcrumb] = + splitBreadcrumbs(breadcrumbs) + return ( - ) } + +function Breadcrumb({ + className = "", + href, + children, + ...props +}: PropsWithChildren<{ + className?: string + href?: string +}>) { + return ( + + {href ? ( + <> + + {children} + + + ) +} + +function splitBreadcrumbs( + breadcrumbs: Breadcrumb[] +): [Breadcrumb, Breadcrumb[], Breadcrumb] { + const copy = breadcrumbs.slice(0) + const first = copy.shift()! + const last = copy.pop()! + return [first, copy, last] +} diff --git a/apps/scandic-web/components/TempDesignSystem/Popover/Arrow/arrow.module.css b/apps/scandic-web/components/TempDesignSystem/Popover/Arrow/arrow.module.css index 522b5aff8..2a0d21ac2 100644 --- a/apps/scandic-web/components/TempDesignSystem/Popover/Arrow/arrow.module.css +++ b/apps/scandic-web/components/TempDesignSystem/Popover/Arrow/arrow.module.css @@ -1,22 +1,39 @@ -.arrow { - transform-origin: center; - transform: translateY(-2px); +.arrow > svg { + display: block; } [data-placement="left"] .arrow, [data-placement="left top"] .arrow, [data-placement="left bottom"] .arrow { - transform: rotate(270deg) translateY(-6px); + transform: rotate(270deg); } [data-placement="right"] .arrow, [data-placement="right top"] .arrow, [data-placement="right bottom"] .arrow { - transform: rotate(90deg) translateY(-6px); + transform: rotate(90deg); } [data-placement="bottom"] .arrow, [data-placement="bottom left"] .arrow, [data-placement="bottom right"] .arrow { - transform: rotate(180deg) translateY(-2px); + transform: rotate(180deg); +} + +.small { + width: 14px; + height: 8px; +} + +.medium { + width: 27px; + height: 13px; +} + +.black { + fill: var(--Surface-UI-Fill-Intense); +} + +.white { + fill: var(--Surface-UI-Fill-Default); } diff --git a/apps/scandic-web/components/TempDesignSystem/Popover/Arrow/arrow.ts b/apps/scandic-web/components/TempDesignSystem/Popover/Arrow/arrow.ts new file mode 100644 index 000000000..67e279e12 --- /dev/null +++ b/apps/scandic-web/components/TempDesignSystem/Popover/Arrow/arrow.ts @@ -0,0 +1,7 @@ +import type { VariantProps } from "class-variance-authority" + +import type { arrowVariants } from "./variants" + +export interface ArrowProps + extends Omit, "color">, + VariantProps {} diff --git a/apps/scandic-web/components/TempDesignSystem/Popover/Arrow/index.tsx b/apps/scandic-web/components/TempDesignSystem/Popover/Arrow/index.tsx index 4c67b059b..eff65398a 100644 --- a/apps/scandic-web/components/TempDesignSystem/Popover/Arrow/index.tsx +++ b/apps/scandic-web/components/TempDesignSystem/Popover/Arrow/index.tsx @@ -1,18 +1,14 @@ -import styles from "./arrow.module.css" +import { arrowVariants } from "./variants" + +import type { ArrowProps } from "./arrow" + +export function Arrow({ color, size }: ArrowProps) { + const className = arrowVariants({ color, size }) -export function Arrow() { return ( -
- - +
+ +
) diff --git a/apps/scandic-web/components/TempDesignSystem/Popover/Arrow/variants.ts b/apps/scandic-web/components/TempDesignSystem/Popover/Arrow/variants.ts new file mode 100644 index 000000000..c0baaa0d4 --- /dev/null +++ b/apps/scandic-web/components/TempDesignSystem/Popover/Arrow/variants.ts @@ -0,0 +1,20 @@ +import { cva } from "class-variance-authority" + +import styles from "./arrow.module.css" + +export const arrowVariants = cva(styles.arrow, { + variants: { + color: { + black: styles.black, + white: styles.white, + }, + size: { + small: styles.small, + medium: styles.medium, + }, + }, + defaultVariants: { + color: "white", + size: "medium", + }, +}) diff --git a/apps/scandic-web/server/routers/contentstack/breadcrumbs/output.ts b/apps/scandic-web/server/routers/contentstack/breadcrumbs/output.ts index 586bf3ccd..992aae57e 100644 --- a/apps/scandic-web/server/routers/contentstack/breadcrumbs/output.ts +++ b/apps/scandic-web/server/routers/contentstack/breadcrumbs/output.ts @@ -52,24 +52,26 @@ export const rawBreadcrumbsDataSchema = z.object({ system: systemSchema, }) -export const breadcrumbsSchema = rawBreadcrumbsDataSchema.transform((data) => { - const { parentsConnection, title } = data.web.breadcrumbs - const parentBreadcrumbs = parentsConnection.edges.map((breadcrumb) => { - return { - href: removeMultipleSlashes( - `/${breadcrumb.node.system.locale}/${breadcrumb.node.url}` - ), - title: breadcrumb.node.web.breadcrumbs.title, - uid: breadcrumb.node.system.uid, +export const breadcrumbsSchema = rawBreadcrumbsDataSchema.transform( + (data): { title: string; href: string; uid: string }[] => { + const { parentsConnection, title } = data.web.breadcrumbs + const parentBreadcrumbs = parentsConnection.edges.map((breadcrumb) => { + return { + href: removeMultipleSlashes( + `/${breadcrumb.node.system.locale}/${breadcrumb.node.url}` + ), + title: breadcrumb.node.web.breadcrumbs.title, + uid: breadcrumb.node.system.uid, + } + }) + + const pageBreadcrumb = { + title, + uid: data.system.uid, + href: removeMultipleSlashes(`/${data.system.locale}/${data.url}`), } - }) + const homeBreadcrumb = homeBreadcrumbs[data.system.locale] - const pageBreadcrumb = { - title, - uid: data.system.uid, - href: removeMultipleSlashes(`/${data.system.locale}/${data.url}`), + return [homeBreadcrumb, parentBreadcrumbs, pageBreadcrumb].flat() } - const homeBreadcrumb = homeBreadcrumbs[data.system.locale] - - return [homeBreadcrumb, parentBreadcrumbs, pageBreadcrumb].flat() -}) +)