feat(SW-184): added main components for new header
This commit is contained in:
18
components/Header/MainMenu/Menu/index.tsx
Normal file
18
components/Header/MainMenu/Menu/index.tsx
Normal 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>
|
||||
)
|
||||
}
|
||||
8
components/Header/MainMenu/Menu/menu.module.css
Normal file
8
components/Header/MainMenu/Menu/menu.module.css
Normal file
@@ -0,0 +1,8 @@
|
||||
.menu {
|
||||
list-style: none;
|
||||
margin: 0;
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
gap: var(--Spacing-x4);
|
||||
}
|
||||
5
components/Header/MainMenu/Menu/menu.ts
Normal file
5
components/Header/MainMenu/Menu/menu.ts
Normal file
@@ -0,0 +1,5 @@
|
||||
import { MenuItem } from ".."
|
||||
|
||||
export interface MenuProps {
|
||||
items: MenuItem[]
|
||||
}
|
||||
37
components/Header/MainMenu/MenuItem/index.tsx
Normal file
37
components/Header/MainMenu/MenuItem/index.tsx
Normal 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>
|
||||
)
|
||||
}
|
||||
21
components/Header/MainMenu/MenuItem/menuItem.module.css
Normal file
21
components/Header/MainMenu/MenuItem/menuItem.module.css
Normal 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);
|
||||
}
|
||||
5
components/Header/MainMenu/MenuItem/menuItem.ts
Normal file
5
components/Header/MainMenu/MenuItem/menuItem.ts
Normal file
@@ -0,0 +1,5 @@
|
||||
import { MenuItem } from ".."
|
||||
|
||||
export interface MenuItemProps {
|
||||
item: MenuItem
|
||||
}
|
||||
16
components/Header/MainMenu/User/Avatar/avatar.module.css
Normal file
16
components/Header/MainMenu/User/Avatar/avatar.module.css
Normal 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);
|
||||
}
|
||||
6
components/Header/MainMenu/User/Avatar/avatar.ts
Normal file
6
components/Header/MainMenu/User/Avatar/avatar.ts
Normal file
@@ -0,0 +1,6 @@
|
||||
import { ImageProps } from "next/image"
|
||||
|
||||
export interface AvatarProps {
|
||||
image?: ImageProps
|
||||
initials?: string
|
||||
}
|
||||
19
components/Header/MainMenu/User/Avatar/index.tsx
Normal file
19
components/Header/MainMenu/User/Avatar/index.tsx
Normal 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>
|
||||
}
|
||||
16
components/Header/MainMenu/User/index.tsx
Normal file
16
components/Header/MainMenu/User/index.tsx
Normal 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>
|
||||
)
|
||||
}
|
||||
12
components/Header/MainMenu/User/user.module.css
Normal file
12
components/Header/MainMenu/User/user.module.css
Normal 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;
|
||||
}
|
||||
125
components/Header/MainMenu/index.tsx
Normal file
125
components/Header/MainMenu/index.tsx
Normal 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>
|
||||
)
|
||||
}
|
||||
15
components/Header/MainMenu/mainMenu.module.css
Normal file
15
components/Header/MainMenu/mainMenu.module.css
Normal 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);
|
||||
}
|
||||
9
components/Header/TopMenu/Button/button.module.css
Normal file
9
components/Header/TopMenu/Button/button.module.css
Normal 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;
|
||||
}
|
||||
2
components/Header/TopMenu/Button/button.ts
Normal file
2
components/Header/TopMenu/Button/button.ts
Normal file
@@ -0,0 +1,2 @@
|
||||
export interface ButtonProps
|
||||
extends React.ButtonHTMLAttributes<HTMLButtonElement> {}
|
||||
11
components/Header/TopMenu/Button/index.tsx
Normal file
11
components/Header/TopMenu/Button/index.tsx
Normal 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>
|
||||
)
|
||||
}
|
||||
30
components/Header/TopMenu/LanguageSwitcher/index.tsx
Normal file
30
components/Header/TopMenu/LanguageSwitcher/index.tsx
Normal 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>
|
||||
)
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
12
components/Header/TopMenu/Search/index.tsx
Normal file
12
components/Header/TopMenu/Search/index.tsx
Normal 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>
|
||||
)
|
||||
}
|
||||
9
components/Header/TopMenu/Search/search.module.css
Normal file
9
components/Header/TopMenu/Search/search.module.css
Normal 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;
|
||||
}
|
||||
24
components/Header/TopMenu/index.tsx
Normal file
24
components/Header/TopMenu/index.tsx
Normal 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>
|
||||
)
|
||||
}
|
||||
18
components/Header/TopMenu/topMenu.module.css
Normal file
18
components/Header/TopMenu/topMenu.module.css
Normal 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;
|
||||
}
|
||||
4
components/Header/header.module.css
Normal file
4
components/Header/header.module.css
Normal file
@@ -0,0 +1,4 @@
|
||||
.header {
|
||||
font-family: var(--typography-Body-Regular-fontFamily);
|
||||
color: var(--Base-Text-High-contrast);
|
||||
}
|
||||
13
components/Header/index.tsx
Normal file
13
components/Header/index.tsx
Normal 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>
|
||||
)
|
||||
}
|
||||
Reference in New Issue
Block a user