Files
unraid-glass/sidebar.js
Kaloyan Danchev a2ca0730c5 Initial commit: visionOS glassmorphism theme for Unraid 7.2.3
- style.css: Full CSS theme with frosted glass cards, sidebar styling,
  dashboard individual glass cards, search overlay, FA icon replacements
- sidebar.js: Collapsible sidebar toggle with Unraid logo, search popup
  (moved to body to escape backdrop-filter containing block), Cmd+K shortcut

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-07 23:20:30 +02:00

144 lines
4.5 KiB
JavaScript

(function() {
'use strict';
var KEY = 'unraid-sidebar-expanded';
// Restore state immediately (before DOM ready to avoid flash)
if (localStorage.getItem(KEY) === '1') {
document.documentElement.classList.add('sidebar-expanded');
}
function init() {
var menu = document.getElementById('menu');
if (!menu) return;
// --- Unraid logo (absolute positioned at very top of sidebar) ---
if (!document.getElementById('sidebar-logo')) {
var logo = document.createElement('img');
logo.id = 'sidebar-logo';
logo.src = 'https://cdn.jsdelivr.net/gh/selfhst/icons@main/svg/unraid.svg';
logo.alt = '';
menu.appendChild(logo);
}
// --- Toggle button with chevron (below logo) ---
if (!document.getElementById('sidebar-toggle-btn')) {
var btn = document.createElement('button');
btn.id = 'sidebar-toggle-btn';
btn.type = 'button';
btn.title = 'Toggle Sidebar';
btn.innerHTML = '<svg viewBox="0 0 24 24" width="14" height="14" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round"><polyline points="9 18 15 12 9 6"></polyline></svg>';
btn.addEventListener('click', function(e) {
e.preventDefault();
e.stopPropagation();
var expanded = document.documentElement.classList.toggle('sidebar-expanded');
localStorage.setItem(KEY, expanded ? '1' : '0');
});
menu.appendChild(btn);
}
// --- Search overlay (delayed to wait for search plugin init) ---
setTimeout(initSearchOverlay, 800);
}
function initSearchOverlay() {
if (!window.jQuery) return;
var searchItem = document.querySelector('.nav-item.gui_search');
if (!searchItem) return;
// Remove default hover-based open/close handlers
$(searchItem).off('mouseenter mouseleave');
var link = searchItem.querySelector('a');
if (!link) return;
// Replace hover with click
link.addEventListener('click', function(e) {
e.preventDefault();
e.stopPropagation();
toggleSearch();
});
// Add Cmd+K / Ctrl+K keyboard shortcut (sidebar theme doesn't have one by default)
document.addEventListener('keydown', function(e) {
var isMac = navigator.platform.indexOf('Mac') > -1;
if ((isMac ? e.metaKey : e.ctrlKey) && (e.key === 'k' || e.keyCode === 75)) {
if ($('[role="modal"]:visible').length) return;
e.preventDefault();
toggleSearch();
}
});
}
function toggleSearch() {
var existing = document.getElementById('guiSearchBoxSpan');
if (existing) {
closeSearch();
return;
}
// Call original gui_search to create the input
if (typeof gui_search === 'function') {
gui_search();
}
// Move search box to <body> — backdrop-filter on #menu creates a new
// containing block, so position:fixed inside it is relative to #menu
// and gets clipped by overflow:hidden. Moving to body fixes this.
var searchSpan = document.getElementById('guiSearchBoxSpan');
if (searchSpan) {
document.body.appendChild(searchSpan);
}
// Create backdrop overlay
if (!document.getElementById('search-backdrop')) {
var backdrop = document.createElement('div');
backdrop.id = 'search-backdrop';
backdrop.addEventListener('click', function() {
closeSearch();
});
document.body.appendChild(backdrop);
}
// Override blur/keydown behavior on the input
var input = document.getElementById('guiSearchBox');
if (input && window.jQuery) {
$(input).off('blur keydown');
input.addEventListener('blur', function() {
setTimeout(function() {
var span = document.getElementById('guiSearchBoxSpan');
if (span && !span.contains(document.activeElement)) {
closeSearch();
}
}, 300);
});
input.addEventListener('keydown', function(e) {
if (e.key === 'Escape' || e.keyCode === 27) {
e.preventDefault();
closeSearch();
}
});
input.focus();
}
}
function closeSearch() {
// Remove the search span directly (works whether moved to body or not)
var span = document.getElementById('guiSearchBoxSpan');
if (span) span.remove();
var backdrop = document.getElementById('search-backdrop');
if (backdrop) backdrop.remove();
}
if (document.readyState === 'loading') {
document.addEventListener('DOMContentLoaded', init);
} else {
init();
}
})();