implement basics of menu-bar

This commit is contained in:
CodedSakura 2026-07-09 14:04:23 +03:00
parent ea6813b415
commit ba3adc63c4
4 changed files with 173 additions and 14 deletions

View File

@ -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>

View File

@ -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;
}

View File

@ -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;
} }

View File

@ -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;
}