(function (global, factory) { typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory() : typeof define === 'function' && define.amd ? define(factory) : (global.Layer = factory()); }(this, (function () { 'use strict'; const PREFIX = 'layer-'; // Theme & Styles const css = ` .${PREFIX}overlay { position: fixed; top: 0; left: 0; width: 100%; height: 100%; background: rgba(0, 0, 0, 0.4); display: flex; justify-content: center; align-items: center; z-index: 1000; opacity: 0; transition: opacity 0.3s; } .${PREFIX}overlay.show { opacity: 1; } .${PREFIX}popup { background: #fff; border-radius: 8px; box-shadow: 0 4px 12px rgba(0,0,0,0.15); width: 32em; max-width: 90%; padding: 1.5em; display: flex; flex-direction: column; align-items: center; transform: scale(0.9); transition: transform 0.3s; font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif; } .${PREFIX}overlay.show .${PREFIX}popup { transform: scale(1); } .${PREFIX}title { font-size: 1.8em; font-weight: 600; color: #333; margin: 0 0 0.5em; text-align: center; } .${PREFIX}content { font-size: 1.125em; color: #545454; margin-bottom: 1.5em; text-align: center; line-height: 1.5; } .${PREFIX}actions { display: flex; justify-content: center; gap: 1em; width: 100%; } .${PREFIX}button { border: none; border-radius: 4px; padding: 0.6em 1.2em; font-size: 1em; cursor: pointer; transition: background-color 0.2s, box-shadow 0.2s; color: #fff; } .${PREFIX}confirm { background-color: #3085d6; } .${PREFIX}confirm:hover { background-color: #2b77c0; } .${PREFIX}cancel { background-color: #aaa; } .${PREFIX}cancel:hover { background-color: #999; } .${PREFIX}icon { width: 5em; height: 5em; border: 0.25em solid transparent; border-radius: 50%; margin: 1.5em auto 1.2em; box-sizing: content-box; position: relative; } /* Success Icon */ .${PREFIX}icon.success { border-color: #a5dc86; color: #a5dc86; } .${PREFIX}icon.success::before, .${PREFIX}icon.success::after { content: ''; position: absolute; background: #fff; border-radius: 50%; } .${PREFIX}success-line-tip { width: 1.5em; height: 0.3em; background-color: #a5dc86; display: block; position: absolute; top: 2.85em; left: 0.85em; transform: rotate(45deg); border-radius: 0.15em; } .${PREFIX}success-line-long { width: 2.9em; height: 0.3em; background-color: #a5dc86; display: block; position: absolute; top: 2.4em; right: 0.5em; transform: rotate(-45deg); border-radius: 0.15em; } /* Error Icon */ .${PREFIX}icon.error { border-color: #f27474; color: #f27474; } .${PREFIX}error-x-mark { position: relative; display: block; } .${PREFIX}error-line { position: absolute; height: 0.3em; width: 3em; background-color: #f27474; display: block; top: 2.3em; left: 1em; border-radius: 0.15em; } .${PREFIX}error-line.left { transform: rotate(45deg); } .${PREFIX}error-line.right { transform: rotate(-45deg); } /* Warning Icon */ .${PREFIX}icon.warning { border-color: #facea8; color: #f8bb86; } .${PREFIX}warning-body { position: absolute; width: 0.3em; height: 2.9em; background-color: #f8bb86; left: 50%; top: 0.6em; margin-left: -0.15em; border-radius: 0.15em; } .${PREFIX}warning-dot { position: absolute; width: 0.3em; height: 0.3em; background-color: #f8bb86; left: 50%; bottom: 0.6em; margin-left: -0.15em; border-radius: 50%; } `; // Inject Styles const injectStyles = () => { if (document.getElementById(`${PREFIX}styles`)) return; const styleSheet = document.createElement('style'); styleSheet.id = `${PREFIX}styles`; styleSheet.textContent = css; document.head.appendChild(styleSheet); }; class Layer { constructor() { injectStyles(); this.params = {}; this.dom = {}; this.promise = null; this.resolve = null; this.reject = null; } // Static entry point static fire(options) { const instance = new Layer(); return instance._fire(options); } _fire(options = {}) { if (typeof options === 'string') { options = { title: options }; if (arguments[1]) options.text = arguments[1]; if (arguments[2]) options.icon = arguments[2]; } this.params = { title: '', text: '', icon: null, confirmButtonText: 'OK', cancelButtonText: 'Cancel', showCancelButton: false, confirmButtonColor: '#3085d6', cancelButtonColor: '#aaa', closeOnClickOutside: true, ...options }; this.promise = new Promise((resolve, reject) => { this.resolve = resolve; this.reject = reject; }); this._render(); return this.promise; } _render() { // Remove existing if any const existing = document.querySelector(`.${PREFIX}overlay`); if (existing) existing.remove(); // Create Overlay this.dom.overlay = document.createElement('div'); this.dom.overlay.className = `${PREFIX}overlay`; // Create Popup this.dom.popup = document.createElement('div'); this.dom.popup.className = `${PREFIX}popup`; this.dom.overlay.appendChild(this.dom.popup); // Icon if (this.params.icon) { this.dom.icon = this._createIcon(this.params.icon); this.dom.popup.appendChild(this.dom.icon); } // Title if (this.params.title) { this.dom.title = document.createElement('h2'); this.dom.title.className = `${PREFIX}title`; this.dom.title.textContent = this.params.title; this.dom.popup.appendChild(this.dom.title); } // Text if (this.params.text) { this.dom.content = document.createElement('div'); this.dom.content.className = `${PREFIX}content`; this.dom.content.textContent = this.params.text; this.dom.popup.appendChild(this.dom.content); } // Actions this.dom.actions = document.createElement('div'); this.dom.actions.className = `${PREFIX}actions`; // Cancel Button if (this.params.showCancelButton) { this.dom.cancelBtn = document.createElement('button'); this.dom.cancelBtn.className = `${PREFIX}button ${PREFIX}cancel`; this.dom.cancelBtn.textContent = this.params.cancelButtonText; this.dom.cancelBtn.style.backgroundColor = this.params.cancelButtonColor; this.dom.cancelBtn.onclick = () => this._close(false); this.dom.actions.appendChild(this.dom.cancelBtn); } // Confirm Button this.dom.confirmBtn = document.createElement('button'); this.dom.confirmBtn.className = `${PREFIX}button ${PREFIX}confirm`; this.dom.confirmBtn.textContent = this.params.confirmButtonText; this.dom.confirmBtn.style.backgroundColor = this.params.confirmButtonColor; this.dom.confirmBtn.onclick = () => this._close(true); this.dom.actions.appendChild(this.dom.confirmBtn); this.dom.popup.appendChild(this.dom.actions); // Event Listeners if (this.params.closeOnClickOutside) { this.dom.overlay.addEventListener('click', (e) => { if (e.target === this.dom.overlay) { this._close(null); // Dismiss } }); } document.body.appendChild(this.dom.overlay); // Animation requestAnimationFrame(() => { this.dom.overlay.classList.add('show'); }); } _createIcon(type) { const icon = document.createElement('div'); icon.className = `${PREFIX}icon ${type}`; if (type === 'success') { const tip = document.createElement('div'); tip.className = `${PREFIX}success-line-tip`; const long = document.createElement('div'); long.className = `${PREFIX}success-line-long`; icon.appendChild(tip); icon.appendChild(long); } else if (type === 'error') { const xMark = document.createElement('span'); xMark.className = `${PREFIX}error-x-mark`; const left = document.createElement('span'); left.className = `${PREFIX}error-line left`; const right = document.createElement('span'); right.className = `${PREFIX}error-line right`; xMark.appendChild(left); xMark.appendChild(right); icon.appendChild(xMark); } else if (type === 'warning') { const body = document.createElement('div'); body.className = `${PREFIX}warning-body`; const dot = document.createElement('div'); dot.className = `${PREFIX}warning-dot`; icon.appendChild(body); icon.appendChild(dot); } return icon; } _close(isConfirmed) { this.dom.overlay.classList.remove('show'); setTimeout(() => { if (this.dom.overlay && this.dom.overlay.parentNode) { this.dom.overlay.parentNode.removeChild(this.dom.overlay); } }, 300); if (isConfirmed === true) { this.resolve({ isConfirmed: true, isDenied: false, isDismissed: false }); } else if (isConfirmed === false) { this.resolve({ isConfirmed: false, isDenied: false, isDismissed: true, dismiss: 'cancel' }); } else { this.resolve({ isConfirmed: false, isDenied: false, isDismissed: true, dismiss: 'backdrop' }); } } } return Layer; })));