UNPKG

easy-js-modal

Version:

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

208 lines (207 loc) 6.89 kB
var s = Object.defineProperty var d = (i, e, t) => e in i ? s(i, e, { enumerable: !0, configurable: !0, writable: !0, value: t }) : (i[e] = t) var l = (i, e, t) => (d(i, typeof e != 'symbol' ? e + '' : e, t), t) class r { constructor() { l(this, 'activeModal', null) l(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 a = new r(), c = () => { ;(document.body.style.paddingRight = ''), (document.body.style.overflow = '') }, m = () => { const i = document.createElement('div') ;(i.style.visibility = 'hidden'), (i.style.width = '100px'), (i.style.overflow = 'scroll'), document.body.appendChild(i) const e = document.createElement('div') ;(e.style.width = '100%'), i.appendChild(e) const t = i.offsetWidth - e.offsetWidth return i.remove(), t }, h = (i) => { if (typeof i != 'string') throw new Error('The input should be a string.') return `--ejm-${i.replace(/([a-z0-9])([A-Z])/g, '$1-$2').toLowerCase()}` } class b { constructor(e, t, o) { l(this, 'modalElement') l(this, 'modalWindow') l(this, 'scrollbarWidth') l(this, 'modalBlockClass') l(this, 'closeButtonClass') l(this, 'animationDuration') l(this, 'modalWindowClass') l(this, 'onOpen') l(this, 'onClose') l(this, 'modalStyles') l(this, 'openTimeout', null) l(this, 'closeTimeout', null) l(this, 'handleKeyDown', (e) => { e.key === 'Escape' && this.close(), this.trapFocus(e) }) ;(this.onOpen = t == null ? void 0 : t.onOpen), (this.onClose = t == null ? void 0 : t.onClose), (this.modalBlockClass = (t == null ? void 0 : t.modalBlockClass) || 'modal'), (this.animationDuration = (t == null ? void 0 : t.animationDuration) || 300), (this.closeButtonClass = `${this.modalBlockClass}__close`), (this.modalWindowClass = `${this.modalBlockClass}__window`), (this.modalStyles = { layoutBackgroundColor: (o == null ? void 0 : o.layoutBackgroundColor) || 'rgba(0, 0, 0, 0.5)', animationDuration: `${this.animationDuration}ms`, windowBackgroundColor: (o == null ? void 0 : o.windowBackgroundColor) || '#fff', windowWidth: (o == null ? void 0 : o.windowWidth) || '90%', windowMaxWidth: (o == null ? void 0 : o.windowMaxWidth) || '500px', windowPadding: (o == null ? void 0 : o.windowPadding) || '2rem', windowBorderRadius: (o == null ? void 0 : o.windowBorderRadius) || '0.5rem', closeFontSize: (o == null ? void 0 : o.closeFontSize) || '1.25rem' }), (this.modalWindow = this.createModalWindow(e)), (this.modalElement = this.createModalElement()), (this.scrollbarWidth = m()), this.init() } init() { if (a.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 t = document.createElement('div') t.classList.add(this.modalWindowClass) const o = `<button class="${this.closeButtonClass}">×</button>` return (t.innerHTML = (typeof e == 'string' ? e : e.outerHTML) + o), t } getStylesForModal() { return Object.entries(this.modalStyles).map(([e, t]) => `${h(e)}: ${t};`) } setContent(e) { const t = this.modalWindow.querySelector(`.${this.closeButtonClass}`) ;(this.modalWindow.innerHTML = ''), typeof e == 'string' ? (this.modalWindow.innerHTML = e) : this.modalWindow.appendChild(e), t && this.modalWindow.appendChild(t) } open() { if (a.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(), (a.focusableElementBeforeModal = document.activeElement) const e = this.modalWindow.querySelector( 'button, [href], input, select, textarea, [tabindex]:not([tabindex="-1"])' ) e && e.focus(), a.setActiveModal(this) } close() { var e this.modalElement.classList.remove(`${this.modalBlockClass}--visible`), this.modalWindow.classList.remove(`${this.modalWindowClass}--visible`), c(), (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 = a.focusableElementBeforeModal) == null || e.focus(), a.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 t = this.getFocusableElements(), o = t[0], n = t[t.length - 1] e.shiftKey ? document.activeElement === o && (n.focus(), e.preventDefault()) : document.activeElement === n && (o.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( (o) => !o.hasAttribute('tabindex') || o.tabIndex >= 0 ) } } export { b as default }