Add pagination and improve search UI

This commit adds pagination support to the search results page by updating the limit of results per page, implementing openNextPage and openPrevPage functions, and including navigation buttons for the pagination. It also improves the UI by adding a spinning icon while searching for results and adjusts the overall styling for better readability.

- Added IconChevronLeft and IconChevronRight to icons exports
- Updated the limit of results per page from 50 to 10
- Implemented openNextPage and openPrevPage functions for pagination
- Added navigation buttons for previous and next pages
- Used IconRotateClockwise with animation for a spinning icon while searching
- Updated the styling for better UI readability
This commit is contained in:
Nikita Galaiko 2023-03-24 08:51:27 +01:00
parent 3a372284e8
commit 4cc61cd8d4
4 changed files with 71 additions and 9 deletions

View File

@ -0,0 +1,15 @@
<svg
{...$$restProps}
xmlns="http://www.w3.org/2000/svg"
width="24"
height="24"
viewBox="0 0 24 24"
stroke-width="2"
stroke="currentColor"
fill="none"
stroke-linecap="round"
stroke-linejoin="round"
>
<path stroke="none" d="M0 0h24v24H0z" fill="none" />
<path d="M15 6l-6 6l6 6" />
</svg>

After

Width:  |  Height:  |  Size: 300 B

View File

@ -0,0 +1,15 @@
<svg
{...$$restProps}
xmlns="http://www.w3.org/2000/svg"
width="24"
height="24"
viewBox="0 0 24 24"
stroke-width="2"
stroke="currentColor"
fill="none"
stroke-linecap="round"
stroke-linejoin="round"
>
<path stroke="none" d="M0 0h24v24H0z" fill="none" />
<path d="M9 6l6 6l-6 6" />
</svg>

After

Width:  |  Height:  |  Size: 299 B

View File

@ -14,3 +14,5 @@ export { default as IconSquareRoundedFilled } from './IconSquareRoundedFilled.sv
export { default as IconMapPinFilled } from './IconMapPinFilled.svelte';
export { default as IconCircleFilled } from './IconCircleFilled.svelte';
export { default as IconCircleCancel } from './IconCircleCancel.svelte';
export { default as IconChevronLeft } from './IconChevronLeft.svelte';
export { default as IconChevronRight } from './IconChevronRight.svelte';

View File

@ -1,21 +1,27 @@
<script lang="ts">
import type { PageData } from './$types';
import { search, type SearchResult } from '$lib';
import { IconChevronLeft, IconChevronRight } from '$lib/components/icons';
import { listFiles } from '$lib/sessions';
import { asyncDerived } from '@square/svelte-store';
import { IconRotateClockwise } from '$lib/components/icons';
import { formatDistanceToNow } from 'date-fns';
import { list as listDeltas } from '$lib/deltas';
import { CodeViewer } from '$lib/components';
import { page } from '$app/stores';
import { derived } from 'svelte/store';
import { goto } from '$app/navigation';
export let data: PageData;
const { project } = data;
const limit = 50;
const limit = 10;
const query = derived(page, (page) => page.url.searchParams.get('q'));
const offset = derived(page, (page) => parseInt(page.url.searchParams.get('offset') ?? '0'));
const openNextPage = () => goto(`?q=${$query}&offset=${$offset + limit}`);
const openPrevPage = () => goto(`?q=${$query}&offset=${$offset - limit}`);
const fetchResultData = async ({
sessionId,
projectId,
@ -40,11 +46,12 @@
const { store: searchResults, state: searchState } = asyncDerived(
[query, project, offset],
async ([query, project, offset]) => {
if (!query || !project) return { page: [], total: 0, haveMore: false };
if (!query || !project) return { page: [], total: 0, haveNext: false, havePrev: false };
const results = await search({ projectId: project.id, query, limit, offset });
return {
page: await Promise.all(results.page.map(fetchResultData)),
haveMore: offset + limit < results.total,
haveNext: offset + limit < results.total,
havePrev: offset > 0,
total: results.total
};
},
@ -52,17 +59,21 @@
);
</script>
<figure class="search-results flex h-full flex-col gap-2">
<figure id="search-results" class="flex h-full flex-col gap-10 p-14">
{#if $searchState?.isLoading || $searchState?.isReloading}
<figcaption class="m-auto">
<p class="mb-2 text-xl text-[#D4D4D8]">Looking for "{$query}"...</p>
<figcaption>
<p class="mb-2 text-xl text-[#D4D4D8]">Searching for "{$query}"...</p>
</figcaption>
<div class="mx-auto">
<IconRotateClockwise class="h-20 w-20 animate-spin" />
</div>
{:else if $searchState?.isError}
<figcaption class="m-auto">
<figcaption>
<p class="mb-2 text-xl text-[#D4D4D8]">Error searching for "{$query}"</p>
</figcaption>
{:else if $searchState?.isLoaded}
<figcaption class="mx-14 mb-10 mt-14">
<figcaption>
{#if $searchResults.total > 0}
<p class="mb-2 text-xl text-[#D4D4D8]">Results for "{$query}"</p>
<p class="text-lg text-[#717179]">{$searchResults.total} change instances</p>
@ -71,7 +82,7 @@
{/if}
</figcaption>
<ul class="px-14 flex-auto overflow-auto">
<ul class="flex-auto overflow-auto">
{#each $searchResults.page as { doc, deltas, filepath, highlight }}
{@const timestamp = deltas[deltas.length - 1].timestampMs}
<li class="mt-6">
@ -89,5 +100,24 @@
</li>
{/each}
</ul>
<nav class="mx-auto flex rounded-md border border-zinc-700 text-zinc-400">
<button
on:click={openPrevPage}
disabled={!$searchResults.havePrev}
class:text-zinc-50={$searchResults.havePrev}
class="h-9 w-9"
>
<IconChevronLeft class="ml-1 h-5 w-6" />
</button>
<button
on:click={openNextPage}
disabled={!$searchResults.haveNext}
class:text-zinc-50={$searchResults.haveNext}
class="h-9 w-9 border-l border-zinc-700"
>
<IconChevronRight class="ml-1 h-5 w-6" />
</button>
</nav>
{/if}
</figure>