From 832196393a2f7fc194ff7af29a2d5e7ea21f0b21 Mon Sep 17 00:00:00 2001 From: jahugg <jan@huggenberg.ch> Date: Wed, 22 Feb 2023 14:15:29 +0100 Subject: [PATCH] improved button controls --- src/main.js | 169 ++++++++++++++++++++++++++++++++----------- src/styles/main.css | 7 ++ src/styles/main.less | 8 +- 3 files changed, 139 insertions(+), 45 deletions(-) diff --git a/src/main.js b/src/main.js index e6d7df9..0807dce 100644 --- a/src/main.js +++ b/src/main.js @@ -111,22 +111,29 @@ function init() { // build navigation const appEl = document.getElementById("app"); const navigationEl = document.createElement("nav"); - navigationEl.id = "navigation"; const listEl = document.createElement("ul"); + navigationEl.id = "navigation"; navigationEl.appendChild(listEl); appEl.appendChild(navigationEl); - // navigate to home - navigateTo("/"); + // navigate to current path or home + const pathname = window.location.pathname; + if (pathname) navigateToPath(pathname); + else navigateToPath("/"); - // add two button controls for menu - document.addEventListener("keydown", onKeyDown); -} + // Add a popstate event listener to the window object + window.addEventListener("popstate", function (event) { + var state = event.state; -// navigate to page -function navigateTo(path) { - let navObj = helpers.findNestedObject(navigation, "path", path); - updateNavigation(navObj); + // Load the appropriate page based on the state object + // without pushing it to the history + if (state) navigateToPath(state.pageUrl, false); + else navigateToPath("/", false); + }); + + // adding controls + addMouseControls(); + addButtonControls(); } // update Navigation @@ -137,6 +144,7 @@ function updateNavigation(object) { // add navigation items for (let key in object.submenu) { let itemEl = buildNavigationItemEl(object.submenu[key]); + itemEl.classList.add("forward-btn"); listEl.appendChild(itemEl); } @@ -162,13 +170,14 @@ function updateNavigation(object) { command: "exit", }); + backItemEl.classList.add("back-btn"); listEl.appendChild(backItemEl); } const navigationEl = document.getElementById("navigation"); navigationEl.replaceChildren(listEl); - selectNavigationItem(1); + focusNavigationItem(1); // build navigation item function buildNavigationItemEl(item) { @@ -187,42 +196,41 @@ function updateNavigation(object) { itemEl.appendChild(commandEl); } - linkEl.addEventListener("click", onMouseClick); - return itemEl; } } -// ================== -// Mouse Navigation Handling +// General Navigation +// =================== +// navigate to path +function navigateToPath(path, pushState = true) { + // find object with path + let navObj = helpers.findNestedObject(navigation, "path", path); + if (!navObj) navObj = navigation.home; // fallback to home + updateNavigation(navObj); + + // State Object + var stateObj = { + pageTitle: navObj.label, + pageUrl: navObj.path, + }; -// on menu item mouseclick -function onMouseClick(event) { - event.preventDefault(); - const path = event.target.getAttribute("href"); - navigateTo(path); + // Push state to browser history + if (pushState) + history.pushState(stateObj, stateObj.pageTitle, stateObj.pageUrl); } -// =================== -// Keyboard Navigation Handling - -// on keydown event -function onKeyDown(event) { - event.preventDefault(); - if (event.code === "Tab" || event.code === "ArrowDown") - selectNextNavigationItem(); - else if (event.code === "ArrowUp") selectPreviousNavigationItem(); - else if (event.code === "Enter" || event.code === "ArrowRight") { - const currentItem = document.querySelector( - "#navigation > ul > li[data-selected] a" - ); - const path = currentItem.getAttribute("href"); - navigateTo(path); - } +// navigate to currently selected item +function navigateToSelected() { + const currentItem = document.querySelector( + "#navigation > ul > li[data-selected] a" + ); + const path = currentItem.getAttribute("href"); + navigateToPath(path); } -// select specific navigation item -function selectNavigationItem(n) { +// focus specific navigation item +function focusNavigationItem(n) { // unselect all items const navigationList = document.querySelectorAll("#navigation li"); for (let item of navigationList) delete item.dataset.selected; @@ -234,8 +242,8 @@ function selectNavigationItem(n) { navigationItem.dataset.selected = ""; } -// select next navigation item -function selectNextNavigationItem() { +// focus next navigation item +function focusNextNavigationItem() { // get currently selected element const currentItem = document.querySelector( "#navigation > ul > li[data-selected]" @@ -243,11 +251,11 @@ function selectNextNavigationItem() { if (currentItem.nextSibling) { delete currentItem.dataset.selected; currentItem.nextSibling.dataset.selected = ""; - } else selectNavigationItem(1); + } else focusNavigationItem(1); } -// select previous navigation item -function selectPreviousNavigationItem() { +// focus previous navigation item +function focusPreviousNavigationItem() { // get currently selected element let currentItem = document.querySelector( "#navigation > ul > li[data-selected]" @@ -257,8 +265,81 @@ function selectPreviousNavigationItem() { currentItem.previousSibling.dataset.selected = ""; } else { let childCount = document.querySelector("#navigation > ul").children.length; - selectNavigationItem(childCount); + focusNavigationItem(childCount); + } +} + +// moving down navigation tree +function moveUpNavigationLevel() { + const path = window.location.pathname; + let parentPath = path.slice(0, path.lastIndexOf("/")); + navigateToPath(parentPath); +} + +// moving down navigation tree +function moveDownNavigationLevel() { + const currentItem = document.querySelector( + "#navigation > ul > li[data-selected]" + ); + const isBackButton = currentItem.classList.contains("back-btn"); + if (!isBackButton) navigateToSelected(); +} + +// Add Mouse Click Controls +// ================== +function addMouseControls() { + const appEl = document.getElementById("app"); + appEl.addEventListener("click", onMouseClick); + + // on menu item mouseclick + function onMouseClick(event) { + event.preventDefault(); + const listItem = event.target.closest("li.link"); + if (listItem) { + const link = listItem.querySelector("a"); + const path = link.getAttribute("href"); + navigateToPath(path); + } } } +// Add Button Controls +// =================== +function addButtonControls() { + // add two button controls for menu + document.addEventListener("keydown", onKeyDown); + + // on keydown event + function onKeyDown(event) { + // prevent defaults + if ( + event.code === "Tab" || + event.code === "Enter" || + event.code === "Escape" || + event.code === "ArrowDown" || + event.code === "ArrowUp" || + event.code === "ArrowRight" + ) + event.preventDefault(); + + // browsing navigation items + if (event.code === "Tab" || event.code === "ArrowDown") + focusNextNavigationItem(); + else if (event.code === "ArrowUp") focusPreviousNavigationItem(); + else if (event.code === "Enter") navigateToSelected(); + else if (event.code === "ArrowRight") moveDownNavigationLevel(); + else if (event.code === "ArrowLeft" || event.code === "Escape") + moveUpNavigationLevel(); + } + + // listening for hardware buttons + // navigator.mediaSession.setActionHandler('volumeup', function() { + // // Handle volume up event + // }); + + // navigator.mediaSession.setActionHandler('volumedown', function() { + // // Handle volume down event + // }); +} + init(); diff --git a/src/styles/main.css b/src/styles/main.css index 3bf7590..e1cdecb 100644 --- a/src/styles/main.css +++ b/src/styles/main.css @@ -16,12 +16,19 @@ a { text-decoration: none; border-bottom: solid 0.1em transparent; } +.link { + position: relative; +} .link .command { font-size: 0.5em; line-height: 1em; font-style: italic; color: var(--color-grey-01); } +.link .command:before, +.link .command:after { + content: '"'; +} .link:hover a, .link[data-selected] a { border-color: var(--color-white); diff --git a/src/styles/main.less b/src/styles/main.less index afff171..40c5992 100644 --- a/src/styles/main.less +++ b/src/styles/main.less @@ -20,13 +20,19 @@ a { } .link { + position: relative; .command { font-size: 0.5em; line-height: 1em; font-style: italic; color: var(--color-grey-01); + + &:before, + &:after { + content: '"'; + } } - + &:hover, &[data-selected] { a { -- GitLab