mobile menu

This commit is contained in:
2025-06-08 00:08:55 +02:00
parent 0f09bf8436
commit 18fee933ec
68 changed files with 4112 additions and 416 deletions

View File

@ -0,0 +1,105 @@
<template>
<div class="z-10">
<div
class="dropdown relative inline-flex [--placement:top] [--strategy:absolute]"
>
<button
:id
class="dropdown-toggle btn btn-primary btn-lg btn-square dropdown-open:rotate-45 transition-transform"
aria-haspopup="menu"
aria-expanded="false"
aria-label="Menu"
>
<Icon
:name="icon"
size="46"
/>
</button>
<ul
class="dropdown-menu dropdown-open:opacity-100 hidden min-w-60 bg-transparent shadow-none"
data-dropdown-transition
role="menu"
aria-orientation="vertical"
:aria-labelledby="id"
>
<li
v-for="link in menu"
class="dropdown-item hover:bg-transparent px-0"
>
<NuxtLinkLocale
v-if="link.to"
:to="link.to"
class="btn btn-primary flex items-center no-underline rounded-lg flex-nowrap w-full"
>
<Icon
v-if="link.icon"
:name="link.icon"
class="me-3"
/>
{{ link.label }}
</NuxtLinkLocale>
<button
v-else
@click="link.action"
class="link hover:link-primary flex items-center no-underline w-full"
>
<Icon
v-if="link.icon"
:name="link.icon"
class="me-3"
/>
{{ link.label }}
</button>
</li>
</ul>
</div>
</div>
</template>
<script setup lang="ts">
import type { IActionMenuItem } from './types'
defineProps({
menu: {
type: Array as PropType<IActionMenuItem[]>,
},
icon: {
type: String,
default: 'mdi:plus',
},
})
const id = useId()
</script>
<style lang="css" scoped>
@keyframes fadeInStagger {
from {
opacity: 0;
transform: translateY(15px);
}
to {
opacity: 1;
transform: translateY(0);
}
}
/* 2. Die Listenelemente sind standardmäßig unsichtbar, damit sie nicht aufblitzen */
.stagger-menu li {
opacity: 0;
}
/* 3. Wenn das Menü geöffnet wird, weise die Animation zu */
:global(.dropdown-open) .stagger-menu li {
animation-name: fadeInStagger;
animation-duration: 0.4s;
animation-timing-function: ease-out;
/* SEHR WICHTIG: Sorgt dafür, dass die Elemente nach der Animation sichtbar bleiben (den Zustand von 'to' beibehalten) */
animation-fill-mode: forwards;
/* Die individuelle animation-delay wird per :style im Template gesetzt. */
}
</style>

8
src/components/ui/button/types.d.ts vendored Normal file
View File

@ -0,0 +1,8 @@
import type { RouteLocationRaw } from 'vue-router'
export interface IActionMenuItem {
label: string
icon?: string
action?: () => Promise<unknown>
to?: RouteLocationRaw
}