Dashboard polish: pie charts, CPU bars, lock icon, header shift, toggle switches

- Add pie chart recoloring (vibrant theme colors for System donuts)
- Add system legend color matching for donut chart legends
- Add lock button state toggling (locked/unlocked icon via MutationObserver)
- Add Tailscale light SVG icon replacement in sidebar
- Fix horizontal scrollbar with overflow-x: hidden
- Fix header not shifting with sidebar (add #header to margin-left transition)
- Fix dashboard double-blur (disable stacking backdrop-filter on tables)
- Simplify toggle button style (transparent bg, hover-only highlight)
- Style CPU bars with color-coded gradients (green/orange/red)
- Style donut charts with thinner ring and opaque center
- Style toggle switches with glass appearance
- Style card header control icons (transparent, white)
- Force white text in Docker dashboard view

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
Kaloyan Danchev
2026-02-24 20:19:08 +02:00
parent dbabcdaf37
commit a898c8fea2
2 changed files with 325 additions and 15 deletions

View File

@@ -38,10 +38,34 @@
menu.appendChild(btn);
}
// --- Lock button icon state ---
initLockButton();
// --- Search overlay (delayed to wait for search plugin init) ---
setTimeout(initSearchOverlay, 800);
}
function initLockButton() {
var lockItem = document.querySelector('.nav-item.LockButton');
if (!lockItem) return;
var lockLink = lockItem.querySelector('a');
if (!lockLink) return;
function updateLockState() {
var title = lockLink.getAttribute('title') || '';
if (title.toLowerCase().indexOf('unlock') > -1) {
lockItem.classList.add('is-locked');
} else {
lockItem.classList.remove('is-locked');
}
}
updateLockState();
var observer = new MutationObserver(updateLockState);
observer.observe(lockLink, { attributes: true, attributeFilter: ['title'] });
}
function initSearchOverlay() {
if (!window.jQuery) return;
@@ -319,12 +343,26 @@
observer.observe(target, { childList: true, subtree: true });
}
// Replace Tailscale sidebar icon with light SVG
function replaceTailscaleIcon() {
var imgs = document.querySelectorAll('img[alt="Tailscale"]');
imgs.forEach(function(img) {
var url = CDN_SVG + '/tailscale-light.svg';
if (img.getAttribute('src') !== url) {
img.src = url;
img.removeAttribute('onerror');
}
});
}
// Initialize when DOM is ready
function initIcons() {
// Initial replacement
setTimeout(replaceIcons, 500);
setTimeout(replaceTailscaleIcon, 500);
// Second pass after FolderView finishes its DOM work
setTimeout(replaceIcons, 2000);
setTimeout(replaceTailscaleIcon, 2000);
// Start watching for future changes
startObserver();
}
@@ -335,3 +373,136 @@
initIcons();
}
})();
// =============================================================
// Pie Chart Recoloring — vibrant theme colors for System donuts
// =============================================================
(function() {
'use strict';
// Unraid's muted defaults → vibrant theme replacements
var COLOR_MAP = {
'rgb(68,119,170)': '#7EB8DA', // accent blue (used)
'rgb(204,204,204)': 'rgba(255,255,255,0.10)', // free → subtle
'rgb(34,136,51)': '#4ade80', // green
'rgb(170,68,153)': '#f97316', // purple → orange
'rgb(238,102,119)': '#ef4444', // red
'rgb(0,153,136)': '#22d3ee', // teal → cyan
'rgb(221,204,119)': '#facc15', // yellow
'rgb(136,34,85)': '#f87171', // dark red → light red
'rgb(17,119,51)': '#22c55e', // dark green → bright green
'rgb(51,34,136)': '#818cf8', // dark purple → indigo
'rgb(102,153,204)': '#93c5fd' // light blue
};
function recolorPie(pie) {
var style = pie.getAttribute('style');
if (!style || style.indexOf('conic-gradient') === -1) return;
var newStyle = style;
var keys = Object.keys(COLOR_MAP);
for (var i = 0; i < keys.length; i++) {
// Replace with global flag via split/join
newStyle = newStyle.split(keys[i]).join(COLOR_MAP[keys[i]]);
}
if (newStyle !== style) {
pie.setAttribute('style', newStyle);
}
}
function recolorAll() {
var pies = document.querySelectorAll('div.pie');
pies.forEach(recolorPie);
}
function startObserver() {
var target = document.getElementById('displaybox') || document.body;
var observer = new MutationObserver(function(mutations) {
for (var i = 0; i < mutations.length; i++) {
var m = mutations[i];
if (m.type === 'attributes' && m.attributeName === 'style') {
var el = m.target;
if (el.classList && el.classList.contains('pie')) {
recolorPie(el);
}
}
if (m.type === 'childList' && m.addedNodes.length > 0) {
recolorAll();
}
}
});
observer.observe(target, {
childList: true,
subtree: true,
attributes: true,
attributeFilter: ['style']
});
}
function init() {
setTimeout(recolorAll, 600);
setTimeout(recolorAll, 2000);
startObserver();
}
if (document.readyState === 'loading') {
document.addEventListener('DOMContentLoaded', init);
} else {
init();
}
})();
// =============================================================
// System Legend Colors — match pie segment colors in legends
// =============================================================
(function() {
'use strict';
// Unraid default legend colors → vibrant theme colors
var LEGEND_MAP = {
'rgb(68, 119, 170)': '#7EB8DA',
'rgb(204, 204, 204)': 'rgba(255,255,255,0.35)',
'rgb(34, 136, 51)': '#4ade80',
'rgb(170, 68, 153)': '#f97316',
'rgb(238, 102, 119)': '#ef4444',
'rgb(0, 153, 136)': '#22d3ee',
'rgb(221, 204, 119)': '#facc15',
'rgb(136, 34, 85)': '#f87171',
'rgb(17, 119, 51)': '#22c55e',
'rgb(51, 34, 136)': '#818cf8',
'rgb(102, 153, 204)': '#93c5fd'
};
function recolorLegends() {
var icons = document.querySelectorAll('#dynamic i.fa-circle');
icons.forEach(function(icon) {
var currentColor = icon.style.color;
if (!currentColor) return;
var mapped = LEGEND_MAP[currentColor];
if (mapped) {
icon.style.setProperty('color', mapped, 'important');
}
});
}
function startObserver() {
var target = document.getElementById('dynamic') || document.getElementById('displaybox') || document.body;
var observer = new MutationObserver(function() {
recolorLegends();
});
observer.observe(target, { childList: true, subtree: true });
}
function init() {
setTimeout(recolorLegends, 700);
setTimeout(recolorLegends, 2500);
startObserver();
}
if (document.readyState === 'loading') {
document.addEventListener('DOMContentLoaded', init);
} else {
init();
}
})();