feat(SW-184): implementing mobile design

This commit is contained in:
Erik Tiekstra
2024-08-21 14:38:29 +02:00
parent b51a4c46e8
commit a2e2cf575e
44 changed files with 526 additions and 111 deletions

View File

@@ -104,6 +104,7 @@
--max-width-text-block: 49.5rem;
--mobile-site-header-height: 70.047px;
--max-width-navigation: 89.5rem;
--main-menu-mobile-height: 75px;
}
* {

View File

@@ -0,0 +1,6 @@
.topLink {
display: flex;
align-items: center;
gap: var(--Spacing-x1);
font-size: var(--typography-Caption-Regular-fontSize);
}

View File

@@ -0,0 +1,13 @@
import Link from "@/components/TempDesignSystem/Link"
import styles from "./headerLink.module.css"
import { HeaderLinkProps } from "@/types/components/header/headerLink"
export default function HeaderLink({ children, ...props }: HeaderLinkProps) {
return (
<Link color="burgundy" className={styles.topLink} {...props}>
{children}
</Link>
)
}

View File

@@ -2,6 +2,7 @@
display: flex;
gap: var(--Spacing-x1);
align-items: center;
width: 100%;
background-color: transparent;
color: var(--Base-Text-High-contrast);
border-width: 0;

View File

@@ -0,0 +1,65 @@
"use client"
import { Dialog, Modal } from "react-aria-components"
import { useIntl } from "react-intl"
import useDropdownStore from "@/stores/main-menu"
import { GiftIcon, SearchIcon, ServiceIcon } from "@/components/Icons"
import LanguageSwitcher from "@/components/LanguageSwitcher"
import HeaderLink from "../../HeaderLink"
import NavigationMenu from "../NavigationMenu"
import styles from "./mobileMenu.module.css"
import { MobileMenuProps } from "@/types/components/header/mobileMenu"
export default function MobileMenu({
mainNavigation,
languageUrls,
}: MobileMenuProps) {
const intl = useIntl()
const { isHamburgerMenuOpen, toggleHamburgerMenu } = useDropdownStore()
return (
<>
<button
type="button"
className={`${styles.hamburger} ${isHamburgerMenuOpen ? styles.isExpanded : ""}`}
aria-pressed="false"
aria-label={intl.formatMessage({ id: "Menu" })}
onClick={toggleHamburgerMenu}
>
<span className={styles.bar}></span>
</button>
<Modal
className={styles.modal}
isOpen={isHamburgerMenuOpen}
onOpenChange={toggleHamburgerMenu}
>
<Dialog
className={styles.dialog}
aria-label={intl.formatMessage({ id: "Menu" })}
>
<NavigationMenu variant="mobile" items={mainNavigation} />
<footer className={styles.footer}>
<HeaderLink href="#">
<SearchIcon width={20} height={20} color="burgundy" />
{intl.formatMessage({ id: "Find booking" })}
</HeaderLink>
<HeaderLink href="#">
<GiftIcon width={20} height={20} color="burgundy" />
{intl.formatMessage({ id: "Join Scandic Friends" })}
</HeaderLink>
<HeaderLink href="#">
<ServiceIcon width={20} height={20} color="burgundy" />
{intl.formatMessage({ id: "Customer service" })}
</HeaderLink>
<LanguageSwitcher urls={languageUrls} />
</footer>
</Dialog>
</Modal>
</>
)
}

View File

@@ -0,0 +1,109 @@
@keyframes slide-in {
from {
right: -100vw;
}
to {
right: 0;
}
}
.hamburger {
background-color: transparent;
border: none;
cursor: pointer;
justify-self: flex-start;
padding: 11px 8px 16px;
user-select: none;
}
.bar,
.bar::after,
.bar::before {
background: var(--Base-Text-High-contrast);
border-radius: 2.3px;
display: inline-block;
height: 3px;
position: relative;
transition: all 0.2s;
width: 32px;
}
.bar::after,
.bar::before {
content: "";
left: 0;
position: absolute;
transform-origin: 2.286px center;
}
.bar::after {
top: -8px;
}
.bar::before {
top: 8px;
}
.isExpanded .bar {
background: transparent;
}
.isExpanded .bar::after,
.isExpanded .bar::before {
top: 0;
transform-origin: 50% 50%;
width: 32px;
}
.isExpanded .bar::after {
transform: rotate(-45deg);
}
.isExpanded .bar::before {
transform: rotate(45deg);
}
@media screen and (min-width: 768px) {
.hamburger {
display: none;
}
}
.overlay {
position: absolute;
top: var(--main-menu-mobile-height);
bottom: 0;
left: 0;
right: 0;
}
.modal {
position: fixed;
right: auto;
top: var(--main-menu-mobile-height);
bottom: 0;
width: 100%;
background-color: var(--Base-Surface-Primary-light-Normal);
}
.modal[data-entering] {
animation: slide-in 0.3s;
}
.modal[data-exiting] {
animation: slide-in 0.3s reverse;
}
.dialog {
height: 100%;
overflow-y: auto;
display: grid;
align-content: space-between;
}
.footer {
background-color: var(--Base-Surface-Subtle-Normal);
padding: var(--Spacing-x4) var(--Spacing-x2);
display: grid;
gap: var(--Spacing-x2);
}

View File

@@ -35,7 +35,9 @@ export default function MyPagesMenu({ navigation, user }: MyPagesMenuProps) {
<div className={styles.myPagesMenu}>
<MainMenuButton className={styles.button} onClick={toggleMyPagesMenu}>
<Avatar initials={getInitials(user.firstName, user.lastName)} />
{intl.formatMessage({ id: "Hi" })} {user.firstName}!
<span className={styles.userName}>
{intl.formatMessage({ id: "Hi" })} {user.firstName}!
</span>
<ChevronDownIcon
className={`${styles.chevron} ${isMyPagesMenuOpen ? styles.isExpanded : ""}`}
color="red"
@@ -98,9 +100,15 @@ export default function MyPagesMenu({ navigation, user }: MyPagesMenuProps) {
</nav>
</div>
) : (
<Link href={myPages[lang]} className={styles.link}>
<Link
href={myPages[lang]}
className={styles.loginLink}
aria-label={intl.formatMessage({ id: "Log in/Join" })}
>
<Avatar />
{intl.formatMessage({ id: "Log in/Join" })}
<span className={styles.userName}>
{intl.formatMessage({ id: "Log in/Join" })}
</span>
</Link>
)
}

View File

@@ -2,11 +2,8 @@
position: relative;
}
.button {
font-weight: 600;
}
.chevron {
display: none;
transition: transform 0.2s;
}
@@ -14,6 +11,12 @@
transform: rotate(180deg);
}
.userName {
display: none;
font-weight: 600;
color: var(--Base-Text-High-contrast);
}
.dropdown {
position: absolute;
top: 46px;
@@ -80,6 +83,15 @@
opacity: 0;
}
.link:hover .arrow {
opacity: 1;
.loginLink {
display: flex;
align-items: center;
gap: var(--Spacing-x1);
}
@media screen and (min-width: 768px) {
.userName,
.chevron {
display: initial;
}
}

View File

@@ -2,33 +2,46 @@
import { useState } from "react"
import { ChevronDownIcon } from "@/components/Icons"
import { ChevronDownIcon, ChevronRightIcon } from "@/components/Icons"
import Link from "@/components/TempDesignSystem/Link"
import MainMenuButton from "../../MainMenuButton"
import { navigationMenuItemVariants } from "./variants"
import styles from "./navigationMenuItem.module.css"
import { NavigationMenuItemProps } from "@/types/components/header/navigationMenuItem"
export default function MenuItem({ item }: NavigationMenuItemProps) {
export default function MenuItem({ item, variant }: NavigationMenuItemProps) {
const { children, title, href, seeAllLinkText, infoCard } = item
const [isExpanded, setIsExpanded] = useState(false)
const isMobile = variant === "mobile"
function handleButtonClick() {
setIsExpanded((prev) => !prev)
}
return children?.length ? (
<MainMenuButton onClick={handleButtonClick}>
<MainMenuButton
onClick={handleButtonClick}
className={navigationMenuItemVariants({ variant })}
>
{title}
<ChevronDownIcon
className={`${styles.chevron} ${isExpanded ? styles.isExpanded : ""}`}
color="red"
/>
{isMobile ? (
<ChevronRightIcon className={`${styles.chevron}`} color="red" />
) : (
<ChevronDownIcon
className={`${styles.chevron} ${isExpanded ? styles.isExpanded : ""}`}
color="red"
/>
)}
</MainMenuButton>
) : (
<Link href={href} color="burgundy">
<Link
href={href}
color="burgundy"
className={navigationMenuItemVariants({ variant })}
>
{title}
</Link>
)

View File

@@ -1,3 +1,10 @@
.navigationMenuItem.mobile {
display: flex;
justify-content: space-between;
padding: var(--Spacing-x2) 0;
font-size: var(--typography-Subtitle-1-Mobile-fontSize);
}
.chevron {
transition: transform 0.2s;
}

View File

@@ -0,0 +1,15 @@
import { cva } from "class-variance-authority"
import styles from "./navigationMenuItem.module.css"
export const navigationMenuItemVariants = cva(styles.navigationMenuItem, {
variants: {
variant: {
default: styles.default,
mobile: styles.mobile,
},
},
defaultVariants: {
variant: "default",
},
})

View File

@@ -1,15 +1,19 @@
import NavigationMenuItem from "./NavigationMenuItem"
import { navigationMenuVariants } from "./variants"
import styles from "./navigationMenu.module.css"
import { NavigationMenuProps } from "@/types/components/header/navigationMenu"
export default function NavigationMenu({ items }: NavigationMenuProps) {
export default function NavigationMenu({
items,
variant,
}: NavigationMenuProps) {
return (
<ul className={styles.navigationMenu}>
<ul className={navigationMenuVariants({ variant })}>
{items.map((item) => (
<li key={item.id}>
<NavigationMenuItem item={item} />
<li key={item.id} className={styles.item}>
<NavigationMenuItem variant={variant} item={item} />
</li>
))}
</ul>

View File

@@ -1,8 +1,26 @@
.navigationMenu {
list-style: none;
margin: 0;
display: flex;
justify-content: space-between;
align-items: center;
gap: var(--Spacing-x4);
display: none;
}
.navigationMenu.mobile {
display: grid;
width: 100%;
gap: 0;
justify-content: stretch;
padding: var(--Spacing-x-one-and-half) var(--Spacing-x2) var(--Spacing-x2);
}
.navigationMenu.mobile .item {
border-bottom: 1px solid var(--Base-Border-Subtle);
}
@media screen and (min-width: 768px) {
.navigationMenu.default {
display: flex;
}
}

View File

@@ -0,0 +1,15 @@
import { cva } from "class-variance-authority"
import styles from "./navigationMenu.module.css"
export const navigationMenuVariants = cva(styles.navigationMenu, {
variants: {
variant: {
default: styles.default,
mobile: styles.mobile,
},
},
defaultVariants: {
variant: "default",
},
})

View File

@@ -6,12 +6,15 @@ import Image from "@/components/Image"
import { getIntl } from "@/i18n"
import { navigationMenuItems } from "../tempHeaderData"
import MobileMenu from "./MobileMenu"
import MyPagesMenu from "./MyPagesMenu"
import NavigationMenu from "./NavigationMenu"
import styles from "./mainMenu.module.css"
export default async function MainMenu() {
import { MainMenuProps } from "@/types/components/header/mainMenu"
export default async function MainMenu({ languageUrls }: MainMenuProps) {
const intl = await getIntl()
const myPagesNavigation =
await serverClient().contentstack.myPages.navigation.get()
@@ -28,13 +31,19 @@ export default async function MainMenu() {
data-js="scandiclogoimg"
data-nosvgsrc="/_static/img/scandic-logotype.png"
itemProp="logo"
height={24}
height={22}
src="/_static/img/scandic-logotype.svg"
width={113}
width={103}
/>
</Link>
<NavigationMenu items={navigationMenuItems} />
<MyPagesMenu navigation={myPagesNavigation} user={user} />
<div className={styles.menus}>
<NavigationMenu items={navigationMenuItems} />
<MyPagesMenu navigation={myPagesNavigation} user={user} />
<MobileMenu
languageUrls={languageUrls}
mainNavigation={navigationMenuItems}
/>
</div>
</nav>
</div>
)

View File

@@ -7,8 +7,35 @@
.nav {
max-width: var(--max-width-navigation);
margin: 0 auto;
display: flex;
justify-content: space-between;
display: grid;
grid-template-columns: max-content 1fr;
align-items: center;
gap: var(--Spacing-x3);
gap: var(--Spacing-x2);
}
.menus {
display: flex;
justify-self: end;
align-items: center;
gap: var(--Spacing-x2);
}
.logoLink {
display: inline-flex;
width: auto;
}
.logo {
width: 6.4375rem;
}
@media screen and (min-width: 768px) {
.nav {
display: flex;
justify-content: space-between;
gap: var(--Spacing-x3);
}
.menus {
display: contents;
}
}

View File

@@ -4,15 +4,15 @@ import { useIntl } from "react-intl"
import { SearchIcon } from "@/components/Icons"
import TopMenuButton from "../TopMenuButton"
import styles from "./search.module.css"
export default function Search() {
const intl = useIntl()
return (
<TopMenuButton>
<button type="button" className={styles.button}>
<SearchIcon width={20} height={20} color="burgundy" />
{intl.formatMessage({ id: "Find booking" })}
</TopMenuButton>
</button>
)
}

View File

@@ -1,9 +1,20 @@
.button {
background-color: transparent;
color: var(--Base-Text-High-contrast);
font-family: var(--typography-Caption-Regular-fontFamily);
font-size: var(--typography-Caption-Regular-fontSize);
border-width: 0;
padding: 0;
cursor: pointer;
display: flex;
gap: var(--Spacing-x1);
align-items: center;
width: 100%;
}
@media screen and (min-width: 768px) {
.button {
font-size: var(--typography-Body-Bold-fontSize);
font-family: var(--typography-Body-Bold-fontFamily);
}
}

View File

@@ -1,14 +0,0 @@
import styles from "./topMenuButton.module.css"
import { TopMenuButtonProps } from "@/types/components/header/topMenuButton"
export default function TopMenuButton({
children,
...props
}: TopMenuButtonProps) {
return (
<button type="button" className={styles.button} {...props}>
{children}
</button>
)
}

View File

@@ -1,9 +0,0 @@
.button {
background-color: transparent;
color: var(--Base-Text-High-contrast);
border-width: 0;
cursor: pointer;
display: flex;
gap: var(--Spacing-x1);
align-items: center;
}

View File

@@ -1,32 +1,29 @@
import { serverClient } from "@/lib/trpc/server"
import { GiftIcon } from "@/components/Icons"
import Link from "@/components/TempDesignSystem/Link"
import { GiftIcon, SearchIcon } from "@/components/Icons"
import LanguageSwitcher from "@/components/LanguageSwitcher"
import { getIntl } from "@/i18n"
import LanguageSwitcher from "./LanguageSwitcher"
import Search from "./Search"
import HeaderLink from "../HeaderLink"
import styles from "./topMenu.module.css"
export default async function TopMenu() {
const intl = await getIntl()
const languages = await serverClient().contentstack.languageSwitcher.get()
import { TopMenuProps } from "@/types/components/header/topMenu"
if (!languages) {
return null
}
export default async function TopMenu({ languageUrls }: TopMenuProps) {
const intl = await getIntl()
return (
<div className={styles.topMenu}>
<div className={styles.content}>
<Link variant="icon" color="burgundy" href="#" size="small">
<HeaderLink href="#">
<GiftIcon width={20} height={20} color="burgundy" />
{intl.formatMessage({ id: "Join Scandic Friends" })}
</Link>
</HeaderLink>
<div className={styles.right}>
<LanguageSwitcher urls={languages.urls} />
<Search />
<LanguageSwitcher urls={languageUrls} />
<HeaderLink href="#">
<SearchIcon width={20} height={20} color="burgundy" />
{intl.formatMessage({ id: "Find booking" })}
</HeaderLink>
</div>
</div>
</div>

View File

@@ -1,4 +1,5 @@
.topMenu {
display: none;
background-color: var(--Base-Surface-Subtle-Normal);
padding: var(--Spacing-x2) var(--Spacing-x-one-and-half);
border-bottom: 1px solid var(--Base-Border-Subtle);
@@ -17,3 +18,9 @@
gap: var(--Spacing-x2);
align-items: center;
}
@media screen and (min-width: 768px) {
.topMenu {
display: block;
}
}

View File

@@ -1,13 +1,21 @@
import { serverClient } from "@/lib/trpc/server"
import MainMenu from "./MainMenu"
import TopMenu from "./TopMenu"
import styles from "./header.module.css"
export default async function Header() {
const languages = await serverClient().contentstack.languageSwitcher.get()
if (!languages) {
return null
}
return (
<header className={styles.header}>
<TopMenu />
<MainMenu />
<TopMenu languageUrls={languages.urls} />
<MainMenu languageUrls={languages.urls} />
</header>
)
}

View File

@@ -11,32 +11,29 @@ export default function ChevronRightIcon({
return (
<svg
className={classNames}
fill="none"
xmlns="http://www.w3.org/2000/svg"
width="20"
height="20"
viewBox="0 0 20 20"
width="20"
xmlns="http://www.w3.org/2000/svg"
fill="none"
{...props}
>
<g id="chevron_right_small">
<mask
id="mask0_4140_3161"
style={{ maskType: "alpha" }}
maskUnits="userSpaceOnUse"
x="0"
y="0"
width="20"
height="20"
>
<rect id="Bounding box" width="20" height="20" fill="#D9D9D9" />
</mask>
<g mask="url(#mask0_4140_3161)">
<path
id="Vector"
d="M10.5417 10.0001L7.3125 6.77095C7.16667 6.62512 7.09375 6.4463 7.09375 6.23449C7.09375 6.02269 7.16667 5.84039 7.3125 5.68762C7.45833 5.53484 7.63889 5.45671 7.85417 5.45324C8.06944 5.44977 8.25347 5.52442 8.40625 5.6772L12.1771 9.44803C12.2535 9.52442 12.3108 9.60949 12.349 9.70324C12.3872 9.79699 12.4063 9.89595 12.4063 10.0001C12.4063 10.1043 12.3872 10.2032 12.349 10.297C12.3108 10.3907 12.2535 10.4758 12.1771 10.5522L8.40625 14.323C8.25347 14.4758 8.06944 14.5505 7.85417 14.547C7.63889 14.5435 7.45833 14.4654 7.3125 14.3126C7.16667 14.1598 7.09375 13.9775 7.09375 13.7657C7.09375 13.5539 7.16667 13.3751 7.3125 13.2293L10.5417 10.0001Z"
fill="#8F4350"
/>
</g>
<mask
id="mask0_2291_1656"
style={{ maskType: "alpha" }}
maskUnits="userSpaceOnUse"
x="0"
y="0"
width="20"
height="20"
>
<rect width="20" height="20" fill="#D9D9D9" />
</mask>
<g mask="url(#mask0_2291_1656)">
<path
d="M12.1042 9.99967L6.28125 4.17676C6.10069 3.9962 6.00868 3.77572 6.00521 3.5153C6.00174 3.25488 6.09028 3.0344 6.27083 2.85384C6.45139 2.67329 6.67188 2.58301 6.93229 2.58301C7.19271 2.58301 7.41319 2.67329 7.59375 2.85384L13.7708 9.02051C13.9167 9.16634 14.0191 9.31912 14.0781 9.47884C14.1372 9.63856 14.1667 9.81217 14.1667 9.99967C14.1667 10.1872 14.1372 10.3608 14.0781 10.5205C14.0191 10.6802 13.9167 10.833 13.7708 10.9788L7.60417 17.1455C7.42361 17.3261 7.20139 17.4181 6.9375 17.4215C6.67361 17.425 6.45139 17.3365 6.27083 17.1559C6.09028 16.9754 6 16.7549 6 16.4945C6 16.234 6.09028 16.0136 6.27083 15.833L12.1042 9.99967Z"
fill="#CD0921"
/>
</g>
</svg>
)

View File

@@ -2,7 +2,7 @@ import { iconVariants } from "./variants"
import type { IconProps } from "@/types/components/icon"
export default function GiftIcon({ className, color, ...props }: IconProps) {
export default function SearchIcon({ className, color, ...props }: IconProps) {
const classNames = iconVariants({ className, color })
return (
<svg

View File

@@ -0,0 +1,36 @@
import { iconVariants } from "./variants"
import type { IconProps } from "@/types/components/icon"
export default function ServiceIcon({ className, color, ...props }: IconProps) {
const classNames = iconVariants({ className, color })
return (
<svg
className={classNames}
xmlns="http://www.w3.org/2000/svg"
width="20"
height="20"
viewBox="0 0 20 20"
fill="none"
{...props}
>
<mask
id="mask0_2291_1682"
style={{ maskType: "alpha" }}
maskUnits="userSpaceOnUse"
x="0"
y="0"
width="20"
height="20"
>
<rect width="20" height="20" fill="#D9D9D9" />
</mask>
<g mask="url(#mask0_2291_1682)">
<path
d="M15.792 18.3337C14.0559 18.3337 12.3406 17.9552 10.6462 17.1982C8.95171 16.4413 7.41005 15.3684 6.02116 13.9795C4.63227 12.5906 3.55935 11.0489 2.80241 9.35449C2.04546 7.66005 1.66699 5.94477 1.66699 4.20866C1.66699 3.95866 1.75033 3.75033 1.91699 3.58366C2.08366 3.41699 2.29199 3.33366 2.54199 3.33366H5.91699C6.11144 3.33366 6.28505 3.39963 6.43783 3.53158C6.5906 3.66352 6.68088 3.81977 6.70866 4.00033L7.25033 6.91699C7.2781 7.13921 7.27116 7.32671 7.22949 7.47949C7.18783 7.63227 7.11144 7.76421 7.00033 7.87533L4.97949 9.91699C5.25727 10.4309 5.58713 10.9274 5.96908 11.4066C6.35102 11.8857 6.77116 12.3475 7.22949 12.792C7.66005 13.2225 8.11144 13.6219 8.58366 13.9899C9.05588 14.358 9.55588 14.6948 10.0837 15.0003L12.042 13.042C12.167 12.917 12.3302 12.8232 12.5316 12.7607C12.733 12.6982 12.9309 12.6809 13.1253 12.7087L16.0003 13.292C16.1948 13.3475 16.3545 13.4482 16.4795 13.5941C16.6045 13.7399 16.667 13.9031 16.667 14.0837V17.4587C16.667 17.7087 16.5837 17.917 16.417 18.0837C16.2503 18.2503 16.042 18.3337 15.792 18.3337ZM4.18783 8.33366L5.56283 6.95866L5.20866 5.00033H3.35449C3.42394 5.56977 3.52116 6.13227 3.64616 6.68783C3.77116 7.24338 3.95171 7.79199 4.18783 8.33366ZM11.6462 15.792C12.1878 16.0281 12.7399 16.2156 13.3024 16.3545C13.8649 16.4934 14.4309 16.5837 15.0003 16.6253V14.792L13.042 14.3962L11.6462 15.792ZM14.167 10.0003C13.0142 10.0003 12.0316 9.59408 11.2191 8.78158C10.4066 7.96908 10.0003 6.98644 10.0003 5.83366C10.0003 4.68088 10.4066 3.69824 11.2191 2.88574C12.0316 2.07324 13.0142 1.66699 14.167 1.66699C15.3198 1.66699 16.3024 2.07324 17.1149 2.88574C17.9274 3.69824 18.3337 4.68088 18.3337 5.83366C18.3337 6.98644 17.9274 7.96908 17.1149 8.78158C16.3024 9.59408 15.3198 10.0003 14.167 10.0003ZM13.7503 8.33366H14.5837V5.00033H13.7503V8.33366ZM14.167 4.16699C14.2781 4.16699 14.3753 4.12533 14.4587 4.04199C14.542 3.95866 14.5837 3.86144 14.5837 3.75033C14.5837 3.63921 14.542 3.54199 14.4587 3.45866C14.3753 3.37533 14.2781 3.33366 14.167 3.33366C14.0559 3.33366 13.9587 3.37533 13.8753 3.45866C13.792 3.54199 13.7503 3.63921 13.7503 3.75033C13.7503 3.86144 13.792 3.95866 13.8753 4.04199C13.9587 4.12533 14.0559 4.16699 14.167 4.16699Z"
fill="#4D001B"
/>
</g>
</svg>
)
}

View File

@@ -41,6 +41,7 @@ import {
RestaurantIcon,
SaunaIcon,
SearchIcon,
ServiceIcon,
TshirtWashIcon,
WarningTriangle,
WifiIcon,
@@ -128,6 +129,8 @@ export function getIconByIconName(icon?: IconName): FC<IconProps> | null {
return SaunaIcon
case IconName.Search:
return SearchIcon
case IconName.Service:
return ServiceIcon
case IconName.Tripadvisor:
return TripAdvisorIcon
case IconName.TshirtWash:

View File

@@ -39,6 +39,7 @@ export { default as RestaurantIcon } from "./Restaurant"
export { default as SaunaIcon } from "./Sauna"
export { default as ScandicLogoIcon } from "./ScandicLogo"
export { default as SearchIcon } from "./Search"
export { default as ServiceIcon } from "./Service"
export { default as TshirtWashIcon } from "./TshirtWash"
export { default as WarningTriangle } from "./WarningTriangle"
export { default as WifiIcon } from "./Wifi"

View File

@@ -8,8 +8,6 @@ import useDropdownStore from "@/stores/main-menu"
import { CheckIcon, ChevronDownIcon, GlobeIcon } from "@/components/Icons"
import useLang from "@/hooks/useLang"
import TopMenuButton from "../TopMenuButton"
import styles from "./languageSwitcher.module.css"
import { LanguageSwitcherProps } from "@/types/components/current/languageSwitcher"
@@ -22,7 +20,11 @@ export default function LanguageSwitcher({ urls }: LanguageSwitcherProps) {
return (
<div className={styles.languageSwitcher}>
<TopMenuButton onClick={toggleLanguageSwitcher}>
<button
type="button"
className={styles.button}
onClick={toggleLanguageSwitcher}
>
<GlobeIcon width={20} height={20} color="burgundy" />
<span>{languages[currentLanguage]}</span>
<ChevronDownIcon
@@ -31,7 +33,8 @@ export default function LanguageSwitcher({ urls }: LanguageSwitcherProps) {
height={20}
color="burgundy"
/>
</TopMenuButton>
</button>
<div
className={`${styles.dropdown} ${isLanguageSwitcherOpen ? styles.isExpanded : ""}`}
>

View File

@@ -5,14 +5,20 @@
.button {
background-color: transparent;
color: var(--Base-Text-High-contrast);
font-family: var(--typography-Caption-Regular-fontFamily);
font-size: var(--typography-Caption-Regular-fontSize);
border-width: 0;
padding: 0;
cursor: pointer;
display: flex;
display: grid;
grid-template-columns: repeat(2, max-content) 1fr;
gap: var(--Spacing-x1);
align-items: center;
width: 100%;
}
.chevron {
justify-self: end;
transition: transform 0.2s;
}
@@ -78,3 +84,11 @@
.link:hover {
font-weight: 600;
}
@media screen and (min-width: 768px) {
.button {
grid-template-columns: repeat(3, max-content);
font-size: var(--typography-Body-Bold-fontSize);
font-family: var(--typography-Body-Bold-fontFamily);
}
}

View File

@@ -44,6 +44,7 @@
"Country code": "Landekode",
"Credit card deleted successfully": "Kreditkort blev slettet",
"Current password": "Nuværende kodeord",
"Customer service": "Kundeservice",
"Date of Birth": "Fødselsdato",
"Day": "Dag",
"Description": "Beskrivelse",
@@ -94,6 +95,7 @@
"Members": "Medlemmer",
"Membership cards": "Medlemskort",
"Membership ID": "Medlems-id",
"Menu": "Menu",
"Modify": "Ændre",
"Month": "Måned",
"My communication preferences": "Mine kommunikationspræferencer",

View File

@@ -43,6 +43,7 @@
"Country code": "Landesvorwahl",
"Credit card deleted successfully": "Kreditkarte erfolgreich gelöscht",
"Current password": "Aktuelles Passwort",
"Customer service": "Kundendienst",
"Date of Birth": "Geburtsdatum",
"Day": "Tag",
"Description": "Beschreibung",
@@ -92,6 +93,7 @@
"Members": "Mitglieder",
"Membership cards": "Mitgliedskarten",
"Membership ID": "Mitglieds-ID",
"Menu": "Menu",
"Modify": "Ändern",
"Month": "Monat",
"My communication preferences": "Meine Kommunikationseinstellungen",

View File

@@ -44,6 +44,7 @@
"Country code": "Country code",
"Credit card deleted successfully": "Credit card deleted successfully",
"Current password": "Current password",
"Customer service": "Customer service",
"Date of Birth": "Date of Birth",
"Day": "Day",
"Description": "Description",
@@ -99,6 +100,7 @@
"Members": "Members",
"Membership cards": "Membership cards",
"Membership ID": "Membership ID",
"Menu": "Menu",
"Modify": "Modify",
"Month": "Month",
"My communication preferences": "My communication preferences",

View File

@@ -44,6 +44,7 @@
"Country code": "Maatunnus",
"Credit card deleted successfully": "Luottokortti poistettu onnistuneesti",
"Current password": "Nykyinen salasana",
"Customer service": "Asiakaspalvelu",
"Date of Birth": "Syntymäaika",
"Day": "Päivä",
"Description": "Kuvaus",
@@ -93,6 +94,7 @@
"Members": "Jäsenet",
"Membership cards": "Jäsenkortit",
"Membership ID": "Jäsentunnus",
"Menu": "Menu",
"Modify": "Muokkaa",
"Month": "Kuukausi",
"My communication preferences": "Viestintämieltymykseni",

View File

@@ -42,6 +42,7 @@
"Could not find requested resource": "Kunne ikke finne den forespurte ressursen",
"Country": "Land",
"Country code": "Landskode",
"Customer service": "Kundeservice",
"Credit card deleted successfully": "Kredittkort slettet",
"Current password": "Nåværende passord",
"Date of Birth": "Fødselsdato",
@@ -94,6 +95,7 @@
"Members": "Medlemmer",
"Membership cards": "Medlemskort",
"Membership ID": "Medlems-ID",
"Menu": "Menu",
"Modify": "Endre",
"Month": "Måned",
"My communication preferences": "Mine kommunikasjonspreferanser",

View File

@@ -44,6 +44,7 @@
"Country code": "Landskod",
"Credit card deleted successfully": "Kreditkort har tagits bort",
"Current password": "Nuvarande lösenord",
"Customer service": "Kundservice",
"Date of Birth": "Födelsedatum",
"Day": "Dag",
"Description": "Beskrivning",
@@ -96,6 +97,7 @@
"Members": "Medlemmar",
"Membership cards": "Medlemskort",
"Membership ID": "Medlems-ID",
"Menu": "Meny",
"Modify": "Ändra",
"Month": "Månad",
"My communication preferences": "Mina kommunikationspreferenser",

View File

@@ -19,13 +19,7 @@ const useDropdownStore = create<DropdownState>((set) => ({
isMyPagesMenuOpen: false,
isLanguageSwitcherOpen: false,
toggleHamburgerMenu: () =>
set((state) => {
// Close the other dropdown if it's open
if (!state.isHamburgerMenuOpen && state.isMyPagesMobileMenuOpen) {
set({ isMyPagesMobileMenuOpen: false })
}
return { isHamburgerMenuOpen: !state.isHamburgerMenuOpen }
}),
set((state) => ({ isHamburgerMenuOpen: !state.isHamburgerMenuOpen })),
toggleMyPagesMobileMenu: () =>
set((state) => {
// Close the other dropdown if it's open

View File

@@ -0,0 +1,3 @@
import { LinkProps } from "@/components/TempDesignSystem/Link/link"
export interface HeaderLinkProps extends React.PropsWithChildren<LinkProps> {}

View File

@@ -0,0 +1,5 @@
import { LanguageSwitcherData } from "@/types/requests/languageSwitcher"
export interface MainMenuProps {
languageUrls: LanguageSwitcherData
}

View File

@@ -0,0 +1,8 @@
import { MainNavigationItem } from "./mainNavigationItem"
import { LanguageSwitcherData } from "@/types/requests/languageSwitcher"
export interface MobileMenuProps {
languageUrls: LanguageSwitcherData
mainNavigation: MainNavigationItem[]
}

View File

@@ -1,5 +1,10 @@
import { VariantProps } from "class-variance-authority"
import { navigationMenuVariants } from "@/components/Header/MainMenu/NavigationMenu/variants"
import { MainNavigationItem } from "@/types/components/header/mainNavigationItem"
export interface NavigationMenuProps {
export interface NavigationMenuProps
extends VariantProps<typeof navigationMenuVariants> {
items: MainNavigationItem[]
}

View File

@@ -1,5 +1,10 @@
import { VariantProps } from "class-variance-authority"
import { navigationMenuItemVariants } from "@/components/Header/MainMenu/NavigationMenu/NavigationMenuItem/variants"
import { MainNavigationItem } from "@/types/components/header/mainNavigationItem"
export interface NavigationMenuItemProps {
export interface NavigationMenuItemProps
extends VariantProps<typeof navigationMenuItemVariants> {
item: MainNavigationItem
}

View File

@@ -0,0 +1,5 @@
import { LanguageSwitcherData } from "@/types/requests/languageSwitcher"
export interface TopMenuProps {
languageUrls: LanguageSwitcherData
}

View File

@@ -46,6 +46,7 @@ export enum IconName {
Restaurant = "Restaurant",
Sauna = "Sauna",
Search = "Search",
Service = "Service",
Tripadvisor = "Tripadvisor",
TshirtWash = "TshirtWash",
Wifi = "Wifi",