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:
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -809,8 +809,7 @@
|
||||
animate: fitBounds == "animate",
|
||||
},
|
||||
trackUserLocation: true,
|
||||
}),
|
||||
);
|
||||
}));
|
||||
|
||||
if (showStyleSwitcher) {
|
||||
map.addControl(switcherControl);
|
||||
|
||||
@@ -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,
|
||||
@@ -43,6 +44,20 @@
|
||||
{ text: $_("list", { values: { n: 1 } }), value: "list" },
|
||||
{ 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);
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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"
|
||||
>
|
||||
|
||||
@@ -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
|
||||
}
|
||||
}),
|
||||
|
||||
@@ -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
|
||||
}
|
||||
}),
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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>
|
||||
|
||||
Reference in New Issue
Block a user