Skip to content

Commit 32ae6db

Browse files
author
Hofstetter Benjamin
committed
use a stack for subMenu title
1 parent cec4ec9 commit 32ae6db

File tree

2 files changed

+197
-21
lines changed

2 files changed

+197
-21
lines changed

opendata.swiss/ui/app/components/OdsDropdownMenu.vue

Lines changed: 194 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
<script setup lang="ts">
22
import { ref, onMounted, onBeforeUnmount, type PropType } from 'vue';
3-
import OdsDesktopNavigationPanel from '@/components/OdsDesktopNavigationPanel.vue';
43
import OdsButton from './OdsButton.vue';
4+
import SvgIcon from './SvgIcon.vue';
55
import type { OdsNavTabItem } from './headers/model/ods-nav-tab-item';
66
77
const props = defineProps({
@@ -36,23 +36,62 @@ const isOpen = ref(false);
3636
const dropdownRef = ref<HTMLElement | null>(null);
3737
3838
function toggleDropdown() {
39-
console.log('toggleDropdown');
4039
isOpen.value = !isOpen.value;
4140
}
4241
4342
43+
const parentLabelStack = ref<string[]>([props.menu.label]);
44+
45+
const menuStack = ref<OdsNavTabItem[][]>([]);
46+
47+
const menuKey = computed(() => menuStack.value.length);
48+
49+
const currentMenu = computed(() => {
50+
51+
if (menuStack.value.length === 0) {
52+
return props.menu.subMenu ?? []
53+
};
54+
return menuStack.value[menuStack.value.length - 1];
55+
});
56+
57+
const direction = ref<'forward' | 'backward'>('forward');
58+
59+
const transitionName = computed(() => direction.value === 'forward' ? 'slide-menu-right' : 'slide-menu-left');
60+
61+
62+
function drillDown(item: OdsNavTabItem) {
63+
if (item.subMenu && item.subMenu.length > 0) {
64+
direction.value = 'forward';
65+
menuStack.value.push(item.subMenu);
66+
parentLabelStack.value.push(item.label);
67+
}
68+
}
69+
70+
function goBack() {
71+
if (menuStack.value.length > 0) {
72+
direction.value = 'backward';
73+
menuStack.value.pop();
74+
parentLabelStack.value.pop();
75+
}
76+
};
77+
78+
// Close dropdown when clicking outside
4479
function handleClickOutside(event: MouseEvent) {
4580
if (dropdownRef.value && !dropdownRef.value.contains(event.target as Node)) {
4681
isOpen.value = false;
4782
}
4883
}
4984
85+
// start listening for clicks outside the dropdown
5086
onMounted(() => {
5187
document.addEventListener('mousedown', handleClickOutside);
5288
});
89+
90+
// stop listening when component is unmounted
5391
onBeforeUnmount(() => {
5492
document.removeEventListener('mousedown', handleClickOutside);
5593
});
94+
5695
</script>
5796

5897
<template>
@@ -63,25 +102,58 @@ onBeforeUnmount(() => {
63102
</a>
64103
<div v-if="isOpen" id="desktop-menu__drawer" class="desktop-menu__drawer ods-drop-down-panel" >
65104
<div class="close-button-container">
105+
<SvgIcon v-if="menuStack.length > 0" icon="ArrowLeft" size="lg" @click.prevent="goBack"/>
106+
107+
<div v-else />
108+
<!-- <li v-if="menuStack.length > 0" style="border-bottom: none;" @click="goBack">
109+
<a style="padding-top: 12px; padding-bottom: 12px;">
110+
<span>
111+
<SvgIcon icon="ArrowLeft" size="lg"/>
112+
</span>
113+
</a>
114+
</li>-->
66115
<OdsButton
67-
icon="Cancel"
68-
title="close menu"
69-
variant="bare"
70-
size="sm"
71-
icon-right
72-
class="ods-close"
73-
@click="isOpen = false"
74-
>
116+
icon="Cancel"
117+
title="close menu"
118+
variant="bare"
119+
size="sm"
120+
icon-right
121+
class="ods-close"
122+
@click="isOpen = false"
123+
>
75124
<span class="ods-close">Schliessen</span>
76125
</OdsButton>
77126
</div>
78-
<OdsDesktopNavigationPanel :menu="props.menu"/>
79-
<!-- <div v-for="subItem in props.subMenu" :key="subItem.to">
80-
<NuxtLinkLocale :to="subItem.to" @click="isOpen = false">
81-
{{ props.t(subItem.label) }}
82-
</NuxtLinkLocale>
83-
</div>-->
127+
<Transition :name="transitionName" mode="out-in">
128+
<div :key="menuKey">
129+
<div class="navy">
130+
<h2 class="navy__title">{{ t(parentLabelStack[parentLabelStack.length - 1] ?? '') }}</h2>
84131

132+
<nav class="navy__level-0 navy-menu__level-0">
133+
<ul>
134+
<template v-for="item in currentMenu" :key="item.label">
135+
<li v-if="item.to">
136+
<NuxtLinkLocale
137+
:to="item.to"
138+
active-class="active"
139+
:aria-label="t(item.label)"
140+
@click="isOpen = false"
141+
>
142+
<span>{{t(item.label)}}</span>
143+
</NuxtLinkLocale><div/>
144+
</li>
145+
<li
146+
v-else-if="(item.subMenu?.length ?? -1) > 0"
147+
@click="drillDown(item)"
148+
>
149+
<a>{{t(item.label)}} <SvgIcon size="lg" icon="ArrowRight" /></a>
150+
</li>
151+
</template>
152+
</ul>
153+
</nav>
154+
</div>
155+
</div>
156+
</Transition>
85157
</div>
86158
</div>
87159
</template>
@@ -90,7 +162,7 @@ onBeforeUnmount(() => {
90162
91163
.close-button-container {
92164
display: flex;
93-
justify-content: flex-end;
165+
justify-content: space-between;
94166
}
95167
.ods-close {
96168
white-space: nowrap;
@@ -110,6 +182,7 @@ onBeforeUnmount(() => {
110182
padding-right: 48px;
111183
padding-bottom: 48px;
112184
padding-top: 24px;
185+
z-index: 100;
113186
}
114187
.ods-dropdown {
115188
position: relative;
@@ -147,4 +220,108 @@ onBeforeUnmount(() => {
147220
.ods-dropdown__menu a:hover {
148221
background: #f5f5f5;
149222
}
223+
224+
.holly {
225+
margin-top: -40px;
226+
}
227+
// list styles
228+
.navy {
229+
position: unset !important;
230+
perspective: none !important;
231+
}
232+
233+
.navy__level-0 {
234+
position: unset;
235+
ul {
236+
display: block;
237+
}
238+
}
239+
.navy__title {
240+
margin-top: 0;
241+
margin-left: 12px
242+
}
243+
244+
li {
245+
border-bottom-width: 1px;
246+
font-size: 0.875rem;
247+
width: 100%;
248+
cursor: pointer;
249+
line-height: 24px;
250+
height: 46px;
251+
margin-top: 0 !important;
252+
253+
}
254+
li:hover {
255+
background-color: var(--color-secondary-50);
256+
}
257+
258+
#mobile-menu {
259+
width: 100vw;
260+
}
261+
262+
ul {
263+
list-style: none !important;
264+
margin: 0 !important;
265+
padding: 0 !important;
266+
}
267+
268+
a {
269+
text-decoration: none;
270+
display: flex;
271+
align-items: center;
272+
justify-content: space-between;
273+
height: 100%;
274+
padding-left: 16px;
275+
padding-right: 32px;
276+
color: black;
277+
}
278+
279+
.submenu-arrow {
280+
float: right;
281+
font-size: 1.2em;
282+
margin-left: 8px;
283+
}
284+
.back-btn {
285+
font-weight: bold;
286+
color: #2ecc40;
287+
cursor: pointer;
288+
}
289+
.slide-menu-right-enter-active, .slide-menu-right-leave-active {
290+
transition: transform 0.3s cubic-bezier(0.4,0,0.2,1), opacity 0.3s cubic-bezier(0.4,0,0.2,1);
291+
}
292+
.slide-menu-right-enter-from {
293+
transform: translateX(100%);
294+
opacity: 0;
295+
}
296+
.slide-menu-right-enter-to {
297+
transform: translateX(0);
298+
opacity: 1;
299+
}
300+
.slide-menu-right-leave-from {
301+
transform: translateX(0);
302+
opacity: 1;
303+
}
304+
.slide-menu-right-leave-to {
305+
transform: translateX(-100%);
306+
opacity: 0;
307+
}
308+
.slide-menu-left-enter-active, .slide-menu-left-leave-active {
309+
transition: transform 0.3s cubic-bezier(0.4,0,0.2,1), opacity 0.3s cubic-bezier(0.4,0,0.2,1);
310+
}
311+
.slide-menu-left-enter-from {
312+
transform: translateX(-100%);
313+
opacity: 0;
314+
}
315+
.slide-menu-left-enter-to {
316+
transform: translateX(0);
317+
opacity: 1;
318+
}
319+
.slide-menu-left-leave-from {
320+
transform: translateX(0);
321+
opacity: 1;
322+
}
323+
.slide-menu-left-leave-to {
324+
transform: translateX(100%);
325+
opacity: 0;
326+
}
150327
</style>

opendata.swiss/ui/app/constants/navigation-items.ts

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -28,10 +28,9 @@ export const APP_NAVIGATION_ITEMS: OdsNavTabItem[] = [
2828
{ label: 'message.header.navigation.terms_of_use', to: '/terms-of-use' },
2929
{ label: 'message.header.navigation.contact', to: '/contact' },
3030
{
31-
label: 'fake sub menu ', subMenu: [
32-
{ label: 'fake sub menu 1', to: '/about' },
33-
{ label: 'fake sub menu 2', to: '/terms-of-use' },
34-
{ label: 'fake sub menu 3', to: '/contact' }
31+
label: 'message.header.navigation.imprint', subMenu: [
32+
{ label: 'message.header.navigation.imprint', to: '/imprint' },
33+
{ label: 'message.header.navigation.privacy', to: '/privacy' }
3534
]
3635
}
3736
]

0 commit comments

Comments
 (0)