62 lines
2.5 KiB
JavaScript
62 lines
2.5 KiB
JavaScript
/*!
|
|
* Tectum
|
|
* Copyright © 2026 Machiel Heinen.
|
|
* Alle rechten voorbehouden. Gebruik uitsluitend met schriftelijke toestemming.
|
|
* Zie LICENTIE.md.
|
|
*/
|
|
// Menubalk: menu = { label, items: [{ label, icon, keys, run, enabled(), checked() } | { sep: true } | { head }] }.
|
|
// Beschikbaarheid wordt bij het openen bepaald; is een menu open, dan opent hover het buurmenu.
|
|
|
|
window.Menubar = (() => {
|
|
let root = null, menus = [], openIdx = -1;
|
|
|
|
function itemHtml(it, mi, ii) {
|
|
if (it.sep) return '<div class="mb-sep"></div>';
|
|
if (it.head) return `<div class="mb-head">${it.head}</div>`;
|
|
const off = it.enabled && !it.enabled();
|
|
const on = it.checked && it.checked();
|
|
return `<button type="button" class="mb-item${on ? ' checked' : ''}" data-mi="${mi}" data-ii="${ii}"${off ? ' disabled' : ''}>
|
|
<span class="mb-ico">${on ? '<i class="bi bi-check2"></i>' : it.icon ? `<i class="bi bi-${it.icon}"></i>` : ''}</span>
|
|
<span class="mb-label">${it.label}</span><span class="mb-keys">${it.keys || ''}</span></button>`;
|
|
}
|
|
|
|
function close() {
|
|
if (openIdx < 0) return false;
|
|
openIdx = -1;
|
|
root.querySelectorAll('.mb-menu').forEach(m => m.classList.remove('open'));
|
|
root.querySelectorAll('.mb-drop').forEach(d => { d.innerHTML = ''; });
|
|
return true;
|
|
}
|
|
|
|
function openMenu(i) {
|
|
close();
|
|
openIdx = i;
|
|
const wrap = root.querySelectorAll('.mb-menu')[i];
|
|
wrap.classList.add('open');
|
|
wrap.querySelector('.mb-drop').innerHTML = menus[i].items.map((it, ii) => itemHtml(it, i, ii)).join('');
|
|
}
|
|
|
|
function init(el, list) {
|
|
root = el;
|
|
menus = list;
|
|
el.innerHTML = menus.map((m, i) => `<div class="mb-menu"><button type="button" class="mb-top" data-menu="${i}">${m.label}</button><div class="mb-drop" role="menu"></div></div>`).join('');
|
|
el.addEventListener('click', (e) => {
|
|
e.stopPropagation();
|
|
const top = e.target.closest('[data-menu]');
|
|
if (top) { const i = Number(top.dataset.menu); if (openIdx === i) close(); else openMenu(i); return; }
|
|
const item = e.target.closest('.mb-item');
|
|
if (!item || item.disabled) return;
|
|
const it = menus[Number(item.dataset.mi)].items[Number(item.dataset.ii)];
|
|
close();
|
|
it.run();
|
|
});
|
|
el.addEventListener('mouseover', (e) => {
|
|
const top = e.target.closest('[data-menu]');
|
|
if (top && openIdx >= 0 && Number(top.dataset.menu) !== openIdx) openMenu(Number(top.dataset.menu));
|
|
});
|
|
document.addEventListener('click', close);
|
|
}
|
|
|
|
return { init, close };
|
|
})();
|