From 973345ad50f9b237714fcb364cf7f665b3909f9d Mon Sep 17 00:00:00 2001 From: Alan Pearce Date: Wed, 8 May 2024 00:15:52 +0200 Subject: feat: paginate search results --- frontend/static/search.js | 66 +++++++++++++++++++++++++++++++++++------------ frontend/static/style.css | 23 ++++++++++++++++- 2 files changed, 71 insertions(+), 18 deletions(-) (limited to 'frontend/static') diff --git a/frontend/static/search.js b/frontend/static/search.js index 60fa30d..2282558 100644 --- a/frontend/static/search.js +++ b/frontend/static/search.js @@ -1,12 +1,14 @@ const search = document.getElementById("search"); let results = document.getElementById("results"); +let pagination = document.getElementById("pagination"); const range = new Range(); range.setStartAfter(search); range.setEndAfter(search.parentNode.lastChild); let state = history.state || { - url: new URL(location).toJSON(), + url: new URL(location).toString(), + fragment: range.cloneContents().innerHTML || "", opened: [], }; @@ -35,44 +37,69 @@ function addToggleEventListeners(results) { details.addEventListener("toggle", detailsToggled, { passive: true }), ); } -search.addEventListener("submit", function (ev) { - const url = new URL(this.action); - url.search = new URLSearchParams(new FormData(this)).toString(); + +function paginationLinkClicked(ev) { + const url = new URL(ev.target.href); + getResults(url); + ev.preventDefault(); +} + +function addPaginationEventListeners() { + pagination.addEventListener("click", paginationLinkClicked); +} + +function renderFragmentHTML(html) { + const fragment = range.createContextualFragment( + escapePolicy !== null ? escapePolicy.createHTML(html) : html, + ); + results = fragment.querySelector("#results"); + pagination = fragment.querySelector("#pagination"); + range.deleteContents(); + range.insertNode(fragment); + addToggleEventListeners(results); + addPaginationEventListeners(pagination); +} + +function getResults(url) { fetch(url, { headers: { fetch: "true", }, }) .then(async function (res) { - state.url = url.toJSON(); - state.opened = []; if (res.ok) { - history.pushState(state, null, url); return res.text(); } else { throw new Error(`${res.status} ${res.statusText}: ${await res.text()}`); } }) .then(function (html) { - const fragment = range.createContextualFragment( - escapePolicy !== null ? escapePolicy.createHTML(html) : html, - ); - const results = fragment.firstElementChild; - range.deleteContents(); - range.insertNode(results); - addToggleEventListeners(results); + state.url = url.toJSON(); + state.opened = []; + state.fragment = html; + history.pushState(state, null, url); + return renderFragmentHTML(html); }) .catch(function (error) { range.deleteContents(); range.insertNode(new Text(error.message)); console.error("fetch failed", error); }); +} + +search.addEventListener("submit", function (ev) { + const url = new URL(this.action); + url.search = new URLSearchParams(new FormData(this)).toString(); + getResults(url); ev.preventDefault(); }); if (results !== null) { addToggleEventListeners(results); } +if (pagination !== null) { + addPaginationEventListeners(pagination); +} if (state.opened.length > 0) { state.opened.forEach((id) => @@ -83,8 +110,13 @@ if (state.opened.length > 0) { } addEventListener("popstate", function (ev) { - if (ev.state == null || ev.state.url.pathname.startsWith("/search/")) { - range.deleteContents(); - search.reset(); + if (ev.state != null) { + url = new URL(ev.state.url); + if (!url.pathname.endsWith("/search") && ev.state.fragment !== null) { + renderFragmentHTML(ev.state.fragment); + return; + } } + range.deleteContents(); + search.reset(); }); diff --git a/frontend/static/style.css b/frontend/static/style.css index 375883e..dabf39c 100644 --- a/frontend/static/style.css +++ b/frontend/static/style.css @@ -41,7 +41,7 @@ body > header { } form { - margin-top: 1.5rem; + margin: 1.5rem 0; } fieldset { @@ -49,6 +49,13 @@ fieldset { column-gap: 1ex; border: none; padding: unset; + margin: unset; +} + +section { + border-top: none; + margin: unset; + padding: unset; } input[type="search"] { @@ -83,3 +90,17 @@ pre { pre:has(> code) { background: var(--bg); } + +section { + nav { + display: flex; + justify-content: space-between; + align-items: baseline; + a[rel="next"] { + margin-left: auto; + } + } + footer { + text-align: center; + } +} -- cgit 1.4.1