| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843 |
- (function (global, factory) {
- const LayerClass = factory();
- // Allow usage as `Layer({...})` or `new Layer()`
- // But Layer is a class. We can wrap it in a proxy or factory function.
-
- function LayerFactory(options) {
- if (options && typeof options === 'object') {
- return LayerClass.$(options);
- }
- return new LayerClass();
- }
-
- // Copy static methods (including non-enumerable class statics like `fire` / `$`)
- // Class static methods are non-enumerable by default, so Object.assign() would miss them.
- const copyStatic = (to, from) => {
- try {
- Object.getOwnPropertyNames(from).forEach((k) => {
- if (k === 'prototype' || k === 'name' || k === 'length') return;
- const desc = Object.getOwnPropertyDescriptor(from, k);
- if (!desc) return;
- Object.defineProperty(to, k, desc);
- });
- } catch (e) {
- // Best-effort fallback
- try { Object.assign(to, from); } catch {}
- }
- };
- copyStatic(LayerFactory, LayerClass);
- // Also copy prototype for instanceof checks if needed (though tricky with factory)
- LayerFactory.prototype = LayerClass.prototype;
-
- typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = LayerFactory :
- typeof define === 'function' && define.amd ? define(() => LayerFactory) :
- (global.Layer = LayerFactory);
- }(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;
- position: relative;
- z-index: 1;
- transform: scale(0.92);
- transition: transform 0.26s ease;
- 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 {
- position: relative;
- box-sizing: content-box;
- width: 5em;
- height: 5em;
- margin: 0.5em auto 1.8em;
- border: 0.25em solid rgba(0, 0, 0, 0);
- border-radius: 50%;
- font-family: inherit;
- line-height: 5em;
- cursor: default;
- user-select: none;
- /* Ring sweep angles (match SweetAlert2 success feel) */
- --${PREFIX}ring-start-rotate: -45deg;
- /* End is one full turn from start so it "sweeps then settles" */
- --${PREFIX}ring-end-rotate: -405deg;
- }
- /* SVG mark content (kept), sits above the ring */
- .${PREFIX}icon svg {
- width: 100%;
- height: 100%;
- display: block;
- overflow: visible;
- position: relative;
- z-index: 3;
- }
- .${PREFIX}svg-ring,
- .${PREFIX}svg-mark {
- fill: none;
- stroke-linecap: round;
- stroke-linejoin: round;
- }
- .${PREFIX}svg-ring {
- stroke-width: 4.5;
- opacity: 0.95;
- }
- .${PREFIX}svg-mark {
- stroke-width: 6;
- }
- .${PREFIX}svg-dot {
- transform-box: fill-box;
- transform-origin: center;
- }
- /* =========================================
- "success-like" ring (shared by all icons)
- - we reuse the success ring pieces/rotation for every icon type
- - color is driven by currentColor
- ========================================= */
- .${PREFIX}icon.success { border-color: #a5dc86; color: #a5dc86; }
- .${PREFIX}icon.error { border-color: #f27474; color: #f27474; }
- .${PREFIX}icon.warning { border-color: #f8bb86; color: #f8bb86; }
- .${PREFIX}icon.info { border-color: #3fc3ee; color: #3fc3ee; }
- .${PREFIX}icon.question { border-color: #b18cff; color: #b18cff; }
- /* Keep ring sweep logic identical across built-in icons (match success). */
- .${PREFIX}icon .${PREFIX}success-ring {
- position: absolute;
- z-index: 2;
- top: -0.25em;
- left: -0.25em;
- box-sizing: content-box;
- width: 100%;
- height: 100%;
- border: 0.25em solid currentColor;
- opacity: 0.3;
- border-radius: 50%;
- }
- .${PREFIX}icon .${PREFIX}success-fix {
- position: absolute;
- z-index: 1;
- top: 0.5em;
- left: 1.625em;
- width: 0.4375em;
- height: 5.625em;
- transform: rotate(-45deg);
- background-color: #fff; /* adjusted at runtime to popup bg */
- }
- .${PREFIX}icon .${PREFIX}success-circular-line-left,
- .${PREFIX}icon .${PREFIX}success-circular-line-right {
- position: absolute;
- width: 3.75em;
- height: 7.5em;
- border-radius: 50%;
- background-color: #fff; /* adjusted at runtime to popup bg */
- }
- .${PREFIX}icon .${PREFIX}success-circular-line-left {
- top: -0.4375em;
- left: -2.0635em;
- transform: rotate(-45deg);
- transform-origin: 3.75em 3.75em;
- border-radius: 7.5em 0 0 7.5em;
- }
- .${PREFIX}icon .${PREFIX}success-circular-line-right {
- top: -0.6875em;
- left: 1.875em;
- transform: rotate(-45deg);
- transform-origin: 0 3.75em;
- border-radius: 0 7.5em 7.5em 0;
- }
- .${PREFIX}icon .${PREFIX}success-line-tip,
- .${PREFIX}icon .${PREFIX}success-line-long {
- display: block;
- position: absolute;
- z-index: 2;
- height: 0.3125em;
- border-radius: 0.125em;
- background-color: currentColor;
- }
- .${PREFIX}icon .${PREFIX}success-line-tip {
- top: 2.875em;
- left: 0.8125em;
- width: 1.5625em;
- transform: rotate(45deg);
- }
- .${PREFIX}icon .${PREFIX}success-line-long {
- top: 2.375em;
- right: 0.5em;
- width: 2.9375em;
- transform: rotate(-45deg);
- }
- /* Triggered when icon has .${PREFIX}icon-show */
- .${PREFIX}icon.${PREFIX}icon-show .${PREFIX}success-line-tip {
- animation: ${PREFIX}animate-success-line-tip 0.75s;
- }
- .${PREFIX}icon.${PREFIX}icon-show .${PREFIX}success-line-long {
- animation: ${PREFIX}animate-success-line-long 0.75s;
- }
- .${PREFIX}icon.${PREFIX}icon-show .${PREFIX}success-circular-line-right {
- animation: ${PREFIX}rotate-icon-circular-line 4.25s ease-in;
- }
- @keyframes ${PREFIX}animate-success-line-tip {
- 0% {
- top: 1.1875em;
- left: 0.0625em;
- width: 0;
- }
- 54% {
- top: 1.0625em;
- left: 0.125em;
- width: 0;
- }
- 70% {
- top: 2.1875em;
- left: -0.375em;
- width: 3.125em;
- }
- 84% {
- top: 3em;
- left: 1.3125em;
- width: 1.0625em;
- }
- 100% {
- top: 2.8125em;
- left: 0.8125em;
- width: 1.5625em;
- }
- }
- @keyframes ${PREFIX}animate-success-line-long {
- 0% {
- top: 3.375em;
- right: 2.875em;
- width: 0;
- }
- 65% {
- top: 3.375em;
- right: 2.875em;x
- width: 0;
- }
- 84% {
- top: 2.1875em;
- right: 0;
- width: 3.4375em;
- }
- 100% {
- top: 2.375em;
- right: 0.5em;
- width: 2.9375em;
- }
- }
- @keyframes ${PREFIX}rotate-icon-circular-line {
- 0% { transform: rotate(var(--${PREFIX}ring-start-rotate)); }
- 5% { transform: rotate(var(--${PREFIX}ring-start-rotate)); }
- 12% { transform: rotate(var(--${PREFIX}ring-end-rotate)); }
- 100% { transform: rotate(var(--${PREFIX}ring-end-rotate)); } /* match 12% so it "sweeps then stops" */
- }
- /* (Other icon shapes stay SVG-driven; only the ring animation is unified above) */
- `;
- // 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;
- this._onKeydown = null;
- }
- // Constructor helper when called as function: const popup = Layer({...})
- static get isProxy() { return true; }
-
- // Static entry point
- static fire(options) {
- const instance = new Layer();
- return instance._fire(options);
- }
-
- // Chainable entry point (builder-style)
- // Example:
- // Layer.$({ title: 'Hi' }).fire().then(...)
- // Layer.$().config({ title: 'Hi' }).fire()
- static $(options) {
- const instance = new Layer();
- if (options !== undefined) instance.config(options);
- return instance;
- }
-
- // Chainable config helper (does not render until `.fire()` is called)
- config(options = {}) {
- // Support the same shorthand as Layer.fire(title, text, icon)
- if (typeof options === 'string') {
- options = { title: options };
- if (arguments[1]) options.text = arguments[1];
- if (arguments[2]) options.icon = arguments[2];
- }
- this.params = { ...(this.params || {}), ...options };
- return this;
- }
-
- // Instance entry point (chainable)
- fire(options) {
- const merged = (options === undefined) ? (this.params || {}) : options;
- return this._fire(merged);
- }
- _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,
- iconSize: null, // e.g. '6em' / '72px'
- confirmButtonText: 'OK',
- cancelButtonText: 'Cancel',
- showCancelButton: false,
- confirmButtonColor: '#3085d6',
- cancelButtonColor: '#aaa',
- closeOnClickOutside: true,
- closeOnEsc: true,
- iconAnimation: true,
- popupAnimation: true,
- ...options
- };
- this.promise = new Promise((resolve, reject) => {
- this.resolve = resolve;
- this.reject = reject;
- });
- this._render();
- return this.promise;
- }
- static _getXjs() {
- // Prefer xjs, fallback to animal (compat)
- const g = (typeof window !== 'undefined') ? window : (typeof globalThis !== 'undefined' ? globalThis : null);
- if (!g) return null;
- const x = g.xjs || g.animal;
- return (typeof x === 'function') ? x : null;
- }
- _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);
- }
- // Content (Text / HTML / Element)
- if (this.params.text || this.params.html || this.params.content) {
- this.dom.content = document.createElement('div');
- this.dom.content.className = `${PREFIX}content`;
-
- if (this.params.content) {
- // DOM Element or Selector
- let el = this.params.content;
- if (typeof el === 'string') el = document.querySelector(el);
- if (el instanceof Element) this.dom.content.appendChild(el);
- } else if (this.params.html) {
- this.dom.content.innerHTML = this.params.html;
- } else {
- 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');
- this._didOpen();
- });
- }
- _createIcon(type) {
- const icon = document.createElement('div');
- icon.className = `${PREFIX}icon ${type}`;
- const applyIconSize = (mode) => {
- if (!(this.params && this.params.iconSize)) return;
- try {
- const raw = this.params.iconSize;
- const s = String(raw).trim();
- if (!s) return;
- // For SweetAlert2-style success icon, scale via font-size so all `em`-based
- // parts remain proportional.
- if (mode === 'font') {
- const m = s.match(/^(-?\d*\.?\d+)\s*(px|em|rem)$/i);
- if (m) {
- const n = parseFloat(m[1]);
- const unit = m[2];
- if (Number.isFinite(n) && n > 0) {
- icon.style.fontSize = (n / 5) + unit; // icon is 5em wide/tall
- return;
- }
- }
- }
- // Fallback: directly size the box (works great for SVG icons)
- icon.style.width = s;
- icon.style.height = s;
- } catch {}
- };
-
- const svgNs = 'http://www.w3.org/2000/svg';
- if (type === 'success') {
- // SweetAlert2-compatible DOM structure (circle + tick) for exact style parity
- // <div class="...success-circular-line-left"></div>
- // <span class="...success-line-tip"></span>
- // <span class="...success-line-long"></span>
- // <div class="...success-ring"></div>
- // <div class="...success-fix"></div>
- // <div class="...success-circular-line-right"></div>
- const left = document.createElement('div');
- left.className = `${PREFIX}success-circular-line-left`;
- const tip = document.createElement('span');
- tip.className = `${PREFIX}success-line-tip`;
- const long = document.createElement('span');
- long.className = `${PREFIX}success-line-long`;
- const ring = document.createElement('div');
- ring.className = `${PREFIX}success-ring`;
- const fix = document.createElement('div');
- fix.className = `${PREFIX}success-fix`;
- const right = document.createElement('div');
- right.className = `${PREFIX}success-circular-line-right`;
- icon.appendChild(left);
- icon.appendChild(tip);
- icon.appendChild(long);
- icon.appendChild(ring);
- icon.appendChild(fix);
- icon.appendChild(right);
- applyIconSize('font');
- return icon;
- }
- if (type === 'error' || type === 'warning' || type === 'info' || type === 'question') {
- // Use the same "success-like" ring parts for every icon
- const left = document.createElement('div');
- left.className = `${PREFIX}success-circular-line-left`;
- const ring = document.createElement('div');
- ring.className = `${PREFIX}success-ring`;
- const fix = document.createElement('div');
- fix.className = `${PREFIX}success-fix`;
- const right = document.createElement('div');
- right.className = `${PREFIX}success-circular-line-right`;
- icon.appendChild(left);
- icon.appendChild(ring);
- icon.appendChild(fix);
- icon.appendChild(right);
- applyIconSize('font');
- // SVG only draws the inner symbol (no SVG ring)
- const svg = document.createElementNS(svgNs, 'svg');
- svg.setAttribute('viewBox', '0 0 80 80');
- svg.setAttribute('aria-hidden', 'true');
- svg.setAttribute('focusable', 'false');
- const addPath = (d, extraClass) => {
- const p = document.createElementNS(svgNs, 'path');
- p.setAttribute('d', d);
- p.setAttribute('class', `${PREFIX}svg-mark ${PREFIX}svg-draw${extraClass ? ' ' + extraClass : ''}`);
- p.setAttribute('stroke', 'currentColor');
- svg.appendChild(p);
- return p;
- };
- if (type === 'error') {
- addPath('M28 28 L52 52', `${PREFIX}svg-error-left`);
- addPath('M52 28 L28 52', `${PREFIX}svg-error-right`);
- } else if (type === 'warning') {
- addPath('M40 20 L40 46', `${PREFIX}svg-warning-line`);
- const dot = document.createElementNS(svgNs, 'circle');
- dot.setAttribute('class', `${PREFIX}svg-dot`);
- dot.setAttribute('cx', '40');
- dot.setAttribute('cy', '58');
- dot.setAttribute('r', '3.2');
- dot.setAttribute('fill', 'currentColor');
- svg.appendChild(dot);
- } else if (type === 'info') {
- addPath('M40 34 L40 56', `${PREFIX}svg-info-line`);
- const dot = document.createElementNS(svgNs, 'circle');
- dot.setAttribute('class', `${PREFIX}svg-dot`);
- dot.setAttribute('cx', '40');
- dot.setAttribute('cy', '25');
- dot.setAttribute('r', '3.2');
- dot.setAttribute('fill', 'currentColor');
- svg.appendChild(dot);
- } else if (type === 'question') {
- addPath('M30 30 C30 23 35 19 42 19 C49 19 54 23 54 30 C54 36 50 39 46 41 C43 42 42 44 42 48 L42 52', `${PREFIX}svg-question`);
- const dot = document.createElementNS(svgNs, 'circle');
- dot.setAttribute('class', `${PREFIX}svg-dot`);
- dot.setAttribute('cx', '42');
- dot.setAttribute('cy', '61');
- dot.setAttribute('r', '3.2');
- dot.setAttribute('fill', 'currentColor');
- svg.appendChild(dot);
- }
- icon.appendChild(svg);
- return icon;
- }
- // Default to SVG icons for other/custom types
- applyIconSize('box');
- const svg = document.createElementNS(svgNs, 'svg');
- svg.setAttribute('viewBox', '0 0 80 80');
- svg.setAttribute('aria-hidden', 'true');
- svg.setAttribute('focusable', 'false');
- const ring = document.createElementNS(svgNs, 'circle');
- ring.setAttribute('class', `${PREFIX}svg-ring ${PREFIX}svg-draw`);
- ring.setAttribute('cx', '40');
- ring.setAttribute('cy', '40');
- ring.setAttribute('r', '34');
- ring.setAttribute('stroke', 'currentColor');
- svg.appendChild(ring);
- const addPath = (d, extraClass) => {
- const p = document.createElementNS(svgNs, 'path');
- p.setAttribute('d', d);
- p.setAttribute('class', `${PREFIX}svg-mark ${PREFIX}svg-draw${extraClass ? ' ' + extraClass : ''}`);
- p.setAttribute('stroke', 'currentColor');
- svg.appendChild(p);
- return p;
- };
- if (type === 'error') {
- addPath('M28 28 L52 52', `${PREFIX}svg-error-left`);
- addPath('M52 28 L28 52', `${PREFIX}svg-error-right`);
- } else if (type === 'warning') {
- addPath('M40 20 L40 46', `${PREFIX}svg-warning-line`);
- const dot = document.createElementNS(svgNs, 'circle');
- dot.setAttribute('class', `${PREFIX}svg-dot`);
- dot.setAttribute('cx', '40');
- dot.setAttribute('cy', '58');
- dot.setAttribute('r', '3.2');
- dot.setAttribute('fill', 'currentColor');
- svg.appendChild(dot);
- } else if (type === 'info') {
- addPath('M40 34 L40 56', `${PREFIX}svg-info-line`);
- const dot = document.createElementNS(svgNs, 'circle');
- dot.setAttribute('class', `${PREFIX}svg-dot`);
- dot.setAttribute('cx', '40');
- dot.setAttribute('cy', '25');
- dot.setAttribute('r', '3.2');
- dot.setAttribute('fill', 'currentColor');
- svg.appendChild(dot);
- } else if (type === 'question') {
- // Question mark (single stroke + dot)
- addPath('M30 30 C30 23 35 19 42 19 C49 19 54 23 54 30 C54 36 50 39 46 41 C43 42 42 44 42 48 L42 52', `${PREFIX}svg-question`);
- const dot = document.createElementNS(svgNs, 'circle');
- dot.setAttribute('class', `${PREFIX}svg-dot`);
- dot.setAttribute('cx', '42');
- dot.setAttribute('cy', '61');
- dot.setAttribute('r', '3.2');
- dot.setAttribute('fill', 'currentColor');
- svg.appendChild(dot);
- } else {
- // Fallback: ring only
- }
- icon.appendChild(svg);
-
- return icon;
- }
- _adjustRingBackgroundColor() {
- try {
- const icon = this.dom && this.dom.icon;
- const popup = this.dom && this.dom.popup;
- if (!icon || !popup) return;
- const bg = getComputedStyle(popup).backgroundColor;
- const parts = icon.querySelectorAll(`.${PREFIX}success-circular-line-left, .${PREFIX}success-circular-line-right, .${PREFIX}success-fix`);
- parts.forEach((el) => {
- try { el.style.backgroundColor = bg; } catch {}
- });
- } catch {}
- }
- _didOpen() {
- // Keyboard close (ESC)
- if (this.params.closeOnEsc) {
- this._onKeydown = (e) => {
- if (!e) return;
- if (e.key === 'Escape') this._close(null);
- };
- document.addEventListener('keydown', this._onKeydown);
- }
- // Keep the "success-like" ring perfectly blended with popup bg
- this._adjustRingBackgroundColor();
- // Popup animation (optional)
- if (this.params.popupAnimation) {
- const X = Layer._getXjs();
- if (X && this.dom.popup) {
- // Override the CSS scale transition with a spring-ish entrance.
- try {
- this.dom.popup.style.transition = 'none';
- this.dom.popup.style.transform = 'scale(0.92)';
- X(this.dom.popup).animate({
- scale: [0.92, 1],
- y: [-6, 0],
- duration: 520,
- easing: { stiffness: 260, damping: 16 }
- });
- } catch {}
- }
- }
- // Icon SVG draw animation
- if (this.params.iconAnimation) {
- this._animateIcon();
- }
- // User hook (SweetAlert-ish naming)
- try {
- if (typeof this.params.didOpen === 'function') this.params.didOpen(this.dom.popup);
- } catch {}
- }
- _animateIcon() {
- const icon = this.dom.icon;
- if (!icon) return;
- const type = (this.params && this.params.icon) || '';
- const ringTypes = new Set(['success', 'error', 'warning', 'info', 'question']);
- // Ring animation (same as success) for all built-in icons
- if (ringTypes.has(type)) this._adjustRingBackgroundColor();
- try { icon.classList.remove(`${PREFIX}icon-show`); } catch {}
- requestAnimationFrame(() => {
- try { icon.classList.add(`${PREFIX}icon-show`); } catch {}
- });
- // Success tick is CSS-driven; others still draw their SVG mark after the ring starts.
- if (type === 'success') return;
- const X = Layer._getXjs();
- if (!X) return;
- const svg = icon.querySelector('svg');
- if (!svg) return;
- const marks = Array.from(svg.querySelectorAll(`.${PREFIX}svg-mark`));
- const dot = svg.querySelector(`.${PREFIX}svg-dot`);
- // If this is a built-in icon, keep the SweetAlert-like order:
- // ring sweep first (~0.51s), then draw the inner mark.
- const baseDelay = ringTypes.has(type) ? 520 : 0;
- if (type === 'error') {
- // Draw order: left-top -> right-bottom, then right-top -> left-bottom
- // NOTE: A tiny delay (like 70ms) looks simultaneous; make it strictly sequential.
- const a = svg.querySelector(`.${PREFIX}svg-error-left`) || marks[0]; // M28 28 L52 52
- const b = svg.querySelector(`.${PREFIX}svg-error-right`) || marks[1]; // M52 28 L28 52
- const dur = 320;
- const gap = 60;
- try { if (a) X(a).draw({ duration: dur, easing: 'ease-out', delay: baseDelay }); } catch {}
- try { if (b) X(b).draw({ duration: dur, easing: 'ease-out', delay: baseDelay + dur + gap }); } catch {}
- } else {
- // warning / info / question (single stroke) or custom SVG symbols
- marks.forEach((m, i) => {
- try { X(m).draw({ duration: 420, easing: 'ease-out', delay: baseDelay + i * 60 }); } catch {}
- });
- }
- if (dot) {
- try {
- dot.style.opacity = '0';
- // Keep dot pop after the ring begins
- const d = baseDelay + 140;
- if (type === 'info') {
- X(dot).animate({ opacity: [0, 1], y: [-8, 0], scale: [0.2, 1], duration: 420, delay: d, easing: { stiffness: 300, damping: 14 } });
- } else {
- X(dot).animate({ opacity: [0, 1], scale: [0.2, 1], duration: 320, delay: d, easing: { stiffness: 320, damping: 18 } });
- }
- } catch {}
- }
- }
- _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 (this._onKeydown) {
- try { document.removeEventListener('keydown', this._onKeydown); } catch {}
- this._onKeydown = null;
- }
- try {
- if (typeof this.params.willClose === 'function') this.params.willClose(this.dom.popup);
- } catch {}
- try {
- if (typeof this.params.didClose === 'function') this.params.didClose();
- } catch {}
- 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;
- })));
|