UNPKG

easy-js-modal

Version:

EasyJsModal - A lightweight, easy-to-use, and customizable modal plugin for JavaScript applications

215 lines (214 loc) 7.23 kB
;(function (a) { typeof define == 'function' && define.amd ? define(a) : a() })(function () { 'use strict' var h = Object.defineProperty var u = (a, l, s) => l in a ? h(a, l, { enumerable: !0, configurable: !0, writable: !0, value: s }) : (a[l] = s) var i = (a, l, s) => (u(a, typeof l != 'symbol' ? l + '' : l, s), s) class a { constructor() { i(this, 'activeModal', null) i(this, 'focusableElementBeforeModal', null) } setActiveModal(e) { this.activeModal && this.activeModal.close(), (this.activeModal = e) } removeActiveModal(e) { this.activeModal === e && (this.activeModal = null) } get isActiveModal() { return this.activeModal !== null } } const l = new a(), s = () => { ;(document.body.style.paddingRight = ''), (document.body.style.overflow = '') }, r = () => { const n = document.createElement('div') ;(n.style.visibility = 'hidden'), (n.style.width = '100px'), (n.style.overflow = 'scroll'), document.body.appendChild(n) const e = document.createElement('div') ;(e.style.width = '100%'), n.appendChild(e) const o = n.offsetWidth - e.offsetWidth return n.remove(), o }, c = (n) => { if (typeof n != 'string') throw new Error('The input should be a string.') return `--ejm-${n.replace(/([a-z0-9])([A-Z])/g, '$1-$2').toLowerCase()}` } class m { constructor(e, o, t) { i(this, 'modalElement') i(this, 'modalWindow') i(this, 'scrollbarWidth') i(this, 'modalBlockClass') i(this, 'closeButtonClass') i(this, 'animationDuration') i(this, 'modalWindowClass') i(this, 'onOpen') i(this, 'onClose') i(this, 'modalStyles') i(this, 'openTimeout', null) i(this, 'closeTimeout', null) i(this, 'handleKeyDown', (e) => { e.key === 'Escape' && this.close(), this.trapFocus(e) }) ;(this.onOpen = o == null ? void 0 : o.onOpen), (this.onClose = o == null ? void 0 : o.onClose), (this.modalBlockClass = (o == null ? void 0 : o.modalBlockClass) || 'modal'), (this.animationDuration = (o == null ? void 0 : o.animationDuration) || 300), (this.closeButtonClass = `${this.modalBlockClass}__close`), (this.modalWindowClass = `${this.modalBlockClass}__window`), (this.modalStyles = { layoutBackgroundColor: (t == null ? void 0 : t.layoutBackgroundColor) || 'rgba(0, 0, 0, 0.5)', animationDuration: `${this.animationDuration}ms`, windowBackgroundColor: (t == null ? void 0 : t.windowBackgroundColor) || '#fff', windowWidth: (t == null ? void 0 : t.windowWidth) || '90%', windowMaxWidth: (t == null ? void 0 : t.windowMaxWidth) || '500px', windowPadding: (t == null ? void 0 : t.windowPadding) || '2rem', windowBorderRadius: (t == null ? void 0 : t.windowBorderRadius) || '0.5rem', closeFontSize: (t == null ? void 0 : t.closeFontSize) || '1.25rem' }), (this.modalWindow = this.createModalWindow(e)), (this.modalElement = this.createModalElement()), (this.scrollbarWidth = r()), this.init() } init() { if (l.isActiveModal) { console.warn( 'Another modal is already open. Please close it before opening a new one.' ) return } document.body.appendChild(this.modalElement) } createModalElement() { const e = document.createElement('div') return ( e.classList.add(this.modalBlockClass), e.appendChild(this.modalWindow), e ) } createModalWindow(e) { const o = document.createElement('div') o.classList.add(this.modalWindowClass) const t = `<button class="${this.closeButtonClass}">×</button>` return (o.innerHTML = (typeof e == 'string' ? e : e.outerHTML) + t), o } getStylesForModal() { return Object.entries(this.modalStyles).map(([e, o]) => `${c(e)}: ${o};`) } setContent(e) { const o = this.modalWindow.querySelector(`.${this.closeButtonClass}`) ;(this.modalWindow.innerHTML = ''), typeof e == 'string' ? (this.modalWindow.innerHTML = e) : this.modalWindow.appendChild(e), o && this.modalWindow.appendChild(o) } open() { if (l.isActiveModal) { console.warn( 'Another modal is already open. Please close it before opening a new one.' ) return } ;(this.modalElement.style.cssText = this.getStylesForModal().join(' ')), (this.modalElement.style.display = 'flex'), (this.openTimeout = setTimeout(() => { this.modalElement.classList.add(`${this.modalBlockClass}--visible`), this.modalWindow.classList.add(`${this.modalWindowClass}--visible`), this.onOpen && this.onOpen(), this.openTimeout !== null && (clearTimeout(this.openTimeout), (this.openTimeout = null)) }, this.animationDuration + 50)), this.disableScroll(), this.addEventListeners(), (l.focusableElementBeforeModal = document.activeElement) const e = this.modalWindow.querySelector( 'button, [href], input, select, textarea, [tabindex]:not([tabindex="-1"])' ) e && e.focus(), l.setActiveModal(this) } close() { var e this.modalElement.classList.remove(`${this.modalBlockClass}--visible`), this.modalWindow.classList.remove(`${this.modalWindowClass}--visible`), s(), (this.closeTimeout = setTimeout(() => { ;(this.modalElement.style.display = ''), this.removeEventListeners(), this.destroy(), this.onClose && this.onClose(), this.closeTimeout !== null && (clearTimeout(this.closeTimeout), (this.closeTimeout = null)) }, this.animationDuration + 50)), (e = l.focusableElementBeforeModal) == null || e.focus(), l.removeActiveModal(this) } destroy() { this.modalElement.remove() } addEventListeners() { this.modalElement .querySelector(`.${this.closeButtonClass}`) .addEventListener('click', () => this.close()), this.modalElement.addEventListener('click', (e) => { e.target === this.modalElement && this.close() }), document.addEventListener('keydown', this.handleKeyDown) } removeEventListeners() { this.modalElement .querySelector(`.${this.closeButtonClass}`) .removeEventListener('click', () => this.close()), this.modalElement.removeEventListener('click', (e) => { e.target === this.modalElement && this.close() }), document.removeEventListener('keydown', this.handleKeyDown) } disableScroll() { ;(document.body.style.paddingRight = `${this.scrollbarWidth}px`), (document.body.style.overflow = 'hidden') } trapFocus(e) { if (e.key !== 'Tab') return const o = this.getFocusableElements(), t = o[0], d = o[o.length - 1] e.shiftKey ? document.activeElement === t && (d.focus(), e.preventDefault()) : document.activeElement === d && (t.focus(), e.preventDefault()) } getFocusableElements() { const e = ` a[href], button:not([disabled]), textarea:not([disabled]), input[type="text"]:not([disabled]), input[type="radio"]:not([disabled]), input[type="checkbox"]:not([disabled]), select:not([disabled]) ` return Array.from(this.modalElement.querySelectorAll(e)).filter( (t) => !t.hasAttribute('tabindex') || t.tabIndex >= 0 ) } } typeof window < 'u' && (window.EasyJsModal = m) })