Added support for Streamdeck Pedal and updated UI to better fit the Packed UI style
This commit is contained in:
202
pirp/public/assets/command-palette.js
Normal file
202
pirp/public/assets/command-palette.js
Normal file
@@ -0,0 +1,202 @@
|
||||
// PIRP Command Palette (Ctrl+K / Cmd+K)
|
||||
(function() {
|
||||
'use strict';
|
||||
|
||||
var overlay = null;
|
||||
var input = null;
|
||||
var resultsList = null;
|
||||
var debounceTimer = null;
|
||||
var activeIndex = -1;
|
||||
var currentResults = [];
|
||||
|
||||
var typeLabels = {
|
||||
invoice: 'Rechnungen',
|
||||
customer: 'Kunden',
|
||||
expense: 'Ausgaben',
|
||||
journal: 'Journal'
|
||||
};
|
||||
|
||||
var typeIcons = {
|
||||
invoice: '\u25B8',
|
||||
customer: '\u25CF',
|
||||
expense: '\u25A0',
|
||||
journal: '\u25C6'
|
||||
};
|
||||
|
||||
function create() {
|
||||
overlay = document.createElement('div');
|
||||
overlay.className = 'cmd-palette-overlay hidden';
|
||||
overlay.addEventListener('click', function(e) {
|
||||
if (e.target === overlay) close();
|
||||
});
|
||||
|
||||
var modal = document.createElement('div');
|
||||
modal.className = 'cmd-palette';
|
||||
|
||||
input = document.createElement('input');
|
||||
input.type = 'text';
|
||||
input.placeholder = 'Suche nach Rechnungen, Kunden, Ausgaben, Journal...';
|
||||
input.addEventListener('input', function() {
|
||||
clearTimeout(debounceTimer);
|
||||
debounceTimer = setTimeout(doSearch, 200);
|
||||
});
|
||||
input.addEventListener('keydown', handleKeydown);
|
||||
|
||||
resultsList = document.createElement('div');
|
||||
resultsList.className = 'cmd-palette-results';
|
||||
|
||||
modal.appendChild(input);
|
||||
modal.appendChild(resultsList);
|
||||
overlay.appendChild(modal);
|
||||
document.body.appendChild(overlay);
|
||||
}
|
||||
|
||||
function open() {
|
||||
if (!overlay) create();
|
||||
overlay.classList.remove('hidden');
|
||||
input.value = '';
|
||||
resultsList.innerHTML = '';
|
||||
activeIndex = -1;
|
||||
currentResults = [];
|
||||
setTimeout(function() { input.focus(); }, 10);
|
||||
}
|
||||
|
||||
function close() {
|
||||
if (overlay) overlay.classList.add('hidden');
|
||||
}
|
||||
|
||||
function isOpen() {
|
||||
return overlay && !overlay.classList.contains('hidden');
|
||||
}
|
||||
|
||||
function doSearch() {
|
||||
var q = input.value.trim();
|
||||
if (q.length < 2) {
|
||||
resultsList.innerHTML = '';
|
||||
activeIndex = -1;
|
||||
currentResults = [];
|
||||
return;
|
||||
}
|
||||
|
||||
fetch('search_api.php?q=' + encodeURIComponent(q))
|
||||
.then(function(r) { return r.json(); })
|
||||
.then(function(data) {
|
||||
if (!data.ok) return;
|
||||
currentResults = data.results;
|
||||
activeIndex = -1;
|
||||
renderResults(data.results);
|
||||
})
|
||||
.catch(function() {});
|
||||
}
|
||||
|
||||
function renderResults(results) {
|
||||
resultsList.innerHTML = '';
|
||||
if (!results.length) {
|
||||
var empty = document.createElement('div');
|
||||
empty.className = 'cmd-palette-empty';
|
||||
empty.textContent = 'Keine Ergebnisse';
|
||||
resultsList.appendChild(empty);
|
||||
return;
|
||||
}
|
||||
|
||||
// Group by type
|
||||
var groups = {};
|
||||
var order = [];
|
||||
results.forEach(function(r) {
|
||||
if (!groups[r.type]) {
|
||||
groups[r.type] = [];
|
||||
order.push(r.type);
|
||||
}
|
||||
groups[r.type].push(r);
|
||||
});
|
||||
|
||||
var idx = 0;
|
||||
order.forEach(function(type) {
|
||||
var header = document.createElement('div');
|
||||
header.className = 'cmd-palette-group';
|
||||
header.textContent = typeLabels[type] || type;
|
||||
resultsList.appendChild(header);
|
||||
|
||||
groups[type].forEach(function(r) {
|
||||
var item = document.createElement('a');
|
||||
item.className = 'cmd-palette-item';
|
||||
item.href = r.url;
|
||||
item.setAttribute('data-idx', idx);
|
||||
|
||||
var icon = document.createElement('span');
|
||||
icon.className = 'cmd-palette-icon';
|
||||
icon.textContent = typeIcons[r.type] || '\u25B8';
|
||||
item.appendChild(icon);
|
||||
|
||||
var text = document.createElement('span');
|
||||
text.className = 'cmd-palette-text';
|
||||
|
||||
var title = document.createElement('span');
|
||||
title.className = 'cmd-palette-title';
|
||||
title.textContent = r.title;
|
||||
text.appendChild(title);
|
||||
|
||||
if (r.subtitle) {
|
||||
var sub = document.createElement('span');
|
||||
sub.className = 'cmd-palette-subtitle';
|
||||
sub.textContent = r.subtitle;
|
||||
text.appendChild(sub);
|
||||
}
|
||||
|
||||
item.appendChild(text);
|
||||
item.addEventListener('mouseenter', function() {
|
||||
setActive(parseInt(item.getAttribute('data-idx')));
|
||||
});
|
||||
resultsList.appendChild(item);
|
||||
idx++;
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
function setActive(idx) {
|
||||
var items = resultsList.querySelectorAll('.cmd-palette-item');
|
||||
items.forEach(function(el) { el.classList.remove('active'); });
|
||||
activeIndex = idx;
|
||||
if (idx >= 0 && idx < items.length) {
|
||||
items[idx].classList.add('active');
|
||||
items[idx].scrollIntoView({ block: 'nearest' });
|
||||
}
|
||||
}
|
||||
|
||||
function handleKeydown(e) {
|
||||
var items = resultsList.querySelectorAll('.cmd-palette-item');
|
||||
var count = items.length;
|
||||
|
||||
if (e.key === 'ArrowDown') {
|
||||
e.preventDefault();
|
||||
setActive(activeIndex < count - 1 ? activeIndex + 1 : 0);
|
||||
} else if (e.key === 'ArrowUp') {
|
||||
e.preventDefault();
|
||||
setActive(activeIndex > 0 ? activeIndex - 1 : count - 1);
|
||||
} else if (e.key === 'Enter') {
|
||||
e.preventDefault();
|
||||
if (activeIndex >= 0 && activeIndex < count) {
|
||||
window.location = items[activeIndex].href;
|
||||
}
|
||||
} else if (e.key === 'Escape') {
|
||||
e.preventDefault();
|
||||
close();
|
||||
}
|
||||
}
|
||||
|
||||
// Global keyboard listener
|
||||
document.addEventListener('keydown', function(e) {
|
||||
if ((e.ctrlKey || e.metaKey) && e.key === 'k') {
|
||||
e.preventDefault();
|
||||
if (isOpen()) {
|
||||
close();
|
||||
} else {
|
||||
open();
|
||||
}
|
||||
}
|
||||
if (e.key === 'Escape' && isOpen()) {
|
||||
e.preventDefault();
|
||||
close();
|
||||
}
|
||||
});
|
||||
})();
|
||||
Reference in New Issue
Block a user