Select number of search items per page (#577)

* select items per pages

* fix 'all'

* fix page switch

* fix filter update when 'all'

* startDrawing: set map to current user position

* remove startDrawing change from other branch

* handle 413

* remove 'all', cange increment, use local storage

* remove obsolete import, fix 413
This commit is contained in:
slothful-vassal
2025-10-04 17:22:46 +02:00
committed by GitHub
parent 7ed0b98fce
commit b09675f2b2
8 changed files with 186 additions and 30 deletions

View File

@@ -4,16 +4,17 @@
interface Props {
page: number;
totalPages: number;
onpagination?: (page: number) => void;
perPage: number;
onpagination?: (page: number, perPage: number) => void;
}
let { page, totalPages, onpagination }: Props = $props();
let { page, totalPages, perPage, onpagination }: Props = $props();
const maxPagesToRender = 6;
function update(clickedPage: number) {
if (page !== clickedPage) {
onpagination?.(clickedPage);
onpagination?.(clickedPage, perPage);
}
}

View File

@@ -809,8 +809,7 @@
animate: fitBounds == "animate",
},
trackUserLocation: true,
}),
);
}));
if (showStyleSwitcher) {
map.addControl(switcherControl);

View File

@@ -15,14 +15,14 @@
interface Props {
filter?: TrailFilter | null;
trails: Trail[];
pagination?: { page: number; totalPages: number };
pagination?: { page: number; totalPages: number, items: number };
loading?: boolean;
fullWidthCards?: boolean;
onupdate?: (
filter: TrailFilter | null,
selection: Set<Trail> | undefined,
) => void;
onpagination?: (page: number) => void;
onpagination?: (page: number, items: number) => void;
}
let {
@@ -31,6 +31,7 @@
pagination = {
page: 1,
totalPages: 1,
items: 25,
},
loading = false,
fullWidthCards = false,
@@ -44,6 +45,20 @@
{ text: $_("table"), value: "table" },
];
const perPageOptions: SelectItem[] = [
{ text: "10", value: 10 },
{ text: "25", value: 25 },
{ text: "50", value: 50 },
{ text: "100", value: 100 },
];
const perPageOptionsCards: SelectItem[] = [
{ text: "12", value: 12 },
{ text: "24", value: 24 },
{ text: "48", value: 48 },
{ text: "96", value: 96 },
];
let selectedDisplayOption = $state(displayOptions[0].value);
let selection: Set<Trail> | undefined = $state();
@@ -65,6 +80,7 @@
const storedDisplayOption = localStorage.getItem("displayOption");
const storedSort = localStorage.getItem("sort");
const storedSortOrder = localStorage.getItem("sort_order");
const paginationItems = localStorage.getItem("paginationItems");
if (storedDisplayOption) {
selectedDisplayOption = storedDisplayOption;
@@ -78,11 +94,84 @@
(storedSortOrder as typeof filter.sortOrder | null) ??
filter.sortOrder;
}
if (paginationItems) {
pagination.items = +paginationItems;
let itemsChanged = false;
if (selectedDisplayOption == "cards") {
if (pagination.items > 50) {
pagination.items = 96;
itemsChanged = true;
} else if (pagination.items > 25) {
pagination.items = 48;
itemsChanged = true;
} else if (pagination.items > 12) {
pagination.items = 24;
itemsChanged = true;
} else {
pagination.items = 12;
itemsChanged = true;
}
} else {
if (pagination.items > 50) {
pagination.items = 100;
itemsChanged = true;
} else if (pagination.items > 25) {
pagination.items = 50;
itemsChanged = true;
} else if (pagination.items > 12) {
pagination.items = 25;
itemsChanged = true;
} else {
pagination.items = 10;
itemsChanged = true;
}
}
if (itemsChanged) {
localStorage.setItem("paginationItems", pagination.items.toString());
}
}
onupdate?.(filter, selection);
});
function setDisplayOption() {
localStorage.setItem("displayOption", selectedDisplayOption);
let itemsChanged = false;
if (selectedDisplayOption == "cards") {
if (pagination.items > 50) {
pagination.items = 96;
itemsChanged = true;
} else if (pagination.items > 25) {
pagination.items = 48;
itemsChanged = true;
} else if (pagination.items > 12) {
pagination.items = 24;
itemsChanged = true;
} else {
pagination.items = 12;
itemsChanged = true;
}
} else {
if (pagination.items > 50) {
pagination.items = 100;
itemsChanged = true;
} else if (pagination.items > 25) {
pagination.items = 50;
itemsChanged = true;
} else if (pagination.items > 12) {
pagination.items = 25;
itemsChanged = true;
} else {
pagination.items = 10;
itemsChanged = true;
}
}
if (itemsChanged) {
localStorage.setItem("paginationItems", pagination.items.toString());
}
}
function setSort() {
@@ -206,6 +295,11 @@
function handleMouseLeave(trail: Trail) {
handleHoverUpdate(trail);
}
function setItemsPerPage() {
localStorage.setItem("paginationItems", pagination.items.toString());
onpagination?.(1, pagination.items);
}
</script>
<div class="min-w-0">
@@ -214,6 +308,7 @@
<Pagination
page={pagination.page}
totalPages={pagination.totalPages}
perPage={pagination.items}
{onpagination}
></Pagination>
</div>
@@ -267,9 +362,10 @@
trails={null}
selection={new Set<Trail>()}
tableHeader={sortOptions}
items={pagination.items}
></TrailTable>
{:else}
{#each { length: 12 } as _, index}
{#each { length: pagination.items } as _, index}
{#if selectedDisplayOption === "cards"}
<div class="flex-1">
<SkeletonCard></SkeletonCard>
@@ -293,6 +389,7 @@
(option) => option.value !== "elevation_loss",
)}
{filter}
items={pagination.items}
onsort={handleSortUpdate}
onTrailSelect={(t) => handleSelectionUpdate(t)}
></TrailTable>
@@ -330,11 +427,23 @@
{/if}
{/if}
</div>
<Pagination
page={pagination.page}
totalPages={pagination.totalPages}
{onpagination}
></Pagination>
<div class="flex items-end flex-wrap lg:flex-nowrap gap-x-6 gap-y-2 mx-4">
<div class="basis-full order-1 md:order-none">
<Pagination
page={pagination.page}
totalPages={pagination.totalPages}
perPage={pagination.items}
{onpagination}
></Pagination>
</div>
<div class="shrink-0">
<Select
bind:value={pagination.items}
items={selectedDisplayOption == "cards" ? perPageOptionsCards : perPageOptions}
onchange={setItemsPerPage}
></Select>
</div>
</div>
</div>
<style>

View File

@@ -16,11 +16,12 @@
trails?: Trail[] | null;
selection: Set<Trail> | undefined;
filter?: TrailFilter | null;
items: number;
onsort?: (value: any) => void
onTrailSelect?: (value: any) => void
}
let { tableHeader, trails = null, selection, filter = null, onsort, onTrailSelect: onselect }: Props = $props();
let { tableHeader, trails = null, selection, filter = null, items, onsort, onTrailSelect: onselect }: Props = $props();
function getColumnWidth(columnValue: string): string {
switch (columnValue) {
@@ -224,7 +225,7 @@
</tr>
{/each}
{:else}
{#each { length: 12 } as _, index}
{#each { length: items } as _, index}
<tr
class="border-t border-input-border cursor-pointer bg-secondary-hover transition-colors animate-pulse"
>

View File

@@ -89,7 +89,7 @@ export async function profile_trails_index(handle: string, filter: TrailFilter,
options: {
attributesToRetrieve: defaultTrailSearchAttributes,
sort: [`${filter.sort}:${filter.sortOrder == "+" ? "asc" : "desc"}`],
hitsPerPage: 12,
hitsPerPage: perPage,
page: page
}
}),

View File

@@ -56,11 +56,12 @@ export async function trails_recommend(size: number, f: (url: RequestInfo | URL,
}
export async function trails_search_filter(filter: TrailFilter, page: number = 1, f: (url: RequestInfo | URL, config?: RequestInit) => Promise<Response> = fetch) {
export async function trails_search_filter(filter: TrailFilter, page: number = 1, perPage: number, f: (url: RequestInfo | URL, config?: RequestInit) => Promise<Response> = fetch) {
const user = get(currentUser)
let filterText: string = buildFilterText(user, filter, true);
let r = await f("/api/v1/search/trails", {
method: "POST",
body: JSON.stringify({
@@ -69,7 +70,7 @@ export async function trails_search_filter(filter: TrailFilter, page: number = 1
filter: filterText,
attributesToRetrieve: defaultTrailSearchAttributes,
sort: [`${filter.sort}:${filter.sortOrder == "+" ? "asc" : "desc"}`],
hitsPerPage: 12,
hitsPerPage: perPage,
page: page
}
}),

View File

@@ -13,6 +13,7 @@
let pagination = $state({
page: data.trails.page,
totalPages: data.trails.totalPages,
items: 12,
});
let trails = $state(data.trails);
@@ -26,7 +27,7 @@
page.params.handle,
filter,
pagination.page,
12,
pagination.items,
fetch,
);
} catch (e) {
@@ -40,14 +41,14 @@
}
}
async function paginate(newPage: number) {
async function paginate(newPage: number, items?: number) {
pagination.page = newPage;
try {
trails = await profile_trails_index(
page.params.handle,
filter,
newPage,
12,
items ?? pagination.items,
fetch,
);
} catch (e) {

View File

@@ -3,22 +3,24 @@
import { page } from "$app/state";
import TrailFilterPanel from "$lib/components/trail/trail_filter_panel.svelte";
import TrailList from "$lib/components/trail/trail_list.svelte";
import type { Trail, TrailFilter } from "$lib/models/trail";
import type { Trail, TrailFilter, TrailSearchResult } from "$lib/models/trail";
import { trails_search_filter } from "$lib/stores/trail_store";
import type { Snapshot } from "@sveltejs/kit";
import { onMount } from "svelte";
import { _ } from "svelte-i18n";
import { APIError } from "$lib/util/api_util";
let filterExpanded: boolean = $state(true);
let loading: boolean = $state(true);
let filter: TrailFilter = $state(page.data.filter);
const pagination: { page: number; totalPages: number } = $state({
const pagination: { page: number; totalPages: number, items: number } = $state({
page: page.url.searchParams.has("page")
? parseInt(page.url.searchParams.get("page")!)
: 1,
totalPages: 1,
items: 25,
});
let trails: Trail[] = $state([]);
@@ -55,20 +57,62 @@
async function handleFilterUpdate() {
loading = true;
const response = await trails_search_filter(filter, 1);
trails = response.items;
pagination.page = response.page;
pagination.totalPages = response.totalPages;
await paginate(1, pagination.items);
loading = false;
}
async function paginate(newPage: number) {
async function paginate(newPage: number, items: number) {
pagination.page = newPage;
const response = await trails_search_filter(filter, newPage);
trails = response.items;
try {
await doPaginate(newPage, items);
} catch (err: any) {
let apiError : APIError = err;
if (apiError.status == 413) { // content too large
let newItems = 10;
if (items == 12 || items == 24 || items == 48 || items == 96) { // cards view
if (items > 96) {
newItems = 96;
} else if (items > 48) {
newItems = 48;
} else if (items > 24) {
newItems = 24;
} else {
newItems = 12;
}
} else {
if (items > 100) {
newItems = 100;
} else if (items > 50) {
newItems = 50;
} else if (items > 25) {
newItems = 25;
} else {
newItems = 10;
}
}
await doPaginate(newPage, newItems);
}
}
page.url.searchParams.set("page", newPage.toString());
goto(`?${page.url.searchParams.toString()}`);
}
async function doPaginate(newPage: number, items: number) {
const response = await trails_search_filter(filter, newPage, items);
if (items) {
pagination.items = items;
}
trails = response.items;
pagination.page = response.page;
pagination.totalPages = response.totalPages;
}
</script>
<svelte:head>