feat: complete Svelte 5 migration across entire application
All checks were successful
CI / update (push) Successful in 2m8s

Migrated all components and routes from Svelte 4 to Svelte 5 syntax:

- Converted export let → $props() with generic type syntax
- Replaced createEventDispatcher → callback props
- Migrated $: reactive statements → $derived() and $effect()
- Updated two-way bindings with $bindable()
- Fixed TypeScript syntax: added lang="ts" to script tags
- Converted inline type annotations to generic parameter syntax

- Updated deprecated event directives to Svelte 5 syntax:
  - on:click → onclick
  - on:submit → onsubmit
  - on:change → onchange

- Converted deprecated <slot> elements → {@render children()}
- Updated slot props to Snippet types
- Fixed season/icon selector components with {#snippet} blocks

- Fixed non-reactive state by converting let → $state()
- Fixed infinite loop in EnhancedBalance by converting $effect → $derived
- Fixed Chart.js integration by converting $state proxies to plain arrays
- Updated cospend dashboard and payment pages with proper reactivity

- Migrated 20+ route files from export let data → $props()
- Fixed TypeScript type annotations in page components
- Updated reactive statements in error and cospend routes

- Removed invalid onchange attribute from Toggle component
- Fixed modal ID isolation in CreateIngredientList/CreateStepList
- Fixed dark mode button visibility in TranslationApproval
- Build now succeeds with zero deprecation warnings

All functionality tested and working. No breaking changes to user experience.
This commit is contained in:
2026-01-10 16:20:43 +01:00
parent 8eee15d901
commit 5c8605c690
72 changed files with 1011 additions and 1043 deletions

View File

@@ -4,29 +4,35 @@ import { browser } from '$app/environment';
import { do_on_key } from '$lib/components/do_on_key.js'
import Check from '$lib/assets/icons/Check.svelte'
export let type: 'ingredients' | 'instructions' = 'ingredients';
export let onSelect: (recipe: any, options: any) => void;
export let open = false;
let {
type = 'ingredients' as 'ingredients' | 'instructions',
onSelect,
open = $bindable(false)
}: {
type?: 'ingredients' | 'instructions',
onSelect: (recipe: any, options: any) => void,
open?: boolean
} = $props();
// Unique dialog ID based on type to prevent conflicts when both are on the same page
const dialogId = `base-recipe-selector-modal-${type}`;
let baseRecipes: any[] = [];
let selectedRecipe: any = null;
let options = {
let baseRecipes: any[] = $state([]);
let selectedRecipe: any = $state(null);
let options = $state({
includeIngredients: false,
includeInstructions: false,
showLabel: true,
labelOverride: ''
};
});
// Reset options whenever type or modal state changes
$: {
$effect(() => {
if (open || type) {
options.includeIngredients = type === 'ingredients';
options.includeInstructions = type === 'instructions';
}
}
});
onMount(async () => {
const res = await fetch('/api/rezepte/base-recipes');
@@ -63,13 +69,15 @@ function openModal() {
}
}
$: if (browser) {
if (open) {
setTimeout(openModal, 0);
} else {
closeModal();
$effect(() => {
if (browser) {
if (open) {
setTimeout(openModal, 0);
} else {
closeModal();
}
}
}
});
</script>
<style>
@@ -232,16 +240,16 @@ dialog h2 {
type="text"
bind:value={options.labelOverride}
placeholder={selectedRecipe?.name || 'Überschrift eingeben...'}
on:keydown={(event) => do_on_key(event, 'Enter', false, handleInsert)}
onkeydown={(event) => do_on_key(event, 'Enter', false, handleInsert)}
/>
</label>
{/if}
<div class="button-group">
<button class="button-insert" on:click={handleInsert} disabled={!selectedRecipe}>
<button class="button-insert" onclick={handleInsert} disabled={!selectedRecipe}>
Einfügen
</button>
<button class="button-cancel" on:click={closeModal}>
<button class="button-cancel" onclick={closeModal}>
Abbrechen
</button>
</div>