-
+
Keyboard Shortcuts
- ?
- Toggle this help modal
-
- s or /
- Focus the search field
+
- Focus the search field
- ↑
- Move up in search results
- ↓
- Move down in search results
- ⏎
- Go to active search result
+
- p
- Open preferences
- Esc
- Clear focus; close this modal
+
diff --git a/lib/docs/main.js b/lib/docs/main.js
index ea734bdee2..168a9b3a6c 100644
--- a/lib/docs/main.js
+++ b/lib/docs/main.js
@@ -8,6 +8,8 @@ const NAV_MODES = {
};
(function () {
+ const domBanner = document.getElementById("banner");
+ const domMain = document.getElementById("main");
const domStatus = document.getElementById("status");
const domSectNav = document.getElementById("sectNav");
const domListNav = document.getElementById("listNav");
@@ -65,10 +67,18 @@ const NAV_MODES = {
const domTdZigVer = document.getElementById("tdZigVer");
const domHdrName = document.getElementById("hdrName");
const domHelpModal = document.getElementById("helpModal");
+ const domSearchKeys = document.getElementById("searchKeys");
+ const domPrefsModal = document.getElementById("prefsModal");
const domSearchPlaceholder = document.getElementById("searchPlaceholder");
const sourceFileUrlTemplate = "src/{{mod}}/{{file}}.html#L{{line}}"
const domLangRefLink = document.getElementById("langRefLink");
+ const domPrefSlashSearch = document.getElementById("prefSlashSearch");
+ const prefs = getLocalStorage();
+ loadPrefs();
+
+ domPrefSlashSearch.addEventListener("change", () => setPrefSlashSearch(domPrefSlashSearch.checked));
+
let searchTimer = null;
let searchTrimResults = true;
@@ -127,21 +137,21 @@ const NAV_MODES = {
window.guideSearch = guidesSearchIndex;
parseGuides();
- // identifiers can contain '?' so we want to allow typing
- // the question mark when the search is focused instead of toggling the help modal
- let canToggleHelpModal = true;
+ // identifiers can contain modal trigger characters so we want to allow typing
+ // such characters when the search is focused instead of toggling the modal
+ let canToggleModal = true;
domSearch.disabled = false;
domSearch.addEventListener("keydown", onSearchKeyDown, false);
domSearch.addEventListener("input", onSearchInput, false);
domSearch.addEventListener("focus", ev => {
domSearchPlaceholder.classList.add("hidden");
- canToggleHelpModal = false;
+ canToggleModal = false;
});
domSearch.addEventListener("blur", ev => {
if (domSearch.value.length == 0)
domSearchPlaceholder.classList.remove("hidden");
- canToggleHelpModal = true;
+ canToggleModal = true;
});
domSectSearchAllResultsLink.addEventListener('click', onClickSearchShowAllResults, false);
function onClickSearchShowAllResults(ev) {
@@ -156,10 +166,13 @@ const NAV_MODES = {
}
// make the modal disappear if you click outside it
- domHelpModal.addEventListener("click", ev => {
- if (ev.target.className == "help-modal")
- domHelpModal.classList.add("hidden");
- });
+ function handleModalClick(ev) {
+ if (ev.target.classList.contains("modal-container")) {
+ hideModal(this);
+ }
+ }
+ domHelpModal.addEventListener("click", handleModalClick);
+ domPrefsModal.addEventListener("click", handleModalClick);
window.addEventListener("hashchange", onHashChange, false);
window.addEventListener("keydown", onWindowKeyDown, false);
@@ -3996,8 +4009,12 @@ function addDeclToSearchResults(decl, declIndex, modNames, item, list, stack) {
// hide the modal if it's visible or return to the previous result page and unfocus the search
function onEscape(ev) {
- if (!domHelpModal.classList.contains("hidden")) {
- domHelpModal.classList.add("hidden");
+ if (isModalVisible(domHelpModal)) {
+ hideModal(domHelpModal);
+ ev.preventDefault();
+ ev.stopPropagation();
+ } else if (isModalVisible(domPrefsModal)) {
+ hideModal(domPrefsModal);
ev.preventDefault();
ev.stopPropagation();
} else {
@@ -4110,8 +4127,10 @@ function addDeclToSearchResults(decl, declIndex, modNames, item, list, stack) {
onEscape(ev);
break;
case "/":
+ if (!getPrefSlashSearch()) break;
+ // fallthrough
case "s":
- if (domHelpModal.classList.contains("hidden")) {
+ if (!isModalVisible(domHelpModal) && !isModalVisible(domPrefsModal)) {
if (ev.target == domSearch) break;
domSearch.focus();
@@ -4123,28 +4142,65 @@ function addDeclToSearchResults(decl, declIndex, modNames, item, list, stack) {
}
break;
case "?":
- if (!canToggleHelpModal) break;
+ if (!canToggleModal) break;
+
+ if (isModalVisible(domPrefsModal)) {
+ hideModal(domPrefsModal);
+ }
// toggle the help modal
- if (!domHelpModal.classList.contains("hidden")) {
- onEscape(ev);
+ if (isModalVisible(domHelpModal)) {
+ hideModal(domHelpModal);
} else {
- ev.preventDefault();
- ev.stopPropagation();
- showHelpModal();
+ showModal(domHelpModal);
}
+ ev.preventDefault();
+ ev.stopPropagation();
break;
+ case "p":
+ if (!canToggleModal) break;
+
+ if (isModalVisible(domHelpModal)) {
+ hideModal(domHelpModal);
+ }
+
+ // toggle the preferences modal
+ if (isModalVisible(domPrefsModal)) {
+ hideModal(domPrefsModal);
+ } else {
+ showModal(domPrefsModal);
+ }
+ ev.preventDefault();
+ ev.stopPropagation();
}
}
- function showHelpModal() {
- domHelpModal.classList.remove("hidden");
- domHelpModal.style.left =
- window.innerWidth / 2 - domHelpModal.clientWidth / 2 + "px";
- domHelpModal.style.top =
- window.innerHeight / 2 - domHelpModal.clientHeight / 2 + "px";
- domHelpModal.focus();
+ function isModalVisible(modal) {
+ return !modal.classList.contains("hidden");
+ }
+
+ function showModal(modal) {
+ modal.classList.remove("hidden");
+ modal.style.left =
+ window.innerWidth / 2 - modal.clientWidth / 2 + "px";
+ modal.style.top =
+ window.innerHeight / 2 - modal.clientHeight / 2 + "px";
+ const firstInput = modal.querySelector("input");
+ if (firstInput) {
+ firstInput.focus();
+ } else {
+ modal.focus();
+ }
domSearch.blur();
+ domBanner.inert = true;
+ domMain.inert = true;
+ }
+
+ function hideModal(modal) {
+ modal.classList.add("hidden");
+ domBanner.inert = false;
+ domMain.inert = false;
+ modal.blur();
}
function clearAsyncSearch() {
@@ -4678,6 +4734,47 @@ function addDeclToSearchResults(decl, declIndex, modNames, item, list, stack) {
}
}
+ function getLocalStorage() {
+ if ("localStorage" in window) {
+ try {
+ return window.localStorage;
+ } catch (ignored) {
+ // localStorage may be disabled (SecurityError)
+ }
+ }
+ // If localStorage isn't available, persist preferences only for the current session
+ const sessionPrefs = {};
+ return {
+ getItem(key) {
+ return key in sessionPrefs ? sessionPrefs[key] : null;
+ },
+ setItem(key, value) {
+ sessionPrefs[key] = String(value);
+ },
+ };
+ }
+
+ function loadPrefs() {
+ const storedPrefSlashSearch = prefs.getItem("slashSearch");
+ if (storedPrefSlashSearch === null) {
+ // Slash search defaults to enabled for all browsers except Firefox
+ setPrefSlashSearch(navigator.userAgent.indexOf("Firefox") === -1);
+ } else {
+ setPrefSlashSearch(storedPrefSlashSearch === "true");
+ }
+ }
+
+ function getPrefSlashSearch() {
+ return prefs.getItem("slashSearch") === "true";
+ }
+
+ function setPrefSlashSearch(enabled) {
+ prefs.setItem("slashSearch", String(enabled));
+ domPrefSlashSearch.checked = enabled;
+ const searchKeys = enabled ? "
/ or
s" : "
s";
+ domSearchKeys.innerHTML = searchKeys;
+ domSearchPlaceholder.innerHTML = searchKeys + " to search,
? for more options";
+ }
})();
function toggleExpand(event) {