budgit/assets/js/sidebar.js
2026-04-12 16:07:06 +00:00

129 lines
4.1 KiB
JavaScript

(function () {
"use strict";
const SIDEBAR_COOKIE_NAME = "sidebar_state";
const SIDEBAR_COOKIE_MAX_AGE = 60 * 60 * 24 * 7; // 7 days
// Portal setup - moves content between desktop and mobile views
function setupMobilePortals() {
const contentElements = document.querySelectorAll(
"[data-tui-sidebar-content]",
);
contentElements.forEach((content) => {
const sidebarId = content.getAttribute("data-tui-sidebar-content");
const portal = document.querySelector(
`[data-tui-sidebar-mobile-portal="${sidebarId}"]`,
);
if (!portal) return;
// Check viewport and move content if needed
const isMobile = window.matchMedia("(max-width: 767px)").matches;
if (isMobile && content.parentElement !== portal) {
// Move to mobile portal
portal.appendChild(content);
} else if (!isMobile && content.parentElement === portal) {
// Move back to desktop sidebar
const desktopContainer = document.querySelector(
`[data-tui-sidebar-wrapper][data-tui-sidebar-id="${sidebarId}"] [data-sidebar="sidebar"] > div`,
);
if (desktopContainer) {
desktopContainer.appendChild(content);
}
}
});
}
// Initial setup
setupMobilePortals();
// Handle viewport changes
window.addEventListener("resize", setupMobilePortals);
// Handle DOM updates (for HTMX, Alpine, etc.)
const observer = new MutationObserver(setupMobilePortals);
observer.observe(document.body, {
childList: true,
subtree: true,
});
// Event delegation for sidebar interactions (Desktop only - Mobile uses Sheet)
document.addEventListener("click", (e) => {
const trigger = e.target.closest("[data-tui-sidebar-trigger]");
if (trigger) {
e.preventDefault();
const targetId = trigger.getAttribute("data-tui-sidebar-target");
if (targetId) {
toggleSidebar(targetId);
}
}
});
// Handle keyboard shortcuts
document.addEventListener("keydown", (e) => {
// Ctrl/Cmd + key - toggle sidebar
if ((e.ctrlKey || e.metaKey) && e.key.length === 1) {
const wrapper = document.querySelector('[data-tui-sidebar-wrapper]');
if (!wrapper) return;
const shortcut = wrapper.getAttribute("data-tui-sidebar-keyboard-shortcut");
if (!shortcut || shortcut.toLowerCase() !== e.key.toLowerCase()) return;
e.preventDefault();
const sidebar = wrapper.querySelector('[data-sidebar="sidebar"]');
if (sidebar && sidebar.id) {
toggleSidebar(sidebar.id);
}
}
});
function toggleSidebar(sidebarId) {
const wrapper = document.querySelector(
`[data-tui-sidebar-wrapper][data-tui-sidebar-id="${sidebarId}"]`,
);
if (!wrapper) return;
const collapsible = wrapper.getAttribute("data-tui-sidebar-collapsible");
// Don't toggle if collapsible is "none"
if (collapsible === "none") {
return;
}
const currentState = wrapper.getAttribute("data-tui-sidebar-state");
const newState = currentState === "expanded" ? "collapsed" : "expanded";
setSidebarState(sidebarId, newState);
}
function setSidebarState(sidebarId, state) {
const wrapper = document.querySelector(
`[data-tui-sidebar-wrapper][data-tui-sidebar-id="${sidebarId}"]`,
);
if (!wrapper) return;
const collapsible = wrapper.getAttribute("data-tui-sidebar-collapsible");
// Don't change state if collapsible is "none"
if (collapsible === "none") {
return;
}
// Update data-tui-sidebar-state attribute
wrapper.setAttribute("data-tui-sidebar-state", state);
// For icon mode, also set data-tui-sidebar-collapsible when collapsed
if (state === "collapsed" && collapsible) {
wrapper.setAttribute("data-tui-sidebar-collapsible", collapsible);
}
// Save state to cookie
const cookieValue = state === "expanded" ? "true" : "false";
saveSidebarState(sidebarId, cookieValue);
}
function saveSidebarState(sidebarId, cookieValue) {
document.cookie = `${SIDEBAR_COOKIE_NAME}=${cookieValue}; path=/; max-age=${SIDEBAR_COOKIE_MAX_AGE}`;
}
})();