Merged in feat/mypages-mobile-dropdown (pull request #256)
Feat/mypages mobile dropdown Approved-by: Michael Zetterberg
This commit is contained in:
committed by
Michael Zetterberg
commit
5af198613a
@@ -0,0 +1,14 @@
|
|||||||
|
import { logout } from "@/constants/routes/handleAuth"
|
||||||
|
import { serverClient } from "@/lib/trpc/server"
|
||||||
|
|
||||||
|
import MyPagesMobileDropdown from "@/components/Current/Header/MyPagesMobileDropdown"
|
||||||
|
|
||||||
|
import { LangParams, PageArgs } from "@/types/params"
|
||||||
|
|
||||||
|
export default async function MyPagesMobileDropdownPage({
|
||||||
|
params,
|
||||||
|
}: PageArgs<LangParams>) {
|
||||||
|
const navigation = await serverClient().contentstack.myPages.navigation.get()
|
||||||
|
if (!navigation) return null
|
||||||
|
return <MyPagesMobileDropdown navigation={navigation} lang={params.lang} />
|
||||||
|
}
|
||||||
@@ -5,8 +5,15 @@ import { LangParams, PageArgs } from "@/types/params"
|
|||||||
export default function HeaderLayout({
|
export default function HeaderLayout({
|
||||||
params,
|
params,
|
||||||
languageSwitcher,
|
languageSwitcher,
|
||||||
|
myPagesMobileDropdown,
|
||||||
}: PageArgs<LangParams> & {
|
}: PageArgs<LangParams> & {
|
||||||
languageSwitcher: React.ReactNode
|
languageSwitcher: React.ReactNode
|
||||||
}) {
|
} & { myPagesMobileDropdown: React.ReactNode }) {
|
||||||
return <Header lang={params.lang} languageSwitcher={languageSwitcher} />
|
return (
|
||||||
|
<Header
|
||||||
|
lang={params.lang}
|
||||||
|
myPagesMobileDropdown={myPagesMobileDropdown}
|
||||||
|
languageSwitcher={languageSwitcher}
|
||||||
|
/>
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,14 +1,20 @@
|
|||||||
import { baseUrls } from "@/constants/routes/baseUrls"
|
import { baseUrls } from "@/constants/routes/baseUrls"
|
||||||
|
import { serverClient } from "@/lib/trpc/server"
|
||||||
|
|
||||||
import Header from "@/components/Current/Header"
|
import Header from "@/components/Current/Header"
|
||||||
import LanguageSwitcher from "@/components/Current/Header/LanguageSwitcher"
|
import LanguageSwitcher from "@/components/Current/Header/LanguageSwitcher"
|
||||||
|
import MyPagesMobileDropdown from "@/components/Current/Header/MyPagesMobileDropdown"
|
||||||
|
|
||||||
import { LangParams, PageArgs } from "@/types/params"
|
import { LangParams, PageArgs } from "@/types/params"
|
||||||
|
|
||||||
export default async function HeaderPage({ params }: PageArgs<LangParams>) {
|
export default async function HeaderPage({ params }: PageArgs<LangParams>) {
|
||||||
|
const navigation = await serverClient().contentstack.myPages.navigation.get()
|
||||||
return (
|
return (
|
||||||
<Header
|
<Header
|
||||||
lang={params.lang}
|
lang={params.lang}
|
||||||
|
myPagesMobileDropdown={
|
||||||
|
<MyPagesMobileDropdown navigation={navigation} lang={params.lang} />
|
||||||
|
}
|
||||||
languageSwitcher={<LanguageSwitcher urls={baseUrls} lang={params.lang} />}
|
languageSwitcher={<LanguageSwitcher urls={baseUrls} lang={params.lang} />}
|
||||||
/>
|
/>
|
||||||
)
|
)
|
||||||
|
|||||||
15
app/[lang]/(live-current)/@myPagesMobileDropdown/page.tsx
Normal file
15
app/[lang]/(live-current)/@myPagesMobileDropdown/page.tsx
Normal file
@@ -0,0 +1,15 @@
|
|||||||
|
import { serverClient } from "@/lib/trpc/server"
|
||||||
|
|
||||||
|
import MyPagesMobileDropdown from "@/components/Current/Header/MyPagesMobileDropdown"
|
||||||
|
|
||||||
|
import { LangParams, PageArgs } from "@/types/params"
|
||||||
|
|
||||||
|
export default async function MyPagesMobileDropdownPage({
|
||||||
|
params,
|
||||||
|
}: PageArgs<LangParams>) {
|
||||||
|
const navigation = await serverClient().contentstack.myPages.navigation.get()
|
||||||
|
if (!navigation) {
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
return <MyPagesMobileDropdown navigation={navigation} lang={params.lang} />
|
||||||
|
}
|
||||||
@@ -24,8 +24,11 @@ export default async function RootLayout({
|
|||||||
children,
|
children,
|
||||||
params,
|
params,
|
||||||
languageSwitcher,
|
languageSwitcher,
|
||||||
|
myPagesMobileDropdown,
|
||||||
}: React.PropsWithChildren<
|
}: React.PropsWithChildren<
|
||||||
LayoutArgs<LangParams> & { languageSwitcher: React.ReactNode }
|
LayoutArgs<LangParams> & { languageSwitcher: React.ReactNode } & {
|
||||||
|
myPagesMobileDropdown: React.ReactNode
|
||||||
|
}
|
||||||
>) {
|
>) {
|
||||||
const { defaultLocale, locale, messages } = await getIntl()
|
const { defaultLocale, locale, messages } = await getIntl()
|
||||||
return (
|
return (
|
||||||
@@ -82,7 +85,11 @@ export default async function RootLayout({
|
|||||||
<LangPopup lang={params.lang} />
|
<LangPopup lang={params.lang} />
|
||||||
<SkipToMainContent />
|
<SkipToMainContent />
|
||||||
<ServerIntlProvider intl={{ defaultLocale, locale, messages }}>
|
<ServerIntlProvider intl={{ defaultLocale, locale, messages }}>
|
||||||
<Header lang={params.lang} languageSwitcher={languageSwitcher} />
|
<Header
|
||||||
|
lang={params.lang}
|
||||||
|
myPagesMobileDropdown={myPagesMobileDropdown}
|
||||||
|
languageSwitcher={languageSwitcher}
|
||||||
|
/>
|
||||||
{children}
|
{children}
|
||||||
<Footer lang={params.lang} />
|
<Footer lang={params.lang} />
|
||||||
</ServerIntlProvider>
|
</ServerIntlProvider>
|
||||||
|
|||||||
@@ -31,7 +31,7 @@
|
|||||||
text-decoration: underline;
|
text-decoration: underline;
|
||||||
}
|
}
|
||||||
|
|
||||||
@media screen and (min-width: 950px) {
|
@media screen and (min-width: 1367px) {
|
||||||
.button {
|
.button {
|
||||||
font-weight: 600;
|
font-weight: 600;
|
||||||
font-size: 16px;
|
font-size: 16px;
|
||||||
|
|||||||
@@ -4,8 +4,10 @@ import { useIntl } from "react-intl"
|
|||||||
|
|
||||||
import { login } from "@/constants/routes/handleAuth"
|
import { login } from "@/constants/routes/handleAuth"
|
||||||
import { myPages } from "@/constants/routes/myPages"
|
import { myPages } from "@/constants/routes/myPages"
|
||||||
|
import useDropdownStore from "@/stores/main-menu"
|
||||||
|
|
||||||
import Image from "@/components/Image"
|
import Image from "@/components/Image"
|
||||||
|
import Avatar from "@/components/MyPages/Avatar"
|
||||||
import Link from "@/components/TempDesignSystem/Link"
|
import Link from "@/components/TempDesignSystem/Link"
|
||||||
|
|
||||||
import BookingButton from "../BookingButton"
|
import BookingButton from "../BookingButton"
|
||||||
@@ -21,16 +23,19 @@ export function MainMenu({
|
|||||||
logo,
|
logo,
|
||||||
topMenuMobileLinks,
|
topMenuMobileLinks,
|
||||||
languageSwitcher,
|
languageSwitcher,
|
||||||
|
myPagesMobileDropdown,
|
||||||
bookingHref,
|
bookingHref,
|
||||||
isLoggedIn,
|
user,
|
||||||
lang,
|
lang,
|
||||||
}: MainMenuProps) {
|
}: MainMenuProps) {
|
||||||
const intl = useIntl()
|
const intl = useIntl()
|
||||||
const [isOpen, setIsOpen] = useState(false)
|
|
||||||
|
|
||||||
function toogleIsOpen() {
|
const {
|
||||||
setIsOpen((prevIsOpen) => !prevIsOpen)
|
isHamburgerMenuOpen,
|
||||||
}
|
isMyPagesMobileMenuOpen,
|
||||||
|
toggleHamburgerMenu,
|
||||||
|
toggleMyPagesMobileMenu,
|
||||||
|
} = useDropdownStore()
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className={styles.mainMenu}>
|
<div className={styles.mainMenu}>
|
||||||
@@ -43,8 +48,8 @@ export function MainMenu({
|
|||||||
<nav className={styles.navBar}>
|
<nav className={styles.navBar}>
|
||||||
<button
|
<button
|
||||||
aria-pressed="false"
|
aria-pressed="false"
|
||||||
className={`${styles.expanderBtn} ${isOpen ? styles.expanded : ""}`}
|
className={`${styles.expanderBtn} ${isHamburgerMenuOpen ? styles.expanded : ""}`}
|
||||||
onClick={toogleIsOpen}
|
onClick={toggleHamburgerMenu}
|
||||||
type="button"
|
type="button"
|
||||||
>
|
>
|
||||||
<span className={styles.iconBars}></span>
|
<span className={styles.iconBars}></span>
|
||||||
@@ -66,13 +71,16 @@ export function MainMenu({
|
|||||||
</a>
|
</a>
|
||||||
|
|
||||||
<ul
|
<ul
|
||||||
className={`${styles.listWrapper} ${isOpen ? styles.isOpen : ""}`}
|
className={`${styles.listWrapper} ${isHamburgerMenuOpen ? styles.isOpen : ""}`}
|
||||||
>
|
>
|
||||||
<ul className={styles.linkRow}>
|
<ul className={styles.linkRow}>
|
||||||
{isLoggedIn ? (
|
{!!user ? (
|
||||||
<>
|
<>
|
||||||
<li>
|
<li className={styles.avatarWrapper}>
|
||||||
<div className={styles.loggedInLogo} />
|
<Avatar
|
||||||
|
firstName={user.firstName}
|
||||||
|
lastName={user.lastName}
|
||||||
|
/>
|
||||||
</li>
|
</li>
|
||||||
<li className={styles.mobileLinkRow}>
|
<li className={styles.mobileLinkRow}>
|
||||||
<Link
|
<Link
|
||||||
@@ -139,8 +147,23 @@ export function MainMenu({
|
|||||||
) : null}
|
) : null}
|
||||||
</ul>
|
</ul>
|
||||||
<div className={styles.buttonContainer}>
|
<div className={styles.buttonContainer}>
|
||||||
|
<div className={styles.myPagesDesktopLink}>
|
||||||
|
<Link className={styles.link} href={myPages[lang]}>
|
||||||
|
{intl.formatMessage({ id: "My pages" })}
|
||||||
|
</Link>
|
||||||
|
</div>
|
||||||
<BookingButton href={bookingHref} />
|
<BookingButton href={bookingHref} />
|
||||||
|
{myPagesMobileDropdown && user ? (
|
||||||
|
<div
|
||||||
|
role="button"
|
||||||
|
onClick={() => toggleMyPagesMobileMenu()}
|
||||||
|
className={styles.avatarButton}
|
||||||
|
>
|
||||||
|
<Avatar firstName={user.firstName} lastName={user.lastName} />
|
||||||
|
</div>
|
||||||
|
) : null}
|
||||||
</div>
|
</div>
|
||||||
|
{isMyPagesMobileMenuOpen ? myPagesMobileDropdown : null}
|
||||||
</nav>
|
</nav>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -1,21 +1,20 @@
|
|||||||
.mainMenu {
|
.mainMenu {
|
||||||
background-color: #fff;
|
background-color: var(--Main-Grey-White);
|
||||||
background-image: none;
|
background-image: none;
|
||||||
box-shadow: 0 0 7px rgba(0, 0, 0, 0.75);
|
box-shadow: 0px 1.001px 1.001px 0px rgba(0, 0, 0, 0.05);
|
||||||
max-height: 100%;
|
max-height: 100%;
|
||||||
overflow: visible;
|
overflow: visible;
|
||||||
position: fixed;
|
position: fixed;
|
||||||
top: 0;
|
top: 0;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
z-index: 99999;
|
z-index: 99999;
|
||||||
height: 52.39px;
|
height: 70.047px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.container {
|
.container {
|
||||||
box-sizing: content-box;
|
box-sizing: content-box;
|
||||||
height: 100%;
|
height: 100%;
|
||||||
margin: 0 auto;
|
margin: 0 auto;
|
||||||
max-width: 1200px;
|
|
||||||
padding: 0;
|
padding: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -26,8 +25,11 @@
|
|||||||
.navBar {
|
.navBar {
|
||||||
display: grid;
|
display: grid;
|
||||||
grid-template-columns: 1fr 80px 1fr;
|
grid-template-columns: 1fr 80px 1fr;
|
||||||
|
grid-template-columns: auto auto 1fr auto;
|
||||||
|
grid-template-areas: "expanderBtn logoLink . buttonContainer";
|
||||||
grid-template-rows: 100%;
|
grid-template-rows: 100%;
|
||||||
height: 100%;
|
height: 100%;
|
||||||
|
padding: 0 var(--Spacing-x2);
|
||||||
}
|
}
|
||||||
|
|
||||||
.expanderBtn {
|
.expanderBtn {
|
||||||
@@ -46,7 +48,7 @@
|
|||||||
background: #757575;
|
background: #757575;
|
||||||
border-radius: 2.3px;
|
border-radius: 2.3px;
|
||||||
display: inline-block;
|
display: inline-block;
|
||||||
height: 5px;
|
height: 3px;
|
||||||
position: relative;
|
position: relative;
|
||||||
transition: 0.3s;
|
transition: 0.3s;
|
||||||
width: 32px;
|
width: 32px;
|
||||||
@@ -98,16 +100,19 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
.logoLink {
|
.logoLink {
|
||||||
display: inline;
|
/*padding: 16px 0 8px;*/
|
||||||
|
display: inline-flex;
|
||||||
|
align-items: center;
|
||||||
height: 100%;
|
height: 100%;
|
||||||
width: 80px;
|
width: 80px;
|
||||||
padding: 16px 0 8px;
|
padding-left: var(--Spacing-x1);
|
||||||
}
|
}
|
||||||
|
|
||||||
.logo {
|
.logo {
|
||||||
width: 80px;
|
width: 80px;
|
||||||
object-fit: fill;
|
object-fit: fill;
|
||||||
}
|
}
|
||||||
|
|
||||||
.listWrapper {
|
.listWrapper {
|
||||||
background-color: #fff;
|
background-color: #fff;
|
||||||
border-top: 1px solid #e3e0db;
|
border-top: 1px solid #e3e0db;
|
||||||
@@ -175,13 +180,8 @@
|
|||||||
padding: 15px 15px 15px 5px;
|
padding: 15px 15px 15px 5px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.loggedInLogo {
|
.avatarWrapper {
|
||||||
height: 35px;
|
|
||||||
width: 35px;
|
|
||||||
border-radius: 50px;
|
|
||||||
background-color: #000;
|
|
||||||
margin-right: 4px;
|
margin-right: 4px;
|
||||||
margin-left: -4px;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.mobileLinkButton {
|
.mobileLinkButton {
|
||||||
@@ -228,50 +228,56 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
.buttonContainer {
|
.buttonContainer {
|
||||||
display: flex;
|
display: inline-flex;
|
||||||
justify-content: flex-end;
|
justify-content: flex-end;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
margin-right: 8px;
|
margin-right: 8px;
|
||||||
|
gap: var(--Spacing-x3);
|
||||||
}
|
}
|
||||||
|
|
||||||
@media screen and (min-width: 950px) {
|
.myPagesDesktopLink {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (min-width: 1367px) {
|
||||||
|
.navBar {
|
||||||
|
grid-template-columns: 140px auto 1fr;
|
||||||
|
height: 82.4px;
|
||||||
|
align-content: center;
|
||||||
|
padding: 0px 0px var(--Spacing-x-quarter) 0px;
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
|
||||||
|
.logoLink {
|
||||||
|
display: inline-block;
|
||||||
|
width: 100%;
|
||||||
|
padding: 27px 30px 26px 0;
|
||||||
|
text-align: center;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
|
||||||
.mainMenu {
|
.mainMenu {
|
||||||
box-shadow: none;
|
box-shadow: none;
|
||||||
background-color: hsla(0, 0%, 100%, 0.95);
|
background-color: hsla(0, 0%, 100%, 0.95);
|
||||||
position: relative;
|
position: relative;
|
||||||
z-index: unset;
|
z-index: unset;
|
||||||
height: 85.09px;
|
height: 82.4px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.container {
|
.container {
|
||||||
padding: 0px 30px;
|
padding: 0 var(--Spacing-x5) 0 120px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.mainLinks {
|
.mainLinks {
|
||||||
padding-top: 2.5px;
|
padding-top: 2.5px;
|
||||||
background-color: transparent;
|
background-color: transparent;
|
||||||
}
|
height: 100%;
|
||||||
|
|
||||||
.navBar {
|
|
||||||
grid-template-columns: 132.18px 1fr auto;
|
|
||||||
align-content: center;
|
|
||||||
padding-bottom: 2px;
|
|
||||||
|
|
||||||
overflow: hidden;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.expanderBtn {
|
.expanderBtn {
|
||||||
display: none;
|
display: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
.logoLink {
|
|
||||||
display: flex;
|
|
||||||
height: 100%;
|
|
||||||
padding: 30px 10px 30px 15px;
|
|
||||||
width: auto;
|
|
||||||
align-items: center;
|
|
||||||
}
|
|
||||||
|
|
||||||
.logo {
|
.logo {
|
||||||
width: 102.17px;
|
width: 102.17px;
|
||||||
height: 100%;
|
height: 100%;
|
||||||
@@ -282,9 +288,11 @@
|
|||||||
border-top: none;
|
border-top: none;
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
padding-bottom: 0;
|
padding-top: 0;
|
||||||
position: static;
|
position: static;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
|
padding-bottom: 0px;
|
||||||
|
height: 100%;
|
||||||
}
|
}
|
||||||
|
|
||||||
.listWrapper.isOpen {
|
.listWrapper.isOpen {
|
||||||
@@ -292,7 +300,7 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
.li {
|
.li {
|
||||||
display: table-cell;
|
display: inline-grid;
|
||||||
float: none;
|
float: none;
|
||||||
vertical-align: middle;
|
vertical-align: middle;
|
||||||
line-height: 1.15;
|
line-height: 1.15;
|
||||||
@@ -300,10 +308,16 @@
|
|||||||
|
|
||||||
.link {
|
.link {
|
||||||
background-image: none;
|
background-image: none;
|
||||||
font-family: Helvetica, Arial, sans-serif;
|
font-family: var(--typography-Body-Regular-fontFamily);
|
||||||
font-weight: 700;
|
font-size: var(--typography-Body-Regular-fontSize);
|
||||||
line-height: 1.15;
|
font-feature-settings:
|
||||||
|
"clig" off,
|
||||||
|
"liga" off;
|
||||||
|
font-weight: 600;
|
||||||
|
line-height: 125%;
|
||||||
padding: 30px 15px;
|
padding: 30px 15px;
|
||||||
|
text-transform: uppercase;
|
||||||
|
color: var(--text-black); /* Design system should return #404040 */
|
||||||
}
|
}
|
||||||
|
|
||||||
.linkRow {
|
.linkRow {
|
||||||
@@ -322,25 +336,11 @@
|
|||||||
.buttonContainer {
|
.buttonContainer {
|
||||||
margin-right: 0;
|
margin-right: 0;
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
@media (min-width: 1200px) {
|
.avatarButton {
|
||||||
.mainMenu {
|
display: none;
|
||||||
height: 82.4px;
|
|
||||||
}
|
}
|
||||||
.navBar {
|
.myPagesDesktopLink {
|
||||||
grid-template-columns: 140px auto 1fr;
|
display: block;
|
||||||
height: 82.4px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.logoLink {
|
|
||||||
display: inline-block;
|
|
||||||
width: 100%;
|
|
||||||
padding: 27px 30px 26px 0;
|
|
||||||
text-align: center;
|
|
||||||
}
|
|
||||||
|
|
||||||
.listWrapper {
|
|
||||||
padding-top: 0;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
80
components/Current/Header/MyPagesMobileDropdown/index.tsx
Normal file
80
components/Current/Header/MyPagesMobileDropdown/index.tsx
Normal file
@@ -0,0 +1,80 @@
|
|||||||
|
"use client"
|
||||||
|
import { Fragment } from "react"
|
||||||
|
import { useIntl } from "react-intl"
|
||||||
|
|
||||||
|
import { Lang } from "@/constants/languages"
|
||||||
|
import { logout } from "@/constants/routes/handleAuth"
|
||||||
|
import { navigationQueryRouter } from "@/server/routers/contentstack/myPages/navigation/query"
|
||||||
|
import useDropdownStore from "@/stores/main-menu"
|
||||||
|
|
||||||
|
import Divider from "@/components/TempDesignSystem/Divider"
|
||||||
|
import Link from "@/components/TempDesignSystem/Link"
|
||||||
|
import Title from "@/components/TempDesignSystem/Text/Title"
|
||||||
|
|
||||||
|
import styles from "./my-pages-mobile-dropdown.module.css"
|
||||||
|
|
||||||
|
type Navigation = Awaited<ReturnType<(typeof navigationQueryRouter)["get"]>>
|
||||||
|
|
||||||
|
export default function MyPagesMobileDropdown({
|
||||||
|
navigation,
|
||||||
|
lang,
|
||||||
|
}: {
|
||||||
|
navigation: Navigation
|
||||||
|
lang: Lang | null
|
||||||
|
}) {
|
||||||
|
const { formatMessage } = useIntl()
|
||||||
|
const { toggleMyPagesMobileMenu, isMyPagesMobileMenuOpen } =
|
||||||
|
useDropdownStore()
|
||||||
|
|
||||||
|
if (!navigation) {
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<nav
|
||||||
|
className={`${styles.navigationMenu} ${isMyPagesMobileMenuOpen ? styles.navigationMenuIsOpen : ""}`}
|
||||||
|
>
|
||||||
|
<Title className={styles.heading} textTransform="capitalize" level="h5">
|
||||||
|
{navigation.title}
|
||||||
|
</Title>
|
||||||
|
{navigation.menuItems.map((menuItem, idx) => (
|
||||||
|
<Fragment key={`${menuItem.display_sign_out_link}-${idx}`}>
|
||||||
|
<div className={styles.dividerWrapper}>
|
||||||
|
<Divider color="subtle" />
|
||||||
|
</div>
|
||||||
|
<ul className={styles.dropdownWrapper}>
|
||||||
|
<ul className={styles.dropdownLinks}>
|
||||||
|
{menuItem.links.map((link) => (
|
||||||
|
<li key={link.uid}>
|
||||||
|
<Link
|
||||||
|
href={link.originalUrl || link.url}
|
||||||
|
partialMatch
|
||||||
|
size={menuItem.display_sign_out_link ? "small" : "regular"}
|
||||||
|
variant="myPageMobileDropdown"
|
||||||
|
color="burgundy"
|
||||||
|
onClick={toggleMyPagesMobileMenu}
|
||||||
|
>
|
||||||
|
{link.linkText}
|
||||||
|
</Link>
|
||||||
|
</li>
|
||||||
|
))}
|
||||||
|
{menuItem.display_sign_out_link && lang ? (
|
||||||
|
<li>
|
||||||
|
<Link
|
||||||
|
color="burgundy"
|
||||||
|
href={logout[lang]}
|
||||||
|
prefetch={false}
|
||||||
|
size="small"
|
||||||
|
variant="sidebar"
|
||||||
|
>
|
||||||
|
{formatMessage({ id: "Log out" })}
|
||||||
|
</Link>
|
||||||
|
</li>
|
||||||
|
) : null}
|
||||||
|
</ul>
|
||||||
|
</ul>
|
||||||
|
</Fragment>
|
||||||
|
))}
|
||||||
|
</nav>
|
||||||
|
)
|
||||||
|
}
|
||||||
@@ -0,0 +1,63 @@
|
|||||||
|
.navigationMenu {
|
||||||
|
background-color: #fff;
|
||||||
|
border-top: 1px solid #e3e0db;
|
||||||
|
display: none;
|
||||||
|
list-style: none;
|
||||||
|
overflow-y: visible;
|
||||||
|
margin: 0;
|
||||||
|
padding-inline-start: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.navigationMenu.navigationMenuIsOpen {
|
||||||
|
display: block;
|
||||||
|
left: 0;
|
||||||
|
position: absolute;
|
||||||
|
right: 0;
|
||||||
|
top: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.dropdownWrapper {
|
||||||
|
display: flex;
|
||||||
|
width: 100%;
|
||||||
|
padding: 20px var(--Spacing-x2);
|
||||||
|
flex-direction: column;
|
||||||
|
justify-content: center;
|
||||||
|
align-items: flex-start;
|
||||||
|
background-color: var(--Main-Grey-White);
|
||||||
|
box-shadow:
|
||||||
|
0px 276px 77px 0px rgba(0, 0, 0, 0),
|
||||||
|
0px 177px 71px 0px rgba(0, 0, 0, 0.01),
|
||||||
|
0px 99px 60px 0px rgba(0, 0, 0, 0.05),
|
||||||
|
0px 44px 44px 0px rgba(0, 0, 0, 0.09),
|
||||||
|
0px 11px 24px 0px rgba(0, 0, 0, 0.1);
|
||||||
|
}
|
||||||
|
|
||||||
|
.dividerWrapper {
|
||||||
|
background-color: var(--Main-Grey-White);
|
||||||
|
padding: 0 var(--Spacing-x2);
|
||||||
|
margin: auto;
|
||||||
|
place-content: center;
|
||||||
|
display: flex;
|
||||||
|
}
|
||||||
|
|
||||||
|
.heading {
|
||||||
|
padding: 20px var(--Spacing-x2);
|
||||||
|
}
|
||||||
|
|
||||||
|
.dropdownLinks {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: var(--Spacing-x-half);
|
||||||
|
width: 100%;
|
||||||
|
list-style: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
@media screen and (min-width: 1367px) {
|
||||||
|
.navigationMenu {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.navigationMenu.navigationMenuIsOpen {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -2,8 +2,8 @@
|
|||||||
display: grid;
|
display: grid;
|
||||||
}
|
}
|
||||||
|
|
||||||
@media screen and (max-width: 950px) {
|
@media screen and (max-width: 1366px) {
|
||||||
.header {
|
.header {
|
||||||
height: 50px;
|
height: 70.047px;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -15,11 +15,18 @@ import { LangParams } from "@/types/params"
|
|||||||
export default async function Header({
|
export default async function Header({
|
||||||
lang,
|
lang,
|
||||||
languageSwitcher,
|
languageSwitcher,
|
||||||
}: LangParams & { languageSwitcher: React.ReactNode }) {
|
myPagesMobileDropdown,
|
||||||
const data = await serverClient().contentstack.base.header({
|
}: LangParams & { languageSwitcher: React.ReactNode } & {
|
||||||
lang,
|
myPagesMobileDropdown: React.ReactNode
|
||||||
})
|
}) {
|
||||||
const session = await auth()
|
const [data, session] = await Promise.all([
|
||||||
|
serverClient().contentstack.base.header({
|
||||||
|
lang,
|
||||||
|
}),
|
||||||
|
auth(),
|
||||||
|
])
|
||||||
|
|
||||||
|
const user = !!session ? await serverClient().user.get() : null
|
||||||
|
|
||||||
if (!data) {
|
if (!data) {
|
||||||
return null
|
return null
|
||||||
@@ -49,8 +56,9 @@ export default async function Header({
|
|||||||
logo={logo}
|
logo={logo}
|
||||||
topMenuMobileLinks={topMenuMobileLinks}
|
topMenuMobileLinks={topMenuMobileLinks}
|
||||||
languageSwitcher={languageSwitcher}
|
languageSwitcher={languageSwitcher}
|
||||||
|
myPagesMobileDropdown={myPagesMobileDropdown}
|
||||||
bookingHref={homeHref}
|
bookingHref={homeHref}
|
||||||
isLoggedIn={!!session}
|
user={user}
|
||||||
lang={lang}
|
lang={lang}
|
||||||
/>
|
/>
|
||||||
</header>
|
</header>
|
||||||
|
|||||||
27
components/MyPages/Avatar/avatar.module.css
Normal file
27
components/MyPages/Avatar/avatar.module.css
Normal file
@@ -0,0 +1,27 @@
|
|||||||
|
.avatar {
|
||||||
|
display: inline-flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
vertical-align: middle;
|
||||||
|
overflow: hidden;
|
||||||
|
cursor: pointer;
|
||||||
|
width: 35px;
|
||||||
|
height: 35px;
|
||||||
|
border-radius: 100%;
|
||||||
|
background-color: rgba(0, 0, 0, 0.05);
|
||||||
|
}
|
||||||
|
|
||||||
|
.avatarInitialsText {
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
background-color: var(--Theme-Primary-Dark-Surface-Normal);
|
||||||
|
color: var(--Theme-Primary-Dark-On-Surface-Text);
|
||||||
|
font-size: var(--typography-Caption-Bold-fontSize);
|
||||||
|
font-family: var(--typography-Body-Regular-fontFamily);
|
||||||
|
font-weight: var(--typography-Caption-Bold-fontWeight);
|
||||||
|
line-height: 150%;
|
||||||
|
letter-spacing: 0.096px;
|
||||||
|
}
|
||||||
20
components/MyPages/Avatar/index.tsx
Normal file
20
components/MyPages/Avatar/index.tsx
Normal file
@@ -0,0 +1,20 @@
|
|||||||
|
import { getInitials } from "@/utils/user"
|
||||||
|
|
||||||
|
import styles from "./avatar.module.css"
|
||||||
|
|
||||||
|
import { User } from "@/types/user"
|
||||||
|
|
||||||
|
export default function Avatar({
|
||||||
|
firstName,
|
||||||
|
lastName,
|
||||||
|
}: {
|
||||||
|
firstName: User["firstName"]
|
||||||
|
lastName: User["lastName"]
|
||||||
|
}) {
|
||||||
|
const initials = getInitials(firstName, lastName)
|
||||||
|
return (
|
||||||
|
<span className={styles.avatar}>
|
||||||
|
<span className={styles.avatarInitialsText}>{initials}</span>
|
||||||
|
</span>
|
||||||
|
)
|
||||||
|
}
|
||||||
@@ -17,6 +17,10 @@
|
|||||||
border-bottom-color: var(--Theme-Primary-Light-On-Surface-Divider);
|
border-bottom-color: var(--Theme-Primary-Light-On-Surface-Divider);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.subtle {
|
||||||
|
border-bottom-color: var(--Base-Border-Subtle);
|
||||||
|
}
|
||||||
|
|
||||||
.opacity100 {
|
.opacity100 {
|
||||||
opacity: 1;
|
opacity: 1;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -7,6 +7,7 @@ export const dividerVariants = cva(styles.divider, {
|
|||||||
color: {
|
color: {
|
||||||
burgundy: styles.burgundy,
|
burgundy: styles.burgundy,
|
||||||
peach: styles.peach,
|
peach: styles.peach,
|
||||||
|
subtle: styles.subtle,
|
||||||
},
|
},
|
||||||
opacity: {
|
opacity: {
|
||||||
100: styles.opacity100,
|
100: styles.opacity100,
|
||||||
|
|||||||
@@ -29,6 +29,33 @@
|
|||||||
text-decoration: underline;
|
text-decoration: underline;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.myPageMobileDropdown {
|
||||||
|
color: var(--Scandic-Brand-Burgundy);
|
||||||
|
font-family: var(--typography-Body-Regular-fontFamily);
|
||||||
|
font-size: var(--typography-Body-Regular-fontSize);
|
||||||
|
line-height: var(--typography-Body-Regular-lineHeight);
|
||||||
|
letter-spacing: 0.096px;
|
||||||
|
padding: var(--Spacing-x1) var(--Spacing-x1) var(--Spacing-x1)
|
||||||
|
var(--Spacing-x-one-and-half);
|
||||||
|
width: 100%;
|
||||||
|
border-radius: var(--Corner-radius-Medium);
|
||||||
|
gap: 4px;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 4px;
|
||||||
|
align-self: stretch;
|
||||||
|
}
|
||||||
|
|
||||||
|
.myPageMobileDropdown.active {
|
||||||
|
background-color: var(--Scandic-Brand-Pale-Peach);
|
||||||
|
border-radius: var(--Corner-radius-Medium);
|
||||||
|
font-family: var(--typography-Body-Underlined-fontFamily);
|
||||||
|
font-size: var(--typography-Body-Underlined-fontSize);
|
||||||
|
font-weight: var(--typography-Body-Underlined-fontWeight);
|
||||||
|
letter-spacing: var(--typography-Body-Underlined-letterSpacing);
|
||||||
|
line-height: var(--typography-Body-Underlined-lineHeight);
|
||||||
|
}
|
||||||
|
|
||||||
.shortcut {
|
.shortcut {
|
||||||
align-items: center;
|
align-items: center;
|
||||||
border-bottom: 0.5px solid var(--Scandic-Beige-20);
|
border-bottom: 0.5px solid var(--Scandic-Beige-20);
|
||||||
|
|||||||
@@ -23,6 +23,7 @@ export const linkVariants = cva(styles.link, {
|
|||||||
default: styles.default,
|
default: styles.default,
|
||||||
icon: styles.icon,
|
icon: styles.icon,
|
||||||
myPage: styles.myPage,
|
myPage: styles.myPage,
|
||||||
|
myPageMobileDropdown: styles.myPageMobileDropdown,
|
||||||
shortcut: styles.shortcut,
|
shortcut: styles.shortcut,
|
||||||
sidebar: styles.sidebar,
|
sidebar: styles.sidebar,
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -68,6 +68,10 @@
|
|||||||
text-decoration: var(--typography-Title-5-textDecoration);
|
text-decoration: var(--typography-Title-5-textDecoration);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.capitalize {
|
||||||
|
text-transform: capitalize;
|
||||||
|
}
|
||||||
|
|
||||||
.regular {
|
.regular {
|
||||||
text-transform: none;
|
text-transform: none;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -16,6 +16,7 @@ const config = {
|
|||||||
left: styles.left,
|
left: styles.left,
|
||||||
},
|
},
|
||||||
textTransform: {
|
textTransform: {
|
||||||
|
capitalize: styles.capitalize,
|
||||||
regular: styles.regular,
|
regular: styles.regular,
|
||||||
uppercase: styles.uppercase,
|
uppercase: styles.uppercase,
|
||||||
},
|
},
|
||||||
|
|||||||
31
stores/main-menu.ts
Normal file
31
stores/main-menu.ts
Normal file
@@ -0,0 +1,31 @@
|
|||||||
|
import { create } from "zustand"
|
||||||
|
|
||||||
|
interface DropdownState {
|
||||||
|
isHamburgerMenuOpen: boolean
|
||||||
|
isMyPagesMobileMenuOpen: boolean
|
||||||
|
toggleHamburgerMenu: () => void
|
||||||
|
toggleMyPagesMobileMenu: () => void
|
||||||
|
}
|
||||||
|
|
||||||
|
const useDropdownStore = create<DropdownState>((set) => ({
|
||||||
|
isHamburgerMenuOpen: false,
|
||||||
|
isMyPagesMobileMenuOpen: false,
|
||||||
|
toggleHamburgerMenu: () =>
|
||||||
|
set((state) => {
|
||||||
|
// Close the other dropdown if it's open
|
||||||
|
if (!state.isHamburgerMenuOpen && state.isMyPagesMobileMenuOpen) {
|
||||||
|
set({ isMyPagesMobileMenuOpen: false })
|
||||||
|
}
|
||||||
|
return { isHamburgerMenuOpen: !state.isHamburgerMenuOpen }
|
||||||
|
}),
|
||||||
|
toggleMyPagesMobileMenu: () =>
|
||||||
|
set((state) => {
|
||||||
|
// Close the other dropdown if it's open
|
||||||
|
if (!state.isMyPagesMobileMenuOpen && state.isHamburgerMenuOpen) {
|
||||||
|
set({ isHamburgerMenuOpen: false })
|
||||||
|
}
|
||||||
|
return { isMyPagesMobileMenuOpen: !state.isMyPagesMobileMenuOpen }
|
||||||
|
}),
|
||||||
|
}))
|
||||||
|
|
||||||
|
export default useDropdownStore
|
||||||
@@ -5,6 +5,7 @@ import type {
|
|||||||
CurrentHeaderLink,
|
CurrentHeaderLink,
|
||||||
TopMenuHeaderLink,
|
TopMenuHeaderLink,
|
||||||
} from "@/types/requests/currentHeader"
|
} from "@/types/requests/currentHeader"
|
||||||
|
import { User } from "@/types/user"
|
||||||
|
|
||||||
export type MainMenuProps = {
|
export type MainMenuProps = {
|
||||||
frontpageLinkText: string
|
frontpageLinkText: string
|
||||||
@@ -13,7 +14,8 @@ export type MainMenuProps = {
|
|||||||
logo: Image
|
logo: Image
|
||||||
topMenuMobileLinks: TopMenuHeaderLink[]
|
topMenuMobileLinks: TopMenuHeaderLink[]
|
||||||
languageSwitcher: React.ReactNode | null
|
languageSwitcher: React.ReactNode | null
|
||||||
|
myPagesMobileDropdown: React.ReactNode | null
|
||||||
bookingHref: string
|
bookingHref: string
|
||||||
isLoggedIn: boolean
|
user: User | null
|
||||||
lang: Lang
|
lang: Lang
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -6,3 +6,13 @@ export function getMembership(memberships: User["memberships"]) {
|
|||||||
membership.membershipType.toLowerCase() === "guestpr" || "scandicfriend's"
|
membership.membershipType.toLowerCase() === "guestpr" || "scandicfriend's"
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function getInitials(
|
||||||
|
firstName: User["firstName"],
|
||||||
|
lastName: User["lastName"]
|
||||||
|
) {
|
||||||
|
if (!firstName || !lastName) return null
|
||||||
|
const firstInitial = firstName.charAt(0).toUpperCase()
|
||||||
|
const lastInitial = lastName.charAt(0).toUpperCase()
|
||||||
|
return `${firstInitial}${lastInitial}`
|
||||||
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user