feat(#35): add new mobile menu

This commit is contained in:
OllyNicholass
2024-10-24 00:28:28 +01:00
parent 6ed1b25583
commit 3a5e961d54
6 changed files with 264 additions and 201 deletions
+6 -1
View File
@@ -1,5 +1,7 @@
<script lang="ts">
import { onMount } from 'svelte';
import { cubicInOut } from 'svelte/easing';
import { slide } from 'svelte/transition';
export let cookiesAccepted;
let showBanner = false;
@@ -18,7 +20,10 @@
</script>
{#if showBanner}
<div class="fixed bottom-0 left-0 w-full bg-gray-800 text-white p-4">
<div
class="fixed bottom-0 left-0 w-full bg-gray-800 text-white p-4"
transition:slide={{ duration: 200, easing: cubicInOut }}
>
<p class="text-sm">
We use cookies to ensure you get the best experience on our website. By continuing, you agree
to our use of cookies.
+13 -5
View File
@@ -1,5 +1,7 @@
<script lang="ts">
import { onMount } from 'svelte';
import { cubicInOut } from 'svelte/easing';
import { slide } from 'svelte/transition';
export let indicator: boolean = false;
export let posRight: boolean = false;
@@ -28,16 +30,22 @@
});
</script>
<div bind:this={dropdown} class="relative">
<div class="dropdown-title">
<button class="flex" on:click={() => isOpen = !isOpen}>
<div bind:this={dropdown} class="relative block">
<div class="dropdown-title block w-full">
<button class="flex w-full" on:click={() => (isOpen = !isOpen)}>
{#if indicator}
<span class="mr-1">{@html isOpen ? DropdownIcon.open : DropdownIcon.closed }</span>
<span class="mr-1">{@html isOpen ? DropdownIcon.open : DropdownIcon.closed}</span>
{/if}
<slot name="dropdown"></slot>
</button>
</div>
<ul class:flex={isOpen} class:hidden={!isOpen} class:right-0={posRight} class="absolute rounded-box min-w-52 shadow z-[1] menu p-2 bg-base-100 text-base-content">
{#if isOpen}
<ul
class:right-0={posRight}
class="absolute rounded-box min-w-52 shadow z-[1] m-0 menu p-2 bg-base-100 text-base-content block"
transition:slide={{ duration: 200, easing: cubicInOut }}
>
<slot />
</ul>
{/if}
</div>
+48 -22
View File
@@ -1,20 +1,14 @@
<script lang="ts">
import type { Session, SupabaseClient } from "@supabase/supabase-js";
import MagicLink from "./MagicLink.svelte";
import NavLinks from "./NavLinks.svelte";
import SignOut from "./SignOut.svelte";
import Dropdown from "./Dropdown.svelte";
import type { Session, SupabaseClient } from '@supabase/supabase-js';
import MagicLink from './MagicLink.svelte';
import NavLinks from './NavLinks.svelte';
import SignOut from './SignOut.svelte';
import Dropdown from './Dropdown.svelte';
import { basket, type Basket } from '$lib/stores/basket.js';
import { onDestroy } from "svelte";
import { onDestroy } from 'svelte';
export let session: Session;
export let supabase: SupabaseClient<any, "public", any>;
let isMobileMenuOpen = false;
function toggleMobileMenu() {
isMobileMenuOpen = !isMobileMenuOpen;
}
export let session: Session | null;
export let supabase: SupabaseClient<any, 'public', any>;
let localBasket: Basket;
const unsubscribeToBasket = basket.subscribe((value) => {
@@ -26,11 +20,13 @@
});
</script>
<nav class="bg-primary text-primary-content p-4">
<div class="container mx-auto flex justify-between items-center">
<!-- Hamburger Menu Button (Mobile) -->
<button on:click={toggleMobileMenu} class="block md:hidden text-white focus:outline-none">
<div class="lg:hidden mr-4">
<Dropdown>
<div slot="dropdown">
<button class="block text-white focus:outline-none">
<svg
class="w-6 h-6"
fill="none"
@@ -46,14 +42,19 @@
></path>
</svg>
</button>
</div>
<!-- Dropdown content -->
<NavLinks isMobile />
</Dropdown>
</div>
<a href="/" class="text-white font-bold text-xl">SvelteKit SaaS Boilerplate</a>
<div class="hidden lg:block">
<NavLinks />
</div>
<!-- Right Side: Basket & User Account -->
<div class="flex items-center space-x-4">
<!-- Basket/Checkout Icon -->
<a href="/checkout" class="btn btn-ghost btn-circle">
<a href="/checkout" class="btn btn-ghost btn-circle hidden lg:flex">
<div class="indicator">
<svg
xmlns="http://www.w3.org/2000/svg"
@@ -74,7 +75,6 @@
{/if}
</div>
</a>
<!-- User Account Dropdown -->
<Dropdown posRight>
<div slot="dropdown">
@@ -82,7 +82,9 @@
<div class="w-10 rounded-full">
{#if session?.user}
<div class="user-circle text-primary-content border-primary-content">
<span class="text-xl">{session?.user.email ? session?.user.email[0].toUpperCase() : '?'}</span>
<span class="text-xl"
>{session?.user.email ? session?.user.email[0].toUpperCase() : '?'}</span
>
</div>
{:else}
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="currentColor">
@@ -96,8 +98,32 @@
</div>
</div>
</div>
<!-- Dropdown content -->
{#if session?.user}
<li class="lg:hidden">
<a href="/checkout">
<svg
xmlns="http://www.w3.org/2000/svg"
class="h-5 w-5"
fill="none"
viewBox="0 0 24 24"
stroke="currentColor"
>
<path
stroke-linecap="round"
stroke-linejoin="round"
stroke-width="2"
d="M3 3h2l.4 2M7 13h10l4-8H5.4M7 13L5.4 5M7 13l-2.293 2.293c-.63.63-.184 1.707.707 1.707H17m0 0a2 2 0 100 4 2 2 0 000-4zm-8 2a2 2 0 11-4 0 2 2 0 014 0z"
/>
</svg>
Basket
{#if localBasket.items.length > 0}
<span class="badge badge-md indicator-item bg-primary text-primary-content"
>{localBasket.items.length}</span
>
{/if}
</a>
</li>
<li><a href="/account">Account</a></li>
<li><a href="/settings">Settings</a></li>
<li><SignOut {supabase} /></li>
+27 -2
View File
@@ -1,14 +1,39 @@
<script lang="ts">
import { type NavLinkItem } from '$lib/types/Nav/NavLinkItem';
import { cubicInOut } from 'svelte/easing';
import Dropdown from './Dropdown.svelte';
import { slide } from 'svelte/transition';
export let text: string;
export let children: NavLinkItem[] | undefined;
export let isMobile: boolean;
let isOpen = false;
</script>
<Dropdown indicator>
{#if !isMobile}
<Dropdown indicator>
<div slot="dropdown">{text}</div>
{#each children ?? [] as child}
<li><a href={child.href}>{child.text}</a></li>
{/each}
</Dropdown>
</Dropdown>
{:else}
<button
class:menu-dropdown-show={isOpen}
class="menu-dropdown-toggle"
on:click={() => (isOpen = !isOpen)}
>
{text}
</button>
{#if isOpen}
<ul
class="menu-dropdown menu-dropdown-show"
transition:slide={{ duration: 200, easing: cubicInOut }}
>
{#each children ?? [] as child}
<li><a href={child.href}>{child.text}</a></li>
{/each}
</ul>
{/if}
{/if}
+11 -9
View File
@@ -2,10 +2,12 @@
import { type NavLinkItem } from '$lib/types/Nav/NavLinkItem';
import NavLinkParent from './NavLinkParent.svelte';
export let isMobile = false;
const links: NavLinkItem[] = [
{
text: 'Products',
href: '/products',
href: '/products'
},
{
text: 'Parent',
@@ -13,26 +15,26 @@
children: [
{
text: 'Submenu 1',
href: '/',
href: '/'
},
{
text: 'Submenu 2',
href: '/',
},
],
href: '/'
}
]
},
{
text: 'Example Product',
href: '/tools/exampleproduct',
},
href: '/tools/exampleproduct'
}
];
</script>
<ul class="flex space-x-4">
<ul class:flex={!isMobile} class:space-x-4={!isMobile}>
{#each links as link}
<li>
{#if link.isParent}
<NavLinkParent text={link.text} children={link.children} />
<NavLinkParent text={link.text} children={link.children} {isMobile} />
{:else}
<a href={link.href}>{link.text}</a>
{/if}
+9 -12
View File
@@ -6,13 +6,13 @@
import { invalidate } from '$app/navigation';
import CookieConsent from '$lib/components/CookieConsent.svelte';
import Nav from '$lib/components/Nav.svelte';
import { slide } from 'svelte/transition';
import { cubicInOut } from 'svelte/easing';
export let data;
let { supabase, session } = data;
$: ({ supabase, session } = data);
let localGeneral: any;
const unsubscribeToGeneralStore = general.subscribe((value) => {
localGeneral = value;
@@ -40,10 +40,6 @@
return () => data.subscription.unsubscribe();
});
</script>
<svelte:head>
@@ -126,15 +122,15 @@
</nav>
</footer>
<div
{#if !localGeneral.hideToast}
<div
id={localGeneral.toastType == 'success' ? 'success-toast' : 'error-toast'}
role="alert"
class="alert {localGeneral.toastType == 'success'
? 'alert-success'
: 'alert-error'} fixed top-20 right-3 max-w-52 text-white {localGeneral.hideToast
? 'hidden'
: ''}"
>
: 'alert-error'} fixed top-20 right-3 max-w-52 text-white"
transition:slide={{ duration: 200, easing: cubicInOut }}
>
{#if localGeneral.toastType == 'success'}
<svg
xmlns="http://www.w3.org/2000/svg"
@@ -171,7 +167,8 @@
{/if}
<span>{localGeneral.toastMessage == '' ? 'Added to basket' : localGeneral.toastMessage}</span>
</div>
</div>
{/if}
<style scoped lang="postcss">
.user-circle {