diff --git a/server/src/wled_controller/static/js/app.js b/server/src/wled_controller/static/js/app.js index 51e29b8..2f633b4 100644 --- a/server/src/wled_controller/static/js/app.js +++ b/server/src/wled_controller/static/js/app.js @@ -37,6 +37,7 @@ import { import { loadDashboard, startDashboardWS, stopDashboardWS, dashboardToggleProfile, dashboardStartTarget, dashboardStopTarget, dashboardStopAll, + toggleDashboardSection, } from './features/dashboard.js'; import { loadPictureSources, switchStreamTab, @@ -151,6 +152,7 @@ Object.assign(window, { dashboardStartTarget, dashboardStopTarget, dashboardStopAll, + toggleDashboardSection, // streams / capture templates / PP templates loadPictureSources, diff --git a/server/src/wled_controller/static/js/features/dashboard.js b/server/src/wled_controller/static/js/features/dashboard.js index 956f4fc..c7244aa 100644 --- a/server/src/wled_controller/static/js/features/dashboard.js +++ b/server/src/wled_controller/static/js/features/dashboard.js @@ -8,6 +8,48 @@ import { t } from '../core/i18n.js'; import { escapeHtml, handle401Error } from '../core/api.js'; import { showToast } from '../core/ui.js'; +const DASHBOARD_COLLAPSED_KEY = 'dashboard_collapsed'; + +function _getCollapsedSections() { + try { return JSON.parse(localStorage.getItem(DASHBOARD_COLLAPSED_KEY)) || {}; } + catch { return {}; } +} + +export function toggleDashboardSection(sectionKey) { + const collapsed = _getCollapsedSections(); + collapsed[sectionKey] = !collapsed[sectionKey]; + localStorage.setItem(DASHBOARD_COLLAPSED_KEY, JSON.stringify(collapsed)); + const header = document.querySelector(`[data-dashboard-section="${sectionKey}"]`); + if (!header) return; + const content = header.nextElementSibling; + const chevron = header.querySelector('.dashboard-section-chevron'); + if (collapsed[sectionKey]) { + content.style.display = 'none'; + chevron.textContent = '\u25B6'; + } else { + content.style.display = ''; + chevron.textContent = '\u25BC'; + } +} + +function _sectionHeader(sectionKey, label, count, extraHtml = '') { + const collapsed = _getCollapsedSections(); + const isCollapsed = !!collapsed[sectionKey]; + const chevron = isCollapsed ? '\u25B6' : '\u25BC'; + return `
+ ${chevron} + ${label} + ${count} + ${extraHtml} +
`; +} + +function _sectionContent(sectionKey, itemsHtml) { + const collapsed = _getCollapsedSections(); + const isCollapsed = !!collapsed[sectionKey]; + return ``; +} + function formatUptime(seconds) { if (!seconds || seconds <= 0) return '-'; const h = Math.floor(seconds / 3600); @@ -67,35 +109,30 @@ export async function loadDashboard() { if (profiles.length > 0) { const activeProfiles = profiles.filter(p => p.is_active); const inactiveProfiles = profiles.filter(p => !p.is_active); + const profileItems = [...activeProfiles, ...inactiveProfiles].map(p => renderDashboardProfile(p)).join(''); html += `
-
- ${t('dashboard.section.profiles')} - ${profiles.length} -
- ${activeProfiles.map(p => renderDashboardProfile(p)).join('')} - ${inactiveProfiles.map(p => renderDashboardProfile(p)).join('')} + ${_sectionHeader('profiles', t('dashboard.section.profiles'), profiles.length)} + ${_sectionContent('profiles', profileItems)}
`; } if (running.length > 0) { + const stopAllBtn = ``; + const runningItems = running.map(target => renderDashboardTarget(target, true, devicesMap)).join(''); + html += `
-
- ${t('dashboard.section.running')} - ${running.length} - -
- ${running.map(target => renderDashboardTarget(target, true, devicesMap)).join('')} + ${_sectionHeader('running', t('dashboard.section.running'), running.length, stopAllBtn)} + ${_sectionContent('running', runningItems)}
`; } if (stopped.length > 0) { + const stoppedItems = stopped.map(target => renderDashboardTarget(target, false, devicesMap)).join(''); + html += `
-
- ${t('dashboard.section.stopped')} - ${stopped.length} -
- ${stopped.map(target => renderDashboardTarget(target, false, devicesMap)).join('')} + ${_sectionHeader('stopped', t('dashboard.section.stopped'), stopped.length)} + ${_sectionContent('stopped', stoppedItems)}
`; } diff --git a/server/src/wled_controller/static/style.css b/server/src/wled_controller/static/style.css index 4c41f44..f7d30cb 100644 --- a/server/src/wled_controller/static/style.css +++ b/server/src/wled_controller/static/style.css @@ -3256,6 +3256,15 @@ input:-webkit-autofill:focus { gap: 6px; text-transform: uppercase; letter-spacing: 0.5px; + cursor: pointer; + user-select: none; +} + +.dashboard-section-chevron { + font-size: 0.6rem; + color: var(--text-secondary); + width: 10px; + display: inline-block; } .dashboard-section-count {