Allow visitors to reopen the cookie preferences modal after making their choice via a configurable floating button (bottom-right or right-side vertical) or any anchor link with href="#att-cc-preferences". Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
201 lines
4.6 KiB
JavaScript
201 lines
4.6 KiB
JavaScript
/**
|
|
* ATT Consent Banner UI
|
|
*
|
|
* Handles banner display, preferences modal, floating widget, anchor link
|
|
* triggers, focus trapping, and user interactions.
|
|
* Depends on AttConsent (consent-manager.js) for consent state management.
|
|
*/
|
|
(function() {
|
|
'use strict';
|
|
|
|
var banner = document.getElementById('att-cc-banner');
|
|
var modal = document.getElementById('att-cc-modal');
|
|
var widget = document.getElementById('att-cc-widget');
|
|
|
|
if (!banner || !modal) {
|
|
return;
|
|
}
|
|
|
|
// --- Modal buttons ---
|
|
|
|
var saveBtn = modal.querySelector('[data-att-cc="save-preferences"]');
|
|
var closeBtn = modal.querySelector('[data-att-cc="close-modal"]');
|
|
var overlay = modal.querySelector('.att-cc-modal__overlay');
|
|
|
|
if (saveBtn) {
|
|
saveBtn.addEventListener('click', function() {
|
|
var categories = {
|
|
functional: document.getElementById('att-cc-functional').checked,
|
|
analytics: document.getElementById('att-cc-analytics').checked,
|
|
marketing: document.getElementById('att-cc-marketing').checked
|
|
};
|
|
AttConsent.update(categories);
|
|
closeModal();
|
|
hideBanner();
|
|
showWidget();
|
|
});
|
|
}
|
|
|
|
if (closeBtn) {
|
|
closeBtn.addEventListener('click', function() {
|
|
closeModal();
|
|
});
|
|
}
|
|
|
|
if (overlay) {
|
|
overlay.addEventListener('click', function() {
|
|
closeModal();
|
|
});
|
|
}
|
|
|
|
// --- Floating widget click handler ---
|
|
|
|
if (widget) {
|
|
widget.addEventListener('click', function() {
|
|
openModal();
|
|
});
|
|
}
|
|
|
|
// --- Anchor link listener ---
|
|
|
|
document.addEventListener('click', function(e) {
|
|
var link = e.target.closest('a[href="#att-cc-preferences"]');
|
|
if (link) {
|
|
e.preventDefault();
|
|
openModal();
|
|
}
|
|
});
|
|
|
|
// --- Initial state ---
|
|
|
|
if (AttConsent.hasConsent()) {
|
|
hideBanner();
|
|
showWidget();
|
|
} else {
|
|
// --- Banner buttons (only needed for first-time visitors) ---
|
|
|
|
var acceptBtn = banner.querySelector('[data-att-cc="accept-all"]');
|
|
var rejectBtn = banner.querySelector('[data-att-cc="reject-all"]');
|
|
var prefsBtn = banner.querySelector('[data-att-cc="preferences"]');
|
|
|
|
if (acceptBtn) {
|
|
acceptBtn.addEventListener('click', function() {
|
|
AttConsent.update({ functional: true, analytics: true, marketing: true });
|
|
hideBanner();
|
|
showWidget();
|
|
});
|
|
}
|
|
|
|
if (rejectBtn) {
|
|
rejectBtn.addEventListener('click', function() {
|
|
AttConsent.update({ functional: false, analytics: false, marketing: false });
|
|
hideBanner();
|
|
showWidget();
|
|
});
|
|
}
|
|
|
|
if (prefsBtn) {
|
|
prefsBtn.addEventListener('click', function() {
|
|
openModal();
|
|
});
|
|
}
|
|
}
|
|
|
|
// --- Helper functions ---
|
|
|
|
function hideBanner() {
|
|
banner.setAttribute('aria-hidden', 'true');
|
|
banner.classList.add('att-cc-hidden');
|
|
}
|
|
|
|
function showWidget() {
|
|
if (widget) {
|
|
widget.style.display = '';
|
|
}
|
|
}
|
|
|
|
function openModal() {
|
|
// Pre-fill checkboxes with existing consent if available.
|
|
var existing = AttConsent.getConsent();
|
|
if (existing) {
|
|
setCheckbox('att-cc-functional', existing.functional);
|
|
setCheckbox('att-cc-analytics', existing.analytics);
|
|
setCheckbox('att-cc-marketing', existing.marketing);
|
|
}
|
|
|
|
modal.setAttribute('aria-hidden', 'false');
|
|
modal.classList.add('att-cc-modal--open');
|
|
document.body.classList.add('att-cc-modal-active');
|
|
trapFocus(modal);
|
|
}
|
|
|
|
function closeModal() {
|
|
modal.setAttribute('aria-hidden', 'true');
|
|
modal.classList.remove('att-cc-modal--open');
|
|
document.body.classList.remove('att-cc-modal-active');
|
|
|
|
// Return focus to the widget if visible, otherwise the banner prefs button.
|
|
if (widget && widget.style.display !== 'none') {
|
|
widget.focus();
|
|
} else {
|
|
var prefsBtn = banner.querySelector('[data-att-cc="preferences"]');
|
|
if (prefsBtn) {
|
|
prefsBtn.focus();
|
|
}
|
|
}
|
|
}
|
|
|
|
function setCheckbox(id, value) {
|
|
var el = document.getElementById(id);
|
|
if (el) {
|
|
el.checked = !!value;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Trap keyboard focus within an element.
|
|
*
|
|
* @param {HTMLElement} element The container to trap focus in.
|
|
*/
|
|
function trapFocus(element) {
|
|
var focusable = element.querySelectorAll(
|
|
'button, [href], input:not([disabled]), select, textarea, [tabindex]:not([tabindex="-1"])'
|
|
);
|
|
|
|
if (focusable.length === 0) {
|
|
return;
|
|
}
|
|
|
|
var first = focusable[0];
|
|
var last = focusable[focusable.length - 1];
|
|
|
|
first.focus();
|
|
|
|
function handleKeydown(e) {
|
|
if (e.key === 'Escape') {
|
|
closeModal();
|
|
element.removeEventListener('keydown', handleKeydown);
|
|
return;
|
|
}
|
|
|
|
if (e.key !== 'Tab') {
|
|
return;
|
|
}
|
|
|
|
if (e.shiftKey) {
|
|
if (document.activeElement === first) {
|
|
last.focus();
|
|
e.preventDefault();
|
|
}
|
|
} else {
|
|
if (document.activeElement === last) {
|
|
first.focus();
|
|
e.preventDefault();
|
|
}
|
|
}
|
|
}
|
|
|
|
element.addEventListener('keydown', handleKeydown);
|
|
}
|
|
})();
|