From 828871dfb119098202eb9312a92fb1334b3016ff Mon Sep 17 00:00:00 2001 From: Chris Amico Date: Thu, 28 Mar 2024 15:32:13 -0400 Subject: [PATCH] Basic infinite scroll for search results --- src/lib/api/documents.ts | 22 +++++-- src/lib/api/types.d.ts | 9 ++- src/lib/components/Search.svelte | 12 ++-- .../components/documents/ResultsList.svelte | 60 +++++++++++++---- .../stories/ResultsList.stories.svelte | 18 ++---- src/routes/app/+page.svelte | 64 ++----------------- src/routes/app/+page.ts | 25 +++++++- src/style/kit.css | 18 ++++++ 8 files changed, 130 insertions(+), 98 deletions(-) diff --git a/src/lib/api/documents.ts b/src/lib/api/documents.ts index 3dfb4baaa..b6b88f99f 100644 --- a/src/lib/api/documents.ts +++ b/src/lib/api/documents.ts @@ -1,7 +1,7 @@ /** API helpers related to documents. * Lots of duplicated code here that should get consolidated at some point. */ -import type { Document, sizes } from "./types"; +import type { Document, DocumentResults, SearchOptions, sizes } from "./types"; import { error } from "@sveltejs/kit"; @@ -10,17 +10,29 @@ import { isOrg } from "@/api/types/orgAndUser"; import { APP_URL, BASE_API_URL } from "@/config/config.js"; import { isErrorCode } from "../utils"; -/** Search documents */ +/** + * Search documents + * https://www.documentcloud.org/help/search/ + * + * */ export async function search( query = "", - highlight = false, + options: SearchOptions = { + hl: Boolean(query), + per_page: 25, + cursor: "", + version: "2.0", + }, fetch = globalThis.fetch, -) { +): Promise { const endpoint = new URL("documents/search/", BASE_API_URL); endpoint.searchParams.set("expand", DEFAULT_EXPAND); endpoint.searchParams.set("q", query); - endpoint.searchParams.set("hl", String(highlight)); + + for (const [k, v] of Object.entries(options)) { + endpoint.searchParams.set(k, String(v)); + } const resp = await fetch(endpoint, { credentials: "include" }); diff --git a/src/lib/api/types.d.ts b/src/lib/api/types.d.ts index d686d6680..474da49ee 100644 --- a/src/lib/api/types.d.ts +++ b/src/lib/api/types.d.ts @@ -105,7 +105,6 @@ export interface Document { note_highlights?: Record; } -// export type DocumentResults = Page; export interface DocumentResults extends Page {} export interface Note { @@ -135,6 +134,14 @@ export interface Section { export type SectionResults = Page
; +export interface SearchOptions { + hl?: boolean; + per_page?: number; + cursor?: string; + expand?: string; + version?: number | string; +} + export interface OEmbed { version: "1.0"; provider_name: "DocumentCloud"; diff --git a/src/lib/components/Search.svelte b/src/lib/components/Search.svelte index b60959d78..a6e59aa50 100644 --- a/src/lib/components/Search.svelte +++ b/src/lib/components/Search.svelte @@ -1,6 +1,5 @@ -
+ + {/if} + diff --git a/src/lib/components/documents/stories/ResultsList.stories.svelte b/src/lib/components/documents/stories/ResultsList.stories.svelte index 886307168..360d7dc3b 100644 --- a/src/lib/components/documents/stories/ResultsList.stories.svelte +++ b/src/lib/components/documents/stories/ResultsList.stories.svelte @@ -1,11 +1,13 @@ -
+
-
+
diff --git a/src/routes/app/+page.svelte b/src/routes/app/+page.svelte index 08702a3fd..2a1f76f74 100644 --- a/src/routes/app/+page.svelte +++ b/src/routes/app/+page.svelte @@ -6,35 +6,11 @@ import PageToolbar from "$lib/components/common/PageToolbar.svelte"; import Search from "$lib/components/Search.svelte"; import Empty from "$lib/components/common/Empty.svelte"; - import Paginator from "@/common/Paginator.svelte"; - import type { DocumentResults } from "@/lib/api/types"; - export let data: { - query: string; - searchResults: Promise; - }; - - let page = 1; - let per_page = 25; - let error: Error; + export let data; $: searchResults = data.searchResults; $: query = data.query; - - async function load(url) { - const res = await fetch(url, { credentials: "include" }).catch((e) => { - error = e; - throw e; // if something went wrong here, something broke - }); - - if (!res.ok) { - // 404 or something similar - console.error(res.statusText); - error = { name: "Loading error", message: res.statusText }; - } - - data.searchResults = res.json(); - } @@ -44,7 +20,11 @@ {#await searchResults} Loading… {:then results} - + {/await} @@ -52,37 +32,5 @@ Select all - - - {#await searchResults then sr} - {@const count = sr.count} - {@const total_pages = Math.ceil(count / per_page)} - {@const next = sr.next} - {@const previous = sr.previous} - { - page = Math.min(total_pages, page + 1); - load(next); - }} - on:previous={(e) => { - page = Math.max(1, page - 1); - load(previous); - }} - /> - {/await} - - - diff --git a/src/routes/app/+page.ts b/src/routes/app/+page.ts index 2798376c6..cf9984f05 100644 --- a/src/routes/app/+page.ts +++ b/src/routes/app/+page.ts @@ -1,11 +1,30 @@ -import { search } from "$lib/api/documents.js"; +import type { SearchOptions } from "$lib/api/types"; +import { search } from "$lib/api/documents"; export async function load({ url, fetch }) { const query = url.searchParams.get("q") || ""; - const searchResults = search(query, true, fetch); + const per_page = +url.searchParams.get("per_page") || 25; + const cursor = url.searchParams.get("cursor") || ""; + + const options: SearchOptions = { + hl: Boolean(query), + version: "2.0", + }; + + if (per_page) { + options.per_page = per_page; + } + + if (cursor) { + options.cursor = cursor; + } + + const searchResults = search(query, options, fetch); return { - searchResults, query, + per_page, + cursor, + searchResults, }; } diff --git a/src/style/kit.css b/src/style/kit.css index d0b920685..1965f00cd 100644 --- a/src/style/kit.css +++ b/src/style/kit.css @@ -139,3 +139,21 @@ dd { font-size: var(--font-s, 0.875rem); font-weight: var(--font-regular, 400); } + +/* +Utility classes +*/ + +/* https://css-tricks.com/inclusively-hidden/ + * Hiding class, making content visible only to screen readers but not visually + * "sr" meaning "screen-reader" + */ +.sr-only:not(:focus):not(:active) { + clip: rect(0 0 0 0); + clip-path: inset(50%); + height: 1px; + overflow: hidden; + position: absolute; + white-space: nowrap; + width: 1px; +}