Add new pages for managing tags, tasks, transitions, users, user groups, and their respective JavaScript functionalities
- Implemented tag management page with filtering, data table, and AJAX functionality. - Created task management page with task listing, filtering, and AJAX data loading. - Developed transition management page with a data table for transitions. - Added user management page with comprehensive user details, filtering options, and AJAX support. - Introduced user group management page with filtering and data table for user groups. - Enhanced JavaScript for data tables, including state saving, filtering, and AJAX data fetching for all new pages.
This commit is contained in:
@@ -31,11 +31,66 @@
|
||||
});
|
||||
}
|
||||
|
||||
// Mobile-aware toggle: add backdrop, aria-expanded, and focus management
|
||||
if (toggleBtn) {
|
||||
let lastFocus = null;
|
||||
function ensureBackdrop() {
|
||||
let bd = document.querySelector('.ots-backdrop');
|
||||
if (!bd) {
|
||||
bd = document.createElement('div');
|
||||
bd.className = 'ots-backdrop';
|
||||
bd.addEventListener('click', function() {
|
||||
sidebar.classList.remove('active');
|
||||
bd.classList.remove('show');
|
||||
toggleBtn.setAttribute('aria-expanded', 'false');
|
||||
if (lastFocus) lastFocus.focus();
|
||||
});
|
||||
document.body.appendChild(bd);
|
||||
}
|
||||
return bd;
|
||||
}
|
||||
|
||||
toggleBtn.setAttribute('role', 'button');
|
||||
toggleBtn.setAttribute('aria-controls', 'ots-sidebar');
|
||||
toggleBtn.setAttribute('aria-expanded', 'false');
|
||||
|
||||
toggleBtn.addEventListener('click', function(e) {
|
||||
e.preventDefault();
|
||||
const isNowActive = !sidebar.classList.contains('active');
|
||||
sidebar.classList.toggle('active');
|
||||
// On small screens show backdrop and manage focus
|
||||
if (window.innerWidth <= 768) {
|
||||
const bd = ensureBackdrop();
|
||||
if (isNowActive) {
|
||||
bd.classList.add('show');
|
||||
toggleBtn.setAttribute('aria-expanded', 'true');
|
||||
lastFocus = document.activeElement;
|
||||
// move focus into the sidebar
|
||||
const firstFocusable = sidebar.querySelector('button, [href], input, select, textarea, [tabindex]:not([tabindex="-1"])');
|
||||
if (firstFocusable) firstFocusable.focus(); else sidebar.setAttribute('tabindex', '-1'), sidebar.focus();
|
||||
// add escape handler
|
||||
document.addEventListener('keydown', escHandler);
|
||||
} else {
|
||||
bd.classList.remove('show');
|
||||
toggleBtn.setAttribute('aria-expanded', 'false');
|
||||
if (lastFocus) lastFocus.focus();
|
||||
document.removeEventListener('keydown', escHandler);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
function escHandler(e) {
|
||||
if (e.key === 'Escape' || e.key === 'Esc') {
|
||||
const bd = document.querySelector('.ots-backdrop');
|
||||
if (sidebar.classList.contains('active')) {
|
||||
sidebar.classList.remove('active');
|
||||
if (bd) bd.classList.remove('show');
|
||||
toggleBtn.setAttribute('aria-expanded', 'false');
|
||||
if (lastFocus) lastFocus.focus();
|
||||
document.removeEventListener('keydown', escHandler);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (collapseBtn) {
|
||||
@@ -43,6 +98,7 @@
|
||||
if (isCollapsed) {
|
||||
sidebar.classList.add('collapsed');
|
||||
body.classList.add('ots-sidebar-collapsed');
|
||||
document.documentElement.classList.add('ots-sidebar-collapsed');
|
||||
updateSidebarStateClass();
|
||||
}
|
||||
|
||||
@@ -51,10 +107,8 @@
|
||||
const nowCollapsed = !sidebar.classList.contains('collapsed');
|
||||
sidebar.classList.toggle('collapsed');
|
||||
body.classList.toggle('ots-sidebar-collapsed', nowCollapsed);
|
||||
document.documentElement.classList.toggle('ots-sidebar-collapsed', nowCollapsed);
|
||||
localStorage.setItem(STORAGE_KEYS.sidebarCollapsed, nowCollapsed ? 'true' : 'false');
|
||||
// Update measured sidebar width when collapsed state changes
|
||||
updateSidebarWidth();
|
||||
// Recalculate nav offset so items remain below header after collapse
|
||||
updateSidebarNavOffset();
|
||||
updateSidebarStateClass();
|
||||
});
|
||||
@@ -65,9 +119,8 @@
|
||||
e.preventDefault();
|
||||
sidebar.classList.remove('collapsed');
|
||||
body.classList.remove('ots-sidebar-collapsed');
|
||||
document.documentElement.classList.remove('ots-sidebar-collapsed');
|
||||
localStorage.setItem(STORAGE_KEYS.sidebarCollapsed, 'false');
|
||||
updateSidebarWidth();
|
||||
// Recalculate nav offset after expanding
|
||||
updateSidebarNavOffset();
|
||||
updateSidebarStateClass();
|
||||
});
|
||||
@@ -90,17 +143,16 @@
|
||||
}
|
||||
|
||||
/**
|
||||
* Measure sidebar width and set CSS variable for layout
|
||||
* Sidebar width is now handled purely by CSS classes (.ots-sidebar-collapsed).
|
||||
* This function is kept as a no-op for backward compatibility.
|
||||
*/
|
||||
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 ? 64 : sidebar.offsetWidth || sidebar.getBoundingClientRect().width || 256;
|
||||
const padding = 0;
|
||||
const value = Math.max(64, Math.round(base + padding));
|
||||
document.documentElement.style.setProperty('--ots-sidebar-width', `${value}px`);
|
||||
// No-op: CSS handles layout via body.ots-sidebar-collapsed class
|
||||
if (window.__otsDebug) {
|
||||
const sidebar = document.querySelector('.ots-sidebar');
|
||||
const collapsed = sidebar ? sidebar.classList.contains('collapsed') : false;
|
||||
console.log('[OTS] updateSidebarWidth (no-op, CSS-driven)', { collapsed });
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
Reference in New Issue
Block a user