refactor: simplify ItemForm API using events instead of callback props

This commit is contained in:
Rasmus Q
2026-03-16 14:20:50 +00:00
parent d63535ad1b
commit 563ee5699b
2 changed files with 34 additions and 36 deletions

View File

@@ -11,16 +11,19 @@
import { languageStore } from '$lib/stores/language.svelte'; import { languageStore } from '$lib/stores/language.svelte';
import ThemeCard from '$lib/components/themes/ThemeCard.svelte'; import ThemeCard from '$lib/components/themes/ThemeCard.svelte';
import { getCardStyle } from '$lib/utils/colors'; import { getCardStyle } from '$lib/utils/colors';
import { createEventDispatcher } from 'svelte';
const dispatch = createEventDispatcher<{
success: void;
cancel: void;
colorChange: { itemId: string; color: string };
positionChange: { itemId: string; newPosition: number };
}>();
interface Props { interface Props {
item?: Item | null; item?: Item | null;
mode: 'add' | 'edit'; mode: 'add' | 'edit';
onSuccess?: () => void; itemCount?: number;
onCancel?: () => void;
onColorChange?: (itemId: string, color: string) => void;
currentPosition?: number;
totalItems?: number;
onPositionChange?: (newPosition: number) => void;
wishlistColor?: string | null; wishlistColor?: string | null;
wishlistTheme?: string | null; wishlistTheme?: string | null;
} }
@@ -28,12 +31,7 @@
let { let {
item = null, item = null,
mode, mode,
onSuccess, itemCount = 1,
onCancel,
onColorChange,
currentPosition = 1,
totalItems = 1,
onPositionChange,
wishlistColor = null, wishlistColor = null,
wishlistTheme = null wishlistTheme = null
}: Props = $props(); }: Props = $props();
@@ -47,6 +45,7 @@
let linkUrl = $state(item?.link || ''); let linkUrl = $state(item?.link || '');
let imageUrl = $state(item?.imageUrl || ''); let imageUrl = $state(item?.imageUrl || '');
let color = $state<string | null>(item?.color || null); let color = $state<string | null>(item?.color || null);
let position = $state(item?.order ? Number(item.order) + 1 : 1);
let scrapedImages = $state<string[]>([]); let scrapedImages = $state<string[]>([]);
let isLoadingImages = $state(false); let isLoadingImages = $state(false);
let debounceTimer: ReturnType<typeof setTimeout> | null = null; let debounceTimer: ReturnType<typeof setTimeout> | null = null;
@@ -97,7 +96,13 @@
function handleColorChange() { function handleColorChange() {
if (isEdit && item) { if (isEdit && item) {
onColorChange?.(item.id, color || ''); dispatch('colorChange', { itemId: item.id, color: color || '' });
}
}
function handlePositionChange() {
if (isEdit && item) {
dispatch('positionChange', { itemId: item.id, newPosition: position });
} }
} }
@@ -121,7 +126,7 @@
use:enhance={() => { use:enhance={() => {
return async ({ update }) => { return async ({ update }) => {
await update({ reset: false }); await update({ reset: false });
onSuccess?.(); dispatch('success');
}; };
}} }}
class="space-y-4" class="space-y-4"
@@ -222,20 +227,16 @@
<Label for="position">{t.form.position}</Label> <Label for="position">{t.form.position}</Label>
<Input <Input
id="position" id="position"
name="position"
type="number" type="number"
min="1" min="1"
max={totalItems} max={itemCount}
value={currentPosition} bind:value={position}
onchange={(e) => { onchange={handlePositionChange}
const newPos = parseInt((e.target as HTMLInputElement).value);
if (newPos >= 1 && newPos <= totalItems) {
onPositionChange?.(newPos);
}
}}
placeholder="1" placeholder="1"
/> />
<p class="text-sm text-muted-foreground"> <p class="text-sm text-muted-foreground">
Choose where this item appears in your wishlist (1 = top, {totalItems} = bottom) Choose where this item appears in your wishlist (1 = top, {itemCount} = bottom)
</p> </p>
</div> </div>
{/if} {/if}
@@ -243,8 +244,8 @@
<div class="flex gap-2"> <div class="flex gap-2">
<Button type="submit" class="flex-1 md:flex-none">{submitLabel}</Button> <Button type="submit" class="flex-1 md:flex-none">{submitLabel}</Button>
{#if onCancel} {#if isEdit}
<Button type="button" variant="outline" class="flex-1 md:flex-none" onclick={onCancel}> <Button type="button" variant="outline" class="flex-1 md:flex-none" onclick={() => dispatch('cancel')}>
{t.form.cancel} {t.form.cancel}
</Button> </Button>
{/if} {/if}

View File

@@ -88,10 +88,8 @@
} }
} }
async function handlePositionChange(newPosition: number) { async function handlePositionChange(itemId: string, newPosition: number) {
if (!editingItem) return; const currentIndex = items.findIndex((item) => item.id === itemId);
const currentIndex = items.findIndex((item) => item.id === editingItem.id);
if (currentIndex === -1) return; if (currentIndex === -1) return;
const newIndex = newPosition - 1; // Convert to 0-based index const newIndex = newPosition - 1; // Convert to 0-based index
@@ -150,7 +148,7 @@
<div bind:this={addFormElement}> <div bind:this={addFormElement}>
<ItemForm <ItemForm
mode="add" mode="add"
onSuccess={handleItemAdded} on:success={handleItemAdded}
wishlistColor={currentColor} wishlistColor={currentColor}
wishlistTheme={currentTheme} wishlistTheme={currentTheme}
/> />
@@ -162,12 +160,11 @@
<ItemForm <ItemForm
mode="edit" mode="edit"
item={editingItem} item={editingItem}
onSuccess={handleItemUpdated} itemCount={items.length}
onCancel={cancelEditing} on:success={handleItemUpdated}
onColorChange={handleColorChange} on:cancel={cancelEditing}
currentPosition={items.findIndex((item) => item.id === editingItem.id) + 1} on:colorChange={(e) => handleColorChange(e.detail.itemId, e.detail.color)}
totalItems={items.length} on:positionChange={(e) => handlePositionChange(e.detail.itemId, e.detail.newPosition)}
onPositionChange={handlePositionChange}
wishlistColor={currentColor} wishlistColor={currentColor}
wishlistTheme={currentTheme} wishlistTheme={currentTheme}
/> />