Files
2026-08-24 21:41:12 +02:00

193 lines
5.7 KiB
JavaScript

if (!customElements.get('cart-disclosure-modal')) {
class CartDisclosureModal extends HTMLElement {
constructor() {
super();
this.onOpenerClick = this.onOpenerClick.bind(this);
this.onCloseClick = this.onCloseClick.bind(this);
this.onModalClick = this.onModalClick.bind(this);
this.onKeyUp = this.onKeyUp.bind(this);
this.removeIfOpenerDisconnected = this.removeIfOpenerDisconnected.bind(this);
}
connectedCallback() {
if (this.initialized) return;
this.initialized = true;
this.dialog = this.querySelector('[role="dialog"]');
const controlsId = this.dialog?.id;
this.opener = this.previousElementSibling?.classList.contains('cart-item__disclosure')
? this.previousElementSibling
: controlsId
? document.querySelector(`[aria-controls="${controlsId}"]`)
: null;
this.closeButton = this.querySelector('[data-cart-disclosure-close]');
let shouldRestoreOpen = false;
document.querySelectorAll('cart-disclosure-modal').forEach((modal) => {
if (modal === this || modal.id !== this.id) return;
shouldRestoreOpen = shouldRestoreOpen || modal.hasAttribute('open');
modal.remove();
});
this.opener?.addEventListener('click', this.onOpenerClick);
this.closeButton?.addEventListener('click', this.onCloseClick);
this.addEventListener('click', this.onModalClick);
this.addEventListener('keyup', this.onKeyUp);
if (this.parentElement !== document.body) {
this.isMoving = true;
document.body.appendChild(this);
this.isMoving = false;
}
this.observeOpener();
if (shouldRestoreOpen) this.show();
}
disconnectedCallback() {
if (this.isMoving) return;
this.openerObserver?.disconnect();
this.opener?.removeEventListener('click', this.onOpenerClick);
this.closeButton?.removeEventListener('click', this.onCloseClick);
this.removeEventListener('click', this.onModalClick);
this.removeEventListener('keyup', this.onKeyUp);
if (this.hasAttribute('open')) this.hide(false);
}
observeOpener() {
this.openerObserver?.disconnect();
if (!this.opener) return;
this.openerObserver = new MutationObserver(this.removeIfOpenerDisconnected);
this.openerObserver.observe(document.body, { childList: true, subtree: true });
this.removeIfOpenerDisconnected();
}
removeIfOpenerDisconnected() {
if (this.isMoving || !this.isConnected || this.opener?.isConnected) return;
this.remove();
}
onOpenerClick(event) {
event.preventDefault();
event.stopPropagation();
this.show();
}
onCloseClick(event) {
event.preventDefault();
event.stopPropagation();
this.hide();
}
onModalClick(event) {
event.stopPropagation();
if (event.target === this) this.hide();
}
onKeyUp(event) {
if (event.code.toUpperCase() !== 'ESCAPE') return;
event.preventDefault();
event.stopPropagation();
this.hide();
}
show() {
if (this.hasAttribute('open')) return;
document.querySelectorAll('cart-disclosure-modal[open]').forEach((modal) => {
if (modal !== this) modal.hide(false);
});
this.lockScroll();
this.setAttribute('open', '');
this.opener?.setAttribute('aria-expanded', 'true');
if (typeof trapFocus === 'function') {
trapFocus(this, this.dialog);
} else {
this.dialog?.focus();
}
window.pauseAllMedia();
}
hide(restoreFocus = true) {
if (!this.hasAttribute('open')) return;
this.removeAttribute('open');
this.opener?.setAttribute('aria-expanded', 'false');
this.unlockScroll();
if (restoreFocus) {
this.restoreFocusTrap();
} else if (typeof removeTrapFocus === 'function') {
removeTrapFocus();
}
window.pauseAllMedia();
}
lockScroll() {
this.bodyOverflowWasHidden = document.body.classList.contains('overflow-hidden');
this.previousBodyPaddingRight = document.body.style.paddingRight;
if (this.bodyOverflowWasHidden) return;
const scrollbarWidth = window.innerWidth - document.documentElement.clientWidth;
if (scrollbarWidth > 0) {
const currentPaddingRight = parseFloat(window.getComputedStyle(document.body).paddingRight) || 0;
document.body.style.paddingRight = `${currentPaddingRight + scrollbarWidth}px`;
}
document.body.classList.add('overflow-hidden');
}
unlockScroll() {
if (this.bodyOverflowWasHidden) return;
document.body.classList.remove('overflow-hidden');
document.body.style.paddingRight = this.previousBodyPaddingRight;
}
restoreFocusTrap() {
const cartDrawer = this.opener?.closest('cart-drawer.active');
const cartNotification = this.opener?.closest('cart-notification')?.querySelector('#cart-notification.active');
if (cartDrawer && typeof trapFocus === 'function') {
const containerToTrapFocusOn = cartDrawer.classList.contains('is-empty')
? cartDrawer.querySelector('.drawer__inner-empty')
: document.getElementById('CartDrawer');
if (containerToTrapFocusOn) {
trapFocus(containerToTrapFocusOn, this.opener);
return;
}
}
if (cartNotification && typeof trapFocus === 'function') {
trapFocus(cartNotification, this.opener);
return;
}
if (typeof removeTrapFocus === 'function') {
removeTrapFocus(this.opener);
} else {
this.opener?.focus();
}
}
}
customElements.define('cart-disclosure-modal', CartDisclosureModal);
}