From 5824993b18beba04c0fb5c6705d017503cf79e45 Mon Sep 17 00:00:00 2001 From: Alexander Bocken Date: Sun, 25 Jan 2026 14:47:26 +0100 Subject: [PATCH] refactor: simplify Card HTML and extract search filter composable - Remove unnecessary wrapper divs in Card component (.card_anchor, .div_div_image) - Flatten Card HTML from 4 levels to 2 levels of nesting - Create reusable createSearchFilter composable in $lib/js/searchFilter.svelte.ts - Apply search filter composable to category, tag, and favorites pages --- src/lib/components/Card.svelte | 40 ++++++------------- src/lib/js/searchFilter.svelte.ts | 27 +++++++++++++ .../category/[category]/+page.svelte | 24 +++-------- .../favorites/+page.svelte | 20 ++-------- .../tag/[tag]/+page.svelte | 26 +++--------- 5 files changed, 54 insertions(+), 83 deletions(-) create mode 100644 src/lib/js/searchFilter.svelte.ts diff --git a/src/lib/components/Card.svelte b/src/lib/components/Card.svelte index 24fa57b..c157945 100644 --- a/src/lib/components/Card.svelte +++ b/src/lib/components/Card.svelte @@ -39,13 +39,6 @@ const img_alt = $derived( ); -
-
+
View recipe: {recipe.name} -
-
- - {img_alt} isloaded=true}/> -
+
+ + {img_alt} isloaded=true}/>
{#if showFavoriteIndicator && isFavorite}
❤️
@@ -281,4 +268,3 @@ const img_alt = $derived(
-
diff --git a/src/lib/js/searchFilter.svelte.ts b/src/lib/js/searchFilter.svelte.ts new file mode 100644 index 0000000..b81c9f2 --- /dev/null +++ b/src/lib/js/searchFilter.svelte.ts @@ -0,0 +1,27 @@ +/** + * Shared search filter composable for recipe list pages. + * Extracts duplicated search state logic from multiple pages. + */ + +type Recipe = { _id: string; [key: string]: any }; + +export function createSearchFilter(getRecipes: () => T[]) { + let matchedRecipeIds = $state(new Set()); + let hasActiveSearch = $state(false); + + function handleSearchResults(ids: Set, _categories?: Set) { + matchedRecipeIds = ids; + hasActiveSearch = ids.size < getRecipes().length; + } + + const filtered = $derived.by(() => { + if (!hasActiveSearch) return getRecipes(); + return getRecipes().filter(r => matchedRecipeIds.has(r._id)); + }); + + return { + get filtered() { return filtered; }, + get hasActiveSearch() { return hasActiveSearch; }, + handleSearchResults + }; +} diff --git a/src/routes/[recipeLang=recipeLang]/category/[category]/+page.svelte b/src/routes/[recipeLang=recipeLang]/category/[category]/+page.svelte index 2d47003..4591071 100644 --- a/src/routes/[recipeLang=recipeLang]/category/[category]/+page.svelte +++ b/src/routes/[recipeLang=recipeLang]/category/[category]/+page.svelte @@ -2,32 +2,18 @@ import type { PageData } from './$types'; import Recipes from '$lib/components/Recipes.svelte'; import Search from '$lib/components/Search.svelte'; + import Card from '$lib/components/Card.svelte'; + import { rand_array } from '$lib/js/randomize'; + import { createSearchFilter } from '$lib/js/searchFilter.svelte'; + let { data } = $props<{ data: PageData }>(); let current_month = new Date().getMonth() + 1; - import Card from '$lib/components/Card.svelte' - import { rand_array } from '$lib/js/randomize'; const isEnglish = $derived(data.lang === 'en'); const label = $derived(isEnglish ? 'Recipes in Category' : 'Rezepte in Kategorie'); const siteTitle = $derived(isEnglish ? 'Bocken Recipes' : 'Bocken Rezepte'); - // Search state - let matchedRecipeIds = $state(new Set()); - let hasActiveSearch = $state(false); - - // Handle search results from Search component - function handleSearchResults(ids, categories) { - matchedRecipeIds = ids; - hasActiveSearch = ids.size < data.recipes.length; - } - - // Filter recipes based on search - const filteredRecipes = $derived.by(() => { - if (!hasActiveSearch) { - return data.recipes; - } - return data.recipes.filter(r => matchedRecipeIds.has(r._id)); - }); + const { filtered: filteredRecipes, handleSearchResults } = createSearchFilter(() => data.recipes);