refactor: simplify Card HTML and extract search filter composable
All checks were successful
CI / update (push) Successful in 1m23s
All checks were successful
CI / update (push) Successful in 1m23s
- 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
This commit is contained in:
@@ -39,13 +39,6 @@ const img_alt = $derived(
|
||||
);
|
||||
</script>
|
||||
<style>
|
||||
.card_anchor{
|
||||
border-radius: var(--radius-card);
|
||||
cursor: pointer;
|
||||
display: inline-block;
|
||||
text-decoration: none;
|
||||
color: inherit;
|
||||
}
|
||||
.card-main-link {
|
||||
position: absolute;
|
||||
inset: 0;
|
||||
@@ -76,11 +69,12 @@ const img_alt = $derived(
|
||||
width: 300px;
|
||||
border-radius: var(--radius-card);
|
||||
background-size: contain;
|
||||
display: flex;
|
||||
display: inline-flex;
|
||||
flex-direction: column;
|
||||
justify-content: end;
|
||||
background-color: var(--blue);
|
||||
background-color: var(--blue);
|
||||
box-shadow: var(--shadow-lg);
|
||||
color: inherit;
|
||||
}
|
||||
/* Position/size overrides for global g-icon-badge */
|
||||
.icon{
|
||||
@@ -106,9 +100,11 @@ const img_alt = $derived(
|
||||
.backdrop_blur{
|
||||
backdrop-filter: blur(10px);
|
||||
}
|
||||
.div_image,
|
||||
.div_div_image{
|
||||
.card-image{
|
||||
width: 300px;
|
||||
height: 255px;
|
||||
position: absolute;
|
||||
top: 0;
|
||||
background-repeat: no-repeat;
|
||||
background-size: cover;
|
||||
background-position: center;
|
||||
@@ -116,12 +112,6 @@ const img_alt = $derived(
|
||||
border-top-left-radius: inherit;
|
||||
border-top-right-radius: inherit;
|
||||
}
|
||||
.div_div_image{
|
||||
height: 255px;
|
||||
position: absolute;
|
||||
width: 300px;
|
||||
top: 0;
|
||||
}
|
||||
|
||||
.card:hover,
|
||||
.card:focus-within{
|
||||
@@ -238,18 +228,15 @@ const img_alt = $derived(
|
||||
}
|
||||
</style>
|
||||
|
||||
<div class=card_anchor class:search_me={search} data-tags="[{recipe.tags}]">
|
||||
<div class="card" class:margin_right={do_margin_right}>
|
||||
<div class="card" class:search_me={search} class:margin_right={do_margin_right} data-tags="[{recipe.tags}]">
|
||||
<a href="{routePrefix}/{recipe.short_name}" class="card-main-link" aria-label="View recipe: {recipe.name}">
|
||||
<span class="visually-hidden">View recipe: {recipe.name}</span>
|
||||
</a>
|
||||
<div class=div_div_image >
|
||||
<div class=div_image style="background-image:url(https://bocken.org/static/rezepte/placeholder/{img_name})">
|
||||
<noscript>
|
||||
<img class="image backdrop_blur" src="https://bocken.org/static/rezepte/thumb/{img_name}" loading={loading_strat} alt="{img_alt}"/>
|
||||
</noscript>
|
||||
<img class="image backdrop_blur" class:blur={!isloaded} src={'https://bocken.org/static/rezepte/thumb/' + img_name} loading={loading_strat} alt="{img_alt}" onload={() => isloaded=true}/>
|
||||
</div>
|
||||
<div class="card-image" style="background-image:url(https://bocken.org/static/rezepte/placeholder/{img_name})">
|
||||
<noscript>
|
||||
<img class="image backdrop_blur" src="https://bocken.org/static/rezepte/thumb/{img_name}" loading={loading_strat} alt="{img_alt}"/>
|
||||
</noscript>
|
||||
<img class="image backdrop_blur" class:blur={!isloaded} src={'https://bocken.org/static/rezepte/thumb/' + img_name} loading={loading_strat} alt="{img_alt}" onload={() => isloaded=true}/>
|
||||
</div>
|
||||
{#if showFavoriteIndicator && isFavorite}
|
||||
<div class="favorite-indicator">❤️</div>
|
||||
@@ -281,4 +268,3 @@ const img_alt = $derived(
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
27
src/lib/js/searchFilter.svelte.ts
Normal file
27
src/lib/js/searchFilter.svelte.ts
Normal file
@@ -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<T extends Recipe>(getRecipes: () => T[]) {
|
||||
let matchedRecipeIds = $state(new Set<string>());
|
||||
let hasActiveSearch = $state(false);
|
||||
|
||||
function handleSearchResults(ids: Set<string>, _categories?: Set<string>) {
|
||||
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
|
||||
};
|
||||
}
|
||||
Reference in New Issue
Block a user