Add live registry search and themed input states

This commit is contained in:
JB
2026-07-25 21:36:05 -04:00
parent c0a8e05e78
commit f0fbdc2853
3 changed files with 54 additions and 4 deletions
+35 -4
View File
@@ -1,5 +1,7 @@
<script lang="ts">
import { page as currentPage } from '$app/state';
import { goto } from '$app/navigation';
import { navigating, page as currentPage } from '$app/state';
import { onDestroy } from 'svelte';
let {
pagination,
namespace = '',
@@ -14,6 +16,35 @@
const pageKey = $derived(namespace ? `${namespace}Page` : 'page');
const perPageKey = $derived(namespace ? `${namespace}PerPage` : 'perPage');
const searchKey = $derived(namespace ? `${namespace}Q` : 'q');
let draftSearch = $state<string | null>(null);
const searchValue = $derived(draftSearch ?? search ?? '');
let searchTimer: ReturnType<typeof setTimeout> | undefined;
const searchPending = $derived(Boolean(navigating.to) && navigating.to?.url.searchParams.get(searchKey) !== currentPage.url.searchParams.get(searchKey));
$effect(() => {
if (draftSearch !== null && draftSearch.trim() === (search ?? '').trim()) draftSearch = null;
});
const searchHref = (query: string) => {
const params = new URLSearchParams(currentPage.url.searchParams);
const normalized = query.trim();
if (normalized) params.set(searchKey, normalized);
else params.delete(searchKey);
params.delete(pageKey);
const serialized = params.toString();
return `${currentPage.url.pathname}${serialized ? `?${serialized}` : ''}`;
};
const submitSearch = (query = searchValue) => {
if (searchTimer) clearTimeout(searchTimer);
if (query.trim() === (search ?? '').trim()) return;
void goto(searchHref(query), { replaceState: true, keepFocus: true, noScroll: true });
};
const scheduleSearch = (query: string) => {
draftSearch = query;
if (searchTimer) clearTimeout(searchTimer);
searchTimer = setTimeout(() => submitSearch(query), 350);
};
onDestroy(() => {
if (searchTimer) clearTimeout(searchTimer);
});
const href = (targetPage: number, perPage = pagination.perPage) => {
const params = new URLSearchParams(currentPage.url.searchParams);
params.set(pageKey, String(targetPage));
@@ -38,10 +69,10 @@
<nav class="registry-pagination" aria-label="Registry pagination">
{#if search !== undefined}
<form class="pagination-search">
<form class="pagination-search" role="search" onsubmit={(event) => { event.preventDefault(); submitSearch(); }}>
{#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>
<input type="search" name={searchKey} value={searchValue} maxlength="80" placeholder={searchPlaceholder} aria-label={`${searchPlaceholder} — results update as you type`} oninput={(event) => scheduleSearch(event.currentTarget.value)}/>
<button class="secondary" aria-busy={searchPending} disabled={searchPending}>{searchPending ? 'SEARCHING…' : 'SEARCH'}</button>
{#if search}<a href={clearSearchHref}>CLEAR</a>{/if}
</form>
{/if}