implement basics of menu-bar
This commit is contained in:
parent
ea6813b415
commit
ba3adc63c4
17
index.html
17
index.html
|
|
@ -25,12 +25,15 @@
|
||||||
<div data-section="View" data-alt="v">
|
<div data-section="View" data-alt="v">
|
||||||
<div data-section="Test" data-alt="e">
|
<div data-section="Test" data-alt="e">
|
||||||
<div data-section="Test" data-alt="e">
|
<div data-section="Test" data-alt="e">
|
||||||
<button disabled>-</button>
|
<button disabled>1</button>
|
||||||
</div>
|
</div>
|
||||||
|
<button disabled>2</button>
|
||||||
|
<button>102983019283012</button>
|
||||||
</div>
|
</div>
|
||||||
|
<button disabled>3</button>
|
||||||
</div>
|
</div>
|
||||||
</nav>
|
</nav>
|
||||||
<nav id="left-menu" data-state="planning">
|
<aside id="left-menu" data-state="planning">
|
||||||
<span data-resize="left-menu"></span>
|
<span data-resize="left-menu"></span>
|
||||||
<section id="block-picker">
|
<section id="block-picker">
|
||||||
<label>
|
<label>
|
||||||
|
|
@ -43,7 +46,7 @@
|
||||||
</template>
|
</template>
|
||||||
</div>
|
</div>
|
||||||
</section>
|
</section>
|
||||||
</nav>
|
</aside>
|
||||||
<main>
|
<main>
|
||||||
<div data-state="initialization">
|
<div data-state="initialization">
|
||||||
Select configuration file:
|
Select configuration file:
|
||||||
|
|
@ -62,11 +65,11 @@
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</main>
|
</main>
|
||||||
<nav id="right-menu">
|
<aside id="right-menu">
|
||||||
<span data-resize="right-menu"></span>
|
<span data-resize="right-menu"></span>
|
||||||
</nav>
|
</aside>
|
||||||
<nav id="bottom-menu">
|
<footer id="bottom-menu">
|
||||||
<span data-resize="bottom-menu"></span>
|
<span data-resize="bottom-menu"></span>
|
||||||
</nav>
|
</footer>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
|
|
||||||
|
|
@ -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;
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -49,6 +49,14 @@ export function deleteTooltip(id) {
|
||||||
delete tooltips[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() {
|
function showTooltip() {
|
||||||
if (suppressTooltips) return;
|
if (suppressTooltips) return;
|
||||||
if (!lastCoordinates) return;
|
if (!lastCoordinates) return;
|
||||||
|
|
@ -66,11 +74,7 @@ function showTooltip() {
|
||||||
dom.style.top = `${targetRect.bottom}px`;
|
dom.style.top = `${targetRect.bottom}px`;
|
||||||
|
|
||||||
target.appendChild(dom);
|
target.appendChild(dom);
|
||||||
|
positionAsTooltip(target, 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`;
|
|
||||||
|
|
||||||
activeTooltipTarget = target;
|
activeTooltipTarget = target;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
47
style.css
47
style.css
|
|
@ -14,6 +14,7 @@
|
||||||
--default-size: 16px;
|
--default-size: 16px;
|
||||||
|
|
||||||
--dark-bg: #990099;
|
--dark-bg: #990099;
|
||||||
|
--dark-bg-text: #fffe;
|
||||||
--menu-border-color: #666;
|
--menu-border-color: #666;
|
||||||
--resize-handle-color: #666;
|
--resize-handle-color: #666;
|
||||||
--resize-handle-width: 3px;
|
--resize-handle-width: 3px;
|
||||||
|
|
@ -45,7 +46,7 @@
|
||||||
body {
|
body {
|
||||||
display: grid;
|
display: grid;
|
||||||
grid-template-columns: var(--left-menu-size, 15%) 1fr var(--right-menu-size, 10%);
|
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%;
|
height: 100%;
|
||||||
margin: 0;
|
margin: 0;
|
||||||
}
|
}
|
||||||
|
|
@ -54,6 +55,7 @@ body {
|
||||||
grid-row: 1;
|
grid-row: 1;
|
||||||
|
|
||||||
background-color: var(--dark-bg);
|
background-color: var(--dark-bg);
|
||||||
|
color: var(--dark-bg-text);
|
||||||
}
|
}
|
||||||
#left-menu {
|
#left-menu {
|
||||||
grid-column: 1;
|
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);
|
border: var(--tooltip-border-size, 1px) solid var(--tooltip-border-color, #fff);
|
||||||
/*max-width: var(--tooltip-width, 200px);*/
|
/*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;
|
||||||
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue