|
@@ -36,302 +36,67 @@
|
|
|
'use strict';
|
|
'use strict';
|
|
|
|
|
|
|
|
const PREFIX = 'layer-';
|
|
const PREFIX = 'layer-';
|
|
|
|
|
+ const RING_TYPES = new Set(['success', 'error', 'warning', 'info', 'question']);
|
|
|
|
|
+ const RING_BG_PARTS_SELECTOR = `.${PREFIX}success-circular-line-left, .${PREFIX}success-circular-line-right, .${PREFIX}success-fix`;
|
|
|
|
|
+
|
|
|
|
|
+ const normalizeOptions = (options, text, icon) => {
|
|
|
|
|
+ if (typeof options !== 'string') return options || {};
|
|
|
|
|
+ const o = { title: options };
|
|
|
|
|
+ if (text) o.text = text;
|
|
|
|
|
+ if (icon) o.icon = icon;
|
|
|
|
|
+ return o;
|
|
|
|
|
+ };
|
|
|
|
|
|
|
|
- // 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;
|
|
|
|
|
- }
|
|
|
|
|
- /* Clip ONLY the success tick (do not clip the ring sweep masks) */
|
|
|
|
|
- .${PREFIX}icon .${PREFIX}success-mark {
|
|
|
|
|
- position: absolute;
|
|
|
|
|
- inset: 0;
|
|
|
|
|
- border-radius: 50%;
|
|
|
|
|
- overflow: hidden;
|
|
|
|
|
- z-index: 3;
|
|
|
|
|
- pointer-events: none;
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- .${PREFIX}icon .${PREFIX}success-line-tip,
|
|
|
|
|
- .${PREFIX}icon .${PREFIX}success-line-long {
|
|
|
|
|
- display: block;
|
|
|
|
|
- position: absolute;
|
|
|
|
|
- z-index: 1; /* inside success-mark; keep above its clipping bg */
|
|
|
|
|
- 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;
|
|
|
|
|
- width: 0;
|
|
|
|
|
- }
|
|
|
|
|
- 84% {
|
|
|
|
|
- top: 2.1875em;
|
|
|
|
|
- right: 0;
|
|
|
|
|
- width: 3.4375em;
|
|
|
|
|
- }
|
|
|
|
|
- 100% {
|
|
|
|
|
- top: 2.375em;
|
|
|
|
|
- right: 0.5em;
|
|
|
|
|
- width: 2.9375em;
|
|
|
|
|
|
|
+ const el = (tag, className, text) => {
|
|
|
|
|
+ const node = document.createElement(tag);
|
|
|
|
|
+ if (className) node.className = className;
|
|
|
|
|
+ if (text !== undefined) node.textContent = text;
|
|
|
|
|
+ return node;
|
|
|
|
|
+ };
|
|
|
|
|
+
|
|
|
|
|
+ const svgEl = (tag) => document.createElementNS('http://www.w3.org/2000/svg', tag);
|
|
|
|
|
+
|
|
|
|
|
+ // Ensure library CSS is loaded (xjs.css)
|
|
|
|
|
+ // - CSS is centralized in xjs.css (no runtime <style> injection).
|
|
|
|
|
+ // - We still auto-load it for convenience/compat, since consumers may forget the <link>.
|
|
|
|
|
+ const ensureXjsCss = () => {
|
|
|
|
|
+ try {
|
|
|
|
|
+ if (typeof document === 'undefined') return;
|
|
|
|
|
+ if (!document.head) return;
|
|
|
|
|
+
|
|
|
|
|
+ const existingHref = Array.from(document.querySelectorAll('link[rel="stylesheet"]'))
|
|
|
|
|
+ .map((l) => (l.getAttribute('href') || '').trim())
|
|
|
|
|
+ .find((h) => /(^|\/)xjs\.css(\?|#|$)/.test(h));
|
|
|
|
|
+ if (existingHref) return;
|
|
|
|
|
+
|
|
|
|
|
+ const id = 'xjs-css';
|
|
|
|
|
+ if (document.getElementById(id)) return;
|
|
|
|
|
+
|
|
|
|
|
+ const scripts = Array.from(document.getElementsByTagName('script'));
|
|
|
|
|
+ const scriptSrc = scripts
|
|
|
|
|
+ .map((s) => s && s.src)
|
|
|
|
|
+ .find((src) => /(^|\/)(xjs|layer)\.js(\?|#|$)/.test(String(src || '')));
|
|
|
|
|
+
|
|
|
|
|
+ let href = 'xjs.css';
|
|
|
|
|
+ if (scriptSrc) {
|
|
|
|
|
+ href = String(scriptSrc)
|
|
|
|
|
+ .replace(/(^|\/)xjs\.js(\?|#|$)/, '$1xjs.css$2')
|
|
|
|
|
+ .replace(/(^|\/)layer\.js(\?|#|$)/, '$1xjs.css$2');
|
|
|
}
|
|
}
|
|
|
- }
|
|
|
|
|
- @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);
|
|
|
|
|
|
|
+ const link = document.createElement('link');
|
|
|
|
|
+ link.id = id;
|
|
|
|
|
+ link.rel = 'stylesheet';
|
|
|
|
|
+ link.href = href;
|
|
|
|
|
+ document.head.appendChild(link);
|
|
|
|
|
+ } catch {
|
|
|
|
|
+ // ignore
|
|
|
|
|
+ }
|
|
|
};
|
|
};
|
|
|
|
|
|
|
|
class Layer {
|
|
class Layer {
|
|
|
constructor() {
|
|
constructor() {
|
|
|
- injectStyles();
|
|
|
|
|
|
|
+ ensureXjsCss();
|
|
|
this.params = {};
|
|
this.params = {};
|
|
|
this.dom = {};
|
|
this.dom = {};
|
|
|
this.promise = null;
|
|
this.promise = null;
|
|
@@ -362,11 +127,7 @@
|
|
|
// Chainable config helper (does not render until `.fire()` is called)
|
|
// Chainable config helper (does not render until `.fire()` is called)
|
|
|
config(options = {}) {
|
|
config(options = {}) {
|
|
|
// Support the same shorthand as Layer.fire(title, text, icon)
|
|
// 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];
|
|
|
|
|
- }
|
|
|
|
|
|
|
+ options = normalizeOptions(options, arguments[1], arguments[2]);
|
|
|
this.params = { ...(this.params || {}), ...options };
|
|
this.params = { ...(this.params || {}), ...options };
|
|
|
return this;
|
|
return this;
|
|
|
}
|
|
}
|
|
@@ -378,11 +139,7 @@
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
_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];
|
|
|
|
|
- }
|
|
|
|
|
|
|
+ options = normalizeOptions(options, arguments[1], arguments[2]);
|
|
|
|
|
|
|
|
this.params = {
|
|
this.params = {
|
|
|
title: '',
|
|
title: '',
|
|
@@ -424,12 +181,10 @@
|
|
|
if (existing) existing.remove();
|
|
if (existing) existing.remove();
|
|
|
|
|
|
|
|
// Create Overlay
|
|
// Create Overlay
|
|
|
- this.dom.overlay = document.createElement('div');
|
|
|
|
|
- this.dom.overlay.className = `${PREFIX}overlay`;
|
|
|
|
|
|
|
+ this.dom.overlay = el('div', `${PREFIX}overlay`);
|
|
|
|
|
|
|
|
// Create Popup
|
|
// Create Popup
|
|
|
- this.dom.popup = document.createElement('div');
|
|
|
|
|
- this.dom.popup.className = `${PREFIX}popup`;
|
|
|
|
|
|
|
+ this.dom.popup = el('div', `${PREFIX}popup`);
|
|
|
this.dom.overlay.appendChild(this.dom.popup);
|
|
this.dom.overlay.appendChild(this.dom.popup);
|
|
|
|
|
|
|
|
// Icon
|
|
// Icon
|
|
@@ -440,16 +195,13 @@
|
|
|
|
|
|
|
|
// Title
|
|
// Title
|
|
|
if (this.params.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.title = el('h2', `${PREFIX}title`, this.params.title);
|
|
|
this.dom.popup.appendChild(this.dom.title);
|
|
this.dom.popup.appendChild(this.dom.title);
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
// Content (Text / HTML / Element)
|
|
// Content (Text / HTML / Element)
|
|
|
if (this.params.text || this.params.html || this.params.content) {
|
|
if (this.params.text || this.params.html || this.params.content) {
|
|
|
- this.dom.content = document.createElement('div');
|
|
|
|
|
- this.dom.content.className = `${PREFIX}content`;
|
|
|
|
|
|
|
+ this.dom.content = el('div', `${PREFIX}content`);
|
|
|
|
|
|
|
|
if (this.params.content) {
|
|
if (this.params.content) {
|
|
|
// DOM Element or Selector
|
|
// DOM Element or Selector
|
|
@@ -466,23 +218,18 @@
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
// Actions
|
|
// Actions
|
|
|
- this.dom.actions = document.createElement('div');
|
|
|
|
|
- this.dom.actions.className = `${PREFIX}actions`;
|
|
|
|
|
|
|
+ this.dom.actions = el('div', `${PREFIX}actions`);
|
|
|
|
|
|
|
|
// Cancel Button
|
|
// Cancel Button
|
|
|
if (this.params.showCancelButton) {
|
|
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 = el('button', `${PREFIX}button ${PREFIX}cancel`, this.params.cancelButtonText);
|
|
|
this.dom.cancelBtn.style.backgroundColor = this.params.cancelButtonColor;
|
|
this.dom.cancelBtn.style.backgroundColor = this.params.cancelButtonColor;
|
|
|
this.dom.cancelBtn.onclick = () => this._close(false);
|
|
this.dom.cancelBtn.onclick = () => this._close(false);
|
|
|
this.dom.actions.appendChild(this.dom.cancelBtn);
|
|
this.dom.actions.appendChild(this.dom.cancelBtn);
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
// Confirm Button
|
|
// 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 = el('button', `${PREFIX}button ${PREFIX}confirm`, this.params.confirmButtonText);
|
|
|
this.dom.confirmBtn.style.backgroundColor = this.params.confirmButtonColor;
|
|
this.dom.confirmBtn.style.backgroundColor = this.params.confirmButtonColor;
|
|
|
this.dom.confirmBtn.onclick = () => this._close(true);
|
|
this.dom.confirmBtn.onclick = () => this._close(true);
|
|
|
this.dom.actions.appendChild(this.dom.confirmBtn);
|
|
this.dom.actions.appendChild(this.dom.confirmBtn);
|
|
@@ -508,17 +255,13 @@
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
_createIcon(type) {
|
|
_createIcon(type) {
|
|
|
- const icon = document.createElement('div');
|
|
|
|
|
- icon.className = `${PREFIX}icon ${type}`;
|
|
|
|
|
-
|
|
|
|
|
|
|
+ const icon = el('div', `${PREFIX}icon ${type}`);
|
|
|
const applyIconSize = (mode) => {
|
|
const applyIconSize = (mode) => {
|
|
|
if (!(this.params && this.params.iconSize)) return;
|
|
if (!(this.params && this.params.iconSize)) return;
|
|
|
try {
|
|
try {
|
|
|
- const raw = this.params.iconSize;
|
|
|
|
|
- const s = String(raw).trim();
|
|
|
|
|
|
|
+ const s = String(this.params.iconSize).trim();
|
|
|
if (!s) return;
|
|
if (!s) return;
|
|
|
- // For SweetAlert2-style success icon, scale via font-size so all `em`-based
|
|
|
|
|
- // parts remain proportional.
|
|
|
|
|
|
|
+ // For SweetAlert2-style success icon, scale via font-size so all `em`-based parts remain proportional.
|
|
|
if (mode === 'font') {
|
|
if (mode === 'font') {
|
|
|
const m = s.match(/^(-?\d*\.?\d+)\s*(px|em|rem)$/i);
|
|
const m = s.match(/^(-?\d*\.?\d+)\s*(px|em|rem)$/i);
|
|
|
if (m) {
|
|
if (m) {
|
|
@@ -536,7 +279,58 @@
|
|
|
} catch {}
|
|
} catch {}
|
|
|
};
|
|
};
|
|
|
|
|
|
|
|
- const svgNs = 'http://www.w3.org/2000/svg';
|
|
|
|
|
|
|
+ const appendRingParts = () => {
|
|
|
|
|
+ // Use the same "success-like" ring parts for every built-in icon
|
|
|
|
|
+ icon.appendChild(el('div', `${PREFIX}success-circular-line-left`));
|
|
|
|
|
+ icon.appendChild(el('div', `${PREFIX}success-ring`));
|
|
|
|
|
+ icon.appendChild(el('div', `${PREFIX}success-fix`));
|
|
|
|
|
+ icon.appendChild(el('div', `${PREFIX}success-circular-line-right`));
|
|
|
|
|
+ };
|
|
|
|
|
+
|
|
|
|
|
+ const createInnerMarkSvg = () => {
|
|
|
|
|
+ const svg = svgEl('svg');
|
|
|
|
|
+ svg.setAttribute('viewBox', '0 0 80 80');
|
|
|
|
|
+ svg.setAttribute('aria-hidden', 'true');
|
|
|
|
|
+ svg.setAttribute('focusable', 'false');
|
|
|
|
|
+ return svg;
|
|
|
|
|
+ };
|
|
|
|
|
+
|
|
|
|
|
+ const addMarkPath = (svg, d, extraClass) => {
|
|
|
|
|
+ const p = svgEl('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;
|
|
|
|
|
+ };
|
|
|
|
|
+
|
|
|
|
|
+ const addDot = (svg, cx, cy) => {
|
|
|
|
|
+ const dot = svgEl('circle');
|
|
|
|
|
+ dot.setAttribute('class', `${PREFIX}svg-dot`);
|
|
|
|
|
+ dot.setAttribute('cx', String(cx));
|
|
|
|
|
+ dot.setAttribute('cy', String(cy));
|
|
|
|
|
+ dot.setAttribute('r', '3.2');
|
|
|
|
|
+ dot.setAttribute('fill', 'currentColor');
|
|
|
|
|
+ svg.appendChild(dot);
|
|
|
|
|
+ return dot;
|
|
|
|
|
+ };
|
|
|
|
|
+
|
|
|
|
|
+ const appendBuiltInInnerMark = (svg) => {
|
|
|
|
|
+ if (type === 'error') {
|
|
|
|
|
+ addMarkPath(svg, 'M28 28 L52 52', `${PREFIX}svg-error-left`);
|
|
|
|
|
+ addMarkPath(svg, 'M52 28 L28 52', `${PREFIX}svg-error-right`);
|
|
|
|
|
+ } else if (type === 'warning') {
|
|
|
|
|
+ addMarkPath(svg, 'M40 20 L40 46', `${PREFIX}svg-warning-line`);
|
|
|
|
|
+ addDot(svg, 40, 58);
|
|
|
|
|
+ } else if (type === 'info') {
|
|
|
|
|
+ addMarkPath(svg, 'M40 34 L40 56', `${PREFIX}svg-info-line`);
|
|
|
|
|
+ addDot(svg, 40, 25);
|
|
|
|
|
+ } else if (type === 'question') {
|
|
|
|
|
+ addMarkPath(svg, '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`);
|
|
|
|
|
+ addDot(svg, 42, 61);
|
|
|
|
|
+ }
|
|
|
|
|
+ };
|
|
|
|
|
+
|
|
|
if (type === 'success') {
|
|
if (type === 'success') {
|
|
|
// SweetAlert2-compatible DOM structure (circle + tick) for exact style parity
|
|
// SweetAlert2-compatible DOM structure (circle + tick) for exact style parity
|
|
|
// <div class="...success-circular-line-left"></div>
|
|
// <div class="...success-circular-line-left"></div>
|
|
@@ -544,27 +338,14 @@
|
|
|
// <div class="...success-ring"></div>
|
|
// <div class="...success-ring"></div>
|
|
|
// <div class="...success-fix"></div>
|
|
// <div class="...success-fix"></div>
|
|
|
// <div class="...success-circular-line-right"></div>
|
|
// <div class="...success-circular-line-right"></div>
|
|
|
- const left = document.createElement('div');
|
|
|
|
|
- left.className = `${PREFIX}success-circular-line-left`;
|
|
|
|
|
- const mark = document.createElement('div');
|
|
|
|
|
- mark.className = `${PREFIX}success-mark`;
|
|
|
|
|
- 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);
|
|
|
|
|
- mark.appendChild(tip);
|
|
|
|
|
- mark.appendChild(long);
|
|
|
|
|
|
|
+ icon.appendChild(el('div', `${PREFIX}success-circular-line-left`));
|
|
|
|
|
+ const mark = el('div', `${PREFIX}success-mark`);
|
|
|
|
|
+ mark.appendChild(el('span', `${PREFIX}success-line-tip`));
|
|
|
|
|
+ mark.appendChild(el('span', `${PREFIX}success-line-long`));
|
|
|
icon.appendChild(mark);
|
|
icon.appendChild(mark);
|
|
|
- icon.appendChild(ring);
|
|
|
|
|
- icon.appendChild(fix);
|
|
|
|
|
- icon.appendChild(right);
|
|
|
|
|
|
|
+ icon.appendChild(el('div', `${PREFIX}success-ring`));
|
|
|
|
|
+ icon.appendChild(el('div', `${PREFIX}success-fix`));
|
|
|
|
|
+ icon.appendChild(el('div', `${PREFIX}success-circular-line-right`));
|
|
|
|
|
|
|
|
applyIconSize('font');
|
|
applyIconSize('font');
|
|
|
return icon;
|
|
return icon;
|
|
@@ -572,68 +353,13 @@
|
|
|
|
|
|
|
|
if (type === 'error' || type === 'warning' || type === 'info' || type === 'question') {
|
|
if (type === 'error' || type === 'warning' || type === 'info' || type === 'question') {
|
|
|
// Use the same "success-like" ring parts for every icon
|
|
// 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);
|
|
|
|
|
|
|
+ appendRingParts();
|
|
|
|
|
|
|
|
applyIconSize('font');
|
|
applyIconSize('font');
|
|
|
|
|
|
|
|
// SVG only draws the inner symbol (no SVG ring)
|
|
// 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);
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
|
|
+ const svg = createInnerMarkSvg();
|
|
|
|
|
+ appendBuiltInInnerMark(svg);
|
|
|
icon.appendChild(svg);
|
|
icon.appendChild(svg);
|
|
|
return icon;
|
|
return icon;
|
|
|
}
|
|
}
|
|
@@ -641,12 +367,9 @@
|
|
|
// Default to SVG icons for other/custom types
|
|
// Default to SVG icons for other/custom types
|
|
|
applyIconSize('box');
|
|
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 svg = createInnerMarkSvg();
|
|
|
|
|
|
|
|
- const ring = document.createElementNS(svgNs, 'circle');
|
|
|
|
|
|
|
+ const ring = svgEl('circle');
|
|
|
ring.setAttribute('class', `${PREFIX}svg-ring ${PREFIX}svg-draw`);
|
|
ring.setAttribute('class', `${PREFIX}svg-ring ${PREFIX}svg-draw`);
|
|
|
ring.setAttribute('cx', '40');
|
|
ring.setAttribute('cx', '40');
|
|
|
ring.setAttribute('cy', '40');
|
|
ring.setAttribute('cy', '40');
|
|
@@ -654,50 +377,7 @@
|
|
|
ring.setAttribute('stroke', 'currentColor');
|
|
ring.setAttribute('stroke', 'currentColor');
|
|
|
|
|
|
|
|
svg.appendChild(ring);
|
|
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
|
|
|
|
|
- }
|
|
|
|
|
|
|
+ // For custom types, we draw ring only by default (no inner mark).
|
|
|
|
|
|
|
|
icon.appendChild(svg);
|
|
icon.appendChild(svg);
|
|
|
|
|
|
|
@@ -710,7 +390,7 @@
|
|
|
const popup = this.dom && this.dom.popup;
|
|
const popup = this.dom && this.dom.popup;
|
|
|
if (!icon || !popup) return;
|
|
if (!icon || !popup) return;
|
|
|
const bg = getComputedStyle(popup).backgroundColor;
|
|
const bg = getComputedStyle(popup).backgroundColor;
|
|
|
- const parts = icon.querySelectorAll(`.${PREFIX}success-circular-line-left, .${PREFIX}success-circular-line-right, .${PREFIX}success-fix`);
|
|
|
|
|
|
|
+ const parts = icon.querySelectorAll(RING_BG_PARTS_SELECTOR);
|
|
|
parts.forEach((el) => {
|
|
parts.forEach((el) => {
|
|
|
try { el.style.backgroundColor = bg; } catch {}
|
|
try { el.style.backgroundColor = bg; } catch {}
|
|
|
});
|
|
});
|
|
@@ -764,10 +444,8 @@
|
|
|
if (!icon) return;
|
|
if (!icon) return;
|
|
|
const type = (this.params && this.params.icon) || '';
|
|
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
|
|
// Ring animation (same as success) for all built-in icons
|
|
|
- if (ringTypes.has(type)) this._adjustRingBackgroundColor();
|
|
|
|
|
|
|
+ if (RING_TYPES.has(type)) this._adjustRingBackgroundColor();
|
|
|
try { icon.classList.remove(`${PREFIX}icon-show`); } catch {}
|
|
try { icon.classList.remove(`${PREFIX}icon-show`); } catch {}
|
|
|
requestAnimationFrame(() => {
|
|
requestAnimationFrame(() => {
|
|
|
try { icon.classList.add(`${PREFIX}icon-show`); } catch {}
|
|
try { icon.classList.add(`${PREFIX}icon-show`); } catch {}
|
|
@@ -787,7 +465,7 @@
|
|
|
|
|
|
|
|
// If this is a built-in icon, keep the SweetAlert-like order:
|
|
// If this is a built-in icon, keep the SweetAlert-like order:
|
|
|
// ring sweep first (~0.51s), then draw the inner mark.
|
|
// ring sweep first (~0.51s), then draw the inner mark.
|
|
|
- const baseDelay = ringTypes.has(type) ? 520 : 0;
|
|
|
|
|
|
|
+ const baseDelay = RING_TYPES.has(type) ? 520 : 0;
|
|
|
|
|
|
|
|
if (type === 'error') {
|
|
if (type === 'error') {
|
|
|
// Draw order: left-top -> right-bottom, then right-top -> left-bottom
|
|
// Draw order: left-top -> right-bottom, then right-top -> left-bottom
|