Improved graceful handling of broken search when browsing locally

This commit is contained in:
squidfunk
2021-07-10 11:42:38 +02:00
parent 52d773b81b
commit 580f1181f5
5 changed files with 96 additions and 89 deletions

View File

@@ -223,7 +223,7 @@
</script> </script>
{% endblock %} {% endblock %}
{% block scripts %} {% block scripts %}
<script src="{{ 'assets/javascripts/bundle.d7b0ad22.min.js' | url }}"></script> <script src="{{ 'assets/javascripts/bundle.ddd52ceb.min.js' | url }}"></script>
{% for path in config["extra_javascript"] %} {% for path in config["extra_javascript"] %}
<script src="{{ path | url }}"></script> <script src="{{ path | url }}"></script>
{% endfor %} {% endfor %}

View File

@@ -55,7 +55,7 @@ export function resetBackToTopState(
* @param el - Back-to-top element * @param el - Back-to-top element
* @param value - Back-to-top offset * @param value - Back-to-top offset
*/ */
export function setBackToTopOffset( export function setBackToTopOffset(
el: HTMLElement, value: number el: HTMLElement, value: number
): void { ): void {
el.style.top = `${value}px` el.style.top = `${value}px`

View File

@@ -85,96 +85,103 @@ export function mountSearch(
el: HTMLElement, { index$, keyboard$ }: MountOptions el: HTMLElement, { index$, keyboard$ }: MountOptions
): Observable<Component<Search>> { ): Observable<Component<Search>> {
const config = configuration() const config = configuration()
const worker = setupSearchWorker(config.search, index$) try {
const worker = setupSearchWorker(config.search, index$)
/* Retrieve nested components */ /* Retrieve nested components */
const query = getComponentElement("search-query", el) const query = getComponentElement("search-query", el)
const result = getComponentElement("search-result", el) const result = getComponentElement("search-result", el)
/* Re-emit query when search is ready */ /* Re-emit query when search is ready */
const { tx$, rx$ } = worker const { tx$, rx$ } = worker
tx$ tx$
.pipe( .pipe(
filter(isSearchQueryMessage), filter(isSearchQueryMessage),
sample(rx$.pipe(filter(isSearchReadyMessage))), sample(rx$.pipe(filter(isSearchReadyMessage))),
take(1) take(1)
) )
.subscribe(tx$.next.bind(tx$)) .subscribe(tx$.next.bind(tx$))
/* Set up search keyboard handlers */ /* Set up search keyboard handlers */
keyboard$ keyboard$
.pipe( .pipe(
filter(({ mode }) => mode === "search") filter(({ mode }) => mode === "search")
) )
.subscribe(key => { .subscribe(key => {
const active = getActiveElement() const active = getActiveElement()
switch (key.type) { switch (key.type) {
/* Enter: prevent form submission */ /* Enter: prevent form submission */
case "Enter": case "Enter":
if (active === query) if (active === query)
key.claim()
break
/* Escape or Tab: close search */
case "Escape":
case "Tab":
setToggle("search", false)
setElementFocus(query, false)
break
/* Vertical arrows: select previous or next search result */
case "ArrowUp":
case "ArrowDown":
if (typeof active === "undefined") {
setElementFocus(query)
} else {
const els = [query, ...getElements(
":not(details) > [href], summary, details[open] [href]",
result
)]
const i = Math.max(0, (
Math.max(0, els.indexOf(active)) + els.length + (
key.type === "ArrowUp" ? -1 : +1
)
) % els.length)
setElementFocus(els[i])
}
/* Prevent scrolling of page */
key.claim() key.claim()
break break
/* Escape or Tab: close search */ /* All other keys: hand to search query */
case "Escape": default:
case "Tab": if (query !== getActiveElement())
setToggle("search", false) setElementFocus(query)
setElementFocus(query, false) }
break })
/* Vertical arrows: select previous or next search result */ /* Set up global keyboard handlers */
case "ArrowUp": keyboard$
case "ArrowDown": .pipe(
if (typeof active === "undefined") { filter(({ mode }) => mode === "global"),
)
.subscribe(key => {
switch (key.type) {
/* Open search and select query */
case "f":
case "s":
case "/":
setElementFocus(query) setElementFocus(query)
} else { setElementSelection(query)
const els = [query, ...getElements( key.claim()
":not(details) > [href], summary, details[open] [href]", break
result }
)] })
const i = Math.max(0, (
Math.max(0, els.indexOf(active)) + els.length + (
key.type === "ArrowUp" ? -1 : +1
)
) % els.length)
setElementFocus(els[i])
}
/* Prevent scrolling of page */ /* Create and return component */
key.claim() const query$ = mountSearchQuery(query, worker)
break return merge(
query$,
/* All other keys: hand to search query */ mountSearchResult(result, worker, { query$ })
default:
if (query !== getActiveElement())
setElementFocus(query)
}
})
/* Set up global keyboard handlers */
keyboard$
.pipe(
filter(({ mode }) => mode === "global"),
) )
.subscribe(key => {
switch (key.type) {
/* Open search and select query */ /* Gracefully handle broken search */
case "f": } catch (err) {
case "s": el.hidden = true
case "/": return NEVER
setElementFocus(query) }
setElementSelection(query)
key.claim()
break
}
})
/* Create and return component */
const query$ = mountSearchQuery(query, worker)
return merge(
query$,
mountSearchResult(result, worker, { query$ })
)
} }