UNPKG

dialog-lite

Version:

DialogLite is designed to control a dialog box (modal window) on a web page, providing the functionality to open, close and apply custom styles through a simple interface.

101 lines (86 loc) 8.87 kB
(function(l,r){typeof exports=="object"&&typeof module<"u"?r(exports):typeof define=="function"&&define.amd?define(["exports"],r):(l=typeof globalThis<"u"?globalThis:l||self,r(l.DialogLite={}))})(this,(function(l){"use strict";const r=`:root { --z-index-dialog-lite: 992; --z-index-dialog-lite-backdrop: 993; --z-index-dialog-lite-container: 994; } .dialog-lite { position: fixed; inset: 0; z-index: var(--z-index-dialog-lite, 992); width: 100vw; overflow: clip auto; } .dialog-lite--in { -webkit-overflow-scrolling: touch; } .dialog-lite--out { pointer-events: none; } .dialog-lite__backdrop { position: fixed; inset: 0; margin: auto; z-index: var(--z-index-dialog-lite-backdrop, 993); } .dialog-lite--in .dialog-lite__backdrop { background-color: var(--c-dialog-lite-backdrop-in, hsla(240deg 22% 6% / 82%)); transition: background-color 400ms cubic-bezier(0.61, 1, 0.88, 1); } .dialog-lite--out .dialog-lite__backdrop { pointer-events: none; background-color: var(--c-dialog-lite-backdrop-out, hsla(200deg 2% 6% / 0%)); transition: background-color 500ms cubic-bezier(0, 0, 0.5, 1); } .dialog-lite__container { pointer-events: none; position: relative; z-index: var(--z-index-dialog-lite-container, 994); display: grid; place-content: center; width: 100vw; } @supports (min-height: 100dvh) { .dialog-lite__container { min-height: 100dvh; } } @supports not (min-height: 100dvh) { .dialog-lite__container { min-height: 100vh; } } .dialog-lite__container-inner { position: relative; margin: 20px; } .dialog-lite--in .dialog-lite__container-inner { pointer-events: auto; opacity: 1; transform: translateY(0); transition: opacity 400ms cubic-bezier(0.61, 1, 0.88, 1), transform 400ms cubic-bezier(0.61, 1, 0.88, 1); } .dialog-lite--out .dialog-lite__container-inner { pointer-events: none; opacity: 0; transform: translateY(40px); transition: opacity 500ms cubic-bezier(0, 0, 0.5, 1), transform 550ms cubic-bezier(0.22, 1, 0.5, 0.95); } .dialog-lite-close-button { cursor: pointer; position: absolute; inset: 0 0 auto auto; display: grid; place-content: center; width: 50px; height: 50px; } .dialog-lite-close-button .svg-icon { width: 24px; height: 24px; fill: black; } `,c=r;function u(s,t){const e=typeof CSS<"u"&&"escape"in CSS?CSS.escape(t):t.replace(/"/g,'\\"');return s.querySelector(`#${e}`)}function d(s={}){const{target:t=document,cssText:e=r,id:i="dialog-lite-styles"}=s,n=u(t,i);if(n&&n instanceof HTMLStyleElement)return n.textContent=e,n;const o=document.createElement("style");return o.id=i,o.textContent=e,t instanceof Document?t.head.append(o):t.append(o),o}class a{options;dialogEl=null;dialogCloseEl=null;dialogBackdropEl=null;mainContentEl=null;currentExtraClass="";previouslyFocusedElement=null;lastActionTime=0;isOpen=!1;abortController=null;hideTimeout=null;removeExtraClassTimeout=null;prevBodyOverflow=null;prevBodyPaddingRight=null;constructor(t={}){this.options={closingButton:t.closingButton??!1,closingBackdrop:t.closingBackdrop??!1,dialog:t.dialog??".dialog-lite",mainContent:t.mainContent??"#main-content",closeButtonSelector:t.closeButtonSelector??".dialog-lite-close-button",backdropSelector:t.backdropSelector??".dialog-lite__backdrop",debounceMs:t.debounceMs??500,hideDelayMs:t.hideDelayMs??500,focusOnOpenSelector:t.focusOnOpenSelector??'[tabindex="0"]',lockScroll:t.lockScroll??!0,trapFocus:t.trapFocus??!0,role:t.role??"dialog",ariaModal:t.ariaModal??!0,emitEvents:t.emitEvents??!0}}resolveHTMLElement(t){return t==null?null:typeof t=="string"?document.querySelector(t):t}resolveElementsOrThrow(){if(this.dialogEl=this.resolveHTMLElement(this.options.dialog),this.mainContentEl=this.resolveHTMLElement(this.options.mainContent??null),!this.dialogEl)throw new Error("Dialog element not found. Provide { dialog } option or ensure `.dialog-lite` exists.");this.dialogCloseEl=this.dialogEl.querySelector(this.options.closeButtonSelector),this.dialogBackdropEl=this.dialogEl.querySelector(this.options.backdropSelector),this.dialogEl.hasAttribute("tabindex")||this.dialogEl.setAttribute("tabindex","-1"),this.options.role&&this.dialogEl.setAttribute("role",this.options.role),this.options.ariaModal&&this.dialogEl.setAttribute("aria-modal","true")}emit(t,e){!this.options.emitEvents||!this.dialogEl||this.dialogEl.dispatchEvent(new CustomEvent(t,{detail:e}))}lockScroll(){if(!this.options.lockScroll||this.prevBodyOverflow!=null)return;const t=document.body;this.prevBodyOverflow=t.style.overflow,this.prevBodyPaddingRight=t.style.paddingRight;const e=window.innerWidth-document.documentElement.clientWidth;if(e>0){const i=Number.parseFloat(getComputedStyle(t).paddingRight||"0");t.style.paddingRight=`${i+e}px`}t.style.overflow="hidden"}unlockScroll(){if(this.prevBodyOverflow==null)return;const t=document.body;t.style.overflow=this.prevBodyOverflow,t.style.paddingRight=this.prevBodyPaddingRight??"",this.prevBodyOverflow=null,this.prevBodyPaddingRight=null}getFocusableElements(){return this.dialogEl?Array.from(this.dialogEl.querySelectorAll('a[href],button:not([disabled]),input:not([disabled]),select:not([disabled]),textarea:not([disabled]),[tabindex]:not([tabindex="-1"])')).filter(i=>!i.hasAttribute("disabled")&&i.tabIndex>=0&&i.getClientRects().length>0):[]}handleFocusTrapKeydown(t){if(!this.options.trapFocus||!this.isOpen||t.key!=="Tab")return;const e=this.getFocusableElements();if(!e.length){this.dialogEl?.focus?.(),t.preventDefault();return}const i=document.activeElement,n=i instanceof HTMLElement?e.indexOf(i):-1,f=t.shiftKey?n<=0?e.length-1:n-1:n>=e.length-1?0:n+1;e[f]?.focus?.(),t.preventDefault()}clearTimers(){this.hideTimeout!=null&&(window.clearTimeout(this.hideTimeout),this.hideTimeout=null),this.removeExtraClassTimeout!=null&&(window.clearTimeout(this.removeExtraClassTimeout),this.removeExtraClassTimeout=null)}ensureInitialized(){this.abortController||this.init()}init(){this.destroy(),this.resolveElementsOrThrow(),this.abortController=new AbortController;const t=this.abortController.signal;this.options.closingButton&&this.dialogCloseEl&&this.dialogCloseEl.addEventListener("click",()=>this.close(),{signal:t}),this.options.closingBackdrop&&this.dialogBackdropEl&&this.dialogBackdropEl.addEventListener("click",()=>this.close(),{signal:t}),document.addEventListener("keydown",e=>{e.key==="Escape"&&this.isOpen&&this.close()},{signal:t}),this.dialogEl?.addEventListener("keydown",e=>this.handleFocusTrapKeydown(e),{signal:t})}destroy(){this.abortController?.abort(),this.abortController=null,this.clearTimers(),this.unlockScroll()}open({stylingClass:t=""}={}){if(this.isDebounced()||(this.ensureInitialized(),!this.dialogEl))return;this.clearTimers(),this.dialogEl.hidden=!1,this.dialogEl.offsetWidth,this.isOpen=!0,this.lockScroll(),this.mainContentEl&&this.mainContentEl.setAttribute("aria-hidden","true"),this.dialogEl.setAttribute("aria-hidden","false");const e=document.activeElement;this.previouslyFocusedElement=e instanceof HTMLElement?e:null,this.updateClassList({addClass:"dialog-lite--in",removeClass:"dialog-lite--out",newClass:t});const i=this.dialogEl.querySelector(this.options.focusOnOpenSelector);i?.focus?.(),i||this.dialogEl.focus(),this.emit("dialog-lite:open",{stylingClass:t})}close(){this.isDebounced()||(this.ensureInitialized(),this.dialogEl&&(this.isOpen=!1,this.unlockScroll(),this.mainContentEl&&this.mainContentEl.setAttribute("aria-hidden","false"),this.dialogEl.setAttribute("aria-hidden","true"),this.previouslyFocusedElement?.isConnected&&this.previouslyFocusedElement.focus(),this.updateClassList({addClass:"dialog-lite--out",removeClass:"dialog-lite--in",newClass:"",delayRemove:!0}),this.hideTimeout=window.setTimeout(()=>{this.dialogEl&&(this.dialogEl.hidden=!0),this.hideTimeout=null},this.options.hideDelayMs),this.emit("dialog-lite:close",{})))}updateClassList({addClass:t,removeClass:e,newClass:i,delayRemove:n=!1}){if(this.dialogEl){if(this.currentExtraClass)if(n){const o=this.currentExtraClass;this.removeExtraClassTimeout=window.setTimeout(()=>{this.dialogEl?.classList.remove(o),this.currentExtraClass="",this.removeExtraClassTimeout=null},this.options.hideDelayMs)}else this.dialogEl.classList.remove(this.currentExtraClass),this.currentExtraClass="";this.dialogEl.classList.remove(e),this.dialogEl.classList.add(t),i&&(this.dialogEl.classList.add(i),this.currentExtraClass=i)}}isDebounced(){const t=Date.now();return t-this.lastActionTime<this.options.debounceMs?!0:(this.lastActionTime=t,!1)}}function h(s={}){return new a(s)}function g(s={}){const{injectCss:t=!0,cssText:e,cssTarget:i,...n}=s;t&&d({cssText:e,target:i});const o=new a(n);return o.init(),o}l.DialogLite=a,l.createDialogLite=h,l.dialogLiteCssText=c,l.initDialogLite=g,l.injectDialogLiteCss=d,Object.defineProperty(l,Symbol.toStringTag,{value:"Module"})}));