Refactor page structure: Update page classes for consistency

- Changed class from "ots-displays-page" to "ots-static-page ots-displays-page" in multiple Twig view files to standardize page layout.
- Enhanced schedule-page.twig with improved calendar navigation and dropdown management.
- Added global dropdown dismissal functionality to improve user experience across modals and dropdowns.
This commit is contained in:
Matt Batchelder
2026-02-11 20:47:09 -05:00
parent 29b56bef4f
commit b766487411
34 changed files with 4506 additions and 141 deletions

View File

@@ -664,12 +664,94 @@
});
}
/**
* Close every open dropdown / popover on the page.
* Covers: Bootstrap 4 native dropdowns, OTS custom dropdowns,
* the user-menu, notification drawer, and DataTable row menus.
*/
function closeAllDropdowns() {
try {
// Row dropdown menus appended to body
document.querySelectorAll('.ots-row-dropdown').forEach(function(m) {
m.classList.remove('show', 'ots-row-dropdown');
m.style.cssText = '';
});
document.querySelectorAll('.dropdown.show, .btn-group.show').forEach(function(el) {
el.classList.remove('show');
var m = el.querySelector('.dropdown-menu.show');
if (m) m.classList.remove('show');
});
document.querySelectorAll('.dropdown-menu.show').forEach(function(m) {
m.classList.remove('show');
});
document.querySelectorAll('.dropdown.active, .ots-topbar-action .dropdown.active, .ots-page-actions .dropdown.active').forEach(function(el) {
el.classList.remove('active');
});
var userMenu = document.querySelector('.ots-user-menu-body.ots-user-menu-open');
if (userMenu) {
userMenu.classList.remove('ots-user-menu-open');
var userToggle = document.querySelector('#navbarUserMenu');
if (userToggle) {
var dd = userToggle.closest('.dropdown');
if (dd) dd.classList.remove('active', 'show');
}
}
document.querySelectorAll('.dt-buttons.active').forEach(function(w) { w.classList.remove('active'); });
document.querySelectorAll('.dt-button-collection.show').forEach(function(c) { c.classList.remove('show'); c.style.display = 'none'; });
if (window.jQuery) {
window.jQuery('.dropdown-toggle[aria-expanded="true"]').attr('aria-expanded', 'false');
window.jQuery('.dropdown-menu.show').removeClass('show');
window.jQuery('.dropdown.show, .btn-group.show').removeClass('show');
}
} catch (err) {}
}
/**
* Wire up global listeners that trigger closeAllDropdowns().
*/
function initGlobalDropdownDismiss() {
document.addEventListener('show.bs.modal', closeAllDropdowns, true);
try {
if (window.jQuery) {
window.jQuery(document).on('show.bs.modal', closeAllDropdowns);
window.jQuery(document).on('click', '.XiboFormButton, .XiboAjaxSubmit, .XiboFormRender', function() {
closeAllDropdowns();
});
}
} catch (e) {}
document.addEventListener('click', function(e) {
var link = e.target.closest('.dropdown-menu a, .ots-topbar a.nav-link, .ots-topbar a.dropdown-item, .XiboFormButton');
if (link && !e.defaultPrevented) {
closeAllDropdowns();
}
});
window.addEventListener('popstate', closeAllDropdowns);
try {
var origPush = history.pushState;
var origReplace = history.replaceState;
history.pushState = function() { origPush.apply(this, arguments); closeAllDropdowns(); };
history.replaceState = function() { origReplace.apply(this, arguments); closeAllDropdowns(); };
} catch (e) {}
try {
var content = document.getElementById('content') || document.querySelector('.ots-content');
if (content) {
var contentObs = new MutationObserver(debounce(closeAllDropdowns, 80));
contentObs.observe(content, { childList: true });
}
} catch (e) {}
}
/**
* Initialize all features when DOM is ready
*/
function init() {
initSidebarToggle();
initDropdowns();
initGlobalDropdownDismiss();
initSearch();
initPageInteractions();
initDataTables();