fix(home): Sort-Chip-Wechsel behält Scroll-Position
Beim Klick auf eine andere Sort-Pille wurde allRecipes=[] eager gesetzt; dadurch kollabierte der Block unter den Chips und der Browser snapte nach oben. Jetzt laden wir erst die neuen Treffer, tauschen dann atomar. Sollte sich die Block-Höhe trotzdem ändern (z.B. von 50 geladenen Items zurück auf 10), korrigieren wir per scrollBy-Delta, damit die Chips visuell an Ort bleiben. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -1,5 +1,5 @@
|
||||
<script lang="ts">
|
||||
import { onMount } from 'svelte';
|
||||
import { onMount, tick } from 'svelte';
|
||||
import { page } from '$app/stores';
|
||||
import { CookingPot, X } from 'lucide-svelte';
|
||||
import type { Snapshot } from './$types';
|
||||
@@ -43,6 +43,7 @@
|
||||
let allExhausted = $state(false);
|
||||
let allLoading = $state(false);
|
||||
let allSentinel: HTMLElement | undefined = $state();
|
||||
let allChips: HTMLElement | undefined = $state();
|
||||
let allObserver: IntersectionObserver | null = null;
|
||||
|
||||
type SearchSnapshot = {
|
||||
@@ -105,10 +106,34 @@
|
||||
}
|
||||
}
|
||||
|
||||
function resetAllRecipes() {
|
||||
allRecipes = [];
|
||||
allExhausted = false;
|
||||
allLoading = false;
|
||||
async function setAllSort(next: AllSort) {
|
||||
if (next === allSort) return;
|
||||
allSort = next;
|
||||
if (typeof window !== 'undefined') localStorage.setItem('kochwas.allSort', next);
|
||||
if (allLoading) return;
|
||||
// Position der Sort-Chips vor dem Swap merken — wenn der Rezept-Block
|
||||
// beim Tausch kürzer wird, hält der Browser sonst nicht Schritt und
|
||||
// snapt nach oben. Wir korrigieren nach dem Render per scrollBy.
|
||||
const chipsBefore = allChips?.getBoundingClientRect().top ?? 0;
|
||||
allLoading = true;
|
||||
try {
|
||||
const res = await fetch(
|
||||
`/api/recipes/all?sort=${next}&limit=${ALL_PAGE}&offset=0`
|
||||
);
|
||||
if (!res.ok) return;
|
||||
const body = await res.json();
|
||||
const hits = body.hits as SearchHit[];
|
||||
allRecipes = hits;
|
||||
allExhausted = hits.length < ALL_PAGE;
|
||||
await tick();
|
||||
const chipsAfter = allChips?.getBoundingClientRect().top ?? 0;
|
||||
const delta = chipsAfter - chipsBefore;
|
||||
if (typeof window !== 'undefined' && Math.abs(delta) > 1) {
|
||||
window.scrollBy({ top: delta, left: 0, behavior: 'instant' });
|
||||
}
|
||||
} finally {
|
||||
allLoading = false;
|
||||
}
|
||||
}
|
||||
|
||||
async function loadFavorites(profileId: number) {
|
||||
@@ -136,14 +161,6 @@
|
||||
void loadAllMore();
|
||||
});
|
||||
|
||||
function setAllSort(next: AllSort) {
|
||||
if (next === allSort) return;
|
||||
allSort = next;
|
||||
if (typeof window !== 'undefined') localStorage.setItem('kochwas.allSort', next);
|
||||
resetAllRecipes();
|
||||
void loadAllMore();
|
||||
}
|
||||
|
||||
// IntersectionObserver an den Sentinel hängen — wenn sichtbar, nachladen.
|
||||
$effect(() => {
|
||||
if (typeof window === 'undefined') return;
|
||||
@@ -500,7 +517,12 @@
|
||||
<div class="listing-head">
|
||||
<h2>Alle Rezepte</h2>
|
||||
</div>
|
||||
<div class="sort-chips" role="tablist" aria-label="Sortierung">
|
||||
<div
|
||||
class="sort-chips"
|
||||
role="tablist"
|
||||
aria-label="Sortierung"
|
||||
bind:this={allChips}
|
||||
>
|
||||
{#each ALL_SORTS as s (s.value)}
|
||||
<button
|
||||
type="button"
|
||||
|
||||
Reference in New Issue
Block a user