feature: responsive menu

This commit is contained in:
Benno Tielen 2024-11-22 18:22:12 +01:00
parent 2d01ae6dbf
commit ab83bf0990
8 changed files with 557 additions and 218 deletions

View file

@ -14,4 +14,4 @@ main {
.mainContent {
font-size: 20px;
line-height: 147%;
}
}

View file

@ -16,7 +16,181 @@ export default function RootLayout({
return (
<html lang="en" className={defaultFont.className}>
<body>
<Menu />
<Menu menu={{
leftItems: [
{
text: 'Home',
href: '/'
},
{
text: 'Gemeinschaft finden',
megaMenu: {
text: {
quote: '',
source: ''
},
groups: [
{
title: "Gemeinden",
items: [
{
title: "St. Richard",
description: "Mehr informationen",
href: "/gemeinde/st-richard"
},
{
title: "St. Christophorus",
description: "Mehr informationen",
href: "/gemeinde/st-christophorus"
},
{
title: "St. Clara",
description: "Mehr informationen",
href: "/gemeinde/st-clara"
},
]
},
{
title: "Gruppen",
items: [
{
title: "Kathoccino",
description: "Begegnung mit Gott",
href: "https://"
},
{
title: "Credo & Agape",
description: "Gebet der Meditation",
href: "https://"
},
{
title: "Mädchengruppe",
description: "Stille Begegnung mit Gott",
href: "https://"
},
{
title: "Alphakurs",
description: "Dank, Ehre und Freude",
href: "https://"
},
]
},
{
title: "Aktivitaten",
items: [
{
title: "Kochen",
description: "Begegnung mit Gott",
href: "https://"
},
{
title: "Lernen",
description: "Gebet der Meditation",
href: "https://"
},
{
title: "Wandern",
description: "Stille Begegnung mit Gott",
href: "https://"
},
{
title: "Singen",
description: "Dank, Ehre und Freude",
href: "https://"
},
]
}
]
}
},
{
text: 'Glauben lauben',
megaMenu: {
text: {
quote: '',
source: ''
},
groups: [
{
title: "Sakramenten",
items: [
{
title: "Taufe",
description: "Neues Leben in Christus",
href: "https://"
},
{
title: "Eucharistie",
description: "Gemeinschaft durch Brot und Wein",
href: "https://"
},
{
title: "Firmung",
description: "Stärkung im Heiligen Geist",
href: "https://"
},
{
title: "Ehe",
description: "Bund in Liebe, Treue",
href: "https://"
},
{
title: "Beichte",
description: "Sündenbekenntnis, Vergebung und Neuanfang mit Gottes Gnade",
href: "https://"
},
{
title: "Krankensalbung",
description: "Stärkung und Gottes Beistand",
href: "https://"
}
]
},
{
title: "Gebet",
items: [
{
title: "Gottesdienste",
description: "Begegnung mit Gott",
href: "https://"
},
{
title: "Rosenkranz",
description: "Gebet der Meditation",
href: "https://"
},
{
title: "Anbetung",
description: "Stille Begegnung mit Gott",
href: "https://"
},
{
title: "Lobpreis",
description: "Dank, Ehre und Freude",
href: "https://"
},
]
}
]
}
},
{
text: 'Kontakt',
href: '/kontakt'
}
],
rightItems: [
{
text: 'Mithelfen',
href: '/mithelfen',
},
{
text: 'Neu hier?',
href: '/ich-bin-neu',
display: "button"
}
]
}} />
<main className={"mainContent"}>
{children}
</main>

View file

@ -12,6 +12,7 @@ type MegaMenuProps = {
type ItemProps = {
title: string;
description: string
icon?: string
href: string
}
@ -20,11 +21,13 @@ type ItemGroupProps = {
items: ItemProps[]
}
const Item = ({href, title, description}: ItemProps) => {
const Item = ({href, title, description, icon}: ItemProps) => {
return (
<Link href={href} className={styles.item}>
<div className={styles.itemIcon}>
</div>
{icon &&
<div className={styles.itemIcon}>
</div>
}
<div>
<div className={styles.itemTitle}>
{title}
@ -41,7 +44,9 @@ const ItemGroup = ({title, items}: ItemGroupProps) => {
return (
<div className={styles.itemGroup}>
<div className={styles.groupTitle}>{title}</div>
{items.map(item => <Item key={item.title} title={item.title} href={item.href} description={item.description} />)}
<div className={styles.itemGroupContent}>
{items.map(item => <Item key={item.title} title={item.title} href={item.href} description={item.description} />)}
</div>
</div>
)
}

View file

@ -1,10 +1,8 @@
@import "template.scss";
$width: 220px;
.menu {
background-color: $shade3;
display: inline-flex;
display: flex;
gap: 20px;
}
@ -36,7 +34,6 @@ $width: 220px;
.itemGroup {
padding: 40px;
width: $width;
}
.groupTitle {
@ -68,4 +65,56 @@ $width: 220px;
font-size: 16px;
font-weight: 300;
line-height: 95%;
}
@media screen and (max-width: 576px) {
.menu {
flex-direction: column;
gap: 30px;
background-color: inherit;
}
.menu, .itemGroup, .groupTitle, .itemTitle {
font-size: 15px;
}
.itemGroup {
padding: 0px;
}
.groupTitle {
margin: 0;
color: #2c2c2c;
}
.itemGroupContent {
display: flex;
flex-wrap: wrap;
gap: 10px
}
.itemDescription {
color: #2c2c2c;
}
.item {
flex: 1 1 40%;
height: 100px;
box-sizing: border-box;
padding: 10px;
background-color: $shade3;
text-align: center;
margin: 0;
border-radius: 5px;
justify-content: center;
}
.itemDescription {
font-weight: 300;
font-size: 11px;
}
.bibleText {
display: none;
}
}

View file

@ -9,5 +9,181 @@ type Story = StoryObj<typeof Menu>
export default meta
export const Default: Story = {
args: {},
args: {
menu: {
leftItems: [
{
text: 'Home',
href: '/'
},
{
text: 'Gemeinschaft finden',
megaMenu: {
text: {
quote: '',
source: ''
},
groups: [
{
title: "Gemeinden",
items: [
{
title: "St. Richard",
description: "Mehr informationen",
href: "/gemeinde/st-richard"
},
{
title: "St. Christophorus",
description: "Mehr informationen",
href: "/gemeinde/st-christophorus"
},
{
title: "St. Clara",
description: "Mehr informationen",
href: "/gemeinde/st-clara"
},
]
},
{
title: "Gruppen",
items: [
{
title: "Kathoccino",
description: "Begegnung mit Gott",
href: "https://"
},
{
title: "Credo & Agape",
description: "Gebet der Meditation",
href: "https://"
},
{
title: "Mädchengruppe",
description: "Stille Begegnung mit Gott",
href: "https://"
},
{
title: "Alphakurs",
description: "Dank, Ehre und Freude",
href: "https://"
},
]
},
{
title: "Aktivitaten",
items: [
{
title: "Kochen",
description: "Begegnung mit Gott",
href: "https://"
},
{
title: "Lernen",
description: "Gebet der Meditation",
href: "https://"
},
{
title: "Wandern",
description: "Stille Begegnung mit Gott",
href: "https://"
},
{
title: "Singen",
description: "Dank, Ehre und Freude",
href: "https://"
},
]
}
]
}
},
{
text: 'Glauben lauben',
megaMenu: {
text: {
quote: '',
source: ''
},
groups: [
{
title: "Sakramenten",
items: [
{
title: "Taufe",
description: "Neues Leben in Christus",
href: "https://"
},
{
title: "Eucharistie",
description: "Gemeinschaft durch Brot und Wein",
href: "https://"
},
{
title: "Firmung",
description: "Stärkung im Heiligen Geist",
href: "https://"
},
{
title: "Ehe",
description: "Bund in Liebe, Treue",
href: "https://"
},
{
title: "Beichte",
description: "Sündenbekenntnis, Vergebung und Neuanfang mit Gottes Gnade",
href: "https://"
},
{
title: "Krankensalbung",
description: "Stärkung und Gottes Beistand",
href: "https://"
}
]
},
{
title: "Gebet",
items: [
{
title: "Gottesdienste",
description: "Begegnung mit Gott",
href: "https://"
},
{
title: "Rosenkranz",
description: "Gebet der Meditation",
href: "https://"
},
{
title: "Anbetung",
description: "Stille Begegnung mit Gott",
href: "https://"
},
{
title: "Lobpreis",
description: "Dank, Ehre und Freude",
href: "https://"
},
]
}
]
}
},
{
text: 'Kontakt',
href: '/kontakt'
}
],
rightItems: [
{
text: 'Mithelfen',
href: '/mithelfen',
},
{
text: 'Neu hier?',
href: '/ich-bin-neu',
display: "button"
}
]
}
},
}

View file

@ -4,216 +4,88 @@ import styles from './styles.module.scss'
import MenuIcon from './menu.svg'
import Image from 'next/image'
import classNames from 'classnames'
import {Menu as MenuType, MenuItem as MenuItemType} from "./menu.types"
import { Logo } from '@/components/Logo/Logo'
import { useState } from 'react'
import { MegaMenu } from '@/components/MegaMenu/MegaMenu'
const MenuItem = ({text, href, display = "normal", megaMenu}: MenuItemType) => {
const className = classNames({
[styles.menuLink]: display === "normal",
[styles.button]: display === "button"
});
export const Menu = () => {
const [displayMenu2, setDisplayMenu2] = useState(false);
const [displayMenu1, setDisplayMenu1] = useState(false);
const displayFirstMenu = () => {
setDisplayMenu1(true);
setDisplayMenu2(false);
}
const displaySecondMenu = () => {
setDisplayMenu2(true)
setDisplayMenu1(false)
}
const displayNothing = () => {
setDisplayMenu1(false);
setDisplayMenu2(false);
}
const [displayMegaMenu, setDisplayMegaMenu] = useState(false)
return (
<>
<nav className={classNames(styles.nav)}>
<div className={styles.navMobile}>
<Image src={MenuIcon} width={25} height={25} alt={'Menu'} />
</div>
<div className={styles.itemsLeft}>
<a className={styles.menuLink} href={'/'} onMouseEnter={displayNothing}>
Home
</a>
<a className={styles.menuLink} href={''} onMouseEnter={displayFirstMenu}>
Gemeinschaft finden
</a>
<a className={styles.menuLink} href={''} onMouseEnter={displaySecondMenu}>
Glauben leben
</a>
<a className={styles.menuLink} href={''} onMouseEnter={displayNothing}>
Kontakt
</a>
</div>
<a className={className} href={href} onClick={() => setDisplayMegaMenu(!displayMegaMenu)}>
{text}
</a>
<div className={styles.itemsRight}>
<div>
<a className={styles.menuLink} href={''}>
Mithelfen
</a>
</div>
<div>
<button className={styles.button}>Neu hier?</button>
</div>
{megaMenu && displayMegaMenu &&
<div className={styles.megaMenu}>
<MegaMenu bibleText={megaMenu.text.quote} bibleBook={megaMenu.text.source} groups={megaMenu.groups} />
</div>
</nav>
<div
className={styles.megaMenu}
style={{ display: displayMenu2 ? 'block' : 'none', opacity: displayMenu2 ? 1 : 0 }}
onMouseLeave={() => setDisplayMenu2(false)}
>
<MegaMenu
bibleText={"Ich will den HERRN loben allezeit; sein Lob soll immerdar in meinem Munde sein. Meine Seele soll sich rühmen des HERRN, dass es die Elenden hören und sich freuen."}
bibleBook={"Psalm 34 2,3"}
onClick={() => setDisplayMenu2(false)}
groups={[
{
title: "Sakramenten",
items: [
{
title: "Taufe",
description: "Neues Leben in Christus",
href: "https://"
},
{
title: "Eucharistie",
description: "Gemeinschaft durch Brot und Wein",
href: "https://"
},
{
title: "Firmung",
description: "Stärkung im Heiligen Geist",
href: "https://"
},
{
title: "Ehe",
description: "Bund in Liebe, Treue",
href: "https://"
},
{
title: "Beichte",
description: "Sündenbekenntnis, Vergebung und Neuanfang mit Gottes Gnade",
href: "https://"
},
{
title: "Krankensalbung",
description: "Stärkung und Gottes Beistand",
href: "https://"
}
]
},
{
title: "Gebet",
items: [
{
title: "Gottesdienste",
description: "Begegnung mit Gott",
href: "https://"
},
{
title: "Rosenkranz",
description: "Gebet der Meditation",
href: "https://"
},
{
title: "Anbetung",
description: "Stille Begegnung mit Gott",
href: "https://"
},
{
title: "Lobpreis",
description: "Dank, Ehre und Freude",
href: "https://"
},
]
}
]} />
</div>
<div
className={styles.megaMenu}
style={{ display: displayMenu1 ? 'block' : 'none', opacity: displayMenu1 ? 1 : 0 }}
onMouseLeave={() => setDisplayMenu1(false)}
>
<MegaMenu
bibleText={"„Denn wo zwei oder drei in meinem Namen versammelt sind, da bin ich mitten unter ihnen.“"}
bibleBook={"Matt 18-2"}
onClick={() => setDisplayMenu1(false)}
groups={[
{
title: "Gemeinden",
items: [
{
title: "St. Richard",
description: "Mehr informationen",
href: "/gemeinde/st-richard"
},
{
title: "St. Christophorus",
description: "Mehr informationen",
href: "/gemeinde/st-christophorus"
},
{
title: "St. Clara",
description: "Mehr informationen",
href: "/gemeinde/st-clara"
},
]
},
{
title: "Gruppen",
items: [
{
title: "Kathoccino",
description: "Begegnung mit Gott",
href: "https://"
},
{
title: "Credo & Agape",
description: "Gebet der Meditation",
href: "https://"
},
{
title: "Mädchengruppe",
description: "Stille Begegnung mit Gott",
href: "https://"
},
{
title: "Alphakurs",
description: "Dank, Ehre und Freude",
href: "https://"
},
]
},
{
title: "Aktivitaten",
items: [
{
title: "Kochen",
description: "Begegnung mit Gott",
href: "https://"
},
{
title: "Lernen",
description: "Gebet der Meditation",
href: "https://"
},
{
title: "Wandern",
description: "Stille Begegnung mit Gott",
href: "https://"
},
{
title: "Singen",
description: "Dank, Ehre und Freude",
href: "https://"
},
]
}
]} />
</div>
}
</>
)
}
type MenuItemsProps = {
items: MenuItemType[]
}
const MenuItems = ({items}: MenuItemsProps) => {
return (
<>
{items.map(item =>
<MenuItem
key={item.text}
text={item.text}
href={item.href}
display={item.display}
megaMenu={item.megaMenu}
/>
)}
</>
)
}
type MenuProps = {
menu: MenuType
}
export const Menu = ({menu}: MenuProps) => {
const [displayMenuMobile, setDisplayMenuMobile] = useState(false)
return (
<>
<nav className={classNames(styles.nav, {[styles.full]: displayMenuMobile})}>
<div className={styles.navMobile}>
<Logo withText={false} height={30} color={"#426156"}/>
<Image
src={MenuIcon}
width={25}
height={25}
alt={'Menu'}
onClick={() => setDisplayMenuMobile(!displayMenuMobile)}
/>
</div>
<div className={classNames(styles.itemsLeft, {[styles.hide]: !displayMenuMobile})}>
<MenuItems items={menu.leftItems} />
</div>
<div className={classNames(styles.itemsRight, {[styles.hide]: !displayMenuMobile})}>
<MenuItems items={menu.rightItems} />
</div>
</nav>
</>
)

View file

@ -0,0 +1,33 @@
export type Menu = {
leftItems: MenuItem[]
rightItems: MenuItem[]
}
export type MenuItem = {
text: string,
href?: string
megaMenu?: MegaMenu
display?: "normal" | "button"
}
type MegaMenu = {
groups: MegaMenuGroup[]
text: MegaMenuText
}
type MegaMenuGroup = {
title: string
items: MegaMenuItem[]
}
type MegaMenuItem = {
title: string,
description: string
href: string
icon?: string
}
type MegaMenuText = {
quote: string
source: string
}

View file

@ -2,7 +2,6 @@
.nav {
display: flex;
align-items: center;
gap: 20px;
color: $base-color;
padding: 15px 45px;
@ -14,6 +13,8 @@
box-sizing: border-box;
z-index: 1;
backdrop-filter: blur(8px);
font-size: 18px;
align-items: baseline;
}
.navMobile {
@ -23,6 +24,7 @@
.itemsLeft {
display: flex;
gap: 30px;
flex-grow: 1;
}
.menuLink {
@ -30,6 +32,7 @@
text-decoration: none;
font-weight: 600;
transition: opacity 100ms ease-in;
cursor: pointer;
}
.menuLink:hover {
@ -37,10 +40,8 @@
}
.itemsRight {
margin-left: auto;
display: flex;
gap: 20px;
justify-content: flex-end;
align-items: baseline;
}
@ -48,6 +49,8 @@
padding: 10px;
border-radius: 10px;
border: none;
color: inherit;
text-decoration: none;
background-color: #eeeeee;
transition: background-color 0.1s ease-in-out;
font-family: inherit;
@ -62,17 +65,44 @@
.megaMenu {
position: fixed;
top: 76px;
left: 0;
width: 100%;
z-index: 8;
opacity: 0;
transition: opacity 0.2s ease-in-out;
}
@media screen and (max-width: 800px) {
.nav {
flex-direction: column;
padding: 15px 15px;
align-items: inherit;
}
.megaMenu {
position: inherit;
}
.navMobile {
display: block;
display: flex;
align-items: center;
justify-content: space-between;
}
.itemsLeft {
padding-bottom: 15px;
border-bottom: 1px solid #d0d0d0;
}
.itemsLeft, .itemsRight {
gap: 10px;
flex-direction: column;
}
.hide {
display: none;
}
.full {
height: 100vh;
overflow: scroll;
}
}