refactor: migrate all Svelte components from Svelte 4 to Svelte 5 runes
- Replace `export let` with `$props()` and `$bindable()` across all components
- Replace `$:` reactive statements with `$derived()` and `$effect()`
- Replace `createEventDispatcher` with callback props (e.g. `onchange`)
- Replace `on:event` directives with inline event handlers (`onclick`, `oninput`, etc.)
- Replace `<slot />` with `{@render children()}` in layout
- Use `untrack()` for SSR-safe $state initialization from reactive props
- Replace `blur` + `setTimeout` anti-pattern in TagInput with `clickOutside` action
- Fix `page` store usage in layout to use `$app/state` directly
- 0 errors, 0 warnings after svelte-check
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -1,10 +1,10 @@
|
||||
<script lang="ts">
|
||||
import { goto } from '$app/navigation';
|
||||
export let data;
|
||||
|
||||
let searchTimeout: any;
|
||||
let { data } = $props();
|
||||
|
||||
let searchTimeout: ReturnType<typeof setTimeout>;
|
||||
|
||||
// Live-Suche (Debounce)
|
||||
function handleSearch(e: Event) {
|
||||
const value = (e.target as HTMLInputElement).value;
|
||||
clearTimeout(searchTimeout);
|
||||
@@ -49,7 +49,7 @@ function handleSearch(e: Event) {
|
||||
type="text"
|
||||
placeholder="Namen suchen..."
|
||||
value={data.q || ''}
|
||||
on:input={handleSearch}
|
||||
oninput={handleSearch}
|
||||
class="block w-full rounded-sm border border-gray-300 bg-white py-2.5 pr-10 pl-4 font-sans text-sm text-brand-navy placeholder-gray-400 shadow-sm focus:border-brand-navy focus:ring-1 focus:ring-brand-navy focus:outline-none"
|
||||
/>
|
||||
<div
|
||||
|
||||
@@ -2,21 +2,25 @@
|
||||
import { enhance } from '$app/forms';
|
||||
import PersonTypeahead from '$lib/components/PersonTypeahead.svelte';
|
||||
|
||||
export let data;
|
||||
export let form;
|
||||
let { data, form } = $props();
|
||||
|
||||
$: ({ person, documents } = data);
|
||||
const person = $derived(data.person);
|
||||
const documents = $derived(data.documents);
|
||||
|
||||
let editMode = false;
|
||||
let mergeTargetId = '';
|
||||
let showMergeConfirm = false;
|
||||
let editMode = $state(false);
|
||||
let mergeTargetId = $state('');
|
||||
let showMergeConfirm = $state(false);
|
||||
|
||||
function enterEdit() { editMode = true; }
|
||||
function cancelEdit() { editMode = false; }
|
||||
$effect(() => {
|
||||
if (form?.updated) editMode = false;
|
||||
});
|
||||
|
||||
$: if (form?.updated) { editMode = false; }
|
||||
|
||||
$: person.id, (() => { mergeTargetId = ''; showMergeConfirm = false; })();
|
||||
$effect(() => {
|
||||
// Reset merge state whenever person changes
|
||||
person.id;
|
||||
mergeTargetId = '';
|
||||
showMergeConfirm = false;
|
||||
});
|
||||
</script>
|
||||
|
||||
<div class="max-w-4xl mx-auto py-10 px-4">
|
||||
@@ -83,7 +87,7 @@
|
||||
<button type="submit" class="px-5 py-2 bg-brand-navy text-white text-sm font-bold uppercase tracking-widest rounded hover:bg-brand-navy/80 transition-colors">
|
||||
Speichern
|
||||
</button>
|
||||
<button type="button" on:click={cancelEdit} class="px-5 py-2 border border-gray-300 text-gray-600 text-sm font-bold uppercase tracking-widest rounded hover:bg-gray-50 transition-colors">
|
||||
<button type="button" onclick={() => (editMode = false)} class="px-5 py-2 border border-gray-300 text-gray-600 text-sm font-bold uppercase tracking-widest rounded hover:bg-gray-50 transition-colors">
|
||||
Abbrechen
|
||||
</button>
|
||||
</div>
|
||||
@@ -103,7 +107,7 @@
|
||||
<h1 class="text-4xl font-serif text-brand-navy">
|
||||
{person.firstName} {person.lastName}
|
||||
</h1>
|
||||
<button on:click={enterEdit} class="ml-4 flex-shrink-0 inline-flex items-center gap-1.5 px-3 py-1.5 text-xs font-bold uppercase tracking-widest border border-gray-300 text-gray-500 rounded hover:border-brand-navy hover:text-brand-navy transition-colors">
|
||||
<button onclick={() => (editMode = true)} class="ml-4 flex-shrink-0 inline-flex items-center gap-1.5 px-3 py-1.5 text-xs font-bold uppercase tracking-widest border border-gray-300 text-gray-500 rounded hover:border-brand-navy hover:text-brand-navy transition-colors">
|
||||
<svg class="w-3.5 h-3.5" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M11 5H6a2 2 0 00-2 2v11a2 2 0 002 2h11a2 2 0 002-2v-5m-1.414-9.414a2 2 0 112.828 2.828L11.828 15H9v-2.828l8.586-8.586z"/></svg>
|
||||
Bearbeiten
|
||||
</button>
|
||||
@@ -150,7 +154,7 @@
|
||||
name="_targetPersonDisplay"
|
||||
label="Zusammenführen mit"
|
||||
value={mergeTargetId}
|
||||
on:change={(e) => { mergeTargetId = e.detail.value; showMergeConfirm = false; }}
|
||||
onchange={(value) => { mergeTargetId = value; showMergeConfirm = false; }}
|
||||
/>
|
||||
</div>
|
||||
|
||||
@@ -158,7 +162,7 @@
|
||||
<button
|
||||
type="button"
|
||||
disabled={!mergeTargetId}
|
||||
on:click={() => showMergeConfirm = true}
|
||||
onclick={() => (showMergeConfirm = true)}
|
||||
class="px-4 py-2 text-sm font-bold uppercase tracking-widest border border-red-300 text-red-600 rounded hover:bg-red-50 transition-colors disabled:opacity-40 disabled:cursor-not-allowed"
|
||||
>
|
||||
Zusammenführen
|
||||
@@ -173,7 +177,7 @@
|
||||
</button>
|
||||
<button
|
||||
type="button"
|
||||
on:click={() => showMergeConfirm = false}
|
||||
onclick={() => (showMergeConfirm = false)}
|
||||
class="px-4 py-2 text-sm font-bold uppercase tracking-widest border border-gray-300 text-gray-500 rounded hover:bg-gray-50 transition-colors"
|
||||
>
|
||||
Abbrechen
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
<script lang="ts">
|
||||
export let form;
|
||||
let { form } = $props();
|
||||
</script>
|
||||
|
||||
<div class="mx-auto max-w-2xl px-4 py-8">
|
||||
|
||||
Reference in New Issue
Block a user