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>
{% endblock %}
{% 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"] %}
<script src="{{ path | url }}"></script>
{% endfor %}

View File

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

View File

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