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

@@ -10,25 +10,59 @@
sidebarCollapsed: 'otsTheme:sidebarCollapsed'
};
/**
* Measure sidebar width and set CSS variable for layout
*/
function updateSidebarWidth() {
const sidebar = document.querySelector('.ots-sidebar');
if (!sidebar) return;
const collapsed = sidebar.classList.contains('collapsed');
const base = collapsed ? 88 : sidebar.offsetWidth || sidebar.getBoundingClientRect().width || 240;
const padding = 5;
const value = Math.max(88, Math.round(base + padding));
document.documentElement.style.setProperty('--ots-sidebar-width', `${value}px`);
}
/**
* Initialize sidebar toggle functionality
*/
function initSidebarToggle() {
const toggleBtn = document.querySelector('[data-action="toggle-sidebar"]');
const sidebar = document.querySelector('.ots-sidebar');
const collapseBtn = document.querySelector('.sidebar-collapse-btn');
const body = document.body;
if (!toggleBtn || !sidebar) return;
if (!sidebar) return;
toggleBtn.addEventListener('click', function(e) {
e.preventDefault();
sidebar.classList.toggle('active');
});
if (toggleBtn) {
toggleBtn.addEventListener('click', function(e) {
e.preventDefault();
sidebar.classList.toggle('active');
});
}
if (collapseBtn) {
const isCollapsed = localStorage.getItem(STORAGE_KEYS.sidebarCollapsed) === 'true';
if (isCollapsed) {
sidebar.classList.add('collapsed');
body.classList.add('ots-sidebar-collapsed');
}
collapseBtn.addEventListener('click', function(e) {
e.preventDefault();
const nowCollapsed = !sidebar.classList.contains('collapsed');
sidebar.classList.toggle('collapsed');
body.classList.toggle('ots-sidebar-collapsed', nowCollapsed);
localStorage.setItem(STORAGE_KEYS.sidebarCollapsed, nowCollapsed ? 'true' : 'false');
updateSidebarWidth();
});
}
// Close sidebar when clicking outside on mobile
document.addEventListener('click', function(e) {
if (window.innerWidth <= 768) {
const isClickInsideSidebar = sidebar.contains(e.target);
const isClickOnToggle = toggleBtn.contains(e.target);
const isClickOnToggle = toggleBtn && toggleBtn.contains(e.target);
if (!isClickInsideSidebar && !isClickOnToggle && sidebar.classList.contains('active')) {
sidebar.classList.remove('active');
@@ -37,6 +71,65 @@
});
}
/**
* Initialize sidebar section collapse/expand functionality
*/
function initSidebarSectionToggles() {
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();
const group = toggle.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);
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);
}
/**
* Initialize dropdown menus
*/
@@ -224,6 +317,7 @@
} else {
sidebar.classList.add('mobile');
}
updateSidebarWidth();
});
}
@@ -353,6 +447,7 @@
*/
function init() {
initSidebarToggle();
initSidebarSectionToggles();
initDropdowns();
initSearch();
initPageInteractions();
@@ -360,6 +455,8 @@
enhanceTables();
makeResponsive();
initChartSafeguard();
updateSidebarWidth();
window.addEventListener('resize', updateSidebarWidth);
}
// Wait for DOM to be ready