mirror of
https://github.com/ivabus/gui
synced 2025-04-23 14:07:14 +03:00
preserver infinite scroll positions (#615)
This commit is contained in:
parent
87dd224f9a
commit
c0c2472b12
3 changed files with 71 additions and 4 deletions
|
@ -10,7 +10,8 @@
|
||||||
import Package from "./package.svelte";
|
import Package from "./package.svelte";
|
||||||
import NoInstalls from "./no-installs.svelte";
|
import NoInstalls from "./no-installs.svelte";
|
||||||
import NoUpdates from "./no-updates.svelte";
|
import NoUpdates from "./no-updates.svelte";
|
||||||
import { packagesStore } from "$libs/stores";
|
import { packagesStore, scrollStore } from "$libs/stores";
|
||||||
|
import { afterUpdate, beforeUpdate } from "svelte";
|
||||||
|
|
||||||
const { packageList: allPackages } = packagesStore;
|
const { packageList: allPackages } = packagesStore;
|
||||||
export let packageFilter: SideMenuOptions = SideMenuOptions.all;
|
export let packageFilter: SideMenuOptions = SideMenuOptions.all;
|
||||||
|
@ -20,6 +21,11 @@
|
||||||
let loadMore = 9;
|
let loadMore = 9;
|
||||||
let limit = loadMore + 9;
|
let limit = loadMore + 9;
|
||||||
|
|
||||||
|
const updateLimit = (value: number) => {
|
||||||
|
limit += value;
|
||||||
|
scrollStore.setLimit(packageFilter, limit);
|
||||||
|
};
|
||||||
|
|
||||||
// TODO: figure out a better type strategy here so that this breaks if SideMenuOptions is updated
|
// TODO: figure out a better type strategy here so that this breaks if SideMenuOptions is updated
|
||||||
const pkgFilters: { [key: string]: (pkg: GUIPackage) => boolean } = {
|
const pkgFilters: { [key: string]: (pkg: GUIPackage) => boolean } = {
|
||||||
[SideMenuOptions.all]: (_pkg: GUIPackage) => true,
|
[SideMenuOptions.all]: (_pkg: GUIPackage) => true,
|
||||||
|
@ -46,6 +52,7 @@
|
||||||
const onScroll = (e: Event) => {
|
const onScroll = (e: Event) => {
|
||||||
const target = e.target as HTMLInputElement;
|
const target = e.target as HTMLInputElement;
|
||||||
scrollY = target.scrollTop || 0;
|
scrollY = target.scrollTop || 0;
|
||||||
|
scrollStore.setScrollPosition(packageFilter, scrollY);
|
||||||
};
|
};
|
||||||
|
|
||||||
$: packages = $allPackages.filter(pkgFilters[packageFilter] || pkgFilters.all);
|
$: packages = $allPackages.filter(pkgFilters[packageFilter] || pkgFilters.all);
|
||||||
|
@ -56,13 +63,37 @@
|
||||||
const minCardRows = Math.floor(node.scrollHeight / assumedCardHeight);
|
const minCardRows = Math.floor(node.scrollHeight / assumedCardHeight);
|
||||||
if (cardRows < minCardRows) {
|
if (cardRows < minCardRows) {
|
||||||
const addLimit = 3 * (minCardRows - cardRows);
|
const addLimit = 3 * (minCardRows - cardRows);
|
||||||
limit += addLimit;
|
updateLimit(addLimit);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
let scrollElement: any = null;
|
||||||
|
let prevFilter: SideMenuOptions | null;
|
||||||
|
|
||||||
|
// Restore the limit before the update...
|
||||||
|
beforeUpdate(() => {
|
||||||
|
if (prevFilter !== packageFilter) {
|
||||||
|
limit = scrollStore.getLimit(packageFilter);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// ...and scroll position after the update
|
||||||
|
afterUpdate(() => {
|
||||||
|
if (prevFilter !== packageFilter && scrollElement) {
|
||||||
|
prevFilter = packageFilter;
|
||||||
|
const scrollPosition = scrollStore.getScrollPosition(packageFilter);
|
||||||
|
scrollElement.scrollTop = scrollPosition;
|
||||||
|
}
|
||||||
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<div class="relative h-full w-full">
|
<div class="relative h-full w-full">
|
||||||
<ul class="flex flex-wrap content-start bg-black" use:watchResize={onResize} on:scroll={onScroll}>
|
<ul
|
||||||
|
bind:this={scrollElement}
|
||||||
|
class="flex flex-wrap content-start bg-black"
|
||||||
|
use:watchResize={onResize}
|
||||||
|
on:scroll={onScroll}
|
||||||
|
>
|
||||||
{#if packages.length > 0}
|
{#if packages.length > 0}
|
||||||
{#each packages as pkg, index}
|
{#each packages as pkg, index}
|
||||||
{#if index < limit}
|
{#if index < limit}
|
||||||
|
@ -84,7 +115,7 @@
|
||||||
</section>
|
</section>
|
||||||
{/each}
|
{/each}
|
||||||
{/if}
|
{/if}
|
||||||
<InfiniteScroll threshold={100} on:loadMore={() => (limit += loadMore)} />
|
<InfiniteScroll threshold={100} on:loadMore={() => updateLimit(loadMore)} />
|
||||||
</ul>
|
</ul>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|
|
@ -11,6 +11,7 @@ import pkgStore from "./stores/pkgs";
|
||||||
import initNotificationStore from "./stores/notifications";
|
import initNotificationStore from "./stores/notifications";
|
||||||
import initAppUpdateStore from "./stores/update";
|
import initAppUpdateStore from "./stores/update";
|
||||||
import { trackSearch } from "./analytics";
|
import { trackSearch } from "./analytics";
|
||||||
|
import initScrollStore from "./stores/scroll";
|
||||||
|
|
||||||
export const featuredPackages = writable<Package[]>([]);
|
export const featuredPackages = writable<Package[]>([]);
|
||||||
|
|
||||||
|
@ -140,3 +141,5 @@ export const navStore = initNavStore();
|
||||||
export const notificationStore = initNotificationStore();
|
export const notificationStore = initNotificationStore();
|
||||||
|
|
||||||
export const appUpdateStore = initAppUpdateStore();
|
export const appUpdateStore = initAppUpdateStore();
|
||||||
|
|
||||||
|
export const scrollStore = initScrollStore();
|
||||||
|
|
33
modules/desktop/src/libs/stores/scroll.ts
Normal file
33
modules/desktop/src/libs/stores/scroll.ts
Normal file
|
@ -0,0 +1,33 @@
|
||||||
|
import type { SideMenuOptions } from "$libs/types";
|
||||||
|
import { get, writable } from "svelte/store";
|
||||||
|
|
||||||
|
type ScrollValue = Record<SideMenuOptions, number>;
|
||||||
|
|
||||||
|
const defaultLimit = 18;
|
||||||
|
|
||||||
|
// keep track of the scroll position of each side menu filter so it's not lost after navigation
|
||||||
|
export default function initScrollStore() {
|
||||||
|
const scrollPositions = writable<ScrollValue>({} as ScrollValue);
|
||||||
|
const limits = writable<ScrollValue>({} as ScrollValue);
|
||||||
|
|
||||||
|
return {
|
||||||
|
setScrollPosition: (filter: SideMenuOptions, pos: number) => {
|
||||||
|
scrollPositions.update((prev) => {
|
||||||
|
prev[filter] = pos;
|
||||||
|
return prev;
|
||||||
|
});
|
||||||
|
},
|
||||||
|
getScrollPosition: (filter: SideMenuOptions) => {
|
||||||
|
return get(scrollPositions)[filter] || 0;
|
||||||
|
},
|
||||||
|
setLimit: (filter: SideMenuOptions, limit: number) => {
|
||||||
|
limits.update((prev) => {
|
||||||
|
prev[filter] = limit;
|
||||||
|
return prev;
|
||||||
|
});
|
||||||
|
},
|
||||||
|
getLimit: (filter: SideMenuOptions) => {
|
||||||
|
return get(limits)[filter] || defaultLimit;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
Loading…
Reference in a new issue