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">
|
<script lang="ts">
|
||||||
import { onMount } from 'svelte';
|
import { onMount, tick } from 'svelte';
|
||||||
import { page } from '$app/stores';
|
import { page } from '$app/stores';
|
||||||
import { CookingPot, X } from 'lucide-svelte';
|
import { CookingPot, X } from 'lucide-svelte';
|
||||||
import type { Snapshot } from './$types';
|
import type { Snapshot } from './$types';
|
||||||
@@ -43,6 +43,7 @@
|
|||||||
let allExhausted = $state(false);
|
let allExhausted = $state(false);
|
||||||
let allLoading = $state(false);
|
let allLoading = $state(false);
|
||||||
let allSentinel: HTMLElement | undefined = $state();
|
let allSentinel: HTMLElement | undefined = $state();
|
||||||
|
let allChips: HTMLElement | undefined = $state();
|
||||||
let allObserver: IntersectionObserver | null = null;
|
let allObserver: IntersectionObserver | null = null;
|
||||||
|
|
||||||
type SearchSnapshot = {
|
type SearchSnapshot = {
|
||||||
@@ -105,10 +106,34 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function resetAllRecipes() {
|
async function setAllSort(next: AllSort) {
|
||||||
allRecipes = [];
|
if (next === allSort) return;
|
||||||
allExhausted = false;
|
allSort = next;
|
||||||
allLoading = false;
|
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) {
|
async function loadFavorites(profileId: number) {
|
||||||
@@ -136,14 +161,6 @@
|
|||||||
void loadAllMore();
|
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.
|
// IntersectionObserver an den Sentinel hängen — wenn sichtbar, nachladen.
|
||||||
$effect(() => {
|
$effect(() => {
|
||||||
if (typeof window === 'undefined') return;
|
if (typeof window === 'undefined') return;
|
||||||
@@ -500,7 +517,12 @@
|
|||||||
<div class="listing-head">
|
<div class="listing-head">
|
||||||
<h2>Alle Rezepte</h2>
|
<h2>Alle Rezepte</h2>
|
||||||
</div>
|
</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)}
|
{#each ALL_SORTS as s (s.value)}
|
||||||
<button
|
<button
|
||||||
type="button"
|
type="button"
|
||||||
|
|||||||
Reference in New Issue
Block a user