diff --git a/assets/js/offline-search.js b/assets/js/offline-search.js index 9a1f6211..8153201c 100644 --- a/assets/js/offline-search.js +++ b/assets/js/offline-search.js @@ -37,7 +37,7 @@ search backend. }); // - // Register handler + // Set up search input handler. // $searchInput.on("change", (event) => { @@ -53,12 +53,12 @@ search backend. }); // - // Pagefind + // Callback for searching and rendering the results. // const render = async ($targetSearchInput) => { // - // Dispose existing popover + // Dispose any existing popover. // { @@ -69,7 +69,7 @@ search backend. } // - // Search + // Kick off the search and collect the results. // const searchQuery = $targetSearchInput.val(); @@ -77,34 +77,61 @@ search backend. return; } + // Show the results popover with a spinner while we're busy. + const $spinner = $("
") + .addClass("spinner-container") + .append($("
") + .addClass("spinner-border") + .attr("role", "status") + .append($("
") + .addClass("visually-hidden") + .text("Loading..."))) + .append($("

") + .text("Loading...")); + const popover = new bootstrap.Popover($targetSearchInput, { + content: $spinner[0], + html: true, + customClass: "td-offline-search-results", + placement: "bottom", + }); + popover.show(); + + // Kick off the search. const search = await pagefind.debouncedSearch(searchQuery); if (search === null) { // A more recent search call has been made, nothing to do. return; } - const results = await Promise.all(search.results.slice(0, 20).map(r => r.data())); + + // Load all result details. We may want to switch to a paged UI in future for better performance. + const results = await Promise.all(search.results.slice(0, 100).map(r => r.data())); // - // Make result html + // Construct the search results html. // const $html = $("

"); - $html.append( - $("
") - .css({ - display: "flex", - justifyContent: "space-between", - marginBottom: "1em", + // Add the header and close button. + $html.append($("
") + .css({ + display: "flex", + justifyContent: "space-between", + marginBottom: "1em", + }) + .append($("") + .text("Search results") + .css({ fontWeight: "bold" })) + .append($("") + .addClass("td-offline-search-results__close-button") + .on("click", () => { + $targetSearchInput.val(""); + $targetSearchInput.trigger("change"); }) - .append( - $("").text("Search results").css({ fontWeight: "bold" }) - ) - .append( - $("").addClass("td-offline-search-results__close-button") - ) + ) ); + // Add the main search results body. const $searchResultBody = $("
").css({ maxHeight: `calc(100vh - ${ $targetSearchInput.offset().top - $(window).scrollTop() + 180 @@ -119,7 +146,7 @@ search backend. ); } else { results.forEach((r, index_r) => { - // Add the main result"s page title. + // Add the main result's page title. $searchResultBody.append( $("") .addClass("d-block") @@ -136,8 +163,6 @@ search backend. let $wrapper = null; r.sub_results.forEach((s, index_s) => { - - if (index_s === LIMIT) { const wrapper_id = `collapssible-subresults-${index_r}`; const $expander = $("") @@ -179,20 +204,16 @@ search backend. }); } - $targetSearchInput.one("shown.bs.popover", () => { - $(".td-offline-search-results__close-button").on("click", () => { - $targetSearchInput.val(""); - $targetSearchInput.trigger("change"); - }); - }); - - const popover = new bootstrap.Popover($targetSearchInput, { - content: $html[0], - html: true, - customClass: "td-offline-search-results", - placement: "bottom", - }); - popover.show(); + // Finally, show the search results. + // + // Ideally we would just call setContent but there appears to be a bug in Bootstrap + // that causes the popover to be hidden when setContent is called after the popover + // has been shown. To work around this, we use the hack from [1] to inject the HTML + // content manually. + // + // [1]: https://github.com/twbs/bootstrap/issues/37206#issuecomment-1259541205 + $(popover.tip.querySelector('.popover-body')).html($html[0]); + popover.update(); }; }); })(jQuery); diff --git a/assets/scss/_styles_project.scss b/assets/scss/_styles_project.scss index a2daa0ae..bf2c2ba3 100644 --- a/assets/scss/_styles_project.scss +++ b/assets/scss/_styles_project.scss @@ -664,5 +664,10 @@ dd { /* Style for the page search widget */ .td-offline-search-results { + width: 100%; max-width: 460px; } + +.td-offline-search-results .spinner-container { + text-align: center; +}