81 lines
3.3 KiB
JavaScript
81 lines
3.3 KiB
JavaScript
// Zeichnet das Tray-Menü aus dem Zustand, den der Hauptprozess schickt.
|
|
// Ohne Electron (Seite direkt im Browser geöffnet) greift ein Stub, damit sich die Optik prüfen lässt.
|
|
(function () {
|
|
const api = window.trayApi || {
|
|
onState: () => {},
|
|
action: (id) => console.log('action', id),
|
|
resize: () => {},
|
|
close: () => {}
|
|
};
|
|
|
|
const menu = document.getElementById('menu');
|
|
let buttons = [];
|
|
let activeIndex = -1;
|
|
|
|
function setActive(index) {
|
|
activeIndex = index;
|
|
buttons.forEach((b, i) => b.classList.toggle('active', i === index));
|
|
}
|
|
|
|
function render(state) {
|
|
document.body.classList.toggle('light', state && state.dark === false);
|
|
menu.textContent = '';
|
|
buttons = [];
|
|
activeIndex = -1;
|
|
|
|
for (const item of (state && state.items) || []) {
|
|
if (item.type === 'separator') {
|
|
const sep = document.createElement('div');
|
|
sep.className = 'sep';
|
|
menu.appendChild(sep);
|
|
continue;
|
|
}
|
|
if (item.id === 'header' || item.id === 'status' || item.id === 'queue') {
|
|
const line = document.createElement('div');
|
|
line.className = item.id === 'header' ? 'header' : 'status';
|
|
line.textContent = item.label;
|
|
menu.appendChild(line);
|
|
continue;
|
|
}
|
|
const button = document.createElement('button');
|
|
button.className = 'item';
|
|
button.disabled = item.enabled === false;
|
|
button.setAttribute('role', item.type === 'checkbox' ? 'menuitemcheckbox' : 'menuitem');
|
|
if (item.type === 'checkbox') button.setAttribute('aria-checked', String(!!item.checked));
|
|
|
|
const check = document.createElement('span');
|
|
check.className = 'check';
|
|
check.textContent = item.type === 'checkbox' && item.checked ? '✓' : '';
|
|
const label = document.createElement('span');
|
|
label.textContent = item.label;
|
|
button.append(check, label);
|
|
|
|
button.addEventListener('click', () => api.action(item.id));
|
|
button.addEventListener('mouseenter', () => { if (!button.disabled) setActive(buttons.indexOf(button)); });
|
|
button.addEventListener('mouseleave', () => setActive(-1));
|
|
menu.appendChild(button);
|
|
buttons.push(button);
|
|
}
|
|
|
|
api.resize(Math.ceil(document.body.scrollHeight));
|
|
}
|
|
|
|
function move(step) {
|
|
const enabled = buttons.map((b, i) => (b.disabled ? -1 : i)).filter((i) => i >= 0);
|
|
if (enabled.length === 0) return;
|
|
const pos = enabled.indexOf(activeIndex);
|
|
const next = pos < 0 ? (step > 0 ? 0 : enabled.length - 1) : (pos + step + enabled.length) % enabled.length;
|
|
setActive(enabled[next]);
|
|
}
|
|
|
|
document.addEventListener('keydown', (e) => {
|
|
if (e.key === 'Escape') api.close();
|
|
else if (e.key === 'ArrowDown') { e.preventDefault(); move(1); }
|
|
else if (e.key === 'ArrowUp') { e.preventDefault(); move(-1); }
|
|
else if ((e.key === 'Enter' || e.key === ' ') && buttons[activeIndex]) { e.preventDefault(); buttons[activeIndex].click(); }
|
|
});
|
|
|
|
window.__renderTray = render;
|
|
api.onState(render);
|
|
})();
|