feat(nav): Hamburger-Menü mit Register statt Settings-Icon
All checks were successful
Build & Publish Docker Image / build-and-push (push) Successful in 1m19s
All checks were successful
Build & Publish Docker Image / build-and-push (push) Successful in 1m19s
Ersetzt das Settings-Zahnrad im Header durch ein Dreistriche-Menü. Das Menü enthält zwei Punkte: „Register" führt zu einer neuen /recipes-Route mit allen Rezepten alphabetisch gruppiert (A-Z-Buchstabenchips zum Scrollen, Live-Filter oben, Umlaut-normalisiert). „Einstellungen" zeigt wie bisher /admin. Auf Mobile <520px wird das App-Icon komplett ausgeblendet, damit die Suchleiste mehr Platz bekommt. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -2,7 +2,7 @@
|
||||
import { onMount } from 'svelte';
|
||||
import { page } from '$app/stores';
|
||||
import { goto, afterNavigate } from '$app/navigation';
|
||||
import { Settings, CookingPot, Globe, Utensils } from 'lucide-svelte';
|
||||
import { Settings, CookingPot, Globe, Utensils, Menu, BookOpen } from 'lucide-svelte';
|
||||
import { profileStore } from '$lib/client/profile.svelte';
|
||||
import { wishlistStore } from '$lib/client/wishlist.svelte';
|
||||
import { pwaStore } from '$lib/client/pwa.svelte';
|
||||
@@ -24,6 +24,8 @@
|
||||
let navOpen = $state(false);
|
||||
let navContainer: HTMLElement | undefined = $state();
|
||||
let debounceTimer: ReturnType<typeof setTimeout> | null = null;
|
||||
let menuOpen = $state(false);
|
||||
let menuContainer: HTMLElement | undefined = $state();
|
||||
|
||||
const showHeaderSearch = $derived(
|
||||
$page.url.pathname.startsWith('/recipes/') || $page.url.pathname === '/preview'
|
||||
@@ -86,10 +88,16 @@
|
||||
if (navContainer && !navContainer.contains(e.target as Node)) {
|
||||
navOpen = false;
|
||||
}
|
||||
if (menuContainer && !menuContainer.contains(e.target as Node)) {
|
||||
menuOpen = false;
|
||||
}
|
||||
}
|
||||
|
||||
function handleKey(e: KeyboardEvent) {
|
||||
if (e.key === 'Escape' && navOpen) navOpen = false;
|
||||
if (e.key === 'Escape') {
|
||||
if (navOpen) navOpen = false;
|
||||
if (menuOpen) menuOpen = false;
|
||||
}
|
||||
}
|
||||
|
||||
function pickHit() {
|
||||
@@ -104,6 +112,7 @@
|
||||
navHits = [];
|
||||
navWebHits = [];
|
||||
navOpen = false;
|
||||
menuOpen = false;
|
||||
// Badge nach jeder Client-Navigation frisch halten — sonst kann er
|
||||
// hinter den tatsächlichen Wunschliste-Einträgen herlaufen, wenn
|
||||
// auf einem anderen Gerät oder in einem anderen Tab etwas geändert
|
||||
@@ -234,9 +243,29 @@
|
||||
<span class="badge">{wishlistStore.count}</span>
|
||||
{/if}
|
||||
</a>
|
||||
<a href="/admin" class="nav-link" aria-label="Einstellungen">
|
||||
<Settings size={20} strokeWidth={2} />
|
||||
</a>
|
||||
<div class="menu-wrap" bind:this={menuContainer}>
|
||||
<button
|
||||
class="nav-link"
|
||||
aria-label="Menü"
|
||||
aria-haspopup="menu"
|
||||
aria-expanded={menuOpen}
|
||||
onclick={() => (menuOpen = !menuOpen)}
|
||||
>
|
||||
<Menu size={22} strokeWidth={2} />
|
||||
</button>
|
||||
{#if menuOpen}
|
||||
<div class="menu" role="menu">
|
||||
<a href="/recipes" class="menu-item" role="menuitem" onclick={() => (menuOpen = false)}>
|
||||
<BookOpen size={18} strokeWidth={2} />
|
||||
<span>Register</span>
|
||||
</a>
|
||||
<a href="/admin" class="menu-item" role="menuitem" onclick={() => (menuOpen = false)}>
|
||||
<Settings size={18} strokeWidth={2} />
|
||||
<span>Einstellungen</span>
|
||||
</a>
|
||||
</div>
|
||||
{/if}
|
||||
</div>
|
||||
<ProfileSwitcher />
|
||||
</div>
|
||||
</div>
|
||||
@@ -410,6 +439,43 @@
|
||||
flex-shrink: 0;
|
||||
margin-left: auto;
|
||||
}
|
||||
.menu-wrap {
|
||||
position: relative;
|
||||
}
|
||||
.menu-wrap > .nav-link {
|
||||
background: transparent;
|
||||
border: 0;
|
||||
cursor: pointer;
|
||||
color: inherit;
|
||||
}
|
||||
.menu {
|
||||
position: absolute;
|
||||
top: calc(100% + 0.35rem);
|
||||
right: 0;
|
||||
background: white;
|
||||
border: 1px solid #e4eae7;
|
||||
border-radius: 12px;
|
||||
box-shadow: 0 14px 40px rgba(0, 0, 0, 0.18);
|
||||
min-width: 180px;
|
||||
padding: 0.3rem;
|
||||
z-index: 55;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
.menu-item {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 0.55rem;
|
||||
padding: 0.6rem 0.75rem;
|
||||
border-radius: 8px;
|
||||
text-decoration: none;
|
||||
color: #1a1a1a;
|
||||
font-size: 0.95rem;
|
||||
min-height: 44px;
|
||||
}
|
||||
.menu-item:hover {
|
||||
background: #f4f8f5;
|
||||
}
|
||||
.nav-link {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
@@ -447,21 +513,9 @@
|
||||
margin: 0 auto;
|
||||
}
|
||||
@media (max-width: 520px) {
|
||||
/* App-Icon auf engen Screens komplett aus — die Suche bekommt den Platz. */
|
||||
.brand {
|
||||
font-size: 0;
|
||||
width: 1.6rem;
|
||||
height: 1.6rem;
|
||||
background: #2b6a3d;
|
||||
border-radius: 8px;
|
||||
position: relative;
|
||||
}
|
||||
.brand::after {
|
||||
content: '🍳';
|
||||
font-size: 1rem;
|
||||
position: absolute;
|
||||
inset: 0;
|
||||
display: grid;
|
||||
place-items: center;
|
||||
display: none;
|
||||
}
|
||||
.nav-link {
|
||||
width: 36px;
|
||||
@@ -474,7 +528,7 @@
|
||||
position: absolute;
|
||||
top: 0.6rem;
|
||||
bottom: 0.6rem;
|
||||
left: calc(1rem + 1.6rem + 0.6rem);
|
||||
left: 1rem;
|
||||
right: 1rem;
|
||||
z-index: 60;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user