mirror of
https://github.com/myronblair/novacpx
synced 2026-06-30 17:50:41 -05:00
feat: collapsible sidebar nav with localStorage state (#48)
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01LP9Q4kfCAYAjJnsbHBrViZ
This commit is contained in:
@@ -139,10 +139,21 @@ input[type="date"]::-webkit-calendar-picker-indicator { filter: invert(1) opacit
|
|||||||
|
|
||||||
.sidebar-section { padding: .75rem 0; }
|
.sidebar-section { padding: .75rem 0; }
|
||||||
.sidebar-section-label {
|
.sidebar-section-label {
|
||||||
|
display: flex; align-items: center; justify-content: space-between;
|
||||||
font-size: .7rem; font-weight: 700; letter-spacing: .08em;
|
font-size: .7rem; font-weight: 700; letter-spacing: .08em;
|
||||||
text-transform: uppercase; color: var(--text-muted);
|
text-transform: uppercase; color: var(--text-muted);
|
||||||
padding: .25rem 1.25rem .5rem;
|
padding: .25rem 1.25rem .5rem;
|
||||||
|
cursor: pointer; user-select: none;
|
||||||
|
transition: color .15s;
|
||||||
}
|
}
|
||||||
|
.sidebar-section-label:hover { color: var(--text); }
|
||||||
|
.sidebar-section-label .nav-chevron {
|
||||||
|
font-style: normal; font-size: .65rem; opacity: .5;
|
||||||
|
transition: transform .2s ease;
|
||||||
|
flex-shrink: 0;
|
||||||
|
}
|
||||||
|
.sidebar-section.collapsed .nav-chevron { transform: rotate(-90deg); }
|
||||||
|
.sidebar-section.collapsed .sidebar-link { display: none; }
|
||||||
.sidebar-link {
|
.sidebar-link {
|
||||||
display: flex; align-items: center; gap: .75rem;
|
display: flex; align-items: center; gap: .75rem;
|
||||||
padding: .55rem 1.25rem; text-decoration: none;
|
padding: .55rem 1.25rem; text-decoration: none;
|
||||||
|
|||||||
@@ -139,10 +139,21 @@ input[type="date"]::-webkit-calendar-picker-indicator { filter: invert(1) opacit
|
|||||||
|
|
||||||
.sidebar-section { padding: .75rem 0; }
|
.sidebar-section { padding: .75rem 0; }
|
||||||
.sidebar-section-label {
|
.sidebar-section-label {
|
||||||
|
display: flex; align-items: center; justify-content: space-between;
|
||||||
font-size: .7rem; font-weight: 700; letter-spacing: .08em;
|
font-size: .7rem; font-weight: 700; letter-spacing: .08em;
|
||||||
text-transform: uppercase; color: var(--text-muted);
|
text-transform: uppercase; color: var(--text-muted);
|
||||||
padding: .25rem 1.25rem .5rem;
|
padding: .25rem 1.25rem .5rem;
|
||||||
|
cursor: pointer; user-select: none;
|
||||||
|
transition: color .15s;
|
||||||
}
|
}
|
||||||
|
.sidebar-section-label:hover { color: var(--text); }
|
||||||
|
.sidebar-section-label .nav-chevron {
|
||||||
|
font-style: normal; font-size: .65rem; opacity: .5;
|
||||||
|
transition: transform .2s ease;
|
||||||
|
flex-shrink: 0;
|
||||||
|
}
|
||||||
|
.sidebar-section.collapsed .nav-chevron { transform: rotate(-90deg); }
|
||||||
|
.sidebar-section.collapsed .sidebar-link { display: none; }
|
||||||
.sidebar-link {
|
.sidebar-link {
|
||||||
display: flex; align-items: center; gap: .75rem;
|
display: flex; align-items: center; gap: .75rem;
|
||||||
padding: .55rem 1.25rem; text-decoration: none;
|
padding: .55rem 1.25rem; text-decoration: none;
|
||||||
|
|||||||
@@ -221,6 +221,40 @@ window.Nova = (() => {
|
|||||||
return { api, toast, modal, confirm, initNav, loadPage, progressBar, bytes, relTime, badge, serviceDot, escHtml, loading, loadingDone };
|
return { api, toast, modal, confirm, initNav, loadPage, progressBar, bytes, relTime, badge, serviceDot, escHtml, loading, loadingDone };
|
||||||
})();
|
})();
|
||||||
|
|
||||||
|
// #48 Collapsible sidebar nav — shared across all panels
|
||||||
|
document.addEventListener('DOMContentLoaded', () => {
|
||||||
|
const STORE = 'ncpx_nav_collapsed';
|
||||||
|
const state = JSON.parse(localStorage.getItem(STORE) || '{}');
|
||||||
|
|
||||||
|
document.querySelectorAll('.sidebar-section').forEach(section => {
|
||||||
|
const label = section.querySelector('.sidebar-section-label');
|
||||||
|
if (!label) return;
|
||||||
|
|
||||||
|
// Add chevron icon
|
||||||
|
const chevron = document.createElement('i');
|
||||||
|
chevron.className = 'nav-chevron';
|
||||||
|
chevron.textContent = '▼';
|
||||||
|
label.appendChild(chevron);
|
||||||
|
|
||||||
|
const key = label.textContent.replace('▼','').trim();
|
||||||
|
|
||||||
|
// Restore saved state — default Overview open, others open
|
||||||
|
if (state[key]) section.classList.add('collapsed');
|
||||||
|
|
||||||
|
// Keep active page's section always open
|
||||||
|
const hasActive = section.querySelector('.sidebar-link.active');
|
||||||
|
if (hasActive) section.classList.remove('collapsed');
|
||||||
|
|
||||||
|
label.addEventListener('click', () => {
|
||||||
|
// Don't collapse if it contains the active link
|
||||||
|
if (section.querySelector('.sidebar-link.active')) return;
|
||||||
|
section.classList.toggle('collapsed');
|
||||||
|
state[key] = section.classList.contains('collapsed');
|
||||||
|
localStorage.setItem(STORE, JSON.stringify(state));
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
// #26 Mobile sidebar toggle — shared across all panels
|
// #26 Mobile sidebar toggle — shared across all panels
|
||||||
document.addEventListener('DOMContentLoaded', () => {
|
document.addEventListener('DOMContentLoaded', () => {
|
||||||
const toggle = document.getElementById('sidebar-toggle');
|
const toggle = document.getElementById('sidebar-toggle');
|
||||||
|
|||||||
Reference in New Issue
Block a user