migrate from deprecated slots to snippets and fix event handlers

- Replace deprecated <slot> syntax with modern {#snippet} and {@render} patterns
- Add TypeScript types for snippet props in Header component
- Convert on:click event handlers to onclick attribute throughout
- Update all layout files to use new snippet-based composition pattern
This commit is contained in:
2025-12-27 12:24:30 +01:00
parent abc0d03e01
commit f161d8a15d
7 changed files with 111 additions and 59 deletions

View File

@@ -1,8 +1,23 @@
<script> <script lang="ts">
import "$lib/css/nordtheme.css" import "$lib/css/nordtheme.css"
import { onMount } from "svelte"; import { onMount } from "svelte";
import { page } from '$app/stores'; import { page } from '$app/stores';
import Symbol from "./Symbol.svelte" import Symbol from "./Symbol.svelte"
import type { Snippet } from 'svelte';
let {
links,
language_selector_mobile,
language_selector_desktop,
right_side,
children
}: {
links?: Snippet;
language_selector_mobile?: Snippet;
language_selector_desktop?: Snippet;
right_side?: Snippet;
children?: Snippet;
} = $props();
let underlineLeft = $state(0); let underlineLeft = $state(0);
let underlineWidth = $state(0); let underlineWidth = $state(0);
@@ -297,25 +312,25 @@ footer{
<div class=button_wrapper> <div class=button_wrapper>
<a href="/"><Symbol></Symbol></a> <a href="/"><Symbol></Symbol></a>
<div class="right-buttons"> <div class="right-buttons">
<slot name=language_selector_mobile></slot> {@render language_selector_mobile?.()}
<button class=nav_button on:click={() => {toggle_sidebar()}}><svg xmlns="http://www.w3.org/2000/svg" height="0.5em" viewBox="0 0 448 512"><!--! Font Awesome Free 6.4.0 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license (Commercial License) Copyright 2023 Fonticons, Inc. --><path d="M0 96C0 78.3 14.3 64 32 64H416c17.7 0 32 14.3 32 32s-14.3 32-32 32H32C14.3 128 0 113.7 0 96zM0 256c0-17.7 14.3-32 32-32H416c17.7 0 32 14.3 32 32s-14.3 32-32 32H32c-17.7 0-32-14.3-32-32zM448 416c0 17.7-14.3 32-32 32H32c-17.7 0-32-14.3-32-32s14.3-32 32-32H416c17.7 0 32 14.3 32 32z"/></svg></button> <button class=nav_button onclick={() => {toggle_sidebar()}}><svg xmlns="http://www.w3.org/2000/svg" height="0.5em" viewBox="0 0 448 512"><!--! Font Awesome Free 6.4.0 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license (Commercial License) Copyright 2023 Fonticons, Inc. --><path d="M0 96C0 78.3 14.3 64 32 64H416c17.7 0 32 14.3 32 32s-14.3 32-32 32H32C14.3 128 0 113.7 0 96zM0 256c0-17.7 14.3-32 32-32H416c17.7 0 32 14.3 32 32s-14.3 32-32 32H32c-17.7 0-32-14.3-32-32zM448 416c0 17.7-14.3 32-32 32H32c-17.7 0-32-14.3-32-32s14.3-32 32-32H416c17.7 0 32 14.3 32 32z"/></svg></button>
</div> </div>
</div> </div>
<nav hidden class=nav_site> <nav hidden class=nav_site>
<a class=entry href="/"><Symbol></Symbol></a> <a class=entry href="/"><Symbol></Symbol></a>
<div class="links-wrapper"> <div class="links-wrapper">
<slot name=links></slot> {@render links?.()}
<div class="active-underline" class:no-transition={disableTransition} style="left: {underlineLeft}px; width: {underlineWidth}px;"></div> <div class="active-underline" class:no-transition={disableTransition} style="left: {underlineLeft}px; width: {underlineWidth}px;"></div>
</div> </div>
<div class="header-right"> <div class="header-right">
<div class="language-selector-desktop"> <div class="language-selector-desktop">
<slot name=language_selector_desktop></slot> {@render language_selector_desktop?.()}
</div> </div>
<slot name=right_side></slot> {@render right_side?.()}
</div> </div>
</nav> </nav>
<slot></slot> {@render children?.()}
</div> </div>
<footer> <footer>

View File

@@ -136,7 +136,7 @@ h2 + p{
</style> </style>
{#if user} {#if user}
<button on:click={toggle_options} style="background-image: url(https://bocken.org/static/user/thumb/{user.nickname}.webp)" id=button> <button onclick={toggle_options} style="background-image: url(https://bocken.org/static/user/thumb/{user.nickname}.webp)" id=button>
<div id=options class="speech top" hidden> <div id=options class="speech top" hidden>
<h2>{user.name}</h2> <h2>{user.name}</h2>
<p>({user.nickname})</p> <p>({user.nickname})</p>

View File

@@ -2,16 +2,28 @@
import Header from '$lib/components/Header.svelte' import Header from '$lib/components/Header.svelte'
import UserHeader from '$lib/components/UserHeader.svelte'; import UserHeader from '$lib/components/UserHeader.svelte';
import LanguageSelector from '$lib/components/LanguageSelector.svelte'; import LanguageSelector from '$lib/components/LanguageSelector.svelte';
let { data } = $props(); let { data, children } = $props();
let user = $derived(data.session?.user); let user = $derived(data.session?.user);
</script> </script>
<Header> <Header>
<ul class=site_header slot=links> {#snippet links()}
<ul class=site_header>
</ul> </ul>
<LanguageSelector slot=language_selector_mobile /> {/snippet}
<LanguageSelector slot=language_selector_desktop />
<UserHeader {user} slot=right_side></UserHeader> {#snippet language_selector_mobile()}
<slot></slot> <LanguageSelector />
{/snippet}
{#snippet language_selector_desktop()}
<LanguageSelector />
{/snippet}
{#snippet right_side()}
<UserHeader {user}></UserHeader>
{/snippet}
{@render children()}
</Header> </Header>

View File

@@ -80,8 +80,10 @@
</svelte:head> </svelte:head>
<Header> <Header>
<ul class="site_header" slot="links"> {#snippet links()}
<ul class="site_header">
</ul> </ul>
{/snippet}
<main class="error-page"> <main class="error-page">
<div class="error-container"> <div class="error-container">
@@ -109,38 +111,38 @@
<div class="error-actions"> <div class="error-actions">
{#if status === 401} {#if status === 401}
<button class="btn btn-primary" on:click={login}> <button class="btn btn-primary" onclick={login}>
Anmelden Anmelden
</button> </button>
<button class="btn btn-secondary" on:click={goHome}> <button class="btn btn-secondary" onclick={goHome}>
Zur Startseite Zur Startseite
</button> </button>
{:else if status === 403} {:else if status === 403}
<button class="btn btn-primary" on:click={goHome}> <button class="btn btn-primary" onclick={goHome}>
Zur Startseite Zur Startseite
</button> </button>
<button class="btn btn-secondary" on:click={goBack}> <button class="btn btn-secondary" onclick={goBack}>
Zurück Zurück
</button> </button>
{:else if status === 404} {:else if status === 404}
<button class="btn btn-primary" on:click={goHome}> <button class="btn btn-primary" onclick={goHome}>
Zur Startseite Zur Startseite
</button> </button>
<button class="btn btn-secondary" on:click={goBack}> <button class="btn btn-secondary" onclick={goBack}>
Zurück Zurück
</button> </button>
{:else if status === 500} {:else if status === 500}
<button class="btn btn-primary" on:click={goHome}> <button class="btn btn-primary" onclick={goHome}>
Zur Startseite Zur Startseite
</button> </button>
<button class="btn btn-secondary" on:click={goBack}> <button class="btn btn-secondary" onclick={goBack}>
Erneut versuchen Erneut versuchen
</button> </button>
{:else} {:else}
<button class="btn btn-primary" on:click={goHome}> <button class="btn btn-primary" onclick={goHome}>
Zur Startseite Zur Startseite
</button> </button>
<button class="btn btn-secondary" on:click={goBack}> <button class="btn btn-secondary" onclick={goBack}>
Zurück Zurück
</button> </button>
{/if} {/if}

View File

@@ -3,7 +3,7 @@ import { page } from '$app/stores';
import Header from '$lib/components/Header.svelte' import Header from '$lib/components/Header.svelte'
import UserHeader from '$lib/components/UserHeader.svelte'; import UserHeader from '$lib/components/UserHeader.svelte';
import LanguageSelector from '$lib/components/LanguageSelector.svelte'; import LanguageSelector from '$lib/components/LanguageSelector.svelte';
let { data } = $props(); let { data, children } = $props();
let user = $derived(data.session?.user); let user = $derived(data.session?.user);
@@ -29,7 +29,8 @@ function isActive(path) {
</script> </script>
<Header> <Header>
<ul class=site_header slot=links> {#snippet links()}
<ul class=site_header>
<li><a href="/{data.recipeLang}" class:active={isActive(`/${data.recipeLang}`)}>{labels.allRecipes}</a></li> <li><a href="/{data.recipeLang}" class:active={isActive(`/${data.recipeLang}`)}>{labels.allRecipes}</a></li>
{#if user} {#if user}
<li><a href="/{data.recipeLang}/favorites" class:active={isActive(`/${data.recipeLang}/favorites`)}>{labels.favorites}</a></li> <li><a href="/{data.recipeLang}/favorites" class:active={isActive(`/${data.recipeLang}/favorites`)}>{labels.favorites}</a></li>
@@ -39,8 +40,19 @@ function isActive(path) {
<li><a href="/{data.recipeLang}/icon" class:active={isActive(`/${data.recipeLang}/icon`)}>{labels.icon}</a></li> <li><a href="/{data.recipeLang}/icon" class:active={isActive(`/${data.recipeLang}/icon`)}>{labels.icon}</a></li>
<li><a href="/{data.recipeLang}/tag" class:active={isActive(`/${data.recipeLang}/tag`)}>{labels.keywords}</a></li> <li><a href="/{data.recipeLang}/tag" class:active={isActive(`/${data.recipeLang}/tag`)}>{labels.keywords}</a></li>
</ul> </ul>
<LanguageSelector slot=language_selector_mobile /> {/snippet}
<LanguageSelector slot=language_selector_desktop />
<UserHeader slot=right_side {user}></UserHeader> {#snippet language_selector_mobile()}
<slot></slot> <LanguageSelector />
{/snippet}
{#snippet language_selector_desktop()}
<LanguageSelector />
{/snippet}
{#snippet right_side()}
<UserHeader {user}></UserHeader>
{/snippet}
{@render children()}
</Header> </Header>

View File

@@ -8,7 +8,7 @@
import Header from '$lib/components/Header.svelte'; import Header from '$lib/components/Header.svelte';
import UserHeader from '$lib/components/UserHeader.svelte'; import UserHeader from '$lib/components/UserHeader.svelte';
export let data; let { data, children } = $props();
let showModal = false; let showModal = false;
let paymentId = null; let paymentId = null;
@@ -57,16 +57,21 @@
</script> </script>
<Header> <Header>
<ul class="site_header" slot="links"> {#snippet links()}
<ul class="site_header">
<li><a href="/cospend" class:active={isActive('/cospend')}>Dashboard</a></li> <li><a href="/cospend" class:active={isActive('/cospend')}>Dashboard</a></li>
<li><a href="/cospend/payments" class:active={isActive('/cospend/payments')}>All Payments</a></li> <li><a href="/cospend/payments" class:active={isActive('/cospend/payments')}>All Payments</a></li>
<li><a href="/cospend/recurring" class:active={isActive('/cospend/recurring')}>Recurring Payments</a></li> <li><a href="/cospend/recurring" class:active={isActive('/cospend/recurring')}>Recurring Payments</a></li>
</ul> </ul>
<UserHeader slot="right_side" {user}></UserHeader> {/snippet}
{#snippet right_side()}
<UserHeader {user}></UserHeader>
{/snippet}
<div class="layout-container" class:has-modal={showModal}> <div class="layout-container" class:has-modal={showModal}>
<div class="main-content"> <div class="main-content">
<slot /> {@render children()}
</div> </div>
<div class="side-panel"> <div class="side-panel">

View File

@@ -2,7 +2,7 @@
import { page } from '$app/stores'; import { page } from '$app/stores';
import Header from '$lib/components/Header.svelte' import Header from '$lib/components/Header.svelte'
import UserHeader from '$lib/components/UserHeader.svelte'; import UserHeader from '$lib/components/UserHeader.svelte';
export let data let { data, children } = $props();
function isActive(path) { function isActive(path) {
const currentPath = $page.url.pathname; const currentPath = $page.url.pathname;
@@ -11,11 +11,17 @@ function isActive(path) {
} }
</script> </script>
<Header> <Header>
<ul class=site_header slot=links> {#snippet links()}
<ul class=site_header>
<li><a href="/glaube/gebete" class:active={isActive('/glaube/gebete')}>Gebete</a></li> <li><a href="/glaube/gebete" class:active={isActive('/glaube/gebete')}>Gebete</a></li>
<li><a href="/glaube/rosenkranz" class:active={isActive('/glaube/rosenkranz')}>Rosenkranz</a></li> <li><a href="/glaube/rosenkranz" class:active={isActive('/glaube/rosenkranz')}>Rosenkranz</a></li>
<li><a href="/glaube/predigten" class:active={isActive('/glaube/predigten')}>Predigten</a></li> <li><a href="/glaube/predigten" class:active={isActive('/glaube/predigten')}>Predigten</a></li>
</ul> </ul>
<UserHeader user={data.session?.user} slot=right_side></UserHeader> {/snippet}
<slot></slot>
{#snippet right_side()}
<UserHeader user={data.session?.user}></UserHeader>
{/snippet}
{@render children()}
</Header> </Header>