Files
svsmspcalc/mobile-sync.js
2026-03-15 18:24:36 -04:00

284 lines
8.7 KiB
JavaScript

(function(global) {
'use strict';
function initMobileSync() {
function usesStaticSidebarLayout() {
return global.innerWidth > 1100 || global.matchMedia('(max-width: 780px) and (orientation: landscape)').matches;
}
function canUseSidebarFocusMode() {
return global.innerWidth > 1100;
}
function syncSidebarFocusUi() {
var focusToggle = document.getElementById('sidebarFocusToggle');
var focusOpen = document.body.classList.contains('sidebar-focus-open');
if (!focusToggle) return;
focusToggle.setAttribute('aria-pressed', focusOpen ? 'true' : 'false');
focusToggle.setAttribute('aria-label', focusOpen ? 'Collapse live quote' : 'Expand live quote');
focusToggle.setAttribute('title', focusOpen ? 'Collapse live quote' : 'Expand live quote');
}
function buildMobileSidebar() {
var container = document.getElementById('mobilePanelContent');
var desktopSidebar = document.querySelector('.side-col .sidebar');
if (!container || !desktopSidebar || container.children.length) return;
var mobileSidebar = desktopSidebar.cloneNode(true);
mobileSidebar.removeAttribute('id');
mobileSidebar.querySelectorAll('[id]').forEach(function(el) {
el.id = el.id + '_m';
});
var mobileHstToggle = mobileSidebar.querySelector('#hstToggle_m');
if (mobileHstToggle) {
mobileHstToggle.onchange = function() {
var desktopHstToggle = document.getElementById('hstToggle');
if (desktopHstToggle) desktopHstToggle.checked = this.checked;
global.update();
};
}
var mobileExportJson = mobileSidebar.querySelector('#btnExportJSON_m');
if (mobileExportJson) mobileExportJson.removeAttribute('id');
container.appendChild(mobileSidebar);
}
function syncEl(id) {
var src = document.getElementById(id);
var dst = document.getElementById(id + '_m');
if (src && dst) dst.innerHTML = src.innerHTML;
}
function syncClass(id) {
var src = document.getElementById(id);
var dst = document.getElementById(id + '_m');
if (src && dst) dst.className = src.className;
}
function syncStyle(id) {
var src = document.getElementById(id);
var dst = document.getElementById(id + '_m');
if (src && dst) dst.style.cssText = src.style.cssText;
}
function syncChecked(id) {
var src = document.getElementById(id);
var dst = document.getElementById(id + '_m');
if (src && dst) dst.checked = src.checked;
}
function runSidebarSync(ids, syncFn) {
ids.forEach(syncFn);
}
var sidebarSyncMap = {
html: [
'sl-users-val',
'sl-endpoints-val',
'sl-servers-val',
'sl-zt-val',
'sl-voip-val',
'sl-admin-val',
'sl-monthly-total-val',
'mrrDisplay',
'sl-discount-val',
'sl-discount-detail',
'sl-base-mrr-val',
'sl-hst-val',
'sl-hst-total-val',
'sl-first-mri-val',
'sl-first-hst-val',
'sl-otf-val',
'sl-first-total-val',
'sl-value-m365-val',
'sl-value-term-val',
'sl-value-onboarding-val',
'sl-value-onboarding-label',
'sl-value-admin-val',
'sl-value-byol-val',
'sl-value-total-val',
'annualDisplay',
'perUserDisplay',
'perUserBreakdown',
'sidebarFocusClientName',
'vs-svs-annual',
'vs-1man-cost',
'vs-1man-save',
'vs-1man-save-lbl',
'vs-5man-cost',
'vs-5man-save',
'vs-5man-save-lbl',
'vs-footnote',
'nudgeText',
'nudgeCounter',
'sl-users-sub',
'sl-endpoints-sub',
'sl-admin-sub'
],
class: [
'sl-users',
'sl-users-sub',
'sl-endpoints',
'sl-endpoints-sub',
'sl-admin-sub',
'sl-servers',
'sl-zt',
'sl-voip',
'sl-admin',
'sl-monthly-total-row',
'vsComparison',
'sl-first-hst-row',
'sl-otf-row',
'sl-first-total-row',
'sl-value-m365-row',
'sl-value-term-row',
'sl-value-onboarding-row',
'sl-value-admin-row',
'sl-value-byol-row',
'sl-value-total-row',
'perUserRow',
'perUserBreakdown',
'sl-discount-row',
'sl-base-mrr-row',
'sl-hst-row',
'sl-hst-total-row',
'vs-1man-save-row',
'vs-1man-save',
'vs-1man-save-lbl',
'vs-5man-save-row',
'vs-5man-save',
'vs-5man-save-lbl',
'nudgeBanner'
],
style: [
'sl-users-sub',
'sl-endpoints-sub',
'sl-admin-sub',
'perUserRow'
],
checked: [
'hstToggle'
]
};
buildMobileSidebar();
global.openSidebarFocus = function() {
if (!canUseSidebarFocusMode()) return;
document.body.classList.add('sidebar-focus-open');
syncSidebarFocusUi();
if (typeof global.syncBodyScrollLock === 'function') global.syncBodyScrollLock();
};
global.closeSidebarFocus = function() {
document.body.classList.remove('sidebar-focus-open');
syncSidebarFocusUi();
if (typeof global.syncBodyScrollLock === 'function') global.syncBodyScrollLock();
};
global.toggleSidebarFocus = function() {
if (document.body.classList.contains('sidebar-focus-open')) global.closeSidebarFocus();
else global.openSidebarFocus();
};
// Focus trap for overlays (mobile panel, sidebar focus)
function trapFocus(container, e) {
var focusable = container.querySelectorAll(
'button, [href], input, select, textarea, [tabindex]:not([tabindex="-1"])'
);
if (!focusable.length) return;
var first = focusable[0];
var last = focusable[focusable.length - 1];
if (e.shiftKey && document.activeElement === first) {
e.preventDefault();
last.focus();
} else if (!e.shiftKey && document.activeElement === last) {
e.preventDefault();
first.focus();
}
}
global.openMobilePanel = function() {
var panel = document.getElementById('mobileQuotePanel');
if (panel) {
panel.classList.add('open');
// Force full sync so mobile panel has fresh data
runSidebarSync(sidebarSyncMap.html, syncEl);
runSidebarSync(sidebarSyncMap.class, syncClass);
runSidebarSync(sidebarSyncMap.style, syncStyle);
runSidebarSync(sidebarSyncMap.checked, syncChecked);
global.syncBodyScrollLock();
// Move focus into the panel
var closeBtn = panel.querySelector('.mobile-panel-close-btn');
if (closeBtn) closeBtn.focus();
}
};
global.closeMobilePanel = function() {
var panel = document.getElementById('mobileQuotePanel');
if (panel) {
panel.classList.remove('open');
global.syncBodyScrollLock();
// Return focus to the pill that opened the panel
var pill = document.querySelector('.mobile-quote-pill');
if (pill) pill.focus();
}
};
document.addEventListener('keydown', function(e) {
if (e.key === 'Escape') {
global.closeMobilePanel();
global.closeSidebarFocus();
}
// Focus trap: keep Tab cycling within the open overlay
if (e.key === 'Tab') {
var panel = document.getElementById('mobileQuotePanel');
if (panel && panel.classList.contains('open')) {
trapFocus(panel, e);
}
}
});
global.addEventListener('resize', function() {
if (usesStaticSidebarLayout()) global.closeMobilePanel();
if (!canUseSidebarFocusMode()) global.closeSidebarFocus();
});
var originalUpdate = global.update;
global.update = function() {
originalUpdate();
// Only sync mobile panel elements when panel is open or pill is visible
var panel = document.getElementById('mobileQuotePanel');
var panelOpen = panel && panel.classList.contains('open');
var isMobileViewport = global.innerWidth <= 1100;
if (panelOpen || isMobileViewport) {
runSidebarSync(sidebarSyncMap.html, syncEl);
runSidebarSync(sidebarSyncMap.class, syncClass);
runSidebarSync(sidebarSyncMap.style, syncStyle);
runSidebarSync(sidebarSyncMap.checked, syncChecked);
}
// Always sync the pill MRR (lightweight, one element)
var mrr = document.getElementById('mrrDisplay');
var pill = document.getElementById('mobilePillMrr');
if (mrr && pill) pill.textContent = mrr.textContent;
syncSidebarFocusUi();
};
syncSidebarFocusUi();
global.update();
}
global.SVSQuoteMobileSync = {
initMobileSync
};
initMobileSync();
})(window);