UNPKG

@pageblock/attributes-modal

Version:

Create advanced and accessible modal dialogs for Webflow with no-code.

11 lines (10 loc) 5.02 kB
/** * @pageblock/attributes-modal v0.1.4 * Create advanced and accessible modal dialogs for Webflow with no-code. * * @author PageBlock * @license MIT * @preserve */ const e=new WeakMap;let t=1e3,o=[];class a{constructor(e={}){this.debug=e.debug||!1}init(){this.initializeModals()}initializeModals(){document.querySelectorAll("pageblock-modal[data-modal-id]").forEach((o=>{const a=o.getAttribute("data-modal-id");if(!a)return void console.warn("Modal container found without data-modal-id attribute",o);const n=o.querySelector('[data-pb-modal="sheet"]'),d=o.querySelector('[data-pb-modal="overlay"]');if(!n||!d)return void console.warn(`Modal components missing in container with ID: ${a}`,o);n.setAttribute("role","dialog"),n.setAttribute("aria-modal","true"),n.setAttribute("aria-hidden","true");const s=o.getAttribute("data-animation"),l=o.getAttribute("data-variant");e.set(o,{id:a,isOpen:!1,previousFocus:null,animation:s||"fade",variant:l||"default",zIndex:parseInt(n.style.zIndex)||t,modal:n,overlay:d,focusableElements:this.getFocusableElements(n)})})),document.addEventListener("click",(e=>{const t=e.target.closest("[data-pb-modal-trigger]");if(t){e.preventDefault();const o=t.getAttribute("data-pb-modal-trigger");this.openModal(o,{trigger:t})}const o=e.target.closest('[data-pb-modal="close"]');if(o){e.preventDefault();const t=o.closest("pageblock-modal"),a=t?.getAttribute("data-modal-id");this.closeModal(a)}const a=e.target.closest('[data-pb-modal="overlay"]');if(a&&a===e.target){e.preventDefault();const t=a.closest("pageblock-modal"),o=t?.getAttribute("data-modal-id");this.closeModal(o)}})),document.addEventListener("keydown",(t=>{if("Escape"===t.key&&o.length>0){const e=o[o.length-1];this.closeModal(e)}else if("Tab"===t.key&&o.length>0){const a=o[o.length-1],n=document.querySelector(`pageblock-modal[data-modal-id="${a}"]`);if(n){const o=e.get(n);o&&o.modal&&this.trapFocus(t,o.modal)}}})),document.addEventListener("pb:modal:open",(e=>{const t=e.detail.modalId,o=e.detail.options||{};this.openModal(t,o)})),document.addEventListener("pb:modal:close",(e=>{const t=e.detail.modalId,o=e.detail.options||{};this.closeModal(t,o)})),"undefined"!=typeof window&&(window.PBModal={open:(e,t={})=>{const o=new CustomEvent("pb:modal:open",{detail:{modalId:e,options:t}});document.dispatchEvent(o)},close:(e,t={})=>{const o=new CustomEvent("pb:modal:close",{detail:{modalId:e,options:t}});document.dispatchEvent(o)},closeAll:()=>this.closeAllModals()})}openModal(a,n={}){if(!a)return void console.error("Modal ID is required to open a modal");const d=document.querySelector(`pageblock-modal[data-modal-id="${a}"]`);if(!d)return void console.error(`Modal container not found for ID: ${a}`);const s=e.get(d);if(!s)return void console.error(`Modal state not found for ID: ${a}`);const l=s.modal,i=s.overlay;s.isOpen||(s.previousFocus=document.activeElement,t+=2,s.zIndex=t,l.style.zIndex=s.zIndex,i.style.zIndex=s.zIndex-1,l.setAttribute("aria-hidden","false"),l.classList.add("cc-active"),i.classList.add("cc-active"),n.animation&&(d.dataset.animation=n.animation),n.variant&&(d.dataset.variant=n.variant),document.body.style.overflow="hidden",o.push(a),s.isOpen=!0,e.set(d,s),setTimeout((()=>{const e=l.querySelector("[data-pb-modal-autofocus]")||s.focusableElements[0]||l;e&&e.focus()}),50),d.dispatchEvent(new CustomEvent("modal:opened",{detail:{modalId:a,options:n}})),"function"==typeof n.onOpen&&n.onOpen(l,i,d))}closeModal(t,a={}){if(!t)return void console.error("Modal ID is required to close a modal");const n=document.querySelector(`pageblock-modal[data-modal-id="${t}"]`);if(!n)return void console.error(`Modal container not found for ID: ${t}`);const d=e.get(n);if(!d||!d.isOpen)return;const s=d.modal,l=d.overlay;s.setAttribute("aria-hidden","true"),s.classList.remove("cc-active"),l.classList.remove("cc-active");const i=o.indexOf(t);i>-1&&o.splice(i,1),0===o.length&&(document.body.style.overflow=""),d.isOpen=!1,e.set(n,d);const c=()=>{d.previousFocus&&d.previousFocus.focus&&d.previousFocus.focus(),n.dispatchEvent(new CustomEvent("modal:closed",{detail:{modalId:t,options:a}})),"function"==typeof a.onClose&&a.onClose(s,l,n),s.removeEventListener("transitionend",c)};s.addEventListener("transitionend",c)}closeAllModals(){[...o].forEach((e=>{this.closeModal(e)}))}getFocusableElements(e){return Array.from(e.querySelectorAll(["a[href]:not([disabled])","button:not([disabled])","textarea:not([disabled])","input:not([disabled])","select:not([disabled])",'[tabindex]:not([tabindex="-1"])'].join(",")))}trapFocus(t,o){const a=o.closest("pageblock-modal"),n=e.get(a);if(!n)return;const d=n.focusableElements;if(0===d.length)return;const s=d[0],l=d[d.length-1];t.shiftKey?document.activeElement===s&&(l.focus(),t.preventDefault()):document.activeElement===l&&(s.focus(),t.preventDefault())}}const n=new a;document.addEventListener("DOMContentLoaded",(()=>{n.init()})),"undefined"!=typeof window&&(window.PageBlockModal=n);export{a as Modal,n as default}; //# sourceMappingURL=index.js.map