Add ISAC registry search controls

This commit is contained in:
JB
2026-07-25 21:18:11 -04:00
parent 6d2e8176ee
commit 65aa6361a6
21 changed files with 172 additions and 65 deletions
+32 -3
View File
@@ -1,12 +1,33 @@
<script lang="ts">
import { page as currentPage } from '$app/state';
let { pagination, namespace = '' }: { pagination: { page: number; perPage: number; total: number; pageCount: number }; namespace?: string } = $props();
let {
pagination,
namespace = '',
search,
searchPlaceholder = 'SEARCH BY NAME'
}: {
pagination: { page: number; perPage: number; total: number; pageCount: number };
namespace?: string;
search?: string;
searchPlaceholder?: string;
} = $props();
const pageKey = $derived(namespace ? `${namespace}Page` : 'page');
const perPageKey = $derived(namespace ? `${namespace}PerPage` : 'perPage');
const searchKey = $derived(namespace ? `${namespace}Q` : 'q');
const href = (targetPage: number, perPage = pagination.perPage) => {
const params = new URLSearchParams(currentPage.url.searchParams);
params.set(namespace ? `${namespace}Page` : 'page', String(targetPage));
params.set(namespace ? `${namespace}PerPage` : 'perPage', String(perPage));
params.set(pageKey, String(targetPage));
params.set(perPageKey, String(perPage));
return `${currentPage.url.pathname}?${params}`;
};
const clearSearchHref = $derived.by(() => {
const params = new URLSearchParams(currentPage.url.searchParams);
params.delete(searchKey);
params.delete(pageKey);
const query = params.toString();
return `${currentPage.url.pathname}${query ? `?${query}` : ''}`;
});
const retainedParams = $derived([...currentPage.url.searchParams].filter(([key]) => key !== searchKey && key !== pageKey));
const visiblePages = $derived(
Array.from({ length: pagination.pageCount }, (_, index) => index + 1)
.filter((page) => page === 1 || page === pagination.pageCount || Math.abs(page - pagination.page) <= 2)
@@ -16,6 +37,14 @@
</script>
<nav class="registry-pagination" aria-label="Registry pagination">
{#if search !== undefined}
<form class="pagination-search">
{#each retainedParams as [key, value]}<input type="hidden" name={key} {value}/>{/each}
<input type="search" name={searchKey} value={search} maxlength="80" placeholder={searchPlaceholder} aria-label={searchPlaceholder}/>
<button class="secondary">SEARCH</button>
{#if search}<a href={clearSearchHref}>CLEAR</a>{/if}
</form>
{/if}
<div class="pagination-range"><span>DISPLAYING</span><b>{start}–{end}</b><span>OF {pagination.total}</span></div>
<div class="pagination-pages">
<a class:disabled={pagination.page === 1} aria-disabled={pagination.page === 1} href={href(Math.max(1, pagination.page - 1))}>← PREV</a>
+9
View File
@@ -2,6 +2,15 @@ import type { URL } from 'node:url';
export const PAGE_SIZE_PRESETS = [10, 25, 50, 100] as const;
export function getRegistrySearch(url: URL, namespace = '') {
const key = namespace ? `${namespace}Q` : 'q';
const search = String(url.searchParams.get(key) ?? '').normalize('NFKC').trim().slice(0, 80);
return {
search,
pattern: `%${search.replaceAll('\\', '\\\\').replaceAll('%', '\\%').replaceAll('_', '\\_')}%`
};
}
export function getPagination(url: URL, total: number, namespace = '') {
const pageKey = namespace ? `${namespace}Page` : 'page';
const perPageKey = namespace ? `${namespace}PerPage` : 'perPage';