feat(SW-184): added main components for new header

This commit is contained in:
Erik Tiekstra
2024-08-19 12:52:35 +02:00
parent 70297bec91
commit 08cde7ae2f
31 changed files with 548 additions and 26 deletions

View File

@@ -0,0 +1,18 @@
import MenuItem from "../MenuItem"
import { MenuProps } from "./menu"
import styles from "./menu.module.css"
export default function Menu({ items }: MenuProps) {
function handleButtonClick() {}
return (
<ul className={styles.menu}>
{items.map((item) => (
<li key={item.id}>
<MenuItem item={item} />
</li>
))}
</ul>
)
}

View File

@@ -0,0 +1,8 @@
.menu {
list-style: none;
margin: 0;
display: flex;
justify-content: space-between;
align-items: center;
gap: var(--Spacing-x4);
}

View File

@@ -0,0 +1,5 @@
import { MenuItem } from ".."
export interface MenuProps {
items: MenuItem[]
}

View File

@@ -0,0 +1,37 @@
"use client"
import { useState } from "react"
import { ChevronDownIcon } from "@/components/Icons"
import Link from "@/components/TempDesignSystem/Link"
import { MenuItemProps } from "./menuItem"
import styles from "./menuItem.module.css"
export default function MenuItem({ item }: MenuItemProps) {
const { children, title, href, seeAllLinkText, infoCard } = item
const [isExpanded, setIsExpanded] = useState(false)
function handleButtonClick() {
setIsExpanded((prev) => !prev)
}
return children?.length ? (
<button
type="button"
className={styles.navigationButton}
onClick={handleButtonClick}
>
{title}
<ChevronDownIcon
className={`${styles.chevron} ${isExpanded ? styles.isExpanded : ""}`}
color="red"
/>
</button>
) : (
<Link href={href} color="burgundy">
{title}
</Link>
)
}

View File

@@ -0,0 +1,21 @@
.navigationButton {
display: flex;
gap: var(--Spacing-x1);
align-items: center;
background-color: transparent;
color: var(--Base-Text-High-contrast);
border-width: 0;
padding: 0;
cursor: pointer;
font-family: var(--typography-Body-Bold-fontFamily);
font-weight: var(--typography-Body-Bold-fontWeight);
font-size: var(--typography-Body-Bold-fontSize);
}
.chevron {
transition: transform 0.2s;
}
.chevron.isExpanded {
transform: rotate(180deg);
}

View File

@@ -0,0 +1,5 @@
import { MenuItem } from ".."
export interface MenuItemProps {
item: MenuItem
}

View File

@@ -0,0 +1,16 @@
.avatar {
display: flex;
justify-content: center;
align-items: center;
overflow: hidden;
border-radius: 50%;
width: 2rem;
height: 2rem;
background-color: var(--Main-Grey-40);
}
.initials {
font-size: 0.75rem;
color: var(--Base-Text-Inverted);
background-color: var(--Scandic-Peach-70);
}

View File

@@ -0,0 +1,6 @@
import { ImageProps } from "next/image"
export interface AvatarProps {
image?: ImageProps
initials?: string
}

View File

@@ -0,0 +1,19 @@
import { PersonIcon } from "@/components/Icons"
import Image from "@/components/Image"
import { AvatarProps } from "./avatar"
import styles from "./avatar.module.css"
export default function Avatar({ image, initials }: AvatarProps) {
let classNames = [styles.avatar]
let element = <PersonIcon color="white" />
if (image) {
classNames.push(styles.image)
element = <Image src={image.src} alt={image.alt} width={28} height={28} />
} else if (initials) {
classNames.push(styles.initials)
element = <span>{initials}</span>
}
return <span className={classNames.join(" ")}>{element}</span>
}

View File

@@ -0,0 +1,16 @@
import Link from "next/link"
import Avatar from "./Avatar"
import styles from "./user.module.css"
export default function User() {
return (
<div className={styles.user}>
<Link href="#" className={styles.link}>
<Avatar />
Log in/Join
</Link>
</div>
)
}

View File

@@ -0,0 +1,12 @@
.user {
}
.link {
display: flex;
gap: var(--Spacing-x1);
align-items: center;
font-family: var(--typography-Body-Bold-fontFamily);
font-weight: 600;
color: var(--Base-Text-High-contrast);
text-decoration: none;
}

View File

@@ -0,0 +1,125 @@
import Link from "next/link"
import Image from "@/components/Image"
import Menu from "./Menu"
import User from "./User"
import styles from "./mainMenu.module.css"
export interface MenuItem {
id: string
title: string
href: string
children?: {
groupTitle: string
children: {
id: string
title: string
href: string
}[]
}[]
seeAllLinkText?: string
infoCard?: {
scriptedTitle: string
title: string
description: string
ctaLink: string
}
}
export default async function MainMenu() {
const menuItems: MenuItem[] = [
{
id: "hotels",
title: "Hotels",
href: "/hotels",
children: [],
},
{
id: "business",
title: "Business",
href: "/business",
children: [
{
groupTitle: "Top conference venues",
children: [
{
id: "stockholm",
title: "Stockholm",
href: "/stockholm",
},
{
id: "bergen",
title: "Bergen",
href: "/bergen",
},
{
id: "copenhagen",
title: "Copenhagen",
href: "/copenhagen",
},
],
},
{
groupTitle: "Scandic for business",
children: [
{
id: "book-a-venue",
title: "Book a venue",
href: "/book-a-venue",
},
{
id: "conference-packages",
title: "Conference packages",
href: "/conference-packages",
},
{
id: "co-working",
title: "Co-working",
href: "/co-working",
},
],
},
],
seeAllLinkText: "See all conference & meeting venues",
infoCard: {
scriptedTitle: "Stockholm",
title: "Meeting venues in Stockholm",
description:
"Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed et felis metus. Sed et felis metus.",
ctaLink: "/stockholm",
},
},
{
id: "offers",
title: "Offers",
href: "/offers",
},
{
id: "restaurants",
title: "Restaurants",
href: "/restaurants",
},
]
return (
<div className={styles.mainMenu}>
<nav className={styles.content}>
<Link className={styles.logoLink} href="/">
<Image
alt="Back to scandichotels.com"
className={styles.logo}
data-js="scandiclogoimg"
data-nosvgsrc="/_static/img/scandic-logotype.png"
itemProp="logo"
height={24}
src="/_static/img/scandic-logotype.svg"
width={113}
/>
</Link>
<Menu items={menuItems} />
<User />
</nav>
</div>
)
}

View File

@@ -0,0 +1,15 @@
.mainMenu {
background-color: var(--Base-Surface-Primary-light-Normal);
padding: var(--Spacing-x2);
border-top: 1px solid var(--Base-Border-Subtle);
border-bottom: 1px solid var(--Base-Border-Subtle);
}
.content {
max-width: 89.5rem;
margin: 0 auto;
display: flex;
justify-content: space-between;
align-items: center;
gap: var(--Spacing-x3);
}

View File

@@ -0,0 +1,9 @@
.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

@@ -0,0 +1,2 @@
export interface ButtonProps
extends React.ButtonHTMLAttributes<HTMLButtonElement> {}

View File

@@ -0,0 +1,11 @@
import { ButtonProps } from "./button"
import styles from "./button.module.css"
export default function Button({ children, ...props }: ButtonProps) {
return (
<button type="button" className={styles.button} {...props}>
{children}
</button>
)
}

View File

@@ -0,0 +1,30 @@
"use client"
import { useState } from "react"
import { ChevronDownIcon, GlobeIcon } from "@/components/Icons"
import Button from "../Button"
import styles from "./languageSwitcher.module.css"
export default function LanguageSwitcher() {
const [isExpanded, setIsExpanded] = useState(false)
function handleButtonClick() {
setIsExpanded((prev) => !prev)
}
return (
<Button onClick={handleButtonClick}>
<GlobeIcon width={20} height={20} color="burgundy" />
<span>English</span>
<ChevronDownIcon
className={`${styles.chevron} ${isExpanded ? styles.isExpanded : ""}`}
width={20}
height={20}
color="burgundy"
/>
</Button>
)
}

View File

@@ -0,0 +1,17 @@
.button {
background-color: transparent;
color: var(--Base-Text-High-contrast);
border-width: 0;
cursor: pointer;
display: flex;
gap: var(--Spacing-x1);
align-items: center;
}
.chevron {
transition: transform 0.2s;
}
.chevron.isExpanded {
transform: rotate(180deg);
}

View File

@@ -0,0 +1,12 @@
import { SearchIcon } from "@/components/Icons"
import Button from "../Button"
export default function Search() {
return (
<Button>
<SearchIcon width={20} height={20} color="burgundy" />
<span>Find Booking</span>
</Button>
)
}

View File

@@ -0,0 +1,9 @@
.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

@@ -0,0 +1,24 @@
import { GiftIcon } from "@/components/Icons"
import Link from "@/components/TempDesignSystem/Link"
import LanguageSwitcher from "./LanguageSwitcher"
import Search from "./Search"
import styles from "./topMenu.module.css"
export default async function TopMenu() {
return (
<div className={styles.topMenu}>
<div className={styles.content}>
<Link variant="icon" color="burgundy" href="#" size="small">
<GiftIcon width={20} height={20} color="burgundy" />
Join Scandic Friends
</Link>
<div className={styles.right}>
<LanguageSwitcher />
<Search />
</div>
</div>
</div>
)
}

View File

@@ -0,0 +1,18 @@
.topMenu {
background-color: var(--Base-Surface-Subtle-Normal);
padding: var(--Spacing-x2) var(--Spacing-x-one-and-half);
}
.content {
max-width: 89.5rem;
margin: 0 auto;
display: flex;
justify-content: space-between;
gap: var(--Spacing-x3);
}
.right {
display: flex;
gap: var(--Spacing-x2);
align-items: center;
}

View File

@@ -0,0 +1,4 @@
.header {
font-family: var(--typography-Body-Regular-fontFamily);
color: var(--Base-Text-High-contrast);
}

View File

@@ -0,0 +1,13 @@
import MainMenu from "./MainMenu"
import TopMenu from "./TopMenu"
import styles from "./header.module.css"
export default async function Header() {
return (
<header className={styles.header}>
<TopMenu />
<MainMenu />
</header>
)
}