refactor: simplify ItemForm API using events instead of callback props
This commit is contained in:
@@ -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}
|
||||||
|
|||||||
@@ -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}
|
||||||
/>
|
/>
|
||||||
|
|||||||
Reference in New Issue
Block a user