diff --git a/index.html b/index.html index a911e2d..77f747a 100644 --- a/index.html +++ b/index.html @@ -25,12 +25,15 @@
- +
+ +
+
- +
Select configuration file: @@ -62,11 +65,11 @@
- - + diff --git a/scripts/menu-bar.js b/scripts/menu-bar.js index 7a3128b..dc59be1 100644 --- a/scripts/menu-bar.js +++ b/scripts/menu-bar.js @@ -1 +1,108 @@ -export function initializeMenuBar() {} +import { positionAsTooltip } from "./tooltip.js"; + +let activeMenuBar = null; + +export function initializeMenuBar() { + document.querySelectorAll(".menu-bar").forEach(menuBar => { + menuBar.querySelectorAll("button").forEach(button => { + button.setAttribute("tabindex", "-1"); + button.replaceChildren(createAltSpan(button, button.innerText)); + button.addEventListener("click", e => { + menuBar.classList.remove("active"); + menuBar.querySelectorAll(".active") + .forEach(elem => elem.classList.remove("active")); + activeMenuBar = null; + }); + button.addEventListener("mouseenter", () => { + if (!activeMenuBar) return; + + button.parentElement.querySelectorAll(".active") + .forEach(elem => elem.classList.remove("active")); + }); + }); + menuBar.querySelectorAll("[data-section]").forEach(section => { + const button = document.createElement("button"); + button.setAttribute("tabindex", "-1"); + button.dataset.forSection = ""; + button.appendChild(createAltSpan(section, section.dataset.section)); + section.parentElement.insertBefore(button, section); + + section.addEventListener("click", e => e.stopPropagation()); + button.addEventListener("click", e => e.stopPropagation()); + + function updateActive() { + if (!activeMenuBar) return; + + button.parentElement.querySelectorAll(".active") + .forEach(elem => elem.classList.remove("active")); + button.classList.add("active"); + section.classList.add("active"); + positionAsTooltip(button, section, { + horizontal: section.parentElement === menuBar ? "left" : "right", + vertical: section.parentElement === menuBar ? "bottom" : "top", + }); + } + + if (section.parentElement === menuBar) { + button.addEventListener("click", () => { + if (section.classList.contains("active")) { + menuBar.classList.remove("active"); + menuBar.querySelectorAll(".active") + .forEach(elem => elem.classList.remove("active")); + activeMenuBar = null; + } else { + menuBar.querySelectorAll(".active") + .forEach(elem => elem.classList.remove("active")); + menuBar.classList.add("active"); + section.classList.add("active"); + window.addEventListener("click", handleGlobalClick); + activeMenuBar = menuBar; + updateActive(); + } + }); + } + button.addEventListener("mouseenter", updateActive); + }); + }); + + // TODO: keyboard navigation with alt and arrow keys +} + + +function handleGlobalClick() { + console.log("Global clicked"); + if (!activeMenuBar) return; + activeMenuBar.classList.remove("active"); + activeMenuBar.querySelectorAll(".active") + .forEach(section => section.classList.remove("active")); + activeMenuBar = null; + window.removeEventListener("click", handleGlobalClick); +} + +function createAltSpan(element, text) { + const altKey = element.dataset.alt; + + if (!altKey) { + const span = document.createElement("span"); + span.appendChild(document.createTextNode(text)); + return span; + } + + const [ , before, key, after ] = new RegExp(`^(.*)(${altKey})(.*)$`, "i").exec(text); + const span = document.createElement("span"); + + if (before) { + span.appendChild(document.createTextNode(before)); + } + + const underlineSpan = document.createElement("span"); + underlineSpan.innerText = key; + underlineSpan.classList.add("alt-key"); + span.appendChild(underlineSpan); + + if (after) { + span.appendChild(document.createTextNode(after)); + } + + return span; +} diff --git a/scripts/tooltip.js b/scripts/tooltip.js index eefa824..fd2c112 100644 --- a/scripts/tooltip.js +++ b/scripts/tooltip.js @@ -49,6 +49,14 @@ export function deleteTooltip(id) { delete tooltips[id]; } +export function positionAsTooltip(mainElement, tooltipElement, { horizontal = "left", vertical = "bottom" } = {}) { + const targetRect = mainElement.getBoundingClientRect(); + const domRect = tooltipElement.getBoundingClientRect(); + const windowWidth = window.innerWidth, windowHeight = window.innerHeight; + tooltipElement.style.left = `${Math.min(targetRect[horizontal], windowWidth - domRect.width)}px`; + tooltipElement.style.top = `${Math.min(targetRect[vertical], windowHeight - domRect.height)}px`; +} + function showTooltip() { if (suppressTooltips) return; if (!lastCoordinates) return; @@ -66,11 +74,7 @@ function showTooltip() { dom.style.top = `${targetRect.bottom}px`; target.appendChild(dom); - - const domRect = dom.getBoundingClientRect(); - const windowWidth = window.innerWidth, windowHeight = window.innerHeight; - dom.style.left = `${Math.min(targetRect.left, windowWidth - domRect.width)}px`; - dom.style.top = `${Math.min(targetRect.bottom, windowHeight - domRect.height)}px`; + positionAsTooltip(target, dom); activeTooltipTarget = target; } diff --git a/style.css b/style.css index ada788d..147985e 100644 --- a/style.css +++ b/style.css @@ -14,6 +14,7 @@ --default-size: 16px; --dark-bg: #990099; + --dark-bg-text: #fffe; --menu-border-color: #666; --resize-handle-color: #666; --resize-handle-width: 3px; @@ -45,7 +46,7 @@ body { display: grid; grid-template-columns: var(--left-menu-size, 15%) 1fr var(--right-menu-size, 10%); - grid-template-rows: 28px 1fr var(--bottom-menu-size, 15%); + grid-template-rows: min-content 1fr var(--bottom-menu-size, 15%); height: 100%; margin: 0; } @@ -54,6 +55,7 @@ body { grid-row: 1; background-color: var(--dark-bg); + color: var(--dark-bg-text); } #left-menu { grid-column: 1; @@ -229,3 +231,46 @@ input:not([type="checkbox"]):not([type="radio"]):not([type="file"]) { border: var(--tooltip-border-size, 1px) solid var(--tooltip-border-color, #fff); /*max-width: var(--tooltip-width, 200px);*/ } + +.menu-bar { + user-select: none; + cursor: default; +} +.menu-bar > [data-section] { + display: inline; +} +.menu-bar [data-section]:not(.active) { + display: none; +} +.menu-bar [data-section] { + position: fixed; + background: var(--menu-bg, #222); + color: var(--menu-fg, #fffe); + display: flex; + flex-direction: column; + z-index: 100; + box-shadow: var(--menu-shadow, 0 2px 4px rgba(0, 0, 0, 0.2)); +} +.menu-bar button { + all: initial; + font: inherit; + color: inherit; + padding: var(--menu-item-padding, 0.25em); + font-size: var(--menu-item-font-size, 0.8em); + cursor: default; +} +.menu-bar button:disabled { + color: var(--menu-item-disabled-color, #aaae); +} +.menu-bar [data-section] button.active, .menu-bar [data-section] button:hover:not(:disabled) { + background: var(--menu-bg-hover, #444); + color: var(--menu-fg-hover, #fffe); +} +.menu-bar [data-section] button[data-for-section]::after { + content: '>'; + float: right; + margin-left: 0.5em; +} +.menu-bar .alt-key { + text-decoration: underline; +}