A free, open, self-hosted app platform (GNU AGPLv3): one-click app deploys, Traefik reverse proxy with automatic SSL, rootless Docker support, gluetun VPN routing, and a web dashboard to manage it all. Free & open forever to self-host; optional paid hosted services fund it. See PROMISE.md. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com> Signed-off-by: librelad <librelad@digitalangels.vip>
71 lines
2.2 KiB
JavaScript
Executable File
71 lines
2.2 KiB
JavaScript
Executable File
// Mobile Menu Handler — wires the topbar burger to the slide-in drawer
|
|
// (#mobile-drawer). On pages that ship a page sidebar (#sidebar), the
|
|
// drawer borrows its contents while open so the user gets one unified
|
|
// nav surface on mobile.
|
|
function setupMobileMenu() {
|
|
const toggle = document.getElementById('mobile-menu-toggle');
|
|
const drawer = document.getElementById('mobile-drawer');
|
|
const overlay = document.getElementById('mobile-overlay');
|
|
|
|
if (!toggle || !drawer || !overlay) {
|
|
setTimeout(setupMobileMenu, 100);
|
|
return;
|
|
}
|
|
|
|
const pageSection = document.getElementById('mobile-drawer-page-section');
|
|
let borrowedNodes = [];
|
|
let sidebarOrigin = null;
|
|
|
|
function borrowSidebar() {
|
|
const sidebar = document.getElementById('sidebar');
|
|
if (!sidebar || !pageSection) return;
|
|
sidebarOrigin = sidebar;
|
|
borrowedNodes = Array.from(sidebar.children);
|
|
borrowedNodes.forEach((node) => pageSection.appendChild(node));
|
|
}
|
|
|
|
function returnSidebar() {
|
|
if (!sidebarOrigin || borrowedNodes.length === 0) return;
|
|
borrowedNodes.forEach((node) => sidebarOrigin.appendChild(node));
|
|
borrowedNodes = [];
|
|
sidebarOrigin = null;
|
|
}
|
|
|
|
function openMenu() {
|
|
borrowSidebar();
|
|
drawer.classList.add('mobile-open');
|
|
overlay.classList.add('active');
|
|
document.body.style.overflow = 'hidden';
|
|
}
|
|
|
|
function closeMenu() {
|
|
drawer.classList.remove('mobile-open');
|
|
overlay.classList.remove('active');
|
|
document.body.style.overflow = '';
|
|
returnSidebar();
|
|
}
|
|
|
|
function toggleMenu() {
|
|
if (drawer.classList.contains('mobile-open')) closeMenu();
|
|
else openMenu();
|
|
}
|
|
|
|
toggle.addEventListener('click', toggleMenu);
|
|
overlay.addEventListener('click', closeMenu);
|
|
|
|
// Close when any nav-item, sidebar-item, or category gets clicked
|
|
// (they trigger navigation, so the drawer should dismiss itself).
|
|
drawer.addEventListener('click', (e) => {
|
|
const dismisser = e.target.closest('.nav-item, .sidebar-item, .category');
|
|
if (dismisser) closeMenu();
|
|
});
|
|
|
|
window.addEventListener('resize', () => {
|
|
if (window.innerWidth > 768 && drawer.classList.contains('mobile-open')) {
|
|
closeMenu();
|
|
}
|
|
});
|
|
|
|
window.closeMobileMenu = closeMenu;
|
|
}
|