bookshelf-doc/static/search.js

119 lines
2.9 KiB
JavaScript

/**
* This module is used to handle user's interaction with the search form.
*/
import { DeclarationDataCenter } from "./declaration-data.js";
const SEARCH_FORM = document.querySelector("#search_form");
const SEARCH_INPUT = SEARCH_FORM.querySelector("input[name=q]");
// Create an `div#search_results` to hold all search results.
let sr = document.createElement("div");
sr.id = "search_results";
SEARCH_FORM.appendChild(sr);
/**
* Attach `selected` class to the the selected search result.
*/
function handleSearchCursorUpDown(down) {
const sel = sr.querySelector(`.selected`);
if (sel) {
sel.classList.remove("selected");
const toSelect = down
? sel.nextSibling || sr.firstChild
: sel.previousSibling || sr.lastChild;
toSelect && toSelect.classList.add("selected");
} else {
const toSelect = down ? sr.firstChild : sr.lastChild;
toSelect && toSelect.classList.add("selected");
}
}
/**
* Perform search (when enter is pressed).
*/
function handleSearchEnter() {
const sel = sr.querySelector(`.selected`) || sr.firstChild;
sel.click();
}
/**
* Allow user to navigate search results with up/down arrow keys, and choose with enter.
*/
SEARCH_INPUT.addEventListener("keydown", (ev) => {
switch (ev.key) {
case "Down":
case "ArrowDown":
ev.preventDefault();
handleSearchCursorUpDown(true);
break;
case "Up":
case "ArrowUp":
ev.preventDefault();
handleSearchCursorUpDown(false);
break;
case "Enter":
ev.preventDefault();
handleSearchEnter();
break;
}
});
/**
* Remove all children of a DOM node.
*/
function removeAllChildren(node) {
while (node.firstChild) {
node.removeChild(node.lastChild);
}
}
/**
* Handle user input and perform search.
*/
function handleSearch(dataCenter, err, ev) {
const text = ev.target.value;
// If no input clear all.
if (!text) {
sr.removeAttribute("state");
removeAllChildren(sr);
return;
}
// searching
sr.setAttribute("state", "loading");
if (dataCenter) {
const result = dataCenter.search(text, false);
// in case user has updated the input.
if (ev.target.value != text) return;
// update search results
removeAllChildren(sr);
for (const { name, docLink } of result) {
const d = sr.appendChild(document.createElement("a"));
d.innerText = name;
d.title = name;
d.href = SITE_ROOT + docLink;
}
}
// handle error
else {
removeAllChildren(sr);
const d = sr.appendChild(document.createElement("a"));
d.innerText = `Cannot fetch data, please check your network connection.\n${err}`;
}
sr.setAttribute("state", "done");
}
DeclarationDataCenter.init()
.then((dataCenter) => {
// Search autocompletion.
SEARCH_INPUT.addEventListener("input", ev => handleSearch(dataCenter, null, ev));
})
.catch(e => {
SEARCH_INPUT.addEventListener("input", ev => handleSearch(null, e, ev));
});