feat: Enhance sidebar functionality with dynamic width adjustments and improved toggle interactions

This commit is contained in:
Matt Batchelder
2026-02-04 16:22:51 -05:00
parent f392e5d016
commit d8f8c0f916
4 changed files with 308 additions and 92 deletions

View File

@@ -50,6 +50,8 @@
sidebar.classList.toggle('collapsed');
body.classList.toggle('ots-sidebar-collapsed', nowCollapsed);
localStorage.setItem(STORAGE_KEYS.sidebarCollapsed, nowCollapsed ? 'true' : 'false');
// Update measured sidebar width when collapsed state changes
updateSidebarWidth();
});
}
@@ -69,6 +71,20 @@
});
}
/**
* Measure sidebar width and set CSS variable for layout
*/
function updateSidebarWidth() {
const sidebar = document.querySelector('.ots-sidebar');
if (!sidebar) return;
// If collapsed, use the known collapsed width; otherwise use measured width
const collapsed = sidebar.classList.contains('collapsed');
const base = collapsed ? 88 : sidebar.offsetWidth || sidebar.getBoundingClientRect().width || 240;
const padding = 5; // user requested ~5px padding
const value = Math.max(88, Math.round(base + padding));
document.documentElement.style.setProperty('--ots-sidebar-width', `${value}px`);
}
/**
* Initialize sidebar section collapse/expand functionality
*/
@@ -76,6 +92,15 @@
const groupToggles = document.querySelectorAll('.sidebar-group-toggle');
groupToggles.forEach(toggle => {
const group = toggle.closest('.sidebar-group');
const submenu = group ? group.querySelector('.sidebar-submenu') : null;
const caret = toggle.querySelector('.sidebar-group-caret');
if (submenu) {
const isOpen = group.classList.contains('is-open');
submenu.style.display = isOpen ? 'block' : 'none';
toggle.setAttribute('aria-expanded', isOpen.toString());
}
toggle.addEventListener('click', function(e) {
e.preventDefault();
@@ -88,7 +113,35 @@
toggle.setAttribute('aria-expanded', (!isOpen).toString());
submenu.style.display = isOpen ? 'none' : 'block';
});
if (caret) {
caret.addEventListener('click', function(e) {
e.preventDefault();
e.stopPropagation();
toggle.click();
});
}
});
// Capture-phase handler to override any conflicting listeners
document.addEventListener('click', function(e) {
const caret = e.target.closest('.sidebar-group-caret');
const toggle = e.target.closest('.sidebar-group-toggle');
const target = toggle || (caret ? caret.closest('.sidebar-group-toggle') : null);
if (!target) return;
e.preventDefault();
e.stopPropagation();
const group = target.closest('.sidebar-group');
const submenu = group ? group.querySelector('.sidebar-submenu') : null;
if (!submenu) return;
const isOpen = group.classList.contains('is-open');
group.classList.toggle('is-open', !isOpen);
target.setAttribute('aria-expanded', (!isOpen).toString());
submenu.style.display = isOpen ? 'none' : 'block';
}, true);
}
/**
@@ -273,6 +326,8 @@
} else {
sidebar.classList.add('mobile');
}
// Recompute sidebar width on resize
updateSidebarWidth();
});
}
@@ -405,6 +460,9 @@
enhanceTables();
makeResponsive();
initChartSafeguard();
// Set initial sidebar width variable and keep it updated
updateSidebarWidth();
window.addEventListener('resize', updateSidebarWidth);
}
// Wait for DOM to be ready