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 <j.zhao2k19@gmail.com>

* 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 <j.zhao2k19@gmail.com>
This commit is contained in:
Aaron Pham 2024-02-01 15:56:42 -05:00 committed by GitHub
parent 444e05ee21
commit 9aa6a18be2
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194

View File

@ -122,6 +122,9 @@ document.addEventListener("nav", async (e: CustomEventMap["nav"]) => {
if (preview) { if (preview) {
removeAllChildren(preview) removeAllChildren(preview)
} }
if (searchLayout) {
searchLayout.style.opacity = "0"
}
searchType = "basic" // reset search type after closing searchType = "basic" // reset search type after closing
} }
@ -135,6 +138,8 @@ document.addEventListener("nav", async (e: CustomEventMap["nav"]) => {
searchBar?.focus() searchBar?.focus()
} }
let currentHover: HTMLInputElement | null = null
async function shortcutHandler(e: HTMLElementEventMap["keydown"]) { async function shortcutHandler(e: HTMLElementEventMap["keydown"]) {
if (e.key === "k" && (e.ctrlKey || e.metaKey) && !e.shiftKey) { if (e.key === "k" && (e.ctrlKey || e.metaKey) && !e.shiftKey) {
e.preventDefault() e.preventDefault()
@ -150,51 +155,61 @@ document.addEventListener("nav", async (e: CustomEventMap["nav"]) => {
if (searchBar) searchBar.value = "#" 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 search is active, then we will render the first result and display accordingly
if (!container?.classList.contains("active")) return if (!container?.classList.contains("active")) return
else if (results?.contains(document.activeElement)) { else if (e.key === "Enter") {
const active = document.activeElement as HTMLInputElement // If result has focus, navigate to that one, otherwise pick first result
await displayPreview(active) if (results?.contains(document.activeElement)) {
if (e.key === "Enter") { const active = document.activeElement as HTMLInputElement
if (active.classList.contains("no-match")) return
await displayPreview(active)
active.click() 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 { } else if (e.key === "ArrowUp" || (e.shiftKey && e.key === "Tab")) {
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")) {
e.preventDefault() e.preventDefault()
if (results?.contains(document.activeElement)) { if (results?.contains(document.activeElement)) {
// If an element in results-container already has focus, focus previous one // 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 const prevResult = currentResult?.previousElementSibling as HTMLInputElement | null
currentResult?.classList.remove("focus") currentResult?.classList.remove("focus")
await displayPreview(prevResult) await displayPreview(prevResult)
prevResult?.focus() prevResult?.focus()
currentHover = prevResult
} }
} else if (e.key === "ArrowDown" || e.key === "Tab") { } else if (e.key === "ArrowDown" || e.key === "Tab") {
e.preventDefault() e.preventDefault()
// The results should already been focused, so we need to find the next one. // 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. // The activeElement is the search bar, so we need to find the first result and focus it.
if (!results?.contains(document.activeElement)) { 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 const secondResult = firstResult?.nextElementSibling as HTMLInputElement | null
firstResult?.classList.remove("focus") firstResult?.classList.remove("focus")
await displayPreview(secondResult) await displayPreview(secondResult)
secondResult?.focus() secondResult?.focus()
currentHover = secondResult
} else { } else {
// If an element in results-container already has focus, focus next one // 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") active?.classList.remove("focus")
const nextResult = active?.nextElementSibling as HTMLInputElement | null const nextResult = active?.nextElementSibling as HTMLInputElement | null
await displayPreview(nextResult) await displayPreview(nextResult)
nextResult?.focus() nextResult?.focus()
currentHover = nextResult
} }
} }
} }
@ -282,15 +297,17 @@ document.addEventListener("nav", async (e: CustomEventMap["nav"]) => {
async function onMouseEnter(ev: MouseEvent) { async function onMouseEnter(ev: MouseEvent) {
// Actually when we hover, we need to clean all highlights within the result childs // Actually when we hover, we need to clean all highlights within the result childs
if (!ev.target) return
for (const el of document.getElementsByClassName( for (const el of document.getElementsByClassName(
"result-card", "result-card",
) as HTMLCollectionOf<HTMLElement>) { ) as HTMLCollectionOf<HTMLElement>) {
el.classList.remove("focus") el.classList.remove("focus")
el.blur() el.blur()
} }
const target = ev.target as HTMLAnchorElement const target = ev.target as HTMLInputElement
target.classList.add("focus")
await displayPreview(target) await displayPreview(target)
currentHover = target
currentHover.classList.remove("focus")
} }
async function onMouseLeave(ev: MouseEvent) { async function onMouseLeave(ev: MouseEvent) {
@ -320,7 +337,7 @@ document.addEventListener("nav", async (e: CustomEventMap["nav"]) => {
removeAllChildren(results) removeAllChildren(results)
if (finalResults.length === 0) { if (finalResults.length === 0) {
results.innerHTML = `<a class="result-card"> results.innerHTML = `<a class="result-card no-match">
<h3>No results.</h3> <h3>No results.</h3>
<p>Try another search term?</p> <p>Try another search term?</p>
</a>` </a>`
@ -329,8 +346,13 @@ document.addEventListener("nav", async (e: CustomEventMap["nav"]) => {
} }
// focus on first result, then also dispatch preview immediately // focus on first result, then also dispatch preview immediately
if (results?.firstElementChild) { if (results?.firstElementChild) {
results?.firstElementChild?.classList.add("focus") const firstChild = results.firstElementChild as HTMLElement
await displayPreview(results?.firstElementChild as HTMLElement) if (firstChild.classList.contains("no-match")) {
removeAllChildren(preview as HTMLElement)
} else {
firstChild.classList.add("focus")
await displayPreview(firstChild)
}
} }
} }