Files
mataba/shop/xshop-theme/assets/js/theme.js
T

83 lines
3.2 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
/**
* XShop theme bootstrap – vanilla, no jQuery.
* Feature modules are dynamically imported when DOM markers are present.
*/
(function () {
'use strict';
// Dark mode toggle via [data-theme] if enabled.
function initThemeMode() {
var data = window.xshopData;
if (!data) return;
// Theme mode is server-rendered via data-theme on <html>; this handles toggle clicks.
document.addEventListener('click', function (e) {
var btn = e.target.closest('[data-xshop-theme-toggle]');
if (!btn) return;
var html = document.documentElement;
var next = html.getAttribute('data-theme') === 'dark' ? 'light' : 'dark';
html.setAttribute('data-theme', next);
try { localStorage.setItem('xshop_theme', next); } catch (_) {}
// Cookie for server-side rendering on next request.
document.cookie = 'xshop_theme=' + next + '; path=/; max-age=2592000; SameSite=Lax';
});
}
// Minimal focus trap helper for drawers/modals (used by later modules).
window.xshopTrapFocus = function (container) {
var sel = 'a[href], button:not([disabled]), input:not([disabled]), select:not([disabled]), textarea:not([disabled]), [tabindex]:not([tabindex="-1"])';
var nodes = container.querySelectorAll(sel);
if (!nodes.length) return function () {};
var first = nodes[0], last = nodes[nodes.length - 1];
function onKey(e) {
if (e.key !== 'Tab') return;
if (e.shiftKey && document.activeElement === first) { e.preventDefault(); last.focus(); }
else if (!e.shiftKey && document.activeElement === last) { e.preventDefault(); first.focus(); }
}
container.addEventListener('keydown', onKey);
return function () { container.removeEventListener('keydown', onKey); };
};
function initMobileNav() {
var toggle = document.querySelector('[data-xshop-mobile-toggle]');
var nav = document.getElementById('xshop-mobile-nav');
if (!toggle || !nav) return;
var releaseTrap = null;
function setOpen(open) {
toggle.setAttribute('aria-expanded', open ? 'true' : 'false');
nav.classList.toggle('is-open', open);
nav.hidden = !open;
if (open) {
// Move focus into nav.
var firstLink = nav.querySelector('a, button');
if (firstLink) firstLink.focus();
if (window.xshopTrapFocus) releaseTrap = window.xshopTrapFocus(nav);
document.addEventListener('keydown', onDocKey);
} else {
if (releaseTrap) { releaseTrap(); releaseTrap = null; }
document.removeEventListener('keydown', onDocKey);
toggle.focus();
}
}
function onDocKey(e) {
if (e.key === 'Escape') setOpen(false);
}
toggle.addEventListener('click', function () {
var open = toggle.getAttribute('aria-expanded') === 'true';
setOpen(!open);
});
// Close when clicking outside.
document.addEventListener('click', function (e) {
if (nav.hidden) return;
if (nav.contains(e.target) || toggle.contains(e.target)) return;
setOpen(false);
});
}
initThemeMode();
if (document.readyState === 'loading') {
document.addEventListener('DOMContentLoaded', initMobileNav);
} else {
initMobileNav();
}
})();