From 9aa6a18be2eae0d84c7897470a46ede19d5ac191 Mon Sep 17 00:00:00 2001 From: Aaron Pham <29749331+aarnphm@users.noreply.github.com> Date: Thu, 1 Feb 2024 15:56:42 -0500 Subject: [PATCH] fix(search): improve more general usability (closes #781) (#782) * fix(search): improve more general usability Signed-off-by: Aaron <29749331+aarnphm@users.noreply.github.com> * fix: revert naming Signed-off-by: Aaron <29749331+aarnphm@users.noreply.github.com> * fix: correct check for enter event on no-match cases Signed-off-by: Aaron <29749331+aarnphm@users.noreply.github.com> * Update quartz/components/scripts/search.inline.ts Co-authored-by: Jacky Zhao * chore: remove unecessary class for tracking mouse Signed-off-by: Aaron <29749331+aarnphm@users.noreply.github.com> --------- Signed-off-by: Aaron <29749331+aarnphm@users.noreply.github.com> Co-authored-by: Jacky Zhao --- quartz/components/scripts/search.inline.ts | 66 ++++++++++++++-------- 1 file changed, 44 insertions(+), 22 deletions(-) diff --git a/quartz/components/scripts/search.inline.ts b/quartz/components/scripts/search.inline.ts index 170a8f0..43332a6 100644 --- a/quartz/components/scripts/search.inline.ts +++ b/quartz/components/scripts/search.inline.ts @@ -122,6 +122,9 @@ document.addEventListener("nav", async (e: CustomEventMap["nav"]) => { if (preview) { removeAllChildren(preview) } + if (searchLayout) { + searchLayout.style.opacity = "0" + } searchType = "basic" // reset search type after closing } @@ -135,6 +138,8 @@ document.addEventListener("nav", async (e: CustomEventMap["nav"]) => { searchBar?.focus() } + let currentHover: HTMLInputElement | null = null + async function shortcutHandler(e: HTMLElementEventMap["keydown"]) { if (e.key === "k" && (e.ctrlKey || e.metaKey) && !e.shiftKey) { e.preventDefault() @@ -150,51 +155,61 @@ document.addEventListener("nav", async (e: CustomEventMap["nav"]) => { if (searchBar) searchBar.value = "#" } - const resultCards = document.getElementsByClassName("result-card") + if (currentHover) { + currentHover.classList.remove("focus") + } // If search is active, then we will render the first result and display accordingly if (!container?.classList.contains("active")) return - else if (results?.contains(document.activeElement)) { - const active = document.activeElement as HTMLInputElement - await displayPreview(active) - if (e.key === "Enter") { + else if (e.key === "Enter") { + // If result has focus, navigate to that one, otherwise pick first result + if (results?.contains(document.activeElement)) { + const active = document.activeElement as HTMLInputElement + if (active.classList.contains("no-match")) return + await displayPreview(active) active.click() + } else { + const anchor = document.getElementsByClassName("result-card")[0] as HTMLInputElement | null + if (!anchor || anchor?.classList.contains("no-match")) return + await displayPreview(anchor) + anchor.click() } - } else { - const anchor = resultCards[0] as HTMLInputElement | null - await displayPreview(anchor) - if (e.key === "Enter") { - anchor?.click() - } - } - - if (e.key === "ArrowUp" || (e.shiftKey && e.key === "Tab")) { + } else if (e.key === "ArrowUp" || (e.shiftKey && e.key === "Tab")) { e.preventDefault() if (results?.contains(document.activeElement)) { // If an element in results-container already has focus, focus previous one - const currentResult = document.activeElement as HTMLInputElement | null + const currentResult = currentHover + ? currentHover + : (document.activeElement as HTMLInputElement | null) const prevResult = currentResult?.previousElementSibling as HTMLInputElement | null currentResult?.classList.remove("focus") await displayPreview(prevResult) prevResult?.focus() + currentHover = prevResult } } else if (e.key === "ArrowDown" || e.key === "Tab") { e.preventDefault() // The results should already been focused, so we need to find the next one. // The activeElement is the search bar, so we need to find the first result and focus it. if (!results?.contains(document.activeElement)) { - const firstResult = resultCards[0] as HTMLInputElement | null + const firstResult = currentHover + ? currentHover + : (document.getElementsByClassName("result-card")[0] as HTMLInputElement | null) const secondResult = firstResult?.nextElementSibling as HTMLInputElement | null firstResult?.classList.remove("focus") await displayPreview(secondResult) secondResult?.focus() + currentHover = secondResult } else { // If an element in results-container already has focus, focus next one - const active = document.activeElement as HTMLInputElement | null + const active = currentHover + ? currentHover + : (document.activeElement as HTMLInputElement | null) active?.classList.remove("focus") const nextResult = active?.nextElementSibling as HTMLInputElement | null await displayPreview(nextResult) nextResult?.focus() + currentHover = nextResult } } } @@ -282,15 +297,17 @@ document.addEventListener("nav", async (e: CustomEventMap["nav"]) => { async function onMouseEnter(ev: MouseEvent) { // Actually when we hover, we need to clean all highlights within the result childs + if (!ev.target) return for (const el of document.getElementsByClassName( "result-card", ) as HTMLCollectionOf) { el.classList.remove("focus") el.blur() } - const target = ev.target as HTMLAnchorElement - target.classList.add("focus") + const target = ev.target as HTMLInputElement await displayPreview(target) + currentHover = target + currentHover.classList.remove("focus") } async function onMouseLeave(ev: MouseEvent) { @@ -320,7 +337,7 @@ document.addEventListener("nav", async (e: CustomEventMap["nav"]) => { removeAllChildren(results) if (finalResults.length === 0) { - results.innerHTML = ` + results.innerHTML = `

No results.

Try another search term?

` @@ -329,8 +346,13 @@ document.addEventListener("nav", async (e: CustomEventMap["nav"]) => { } // focus on first result, then also dispatch preview immediately if (results?.firstElementChild) { - results?.firstElementChild?.classList.add("focus") - await displayPreview(results?.firstElementChild as HTMLElement) + const firstChild = results.firstElementChild as HTMLElement + if (firstChild.classList.contains("no-match")) { + removeAllChildren(preview as HTMLElement) + } else { + firstChild.classList.add("focus") + await displayPreview(firstChild) + } } }