From 29f9d14fbcd6041c7f8701e52a4644eca1f5365e Mon Sep 17 00:00:00 2001 From: PierreDemailly Date: Sat, 16 Dec 2023 16:01:24 +0100 Subject: [PATCH] feat(wiki): keyboard navigation --- public/core/network-navigation.js | 4 +-- workspaces/documentation-ui/index.js | 26 ++++++++++++++++++ .../documentation-ui/src/components/header.js | 11 ++++++++ .../src/components/navigation.class.js | 27 +++++++++++++++++++ 4 files changed, 66 insertions(+), 2 deletions(-) diff --git a/public/core/network-navigation.js b/public/core/network-navigation.js index fd7fb2d2..35fe3c5d 100644 --- a/public/core/network-navigation.js +++ b/public/core/network-navigation.js @@ -102,10 +102,10 @@ export class NetworkNavigation { this.#dependenciesMapByLevel.set(0, this.rootNodeParams); document.addEventListener("keydown", (event) => { - const isNetworkView = document.getElementById("network--view").classList.contains("hidden") === false; + const isNetworkViewHidden = document.getElementById("network--view").classList.contains("hidden"); const isWikiOpen = document.getElementById("documentation-root-element").classList.contains("slide-in"); const isSearchOpen = window.searchbar.background.classList.contains("show"); - if (isNetworkView === false || isWikiOpen || isSearchOpen) { + if (isNetworkViewHidden || isWikiOpen || isSearchOpen) { return; } diff --git a/workspaces/documentation-ui/index.js b/workspaces/documentation-ui/index.js index bc1983bb..717e524f 100644 --- a/workspaces/documentation-ui/index.js +++ b/workspaces/documentation-ui/index.js @@ -91,6 +91,32 @@ export function render(rootElement, options = {}) { } rootElement.appendChild(mainContainer); + document.addEventListener("keydown", (event) => { + const isWikiOpen = document.getElementById("documentation-root-element").classList.contains("slide-in"); + // should not be possible but just in case + const isSearchOpen = window.searchbar.background.classList.contains("show"); + if (!isWikiOpen || isSearchOpen) { + return; + } + + const activeNav = navigation[header.active.getAttribute("data-menu")]; + + switch (event.key) { + case "ArrowLeft": + case "ArrowRight": + header.switchActiveView(); + break; + case "ArrowUp": + activeNav.previous(); + break; + case "ArrowDown": + activeNav.next(); + break; + default: + break; + } + }); + return { header, navigation diff --git a/workspaces/documentation-ui/src/components/header.js b/workspaces/documentation-ui/src/components/header.js index 48477326..5dabbfaa 100644 --- a/workspaces/documentation-ui/src/components/header.js +++ b/workspaces/documentation-ui/src/components/header.js @@ -69,4 +69,15 @@ export class Header { this.active = liElement; } } + + switchActiveView() { + const next = this.active.nextElementSibling; + if (next === null) { + const first = this.active.parentElement.firstElementChild; + this.setNewActiveView(first.getAttribute("data-menu")); + } + else { + this.setNewActiveView(next.getAttribute("data-menu")); + } + } } diff --git a/workspaces/documentation-ui/src/components/navigation.class.js b/workspaces/documentation-ui/src/components/navigation.class.js index e1c48fcb..d44159d0 100644 --- a/workspaces/documentation-ui/src/components/navigation.class.js +++ b/workspaces/documentation-ui/src/components/navigation.class.js @@ -65,6 +65,19 @@ export class Navigation extends EventTarget { */ setNewActiveMenu(name) { const domElement = this.menus.get(name); + if (domElement.parentElement?.clientHeight < domElement.offsetTop + domElement.offsetHeight) { + domElement.parentElement?.scrollTo({ + top: domElement.offsetTop, + behavior: "smooth" + }); + } + else if (domElement.parentElement?.scrollTop > domElement.offsetTop) { + domElement.parentElement?.scrollTo({ + top: 0, + behavior: "smooth" + }); + } + if (domElement !== this.active) { if (this.active !== null) { this.active.classList.remove("active"); @@ -77,4 +90,18 @@ export class Navigation extends EventTarget { this.dispatchEvent(new CustomEvent("menuActivated", { detail: name })); } } + + next() { + const next = this.active.nextElementSibling; + if (next !== null) { + this.setNewActiveMenu(next.querySelector(".description").innerHTML); + } + } + + previous() { + const previous = this.active.previousElementSibling; + if (previous !== null) { + this.setNewActiveMenu(previous.querySelector(".description").innerHTML); + } + } }