From 9aa6a18be2eae0d84c7897470a46ede19d5ac191 Mon Sep 17 00:00:00 2001
From: Aaron Pham <29749331+aarnphm@users.noreply.github.com>
Date: Thu, 01 Feb 2024 20:56:42 +0000
Subject: [PATCH] fix(search): improve more general usability (closes #781) (#782)
---
quartz/components/scripts/search.inline.ts | 66 ++++++++++++++++++++++-----------
1 files 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 @@
if (preview) {
removeAllChildren(preview)
}
+ if (searchLayout) {
+ searchLayout.style.opacity = "0"
+ }
searchType = "basic" // reset search type after closing
}
@@ -135,6 +138,8 @@
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 @@
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 @@
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<HTMLElement>) {
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 @@
removeAllChildren(results)
if (finalResults.length === 0) {
- results.innerHTML = `<a class="result-card">
+ results.innerHTML = `<a class="result-card no-match">
<h3>No results.</h3>
<p>Try another search term?</p>
</a>`
@@ -329,8 +346,13 @@
}
// 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)
+ }
}
}
--
Gitblit v1.10.0