feat(shopping): add ChecklistItem.svelte component
Checkbox row with name, quantity/unit, recipe source label, and strikethrough styling when checked. Each toggle submits a form action. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
79
frontend/src/lib/shopping/ChecklistItem.svelte
Normal file
79
frontend/src/lib/shopping/ChecklistItem.svelte
Normal file
@@ -0,0 +1,79 @@
|
|||||||
|
<script lang="ts">
|
||||||
|
import { enhance } from '$app/forms';
|
||||||
|
|
||||||
|
interface RecipeRef {
|
||||||
|
id?: string;
|
||||||
|
name?: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface Props {
|
||||||
|
listId: string;
|
||||||
|
itemId: string;
|
||||||
|
name: string;
|
||||||
|
quantity: number | null;
|
||||||
|
unit: string | null;
|
||||||
|
isChecked: boolean;
|
||||||
|
sourceRecipes: RecipeRef[];
|
||||||
|
}
|
||||||
|
|
||||||
|
let { listId, itemId, name, quantity, unit, isChecked, sourceRecipes }: Props = $props();
|
||||||
|
|
||||||
|
let recipeLabel = $derived(
|
||||||
|
sourceRecipes.length > 0
|
||||||
|
? sourceRecipes
|
||||||
|
.map((r) => r.name)
|
||||||
|
.filter(Boolean)
|
||||||
|
.join(', ')
|
||||||
|
: null
|
||||||
|
);
|
||||||
|
|
||||||
|
let quantityLabel = $derived(
|
||||||
|
quantity ? `${quantity}${unit ? ` ${unit}` : ''}` : null
|
||||||
|
);
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<form method="POST" action="?/check" use:enhance class="group flex items-center gap-3 py-2">
|
||||||
|
<input type="hidden" name="listId" value={listId} />
|
||||||
|
<input type="hidden" name="itemId" value={itemId} />
|
||||||
|
<input type="hidden" name="isChecked" value={!isChecked} />
|
||||||
|
|
||||||
|
<button
|
||||||
|
type="submit"
|
||||||
|
aria-label="{isChecked ? 'Abhaken rückgängig' : 'Abhaken'}: {name}"
|
||||||
|
class="flex h-5 w-5 flex-shrink-0 items-center justify-center rounded border
|
||||||
|
{isChecked
|
||||||
|
? 'border-[var(--green)] bg-[var(--green)] text-white'
|
||||||
|
: 'border-[var(--color-border)] bg-[var(--color-surface)] hover:border-[var(--green-light)]'}"
|
||||||
|
>
|
||||||
|
{#if isChecked}
|
||||||
|
<svg class="h-3 w-3" viewBox="0 0 12 12" fill="none" stroke="currentColor" stroke-width="2">
|
||||||
|
<path d="M2 6l3 3 5-5" />
|
||||||
|
</svg>
|
||||||
|
{/if}
|
||||||
|
</button>
|
||||||
|
|
||||||
|
<div class="min-w-0 flex-1">
|
||||||
|
<p
|
||||||
|
class="font-[var(--font-sans)] text-[14px] {isChecked
|
||||||
|
? 'text-[var(--color-text-muted)] line-through'
|
||||||
|
: 'text-[var(--color-text)]'}"
|
||||||
|
>
|
||||||
|
{name}
|
||||||
|
</p>
|
||||||
|
{#if recipeLabel && !isChecked}
|
||||||
|
<p class="truncate font-[var(--font-sans)] text-[11px] text-[var(--color-text-muted)]">
|
||||||
|
Für: {recipeLabel}
|
||||||
|
</p>
|
||||||
|
{/if}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{#if quantityLabel}
|
||||||
|
<span
|
||||||
|
class="flex-shrink-0 font-[var(--font-sans)] text-[13px] {isChecked
|
||||||
|
? 'text-[var(--color-text-muted)]'
|
||||||
|
: 'text-[var(--color-text)]'}"
|
||||||
|
>
|
||||||
|
{quantityLabel}
|
||||||
|
</span>
|
||||||
|
{/if}
|
||||||
|
</form>
|
||||||
Reference in New Issue
Block a user