@magic-spells/dialog-panel
Version:
A lightweight, customizable Dialog Panel web component for creating accessible and responsive modal dialogs.
2 lines (1 loc) • 6.1 kB
JavaScript
!function(e,t){"object"==typeof exports&&"undefined"!=typeof module?t(exports):"function"==typeof define&&define.amd?define(["exports"],t):t((e="undefined"!=typeof globalThis?globalThis:e||self).DialogPanel={})}(this,(function(e){"use strict";const t=e=>Array.from(e.querySelectorAll('summary, a[href], button:not(:disabled), [tabindex]:not([tabindex^="-"]):not(focus-trap-start):not(focus-trap-end), [draggable], area, input:not([type=hidden]):not(:disabled), select:not(:disabled), textarea:not(:disabled), object, iframe'));class FocusTrap extends HTMLElement{static styleInjected=!1;constructor(){super(),this.trapStart=null,this.trapEnd=null,FocusTrap.styleInjected||(this.injectStyles(),FocusTrap.styleInjected=!0)}injectStyles(){const e=document.createElement("style");e.textContent="\n focus-trap-start,\n focus-trap-end {\n position: absolute;\n width: 1px;\n height: 1px;\n margin: -1px;\n padding: 0;\n border: 0;\n clip: rect(0, 0, 0, 0);\n overflow: hidden;\n white-space: nowrap;\n }\n ",document.head.appendChild(e)}connectedCallback(){this.setupTrap(),this.addEventListener("keydown",this.handleKeyDown)}disconnectedCallback(){this.removeEventListener("keydown",this.handleKeyDown)}setupTrap(){0!==t(this).length&&(this.trapStart=document.createElement("focus-trap-start"),this.trapEnd=document.createElement("focus-trap-end"),this.prepend(this.trapStart),this.append(this.trapEnd))}handleKeyDown=e=>{"Escape"===e.key&&(e.preventDefault(),this.exitTrap())};exitTrap(){const e=this.closest('[aria-hidden="false"]');if(!e)return;e.setAttribute("aria-hidden","true");const t=document.querySelector(`[aria-expanded="true"][aria-controls="${e.id}"]`);t&&(t.setAttribute("aria-expanded","false"),t.focus())}}class FocusTrapStart extends HTMLElement{connectedCallback(){this.setAttribute("tabindex","0"),this.addEventListener("focus",this.handleFocus)}disconnectedCallback(){this.removeEventListener("focus",this.handleFocus)}handleFocus=e=>{const n=this.closest("focus-trap"),s=t(n);if(0===s.length)return;const a=s[0],i=s[s.length-1];e.relatedTarget===a?i.focus():a.focus()}}class FocusTrapEnd extends HTMLElement{connectedCallback(){this.setAttribute("tabindex","0"),this.addEventListener("focus",this.handleFocus)}disconnectedCallback(){this.removeEventListener("focus",this.handleFocus)}handleFocus=()=>{this.closest("focus-trap").querySelector("focus-trap-start").focus()}}customElements.define("focus-trap",FocusTrap),customElements.define("focus-trap-start",FocusTrapStart),customElements.define("focus-trap-end",FocusTrapEnd);class DialogPanel extends HTMLElement{#e;#t=0;disconnectedCallback(){const e=this;e.contentPanel&&e.contentPanel.removeEventListener("transitionend",e.#e),document.body.classList.remove("overflow-hidden"),this.#n()}#s(){this.#t=window.pageYOffset,document.body.classList.add("overflow-hidden"),document.body.style.top=`-${this.#t}px`}#n(){document.body.classList.remove("overflow-hidden"),document.body.style.removeProperty("top"),window.scrollTo(0,this.#t)}constructor(){super();const e=this;if(e.id=e.getAttribute("id"),e.setAttribute("role","dialog"),e.setAttribute("aria-modal","true"),e.setAttribute("aria-hidden","true"),e.contentPanel=e.querySelector("dialog-content"),e.focusTrap=document.createElement("focus-trap"),e.triggerEl=null,e.#e=t=>{"opacity"===t.propertyName&&"true"===e.getAttribute("aria-hidden")&&(e.contentPanel.classList.add("hidden"),e.dispatchEvent(new CustomEvent("afterHide",{bubbles:!0,detail:{triggerElement:e.triggerEl}})))},!e.getAttribute("aria-labelledby")){const t=e.querySelector("h1, h2, h3");t&&!t.id&&(t.id=`${e.id}-title`),t?.id&&e.setAttribute("aria-labelledby",t.id)}e.contentPanel.parentNode.insertBefore(e.focusTrap,e.contentPanel),e.focusTrap.appendChild(e.contentPanel),e.focusTrap.setupTrap(),e.prepend(document.createElement("dialog-overlay")),e.#a(),e.#i()}#a(){const e=this;document.addEventListener("click",(t=>{const n=t.target.closest(`[aria-controls="${e.id}"]`);n&&("true"===n.getAttribute("data-prevent-default")&&t.preventDefault(),e.show(n))})),e.addEventListener("click",(t=>{t.target.closest('[data-action="hide-dialog"]')&&e.hide()})),e.contentPanel.addEventListener("transitionend",e.#e)}#i(){this.addEventListener("keydown",(e=>{"Escape"===e.key&&this.hide()}))}show(e=null){const t=this;t.triggerEl=e||!1;const n=new CustomEvent("beforeShow",{bubbles:!0,cancelable:!0,detail:{triggerElement:t.triggerEl}});return!!t.dispatchEvent(n)&&(t.contentPanel.classList.remove("hidden"),requestAnimationFrame((()=>{t.setAttribute("aria-hidden","false"),t.triggerEl&&t.triggerEl.setAttribute("aria-expanded","true"),t.#s();const e=t.querySelector('button, [href], input, select, textarea, [tabindex]:not([tabindex="-1"])');e&&requestAnimationFrame((()=>{e.focus()})),t.dispatchEvent(new CustomEvent("show",{bubbles:!0,detail:{triggerElement:t.triggerEl}}))})),!0)}hide(){const e=this,t=new CustomEvent("beforeHide",{bubbles:!0,cancelable:!0,detail:{triggerElement:e.triggerEl}});return!!e.dispatchEvent(t)&&(e.#n(),e.triggerEl&&(e.triggerEl.focus(),e.triggerEl.setAttribute("aria-expanded","false")),e.setAttribute("aria-hidden","true"),e.dispatchEvent(new CustomEvent("hide",{bubbles:!0,detail:{triggerElement:e.triggerEl}})),!0)}}class DialogOverlay extends HTMLElement{constructor(){super(),this.setAttribute("tabindex","-1"),this.setAttribute("aria-hidden","true"),this.dialogPanel=this.closest("dialog-panel"),this.#a()}#a(){this.addEventListener("click",(()=>{this.dialogPanel.hide()}))}}class DialogContent extends HTMLElement{constructor(){super(),this.setAttribute("role","document")}}customElements.get("dialog-panel")||customElements.define("dialog-panel",DialogPanel),customElements.get("dialog-overlay")||customElements.define("dialog-overlay",DialogOverlay),customElements.get("dialog-content")||customElements.define("dialog-content",DialogContent),e.DialogContent=DialogContent,e.DialogOverlay=DialogOverlay,e.DialogPanel=DialogPanel,e.default=DialogPanel,Object.defineProperty(e,"__esModule",{value:!0})}));