fernotify 1.2.9 → 1.2.11
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +46 -2
- package/dist/notification-system.esm.js +170 -17
- package/dist/notification-system.esm.min.js +1 -1
- package/dist/notification-system.js +170 -17
- package/dist/notification-system.min.js +1 -1
- package/package.json +4 -1
- package/types/anime.d.ts +17 -0
- package/types/index.d.ts +173 -0
package/README.md
CHANGED
|
@@ -226,10 +226,12 @@ notify.toastQuestion('Nueva solicitud pendiente.');
|
|
|
226
226
|
|
|
227
227
|
// Método genérico con opciones completas
|
|
228
228
|
notify.toast('Mensaje aquí', {
|
|
229
|
-
type: 'success', // 'success' | 'error' | 'warning' | 'info' | 'question'
|
|
229
|
+
type: 'success', // 'success' | 'error' | 'warning' | 'info' | 'question' | 'loading'
|
|
230
230
|
title: 'Título opcional',
|
|
231
231
|
duration: 4000, // ms hasta auto-cierre (0 = sin auto-cierre, default: 4000)
|
|
232
|
-
position: 'top-right'
|
|
232
|
+
position: 'top-right', // 'top-right' | 'top-left' | 'top-center' | 'bottom-right' | 'bottom-left'
|
|
233
|
+
id: 'my-toast', // ID para deduplicación (optional)
|
|
234
|
+
closeable: true // false = oculta el botón × (default: true)
|
|
233
235
|
});
|
|
234
236
|
```
|
|
235
237
|
|
|
@@ -241,6 +243,48 @@ notify.toast('Mensaje aquí', {
|
|
|
241
243
|
- Animación de entrada/salida suave
|
|
242
244
|
- Soporte completo de dark mode
|
|
243
245
|
|
|
246
|
+
### Toast de carga 🔄
|
|
247
|
+
|
|
248
|
+
Muestra un toast con spinner para operaciones asíncronas. Solo puede existir uno a la vez y no se puede cerrar manualmente; ciérralo con `closeToastLoading()`.
|
|
249
|
+
|
|
250
|
+
```javascript
|
|
251
|
+
async function fetchData() {
|
|
252
|
+
notify.toastLoading('Obteniendo datos...', 'Cargando');
|
|
253
|
+
|
|
254
|
+
try {
|
|
255
|
+
const res = await fetch('/api/data');
|
|
256
|
+
const data = await res.json();
|
|
257
|
+
notify.closeToastLoading();
|
|
258
|
+
notify.toastSuccess('Datos cargados correctamente');
|
|
259
|
+
} catch {
|
|
260
|
+
notify.closeToastLoading();
|
|
261
|
+
notify.toastError('No se pudieron cargar los datos');
|
|
262
|
+
}
|
|
263
|
+
}
|
|
264
|
+
|
|
265
|
+
fetchData();
|
|
266
|
+
```
|
|
267
|
+
|
|
268
|
+
```javascript
|
|
269
|
+
// Firma completa
|
|
270
|
+
notify.toastLoading(message?, title?, options?)
|
|
271
|
+
notify.closeToastLoading()
|
|
272
|
+
```
|
|
273
|
+
|
|
274
|
+
### Deduplicación de toasts por ID
|
|
275
|
+
|
|
276
|
+
Asigna un `id` a un toast para evitar duplicados. Si ya hay un toast visible con ese ID, se resetea su cuenta regresiva en lugar de crear uno nuevo.
|
|
277
|
+
|
|
278
|
+
```javascript
|
|
279
|
+
// Aunque el usuario haga clic muchas veces, solo existe un toast
|
|
280
|
+
button.addEventListener('click', () => {
|
|
281
|
+
notify.toastError('Email o contraseña incorrectos', 'Error', {
|
|
282
|
+
id: 'login-error',
|
|
283
|
+
duration: 4000
|
|
284
|
+
});
|
|
285
|
+
});
|
|
286
|
+
```
|
|
287
|
+
|
|
244
288
|
### Personalización de Animaciones
|
|
245
289
|
|
|
246
290
|
```javascript
|
|
@@ -45,6 +45,7 @@ var __rest = (this && this.__rest) || function (s, e) {
|
|
|
45
45
|
this._lastActiveElement = null;
|
|
46
46
|
this._currentLoadingPromise = null;
|
|
47
47
|
this._toastContainers = new Map();
|
|
48
|
+
this._toastInstances = new Map();
|
|
48
49
|
this.injectStyles();
|
|
49
50
|
this.loadBoxicons();
|
|
50
51
|
}
|
|
@@ -408,6 +409,7 @@ var __rest = (this && this.__rest) || function (s, e) {
|
|
|
408
409
|
.notify-toast-icon.warning { background: linear-gradient(135deg, #f59e0b 0%, #d97706 100%); }
|
|
409
410
|
.notify-toast-icon.info { background: linear-gradient(135deg, #3b82f6 0%, #2563eb 100%); }
|
|
410
411
|
.notify-toast-icon.question { background: linear-gradient(135deg, #8b5cf6 0%, #6366f1 100%); }
|
|
412
|
+
.notify-toast-icon.loading { background: linear-gradient(135deg, #6366f1 0%, #8b5cf6 100%); }
|
|
411
413
|
|
|
412
414
|
.notify-toast-content { flex: 1; min-width: 0; }
|
|
413
415
|
.notify-toast-title {
|
|
@@ -416,11 +418,13 @@ var __rest = (this && this.__rest) || function (s, e) {
|
|
|
416
418
|
color: #1f2937;
|
|
417
419
|
margin-bottom: 2px;
|
|
418
420
|
line-height: 1.3;
|
|
421
|
+
cursor: default;
|
|
419
422
|
}
|
|
420
423
|
.notify-toast-message {
|
|
421
424
|
font-size: 13px;
|
|
422
425
|
color: #6b7280;
|
|
423
426
|
line-height: 1.5;
|
|
427
|
+
cursor: default;
|
|
424
428
|
}
|
|
425
429
|
|
|
426
430
|
.notify-toast-close {
|
|
@@ -443,6 +447,9 @@ var __rest = (this && this.__rest) || function (s, e) {
|
|
|
443
447
|
}
|
|
444
448
|
.notify-toast-close:hover { background: rgba(0,0,0,0.1); color: #374151; }
|
|
445
449
|
|
|
450
|
+
/* Sin botón de cierre: reducir padding derecho */
|
|
451
|
+
.notify-toast.notify-toast-no-close { padding-right: 14px; }
|
|
452
|
+
|
|
446
453
|
.notify-toast-progress {
|
|
447
454
|
position: absolute;
|
|
448
455
|
bottom: 0;
|
|
@@ -456,12 +463,41 @@ var __rest = (this && this.__rest) || function (s, e) {
|
|
|
456
463
|
.notify-toast-progress.warning { background: #f59e0b; }
|
|
457
464
|
.notify-toast-progress.info { background: #3b82f6; }
|
|
458
465
|
.notify-toast-progress.question { background: #8b5cf6; }
|
|
466
|
+
.notify-toast-progress.loading { background: #6366f1; }
|
|
467
|
+
|
|
468
|
+
/* Spinner para toast de carga */
|
|
469
|
+
.notify-toast-spinner {
|
|
470
|
+
width: 18px;
|
|
471
|
+
height: 18px;
|
|
472
|
+
border: 2.5px solid rgba(255,255,255,0.35);
|
|
473
|
+
border-top-color: white;
|
|
474
|
+
border-radius: 50%;
|
|
475
|
+
animation: notification-spin 0.8s linear infinite;
|
|
476
|
+
flex-shrink: 0;
|
|
477
|
+
}
|
|
459
478
|
|
|
460
479
|
.dark .notify-toast { background: #0f1724; box-shadow: 0 4px 24px rgba(0,0,0,0.35); }
|
|
461
480
|
.dark .notify-toast-title { color: #e6eef8; }
|
|
462
481
|
.dark .notify-toast-message { color: #cbd5e1; }
|
|
463
482
|
.dark .notify-toast-close { background: rgba(255,255,255,0.06); color: #94a3b8; }
|
|
464
483
|
.dark .notify-toast-close:hover { background: rgba(255,255,255,0.1); color: #e2e8f0; }
|
|
484
|
+
|
|
485
|
+
/* Respeta la preferencia de movimiento reducido del sistema */
|
|
486
|
+
@media (prefers-reduced-motion: reduce) {
|
|
487
|
+
.notify-toast {
|
|
488
|
+
transition: opacity 0.1s ease !important;
|
|
489
|
+
transform: none !important;
|
|
490
|
+
}
|
|
491
|
+
.notify-toast.notify-toast-visible {
|
|
492
|
+
transform: none !important;
|
|
493
|
+
}
|
|
494
|
+
.notify-toast-spinner {
|
|
495
|
+
animation-duration: 1.5s !important;
|
|
496
|
+
}
|
|
497
|
+
.notify-toast-progress {
|
|
498
|
+
transition: none !important;
|
|
499
|
+
}
|
|
500
|
+
}
|
|
465
501
|
`;
|
|
466
502
|
document.head.appendChild(style);
|
|
467
503
|
}
|
|
@@ -471,7 +507,8 @@ var __rest = (this && this.__rest) || function (s, e) {
|
|
|
471
507
|
'error': '<i class="bx bx-x" aria-hidden="true"></i>',
|
|
472
508
|
'warning': '<i class="bx bx-error" aria-hidden="true"></i>',
|
|
473
509
|
'info': '<i class="bx bx-info-circle" aria-hidden="true"></i>',
|
|
474
|
-
'question': '<i class="bx bx-question-mark" aria-hidden="true"></i>'
|
|
510
|
+
'question': '<i class="bx bx-question-mark" aria-hidden="true"></i>',
|
|
511
|
+
'loading': '<div class="notify-toast-spinner" aria-hidden="true"></div>'
|
|
475
512
|
};
|
|
476
513
|
return icons[type] || icons.info;
|
|
477
514
|
}
|
|
@@ -969,16 +1006,27 @@ var __rest = (this && this.__rest) || function (s, e) {
|
|
|
969
1006
|
return `${mm}:${ss}`;
|
|
970
1007
|
}
|
|
971
1008
|
showToast(message, options = {}) {
|
|
972
|
-
var _a;
|
|
1009
|
+
var _a, _b;
|
|
973
1010
|
const type = options.type || 'info';
|
|
974
1011
|
const title = (_a = options.title) !== null && _a !== void 0 ? _a : null;
|
|
975
1012
|
const duration = typeof options.duration === 'number' ? options.duration : 4000;
|
|
976
1013
|
const position = options.position || 'top-right';
|
|
977
1014
|
const showProgress = options.showProgress !== false;
|
|
1015
|
+
const toastId = (_b = options.id) !== null && _b !== void 0 ? _b : null;
|
|
1016
|
+
const closeable = options.closeable !== false;
|
|
1017
|
+
// Deduplicación: si ya existe un toast con este ID, resetear su cuenta regresiva
|
|
1018
|
+
if (toastId !== null) {
|
|
1019
|
+
const existing = this._toastInstances.get(toastId);
|
|
1020
|
+
if (existing) {
|
|
1021
|
+
existing.reset(duration);
|
|
1022
|
+
return;
|
|
1023
|
+
}
|
|
1024
|
+
}
|
|
978
1025
|
let container = this._toastContainers.get(position);
|
|
979
1026
|
if (!container || !document.body.contains(container)) {
|
|
980
1027
|
container = document.createElement('div');
|
|
981
1028
|
container.className = `notify-toast-container notify-toast-${position}`;
|
|
1029
|
+
container.setAttribute('aria-label', 'Notificaciones');
|
|
982
1030
|
document.body.appendChild(container);
|
|
983
1031
|
this._toastContainers.set(position, container);
|
|
984
1032
|
}
|
|
@@ -986,6 +1034,20 @@ var __rest = (this && this.__rest) || function (s, e) {
|
|
|
986
1034
|
const isCenter = position === 'top-center';
|
|
987
1035
|
const toast = document.createElement('div');
|
|
988
1036
|
toast.className = 'notify-toast';
|
|
1037
|
+
if (!closeable) {
|
|
1038
|
+
toast.classList.add('notify-toast-no-close');
|
|
1039
|
+
}
|
|
1040
|
+
// Accesibilidad: role + aria-live según la urgencia del tipo
|
|
1041
|
+
// role="alert" implica aria-live="assertive" + aria-atomic="true" → lector lo interrumpe
|
|
1042
|
+
// role="status" implica aria-live="polite" + aria-atomic="true" → lector espera pausa
|
|
1043
|
+
if (type === 'error' || type === 'warning') {
|
|
1044
|
+
toast.setAttribute('role', 'alert');
|
|
1045
|
+
}
|
|
1046
|
+
else {
|
|
1047
|
+
toast.setAttribute('role', 'status');
|
|
1048
|
+
}
|
|
1049
|
+
toast.setAttribute('aria-atomic', 'true');
|
|
1050
|
+
toast.setAttribute('aria-live', type === 'error' || type === 'warning' ? 'assertive' : 'polite');
|
|
989
1051
|
const iconEl = document.createElement('div');
|
|
990
1052
|
iconEl.className = `notify-toast-icon ${type}`;
|
|
991
1053
|
iconEl.innerHTML = this.getIcon(type);
|
|
@@ -1001,17 +1063,22 @@ var __rest = (this && this.__rest) || function (s, e) {
|
|
|
1001
1063
|
msgEl.className = 'notify-toast-message';
|
|
1002
1064
|
msgEl.textContent = message;
|
|
1003
1065
|
contentEl.appendChild(msgEl);
|
|
1004
|
-
const closeBtn = document.createElement('button');
|
|
1005
|
-
closeBtn.className = 'notify-toast-close';
|
|
1006
|
-
closeBtn.setAttribute('aria-label', 'Cerrar notificación');
|
|
1007
|
-
closeBtn.innerHTML = '×';
|
|
1008
1066
|
toast.appendChild(iconEl);
|
|
1009
1067
|
toast.appendChild(contentEl);
|
|
1010
|
-
|
|
1068
|
+
if (closeable) {
|
|
1069
|
+
const closeBtn = document.createElement('button');
|
|
1070
|
+
closeBtn.className = 'notify-toast-close';
|
|
1071
|
+
closeBtn.setAttribute('aria-label', 'Cerrar notificación');
|
|
1072
|
+
closeBtn.innerHTML = '×';
|
|
1073
|
+
closeBtn.addEventListener('click', removeToast);
|
|
1074
|
+
toast.appendChild(closeBtn);
|
|
1075
|
+
}
|
|
1011
1076
|
let progressEl = null;
|
|
1012
1077
|
if (duration > 0 && showProgress) {
|
|
1013
1078
|
progressEl = document.createElement('div');
|
|
1014
1079
|
progressEl.className = `notify-toast-progress ${type}`;
|
|
1080
|
+
progressEl.setAttribute('role', 'progressbar');
|
|
1081
|
+
progressEl.setAttribute('aria-hidden', 'true'); // decorativo: el timer no añade info que el usuario necesite leer
|
|
1015
1082
|
toast.appendChild(progressEl);
|
|
1016
1083
|
}
|
|
1017
1084
|
if (isBottom || isCenter) {
|
|
@@ -1024,19 +1091,34 @@ var __rest = (this && this.__rest) || function (s, e) {
|
|
|
1024
1091
|
let timerId = null;
|
|
1025
1092
|
let remaining = duration;
|
|
1026
1093
|
let timerStartedAt = 0;
|
|
1027
|
-
|
|
1094
|
+
function removeToast() {
|
|
1028
1095
|
if (dismissed)
|
|
1029
|
-
return;
|
|
1096
|
+
return Promise.resolve();
|
|
1030
1097
|
dismissed = true;
|
|
1098
|
+
// Si el foco estaba dentro del toast, sacarlo antes de que el nodo desaparezca
|
|
1099
|
+
// para evitar que el foco se pierda silenciosamente en el documento
|
|
1100
|
+
if (toast.contains(document.activeElement)) {
|
|
1101
|
+
try {
|
|
1102
|
+
document.activeElement.blur();
|
|
1103
|
+
}
|
|
1104
|
+
catch (e) { }
|
|
1105
|
+
}
|
|
1031
1106
|
toast.classList.remove('notify-toast-visible');
|
|
1032
|
-
|
|
1033
|
-
|
|
1034
|
-
toast.parentNode
|
|
1035
|
-
|
|
1036
|
-
|
|
1107
|
+
return new Promise(resolve => {
|
|
1108
|
+
setTimeout(() => {
|
|
1109
|
+
if (toast.parentNode)
|
|
1110
|
+
toast.parentNode.removeChild(toast);
|
|
1111
|
+
resolve();
|
|
1112
|
+
}, 300);
|
|
1113
|
+
});
|
|
1114
|
+
}
|
|
1037
1115
|
const startCountdown = (ms) => {
|
|
1038
1116
|
timerStartedAt = Date.now();
|
|
1039
|
-
timerId = setTimeout(
|
|
1117
|
+
timerId = setTimeout(() => {
|
|
1118
|
+
if (toastId !== null)
|
|
1119
|
+
this._toastInstances.delete(toastId);
|
|
1120
|
+
removeToast();
|
|
1121
|
+
}, ms);
|
|
1040
1122
|
if (progressEl) {
|
|
1041
1123
|
progressEl.style.transition = `width ${ms}ms linear`;
|
|
1042
1124
|
progressEl.style.width = '0%';
|
|
@@ -1060,7 +1142,46 @@ var __rest = (this && this.__rest) || function (s, e) {
|
|
|
1060
1142
|
return;
|
|
1061
1143
|
startCountdown(remaining);
|
|
1062
1144
|
};
|
|
1063
|
-
|
|
1145
|
+
const resetCountdown = (newDuration) => {
|
|
1146
|
+
if (dismissed)
|
|
1147
|
+
return;
|
|
1148
|
+
if (timerId !== null) {
|
|
1149
|
+
clearTimeout(timerId);
|
|
1150
|
+
timerId = null;
|
|
1151
|
+
}
|
|
1152
|
+
remaining = newDuration;
|
|
1153
|
+
if (newDuration > 0) {
|
|
1154
|
+
if (progressEl) {
|
|
1155
|
+
progressEl.style.transition = 'none';
|
|
1156
|
+
progressEl.style.width = '100%';
|
|
1157
|
+
// Forzar reflow para que la transición se aplique desde el inicio
|
|
1158
|
+
void progressEl.offsetWidth;
|
|
1159
|
+
}
|
|
1160
|
+
startCountdown(newDuration);
|
|
1161
|
+
}
|
|
1162
|
+
else if (progressEl) {
|
|
1163
|
+
progressEl.style.transition = 'none';
|
|
1164
|
+
progressEl.style.width = '100%';
|
|
1165
|
+
}
|
|
1166
|
+
};
|
|
1167
|
+
if (toastId !== null) {
|
|
1168
|
+
const silentDismiss = () => {
|
|
1169
|
+
if (dismissed)
|
|
1170
|
+
return;
|
|
1171
|
+
dismissed = true;
|
|
1172
|
+
if (timerId !== null)
|
|
1173
|
+
clearTimeout(timerId);
|
|
1174
|
+
if (toast.contains(document.activeElement)) {
|
|
1175
|
+
try {
|
|
1176
|
+
document.activeElement.blur();
|
|
1177
|
+
}
|
|
1178
|
+
catch (e) { }
|
|
1179
|
+
}
|
|
1180
|
+
if (toast.parentNode)
|
|
1181
|
+
toast.parentNode.removeChild(toast);
|
|
1182
|
+
};
|
|
1183
|
+
this._toastInstances.set(toastId, { reset: resetCountdown, dismiss: removeToast, _silentDismiss: silentDismiss });
|
|
1184
|
+
}
|
|
1064
1185
|
requestAnimationFrame(() => {
|
|
1065
1186
|
requestAnimationFrame(() => {
|
|
1066
1187
|
toast.classList.add('notify-toast-visible');
|
|
@@ -1069,7 +1190,7 @@ var __rest = (this && this.__rest) || function (s, e) {
|
|
|
1069
1190
|
}
|
|
1070
1191
|
});
|
|
1071
1192
|
});
|
|
1072
|
-
if (duration > 0) {
|
|
1193
|
+
if (duration > 0 && closeable) {
|
|
1073
1194
|
toast.addEventListener('mouseenter', pauseCountdown);
|
|
1074
1195
|
toast.addEventListener('mouseleave', resumeCountdown);
|
|
1075
1196
|
}
|
|
@@ -1098,6 +1219,38 @@ var __rest = (this && this.__rest) || function (s, e) {
|
|
|
1098
1219
|
toastQuestion(message, title, options = {}) {
|
|
1099
1220
|
this.showToast(message, Object.assign(Object.assign({}, options), { type: 'question', title: title !== null && title !== void 0 ? title : options.title }));
|
|
1100
1221
|
}
|
|
1222
|
+
/**
|
|
1223
|
+
* Muestra un toast de carga con spinner.
|
|
1224
|
+
* - No se puede cerrar manualmente (closeable: false por defecto).
|
|
1225
|
+
* - No tiene cuenta regresiva (duration: 0 por defecto).
|
|
1226
|
+
* - Solo puede existir uno a la vez (id '__loading__').
|
|
1227
|
+
* Ciérralo con notify.closeToastLoading().
|
|
1228
|
+
*/
|
|
1229
|
+
toastLoading(message = 'Cargando...', title, options = {}) {
|
|
1230
|
+
this.showToast(message, Object.assign(Object.assign({ position: 'top-right' }, options), { type: 'loading', title: title !== null && title !== void 0 ? title : options.title, id: '__loading__', closeable: false, duration: 0, showProgress: false }));
|
|
1231
|
+
}
|
|
1232
|
+
/** Cierra el toast de carga activo (si existe). Devuelve una Promise que resuelve cuando la animación de salida termina (≈300 ms). */
|
|
1233
|
+
closeToastLoading() {
|
|
1234
|
+
const entry = this._toastInstances.get('__loading__');
|
|
1235
|
+
if (entry) {
|
|
1236
|
+
this._toastInstances.delete('__loading__');
|
|
1237
|
+
return entry.dismiss();
|
|
1238
|
+
}
|
|
1239
|
+
return Promise.resolve();
|
|
1240
|
+
}
|
|
1241
|
+
/**
|
|
1242
|
+
* Reemplaza el toast de carga activo por un toast de resultado en el mismo lugar,
|
|
1243
|
+
* sin animación de salida/entrada — no hay solapamiento ni hueco visual.
|
|
1244
|
+
* Si no existe un toast de carga activo, simplemente muestra un toast normal.
|
|
1245
|
+
*/
|
|
1246
|
+
replaceToastLoading(message, options = {}) {
|
|
1247
|
+
const entry = this._toastInstances.get('__loading__');
|
|
1248
|
+
if (entry) {
|
|
1249
|
+
this._toastInstances.delete('__loading__');
|
|
1250
|
+
entry._silentDismiss();
|
|
1251
|
+
}
|
|
1252
|
+
this.showToast(message, options);
|
|
1253
|
+
}
|
|
1101
1254
|
}
|
|
1102
1255
|
const notifyInstance = new NotificationSystem();
|
|
1103
1256
|
const w = window;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
"use strict";var __rest=this&&this.__rest||function(t,n){var e={};for(var o in t)Object.prototype.hasOwnProperty.call(t,o)&&n.indexOf(o)<0&&(e[o]=t[o]);if(null!=t&&"function"==typeof Object.getOwnPropertySymbols){var i=0;for(o=Object.getOwnPropertySymbols(t);i<o.length;i++)n.indexOf(o[i])<0&&Object.prototype.propertyIsEnumerable.call(t,o[i])&&(e[o[i]]=t[o[i]])}return e};!function(){if("undefined"!=typeof anime)t();else{const n=document.createElement("script");n.src="https://cdnjs.cloudflare.com/ajax/libs/animejs/3.2.1/anime.min.js",n.onload=t,n.onerror=()=>{console.error("FerNotify: No se pudo cargar anime.js. Por favor, cargalo manualmente.")},document.head.appendChild(n)}function t(){const t=new class{constructor(){this.currentNotification=null,this._lastActiveElement=null,this._currentLoadingPromise=null,this._toastContainers=new Map,this.injectStyles(),this.loadBoxicons()}loadBoxicons(){if(!document.querySelector('link[href*="boxicons"]')){const t=document.createElement("link");t.rel="stylesheet",t.href="https://unpkg.com/boxicons@2.1.4/css/boxicons.min.css",document.head.appendChild(t)}}injectStyles(){const t=document.createElement("style");t.textContent="\n .notification-overlay {\n position: fixed;\n top: 0;\n left: 0;\n width: 100%;\n height: 100%;\n background-color: rgba(0, 0, 0, 0.4);\n backdrop-filter: blur(4px);\n -webkit-backdrop-filter: blur(4px);\n display: flex;\n align-items: center;\n justify-content: center;\n z-index: 9999;\n opacity: 0;\n overflow: hidden;\n }\n\n .notification-box {\n background: white;\n border-radius: 16px;\n padding: 40px 30px;\n max-width: 500px;\n width: 90%;\n max-height: 80vh;\n overflow: auto;\n position: relative;\n box-shadow: 0 20px 60px rgba(0, 0, 0, 0.3);\n text-align: center;\n transform: scale(0.7);\n opacity: 0;\n }\n\n .notification-content {\n text-align: left;\n margin-bottom: 18px;\n }\n\n .notification-close {\n position: absolute;\n top: 10px;\n right: 10px;\n width: 38px;\n height: 38px;\n border-radius: 8px;\n border: none;\n background: rgba(0,0,0,0.06);\n color: #111827;\n display: inline-flex;\n align-items: center;\n justify-content: center;\n cursor: pointer;\n font-size: 18px;\n }\n\n .notification-close:hover {\n background: rgba(0,0,0,0.09);\n }\n\n /* Form controls inside the modal */\n .notification-box input,\n .notification-box textarea,\n .notification-box select {\n width: 100%;\n padding: 10px 12px;\n border: 1px solid #e5e7eb;\n border-radius: 8px;\n background: #ffffff;\n color: #111827;\n font-size: 15px;\n box-sizing: border-box;\n transition: box-shadow 0.15s ease, border-color 0.15s ease;\n }\n\n .notification-box input:focus,\n .notification-box textarea:focus,\n .notification-box select:focus {\n outline: none;\n border-color: #6366f1;\n box-shadow: 0 6px 24px rgba(99,102,241,0.12), 0 0 0 4px rgba(99,102,241,0.06);\n }\n\n .notification-box label { display: block; margin-bottom: 6px; color: #374151; font-weight: 600; }\n\n /* Soporte para tema oscuro con clase .dark (Tailwind darkMode: 'class') */\n /* Esto tiene prioridad sobre prefers-color-scheme para respetar la elección del usuario en la web */\n .dark .notification-box { background: #0f1724 !important; color: #e6eef8 !important; }\n .dark .notification-box input,\n .dark .notification-box textarea,\n .dark .notification-box select {\n background: #0b1220 !important;\n border: 1px solid rgba(255,255,255,0.06) !important;\n color: #e6eef8 !important;\n }\n .dark .notification-box .notification-close { background: rgba(255,255,255,0.03) !important; color: #e6eef8 !important; }\n .dark .notification-overlay { background-color: rgba(0,0,0,0.6) !important; }\n .dark .notification-title { color: #e6eef8 !important; }\n .dark .notification-message { color: #cbd5e1 !important; }\n\n /* Forzar modo claro cuando NO hay clase .dark, ignorando prefers-color-scheme */\n html:not(.dark) .notification-box { background: white !important; color: #111827 !important; }\n html:not(.dark) .notification-box input,\n html:not(.dark) .notification-box textarea,\n html:not(.dark) .notification-box select {\n background: #ffffff !important;\n border: 1px solid #e5e7eb !important;\n color: #111827 !important;\n }\n html:not(.dark) .notification-box .notification-close { background: rgba(0,0,0,0.06) !important; color: #111827 !important; }\n html:not(.dark) .notification-overlay { background-color: rgba(0, 0, 0, 0.4) !important; }\n html:not(.dark) .notification-title { color: #1f2937 !important; }\n html:not(.dark) .notification-message { color: #6b7280 !important; }\n\n .notification-icon {\n width: 80px;\n height: 80px;\n border-radius: 50%;\n margin: 0 auto 25px;\n display: flex;\n align-items: center;\n justify-content: center;\n font-size: 40px;\n position: relative;\n }\n\n .notification-icon::before {\n content: '';\n position: absolute;\n width: 100%;\n height: 100%;\n border-radius: 50%;\n opacity: 0.2;\n }\n\n .notification-icon.success {\n background: linear-gradient(135deg, #10b981 0%, #059669 100%);\n color: white;\n }\n\n .notification-icon.success::before {\n background: #10b981;\n }\n\n .notification-icon.error {\n background: linear-gradient(135deg, #ef4444 0%, #dc2626 100%);\n color: white;\n }\n\n .notification-icon.error::before {\n background: #ef4444;\n }\n\n .notification-icon.warning {\n background: linear-gradient(135deg, #f59e0b 0%, #d97706 100%);\n color: white;\n }\n\n .notification-icon.warning::before {\n background: #f59e0b;\n }\n\n .notification-icon.info {\n background: linear-gradient(135deg, #3b82f6 0%, #2563eb 100%);\n color: white;\n }\n\n .notification-icon.info::before {\n background: #3b82f6;\n }\n .notification-icon.question {\n background: linear-gradient(135deg, #3b82f6 0%, #2563eb 100%);\n color: white;\n }\n\n .notification-icon.question::before {\n background: #3b82f6;\n }\n\n .notification-title {\n font-size: 24px;\n font-weight: 700;\n color: #1f2937;\n margin-bottom: 12px;\n line-height: 1.3;\n }\n\n .notification-message {\n font-size: 16px;\n color: #6b7280;\n line-height: 1.6;\n margin-bottom: 30px;\n }\n\n .notification-button {\n color: white;\n border: none;\n padding: 10px 14px;\n border-radius: 8px;\n font-size: 16px;\n font-weight: 600;\n cursor: pointer;\n transition: all 0.3s ease;\n }\n\n .notification-button:hover {\n transform: translateY(-2px);\n filter: brightness(1.1);\n }\n\n .notification-button:active {\n transform: translateY(0);\n }\n\n /* group container for multiple action buttons */\n .notification-button-group {\n display: flex;\n gap: 12px;\n justify-content: center;\n flex-wrap: wrap;\n margin-top: 10px;\n }\n\n .notification-icon-checkmark {\n animation: checkmark-draw 0.6s ease-in-out;\n }\n\n .notification-icon-cross {\n animation: cross-draw 0.5s ease-in-out;\n }\n\n @keyframes checkmark-draw {\n 0% {\n transform: scale(0) rotate(-45deg);\n opacity: 0;\n }\n 50% {\n transform: scale(1.2) rotate(-45deg);\n }\n 100% {\n transform: scale(1) rotate(0deg);\n opacity: 1;\n }\n }\n\n @keyframes cross-draw {\n 0% {\n transform: scale(0) rotate(-180deg);\n opacity: 0;\n }\n 50% {\n transform: scale(1.2) rotate(-90deg);\n }\n 100% {\n transform: scale(1) rotate(0deg);\n opacity: 1;\n }\n }\n\n /* Loading spinner styles */\n .notification-loading-container {\n display: flex;\n flex-direction: column;\n align-items: center;\n justify-content: center;\n margin: 0 auto;\n }\n\n .notification-spinner {\n width: 60px;\n height: 60px;\n border: 5px solid rgba(99, 102, 241, 0.15);\n border-top-color: #6366f1;\n border-radius: 50%;\n animation: notification-spin 1s linear infinite;\n margin: 0 auto;\n }\n\n @keyframes notification-spin {\n to {\n transform: rotate(360deg);\n }\n }\n\n .notification-loading-text {\n font-size: 14px;\n color: #6b7280;\n text-align: center;\n margin-top: 12px;\n }\n\n .dark .notification-loading-text {\n color: #cbd5e1;\n }\n\n /* ==================== Toast ==================== */\n .notify-toast-container {\n position: fixed;\n z-index: 10000;\n display: flex;\n flex-direction: column;\n gap: 10px;\n pointer-events: none;\n width: 360px;\n max-width: calc(100vw - 40px);\n }\n .notify-toast-top-right { top: 20px; right: 20px; }\n .notify-toast-top-left { top: 20px; left: 20px; }\n .notify-toast-top-center { top: 20px; left: 50%; transform: translateX(-50%); }\n .notify-toast-bottom-right { bottom: 20px; right: 20px; flex-direction: column-reverse; }\n .notify-toast-bottom-left { bottom: 20px; left: 20px; flex-direction: column-reverse; }\n\n .notify-toast {\n background: white;\n border-radius: 12px;\n padding: 14px 40px 14px 14px;\n box-shadow: 0 4px 24px rgba(0,0,0,0.12), 0 1px 4px rgba(0,0,0,0.06);\n display: flex;\n align-items: flex-start;\n gap: 12px;\n pointer-events: auto;\n position: relative;\n overflow: hidden;\n opacity: 0;\n transform: translateX(30px);\n transition: opacity 0.25s ease, transform 0.25s ease;\n }\n .notify-toast-top-left .notify-toast,\n .notify-toast-bottom-left .notify-toast { transform: translateX(-30px); }\n .notify-toast-top-center .notify-toast { transform: translateY(-20px); }\n .notify-toast.notify-toast-visible {\n opacity: 1;\n transform: translateX(0) translateY(0) !important;\n }\n\n .notify-toast-icon {\n width: 36px;\n height: 36px;\n border-radius: 50%;\n display: flex;\n align-items: center;\n justify-content: center;\n font-size: 20px;\n flex-shrink: 0;\n color: white;\n }\n .notify-toast-icon.success { background: linear-gradient(135deg, #10b981 0%, #059669 100%); }\n .notify-toast-icon.error { background: linear-gradient(135deg, #ef4444 0%, #dc2626 100%); }\n .notify-toast-icon.warning { background: linear-gradient(135deg, #f59e0b 0%, #d97706 100%); }\n .notify-toast-icon.info { background: linear-gradient(135deg, #3b82f6 0%, #2563eb 100%); }\n .notify-toast-icon.question { background: linear-gradient(135deg, #8b5cf6 0%, #6366f1 100%); }\n\n .notify-toast-content { flex: 1; min-width: 0; }\n .notify-toast-title {\n font-size: 14px;\n font-weight: 700;\n color: #1f2937;\n margin-bottom: 2px;\n line-height: 1.3;\n }\n .notify-toast-message {\n font-size: 13px;\n color: #6b7280;\n line-height: 1.5;\n }\n\n .notify-toast-close {\n position: absolute;\n top: 8px;\n right: 8px;\n width: 24px;\n height: 24px;\n border-radius: 6px;\n border: none;\n background: rgba(0,0,0,0.06);\n color: #6b7280;\n cursor: pointer;\n font-size: 16px;\n display: flex;\n align-items: center;\n justify-content: center;\n line-height: 1;\n padding: 0;\n }\n .notify-toast-close:hover { background: rgba(0,0,0,0.1); color: #374151; }\n\n .notify-toast-progress {\n position: absolute;\n bottom: 0;\n left: 0;\n height: 3px;\n width: 100%;\n border-radius: 0 0 0 12px;\n }\n .notify-toast-progress.success { background: #10b981; }\n .notify-toast-progress.error { background: #ef4444; }\n .notify-toast-progress.warning { background: #f59e0b; }\n .notify-toast-progress.info { background: #3b82f6; }\n .notify-toast-progress.question { background: #8b5cf6; }\n\n .dark .notify-toast { background: #0f1724; box-shadow: 0 4px 24px rgba(0,0,0,0.35); }\n .dark .notify-toast-title { color: #e6eef8; }\n .dark .notify-toast-message { color: #cbd5e1; }\n .dark .notify-toast-close { background: rgba(255,255,255,0.06); color: #94a3b8; }\n .dark .notify-toast-close:hover { background: rgba(255,255,255,0.1); color: #e2e8f0; }\n ",document.head.appendChild(t)}getIcon(t){const n={success:'<i class="bx bx-check" aria-hidden="true"></i>',error:'<i class="bx bx-x" aria-hidden="true"></i>',warning:'<i class="bx bx-error" aria-hidden="true"></i>',info:'<i class="bx bx-info-circle" aria-hidden="true"></i>',question:'<i class="bx bx-question-mark" aria-hidden="true"></i>'};return n[t]||n.info}getDefaultTitle(t){return{success:"¡Éxito!",error:"Error",warning:"Advertencia",info:"Información",question:"Pregunta"}[t]||"Notificación"}getButtonGradient(t){const n={success:"linear-gradient(135deg, #10b981 0%, #059669 100%)",error:"linear-gradient(135deg, #ef4444 0%, #dc2626 100%)",warning:"linear-gradient(135deg, #f59e0b 0%, #d97706 100%)",info:"linear-gradient(135deg, #3b82f6 0%, #2563eb 100%)",question:"linear-gradient(135deg, #8b5cf6 0%, #6366f1 100%)"};return n[t]||n.info}getButtonShadow(t){const n={success:"rgba(16, 185, 129, 0)",error:"rgba(239, 68, 68, 0)",warning:"rgba(245, 159, 11, 0)",info:"rgba(59, 131, 246, 0)",question:"rgba(108, 99, 245, 0)"};return n[t]||n.info}show(t={}){if(this.currentNotification){const t=this.currentNotification;this.currentNotification=null;try{t&&t.parentNode&&t.parentNode.removeChild(t)}catch(t){}}const{type:n="info",title:e=this.getDefaultTitle(n),message:o="",buttonText:i="OK",buttonColor:a=null,onClose:r=null,timer:s=null,allowOutsideClick:c=!0,allowEscapeKey:l=!0,hideButton:d=!1,buttons:f=null}=t,u=!0===t.showCloseButton;try{document.body.style.overflow="hidden"}catch(t){}try{document.documentElement.style.overflow="hidden"}catch(t){}const p=document.createElement("div");p.className="notification-overlay",p.tabIndex=-1,p.setAttribute("role","dialog"),p.setAttribute("aria-modal","true"),p.style.pointerEvents="auto";const m=document.createElement("div");m.className="notification-box";const b=document.createElement("div");b.className=`notification-icon ${n}`,d&&"info"===n?(b.className="notification-loading-container",b.innerHTML='<div class="notification-spinner"></div>',b.style.background="transparent",b.style.boxShadow="none",b.style.width="100px",b.style.height="100px"):b.innerHTML=this.getIcon(n);const h=document.createElement("h3");h.className="notification-title",h.textContent=e;const g=document.createElement("p");g.className="notification-message",g.textContent=o;let x=null;if(t.html||t.content)if(x=document.createElement("div"),x.className="notification-content",t.html)try{x.innerHTML=t.html}catch(n){x.textContent=t.html}else t.content&&t.content instanceof HTMLElement&&x.appendChild(t.content);const y=()=>this.close(r);let w=null,v=null;if(!d)if(Array.isArray(f)&&f.length)v=document.createElement("div"),v.className="notification-button-group",f.forEach(t=>{const e=document.createElement("button");e.className="notification-button",e.textContent=t.text||"OK";const o=t.color||this.getButtonGradient(n),i=t.shadowColor||this.getButtonShadow(n);e.style.background=o,e.style.boxShadow=`0 4px 12px ${i}`,e.addEventListener("click",n=>{n.stopPropagation(),n.preventDefault();try{y().then(()=>{if("function"==typeof t.onClick)try{const n=t.onClick();n&&"function"==typeof n.then&&n.catch(t=>console.error(t))}catch(t){console.error(t)}}).catch(()=>{})}catch(t){console.error(t)}}),e.addEventListener("mouseenter",()=>{e.style.boxShadow=`0 6px 16px ${i}`}),e.addEventListener("mouseleave",()=>{e.style.boxShadow=`0 4px 12px ${i}`}),v.appendChild(e)});else if(t.onConfirm||t.onCancel||t.confirmText||t.cancelText){v=document.createElement("div"),v.className="notification-button-group";const e=t.cancelText||"Cancelar",o=t.confirmText||"Aceptar",i=document.createElement("button");i.className="notification-button",i.textContent=e;const a=t.cancelColor||"linear-gradient(135deg, #9ca3af 0%, #6b7280 100%)",r=t.cancelShadow||"rgba(107,114,128,0.25)";i.style.background=a,i.style.boxShadow=`0 4px 12px ${r}`,i.addEventListener("click",n=>{n.stopPropagation(),n.preventDefault(),y().then(()=>{try{if("function"==typeof t.onCancel){const n=t.onCancel();n&&"function"==typeof n.then&&n.catch(t=>console.error(t))}}catch(t){console.error(t)}}).catch(()=>{})}),i.addEventListener("mouseenter",()=>{i.style.boxShadow=`0 6px 16px ${r}`}),i.addEventListener("mouseleave",()=>{i.style.boxShadow=`0 4px 12px ${r}`});const s=document.createElement("button");s.className="notification-button",s.textContent=o;const c=t.confirmColor||this.getButtonGradient(n),l=t.confirmShadow||this.getButtonShadow(n);s.style.background=c,s.style.boxShadow=`0 4px 12px ${l}`,s.addEventListener("click",async n=>{n.stopPropagation(),n.preventDefault();try{if(await y(),"function"==typeof t.onConfirm){const n=t.onConfirm();n&&"function"==typeof n.then&&await n}}catch(t){console.error(t)}}),s.addEventListener("mouseenter",()=>{s.style.boxShadow=`0 6px 16px ${l}`}),s.addEventListener("mouseleave",()=>{s.style.boxShadow=`0 4px 12px ${l}`}),v.appendChild(i),v.appendChild(s)}else if(i){w=document.createElement("button"),w.className="notification-button",w.textContent=i;const t=a||this.getButtonGradient(n),e=this.getButtonShadow(n);w.style.background=t,w.style.boxShadow=`0 4px 12px ${e}`}let k=null;if(u&&(k=document.createElement("button"),k.setAttribute("aria-label","Cerrar"),k.className="notification-close",k.innerHTML="×",k.addEventListener("click",t=>{t.stopPropagation(),y()})),m.appendChild(b),x){const t="notify-desc-"+Date.now();x.id=t,p.setAttribute("aria-describedby",t),m.appendChild(x)}else m.appendChild(h),m.appendChild(g);k&&m.appendChild(k),v?m.appendChild(v):w&&m.appendChild(w),p.appendChild(m),document.body.appendChild(p);const E=p,C=new Promise(t=>{try{E._externalResolve=t}catch(t){}});try{const t=document.getElementById("notify-live");t&&(t.textContent=`${e}: ${o}`)}catch(t){}try{this._lastActiveElement=document.activeElement}catch(t){this._lastActiveElement=null}this.currentNotification=p;try{const t=m.querySelectorAll('a[href], button:not([disabled]), textarea, input, select, [tabindex]:not([tabindex="-1"])');t&&t.length?t[0].focus():w?w.focus():p.focus()}catch(t){try{p.focus()}catch(t){}}const N=t=>{if("Tab"!==t.key)return;const n=Array.from(m.querySelectorAll('a[href], button:not([disabled]), textarea, input, select, [tabindex]:not([tabindex="-1"])')).filter(t=>t instanceof HTMLElement&&null!==t.offsetParent);if(!n.length)return void t.preventDefault();const e=n[0],o=n[n.length-1];t.shiftKey||document.activeElement!==o?t.shiftKey&&document.activeElement===e&&(t.preventDefault(),o.focus()):(t.preventDefault(),e.focus())};E._focusTrap=N,document.addEventListener("keydown",N);const S=t.anim||{},T="number"==typeof S.overlayDuration?S.overlayDuration:150,L=S.overlayEasing||"easeOutQuad",j="number"==typeof S.boxDuration?S.boxDuration:200,O="number"==typeof S.boxDelay?S.boxDelay:50,_=S.boxEasing||"easeOutBack",D="number"==typeof S.boxStartScale?S.boxStartScale:.8,$="number"==typeof S.iconDuration?S.iconDuration:250,A="number"==typeof S.iconDelay?S.iconDelay:100,q="number"==typeof S.iconRotate?S.iconRotate:"success"===n?-90:"error"===n?90:0;if("number"==typeof S.overlayOpacity&&(p.style.backgroundColor=`rgba(0,0,0,${S.overlayOpacity})`),anime({targets:p,opacity:[0,1],duration:T,easing:L}),anime({targets:m,scale:[D,1],opacity:[0,1],duration:j,easing:_,delay:O}),anime({targets:b,scale:[0,1],rotate:[q,0],duration:$,easing:_,delay:A}),w){const t=this.getButtonShadow(n);w.addEventListener("mouseenter",()=>{w.style.boxShadow=`0 6px 16px ${t}`}),w.addEventListener("mouseleave",()=>{w.style.boxShadow=`0 4px 12px ${t}`}),w.addEventListener("click",t=>{t.stopPropagation(),t.preventDefault(),y().catch(()=>{})})}if(c&&p.addEventListener("click",t=>{m.contains(t.target)||y()}),s&&setTimeout(()=>{y()},s),l){const t=n=>{"Escape"===n.key&&(y(),document.removeEventListener("keydown",t))};E._escHandler=t,document.addEventListener("keydown",t)}return C}close(t=null){if(!this.currentNotification)return Promise.resolve();const n=this.currentNotification,e=n,o=n.querySelector(".notification-box");return this.currentNotification=null,anime({targets:o,scale:.8,opacity:0,duration:100,easing:"easeInQuad"}),new Promise(o=>{anime({targets:n,opacity:0,duration:100,easing:"easeInQuad",complete:()=>{try{e&&e._escHandler&&(document.removeEventListener("keydown",e._escHandler),e._escHandler=void 0)}catch(t){}try{e&&e._focusTrap&&(document.removeEventListener("keydown",e._focusTrap),e._focusTrap=void 0)}catch(t){}try{if(e&&"function"==typeof e._externalResolve){try{e._externalResolve()}catch(t){}e._externalResolve=void 0}}catch(t){}try{n&&n.parentNode&&n.parentNode.removeChild(n)}catch(t){try{n.remove()}catch(t){}}if(!this.currentNotification){try{document.body.style.overflow=""}catch(t){}try{document.documentElement.style.overflow=""}catch(t){}}try{this._lastActiveElement&&"function"==typeof this._lastActiveElement.focus&&this._lastActiveElement.focus()}catch(t){}this._lastActiveElement=null,t&&t(),o()}})})}success(t,n=null,e={}){this.show(Object.assign({type:"success",title:n||this.getDefaultTitle("success"),message:t},e))}error(t,n=null,e={}){this.show(Object.assign({type:"error",title:n||this.getDefaultTitle("error"),message:t},e))}warning(t,n=null,e={}){this.show(Object.assign({type:"warning",title:n||this.getDefaultTitle("warning"),message:t},e))}question(t,n=null,e={}){this.show(Object.assign({type:"question",title:n||this.getDefaultTitle("question"),message:t},e))}info(t,n=null,e={}){this.show(Object.assign({type:"info",title:n||this.getDefaultTitle("info"),message:t},e))}loading(t="Cargando...",n="Espera",e={}){const o=Object.assign({type:"info",title:n,message:t,hideButton:!0,allowOutsideClick:!1,allowEscapeKey:!1},e),i=this.show(o);return this._currentLoadingPromise=i,i}closeLoading(t=null){return this._currentLoadingPromise=null,this.close(t)}hide(t=null){return this.close(t)}hiden(t=null){return this.close(t)}_formatTime(t){const n=Math.max(0,Math.floor(t));return`${Math.floor(n/60).toString().padStart(2,"0")}:${(n%60).toString().padStart(2,"0")}`}showToast(t,n={}){var e;const o=n.type||"info",i=null!==(e=n.title)&&void 0!==e?e:null,a="number"==typeof n.duration?n.duration:4e3,r=n.position||"top-right",s=!1!==n.showProgress;let c=this._toastContainers.get(r);c&&document.body.contains(c)||(c=document.createElement("div"),c.className=`notify-toast-container notify-toast-${r}`,document.body.appendChild(c),this._toastContainers.set(r,c));const l=r.startsWith("bottom"),d="top-center"===r,f=document.createElement("div");f.className="notify-toast";const u=document.createElement("div");u.className=`notify-toast-icon ${o}`,u.innerHTML=this.getIcon(o);const p=document.createElement("div");if(p.className="notify-toast-content",i){const t=document.createElement("div");t.className="notify-toast-title",t.textContent=i,p.appendChild(t)}const m=document.createElement("div");m.className="notify-toast-message",m.textContent=t,p.appendChild(m);const b=document.createElement("button");b.className="notify-toast-close",b.setAttribute("aria-label","Cerrar notificación"),b.innerHTML="×",f.appendChild(u),f.appendChild(p),f.appendChild(b);let h=null;a>0&&s&&(h=document.createElement("div"),h.className=`notify-toast-progress ${o}`,f.appendChild(h)),l||d?c.appendChild(f):c.insertBefore(f,c.firstChild);let g=!1,x=null,y=a,w=0;const v=()=>{g||(g=!0,f.classList.remove("notify-toast-visible"),setTimeout(()=>{f.parentNode&&f.parentNode.removeChild(f)},300))},k=t=>{w=Date.now(),x=setTimeout(v,t),h&&(h.style.transition=`width ${t}ms linear`,h.style.width="0%")};b.addEventListener("click",v),requestAnimationFrame(()=>{requestAnimationFrame(()=>{f.classList.add("notify-toast-visible"),a>0&&k(a)})}),a>0&&(f.addEventListener("mouseenter",()=>{if(g||null===x)return;clearTimeout(x),x=null;const t=Date.now()-w;if(y=Math.max(0,y-t),h){const t=y/a*100;h.style.transition="none",h.style.width=`${t}%`}}),f.addEventListener("mouseleave",()=>{g||y<=0||k(y)}))}toast(t,n={}){if("string"==typeof t)this.showToast(t,n);else{const{message:n=""}=t,e=__rest(t,["message"]);this.showToast(n,e)}}toastSuccess(t,n,e={}){this.showToast(t,Object.assign(Object.assign({},e),{type:"success",title:null!=n?n:e.title}))}toastError(t,n,e={}){this.showToast(t,Object.assign(Object.assign({},e),{type:"error",title:null!=n?n:e.title}))}toastWarning(t,n,e={}){this.showToast(t,Object.assign(Object.assign({},e),{type:"warning",title:null!=n?n:e.title}))}toastInfo(t,n,e={}){this.showToast(t,Object.assign(Object.assign({},e),{type:"info",title:null!=n?n:e.title}))}toastQuestion(t,n,e={}){this.showToast(t,Object.assign(Object.assign({},e),{type:"question",title:null!=n?n:e.title}))}},n=window;n.notify=t,n.Notification=t}}();const NotificationSystem=window.notify?.constructor||function(){throw new Error("NotificationSystem no se pudo cargar. Verifica que anime.js esté disponible.")};export default NotificationSystem;export{NotificationSystem};
|
|
1
|
+
"use strict";var __rest=this&&this.__rest||function(t,n){var e={};for(var o in t)Object.prototype.hasOwnProperty.call(t,o)&&n.indexOf(o)<0&&(e[o]=t[o]);if(null!=t&&"function"==typeof Object.getOwnPropertySymbols){var i=0;for(o=Object.getOwnPropertySymbols(t);i<o.length;i++)n.indexOf(o[i])<0&&Object.prototype.propertyIsEnumerable.call(t,o[i])&&(e[o[i]]=t[o[i]])}return e};!function(){if("undefined"!=typeof anime)t();else{const n=document.createElement("script");n.src="https://cdnjs.cloudflare.com/ajax/libs/animejs/3.2.1/anime.min.js",n.onload=t,n.onerror=()=>{console.error("FerNotify: No se pudo cargar anime.js. Por favor, cargalo manualmente.")},document.head.appendChild(n)}function t(){const t=new class{constructor(){this.currentNotification=null,this._lastActiveElement=null,this._currentLoadingPromise=null,this._toastContainers=new Map,this._toastInstances=new Map,this.injectStyles(),this.loadBoxicons()}loadBoxicons(){if(!document.querySelector('link[href*="boxicons"]')){const t=document.createElement("link");t.rel="stylesheet",t.href="https://unpkg.com/boxicons@2.1.4/css/boxicons.min.css",document.head.appendChild(t)}}injectStyles(){const t=document.createElement("style");t.textContent="\n .notification-overlay {\n position: fixed;\n top: 0;\n left: 0;\n width: 100%;\n height: 100%;\n background-color: rgba(0, 0, 0, 0.4);\n backdrop-filter: blur(4px);\n -webkit-backdrop-filter: blur(4px);\n display: flex;\n align-items: center;\n justify-content: center;\n z-index: 9999;\n opacity: 0;\n overflow: hidden;\n }\n\n .notification-box {\n background: white;\n border-radius: 16px;\n padding: 40px 30px;\n max-width: 500px;\n width: 90%;\n max-height: 80vh;\n overflow: auto;\n position: relative;\n box-shadow: 0 20px 60px rgba(0, 0, 0, 0.3);\n text-align: center;\n transform: scale(0.7);\n opacity: 0;\n }\n\n .notification-content {\n text-align: left;\n margin-bottom: 18px;\n }\n\n .notification-close {\n position: absolute;\n top: 10px;\n right: 10px;\n width: 38px;\n height: 38px;\n border-radius: 8px;\n border: none;\n background: rgba(0,0,0,0.06);\n color: #111827;\n display: inline-flex;\n align-items: center;\n justify-content: center;\n cursor: pointer;\n font-size: 18px;\n }\n\n .notification-close:hover {\n background: rgba(0,0,0,0.09);\n }\n\n /* Form controls inside the modal */\n .notification-box input,\n .notification-box textarea,\n .notification-box select {\n width: 100%;\n padding: 10px 12px;\n border: 1px solid #e5e7eb;\n border-radius: 8px;\n background: #ffffff;\n color: #111827;\n font-size: 15px;\n box-sizing: border-box;\n transition: box-shadow 0.15s ease, border-color 0.15s ease;\n }\n\n .notification-box input:focus,\n .notification-box textarea:focus,\n .notification-box select:focus {\n outline: none;\n border-color: #6366f1;\n box-shadow: 0 6px 24px rgba(99,102,241,0.12), 0 0 0 4px rgba(99,102,241,0.06);\n }\n\n .notification-box label { display: block; margin-bottom: 6px; color: #374151; font-weight: 600; }\n\n /* Soporte para tema oscuro con clase .dark (Tailwind darkMode: 'class') */\n /* Esto tiene prioridad sobre prefers-color-scheme para respetar la elección del usuario en la web */\n .dark .notification-box { background: #0f1724 !important; color: #e6eef8 !important; }\n .dark .notification-box input,\n .dark .notification-box textarea,\n .dark .notification-box select {\n background: #0b1220 !important;\n border: 1px solid rgba(255,255,255,0.06) !important;\n color: #e6eef8 !important;\n }\n .dark .notification-box .notification-close { background: rgba(255,255,255,0.03) !important; color: #e6eef8 !important; }\n .dark .notification-overlay { background-color: rgba(0,0,0,0.6) !important; }\n .dark .notification-title { color: #e6eef8 !important; }\n .dark .notification-message { color: #cbd5e1 !important; }\n\n /* Forzar modo claro cuando NO hay clase .dark, ignorando prefers-color-scheme */\n html:not(.dark) .notification-box { background: white !important; color: #111827 !important; }\n html:not(.dark) .notification-box input,\n html:not(.dark) .notification-box textarea,\n html:not(.dark) .notification-box select {\n background: #ffffff !important;\n border: 1px solid #e5e7eb !important;\n color: #111827 !important;\n }\n html:not(.dark) .notification-box .notification-close { background: rgba(0,0,0,0.06) !important; color: #111827 !important; }\n html:not(.dark) .notification-overlay { background-color: rgba(0, 0, 0, 0.4) !important; }\n html:not(.dark) .notification-title { color: #1f2937 !important; }\n html:not(.dark) .notification-message { color: #6b7280 !important; }\n\n .notification-icon {\n width: 80px;\n height: 80px;\n border-radius: 50%;\n margin: 0 auto 25px;\n display: flex;\n align-items: center;\n justify-content: center;\n font-size: 40px;\n position: relative;\n }\n\n .notification-icon::before {\n content: '';\n position: absolute;\n width: 100%;\n height: 100%;\n border-radius: 50%;\n opacity: 0.2;\n }\n\n .notification-icon.success {\n background: linear-gradient(135deg, #10b981 0%, #059669 100%);\n color: white;\n }\n\n .notification-icon.success::before {\n background: #10b981;\n }\n\n .notification-icon.error {\n background: linear-gradient(135deg, #ef4444 0%, #dc2626 100%);\n color: white;\n }\n\n .notification-icon.error::before {\n background: #ef4444;\n }\n\n .notification-icon.warning {\n background: linear-gradient(135deg, #f59e0b 0%, #d97706 100%);\n color: white;\n }\n\n .notification-icon.warning::before {\n background: #f59e0b;\n }\n\n .notification-icon.info {\n background: linear-gradient(135deg, #3b82f6 0%, #2563eb 100%);\n color: white;\n }\n\n .notification-icon.info::before {\n background: #3b82f6;\n }\n .notification-icon.question {\n background: linear-gradient(135deg, #3b82f6 0%, #2563eb 100%);\n color: white;\n }\n\n .notification-icon.question::before {\n background: #3b82f6;\n }\n\n .notification-title {\n font-size: 24px;\n font-weight: 700;\n color: #1f2937;\n margin-bottom: 12px;\n line-height: 1.3;\n }\n\n .notification-message {\n font-size: 16px;\n color: #6b7280;\n line-height: 1.6;\n margin-bottom: 30px;\n }\n\n .notification-button {\n color: white;\n border: none;\n padding: 10px 14px;\n border-radius: 8px;\n font-size: 16px;\n font-weight: 600;\n cursor: pointer;\n transition: all 0.3s ease;\n }\n\n .notification-button:hover {\n transform: translateY(-2px);\n filter: brightness(1.1);\n }\n\n .notification-button:active {\n transform: translateY(0);\n }\n\n /* group container for multiple action buttons */\n .notification-button-group {\n display: flex;\n gap: 12px;\n justify-content: center;\n flex-wrap: wrap;\n margin-top: 10px;\n }\n\n .notification-icon-checkmark {\n animation: checkmark-draw 0.6s ease-in-out;\n }\n\n .notification-icon-cross {\n animation: cross-draw 0.5s ease-in-out;\n }\n\n @keyframes checkmark-draw {\n 0% {\n transform: scale(0) rotate(-45deg);\n opacity: 0;\n }\n 50% {\n transform: scale(1.2) rotate(-45deg);\n }\n 100% {\n transform: scale(1) rotate(0deg);\n opacity: 1;\n }\n }\n\n @keyframes cross-draw {\n 0% {\n transform: scale(0) rotate(-180deg);\n opacity: 0;\n }\n 50% {\n transform: scale(1.2) rotate(-90deg);\n }\n 100% {\n transform: scale(1) rotate(0deg);\n opacity: 1;\n }\n }\n\n /* Loading spinner styles */\n .notification-loading-container {\n display: flex;\n flex-direction: column;\n align-items: center;\n justify-content: center;\n margin: 0 auto;\n }\n\n .notification-spinner {\n width: 60px;\n height: 60px;\n border: 5px solid rgba(99, 102, 241, 0.15);\n border-top-color: #6366f1;\n border-radius: 50%;\n animation: notification-spin 1s linear infinite;\n margin: 0 auto;\n }\n\n @keyframes notification-spin {\n to {\n transform: rotate(360deg);\n }\n }\n\n .notification-loading-text {\n font-size: 14px;\n color: #6b7280;\n text-align: center;\n margin-top: 12px;\n }\n\n .dark .notification-loading-text {\n color: #cbd5e1;\n }\n\n /* ==================== Toast ==================== */\n .notify-toast-container {\n position: fixed;\n z-index: 10000;\n display: flex;\n flex-direction: column;\n gap: 10px;\n pointer-events: none;\n width: 360px;\n max-width: calc(100vw - 40px);\n }\n .notify-toast-top-right { top: 20px; right: 20px; }\n .notify-toast-top-left { top: 20px; left: 20px; }\n .notify-toast-top-center { top: 20px; left: 50%; transform: translateX(-50%); }\n .notify-toast-bottom-right { bottom: 20px; right: 20px; flex-direction: column-reverse; }\n .notify-toast-bottom-left { bottom: 20px; left: 20px; flex-direction: column-reverse; }\n\n .notify-toast {\n background: white;\n border-radius: 12px;\n padding: 14px 40px 14px 14px;\n box-shadow: 0 4px 24px rgba(0,0,0,0.12), 0 1px 4px rgba(0,0,0,0.06);\n display: flex;\n align-items: flex-start;\n gap: 12px;\n pointer-events: auto;\n position: relative;\n overflow: hidden;\n opacity: 0;\n transform: translateX(30px);\n transition: opacity 0.25s ease, transform 0.25s ease;\n }\n .notify-toast-top-left .notify-toast,\n .notify-toast-bottom-left .notify-toast { transform: translateX(-30px); }\n .notify-toast-top-center .notify-toast { transform: translateY(-20px); }\n .notify-toast.notify-toast-visible {\n opacity: 1;\n transform: translateX(0) translateY(0) !important;\n }\n\n .notify-toast-icon {\n width: 36px;\n height: 36px;\n border-radius: 50%;\n display: flex;\n align-items: center;\n justify-content: center;\n font-size: 20px;\n flex-shrink: 0;\n color: white;\n }\n .notify-toast-icon.success { background: linear-gradient(135deg, #10b981 0%, #059669 100%); }\n .notify-toast-icon.error { background: linear-gradient(135deg, #ef4444 0%, #dc2626 100%); }\n .notify-toast-icon.warning { background: linear-gradient(135deg, #f59e0b 0%, #d97706 100%); }\n .notify-toast-icon.info { background: linear-gradient(135deg, #3b82f6 0%, #2563eb 100%); }\n .notify-toast-icon.question { background: linear-gradient(135deg, #8b5cf6 0%, #6366f1 100%); }\n .notify-toast-icon.loading { background: linear-gradient(135deg, #6366f1 0%, #8b5cf6 100%); }\n\n .notify-toast-content { flex: 1; min-width: 0; }\n .notify-toast-title {\n font-size: 14px;\n font-weight: 700;\n color: #1f2937;\n margin-bottom: 2px;\n line-height: 1.3;\n cursor: default;\n }\n .notify-toast-message {\n font-size: 13px;\n color: #6b7280;\n line-height: 1.5;\n cursor: default;\n }\n\n .notify-toast-close {\n position: absolute;\n top: 8px;\n right: 8px;\n width: 24px;\n height: 24px;\n border-radius: 6px;\n border: none;\n background: rgba(0,0,0,0.06);\n color: #6b7280;\n cursor: pointer;\n font-size: 16px;\n display: flex;\n align-items: center;\n justify-content: center;\n line-height: 1;\n padding: 0;\n }\n .notify-toast-close:hover { background: rgba(0,0,0,0.1); color: #374151; }\n\n /* Sin botón de cierre: reducir padding derecho */\n .notify-toast.notify-toast-no-close { padding-right: 14px; }\n\n .notify-toast-progress {\n position: absolute;\n bottom: 0;\n left: 0;\n height: 3px;\n width: 100%;\n border-radius: 0 0 0 12px;\n }\n .notify-toast-progress.success { background: #10b981; }\n .notify-toast-progress.error { background: #ef4444; }\n .notify-toast-progress.warning { background: #f59e0b; }\n .notify-toast-progress.info { background: #3b82f6; }\n .notify-toast-progress.question { background: #8b5cf6; }\n .notify-toast-progress.loading { background: #6366f1; }\n\n /* Spinner para toast de carga */\n .notify-toast-spinner {\n width: 18px;\n height: 18px;\n border: 2.5px solid rgba(255,255,255,0.35);\n border-top-color: white;\n border-radius: 50%;\n animation: notification-spin 0.8s linear infinite;\n flex-shrink: 0;\n }\n\n .dark .notify-toast { background: #0f1724; box-shadow: 0 4px 24px rgba(0,0,0,0.35); }\n .dark .notify-toast-title { color: #e6eef8; }\n .dark .notify-toast-message { color: #cbd5e1; }\n .dark .notify-toast-close { background: rgba(255,255,255,0.06); color: #94a3b8; }\n .dark .notify-toast-close:hover { background: rgba(255,255,255,0.1); color: #e2e8f0; }\n\n /* Respeta la preferencia de movimiento reducido del sistema */\n @media (prefers-reduced-motion: reduce) {\n .notify-toast {\n transition: opacity 0.1s ease !important;\n transform: none !important;\n }\n .notify-toast.notify-toast-visible {\n transform: none !important;\n }\n .notify-toast-spinner {\n animation-duration: 1.5s !important;\n }\n .notify-toast-progress {\n transition: none !important;\n }\n }\n ",document.head.appendChild(t)}getIcon(t){const n={success:'<i class="bx bx-check" aria-hidden="true"></i>',error:'<i class="bx bx-x" aria-hidden="true"></i>',warning:'<i class="bx bx-error" aria-hidden="true"></i>',info:'<i class="bx bx-info-circle" aria-hidden="true"></i>',question:'<i class="bx bx-question-mark" aria-hidden="true"></i>',loading:'<div class="notify-toast-spinner" aria-hidden="true"></div>'};return n[t]||n.info}getDefaultTitle(t){return{success:"¡Éxito!",error:"Error",warning:"Advertencia",info:"Información",question:"Pregunta"}[t]||"Notificación"}getButtonGradient(t){const n={success:"linear-gradient(135deg, #10b981 0%, #059669 100%)",error:"linear-gradient(135deg, #ef4444 0%, #dc2626 100%)",warning:"linear-gradient(135deg, #f59e0b 0%, #d97706 100%)",info:"linear-gradient(135deg, #3b82f6 0%, #2563eb 100%)",question:"linear-gradient(135deg, #8b5cf6 0%, #6366f1 100%)"};return n[t]||n.info}getButtonShadow(t){const n={success:"rgba(16, 185, 129, 0)",error:"rgba(239, 68, 68, 0)",warning:"rgba(245, 159, 11, 0)",info:"rgba(59, 131, 246, 0)",question:"rgba(108, 99, 245, 0)"};return n[t]||n.info}show(t={}){if(this.currentNotification){const t=this.currentNotification;this.currentNotification=null;try{t&&t.parentNode&&t.parentNode.removeChild(t)}catch(t){}}const{type:n="info",title:e=this.getDefaultTitle(n),message:o="",buttonText:i="OK",buttonColor:a=null,onClose:r=null,timer:s=null,allowOutsideClick:c=!0,allowEscapeKey:l=!0,hideButton:d=!1,buttons:f=null}=t,u=!0===t.showCloseButton;try{document.body.style.overflow="hidden"}catch(t){}try{document.documentElement.style.overflow="hidden"}catch(t){}const p=document.createElement("div");p.className="notification-overlay",p.tabIndex=-1,p.setAttribute("role","dialog"),p.setAttribute("aria-modal","true"),p.style.pointerEvents="auto";const m=document.createElement("div");m.className="notification-box";const b=document.createElement("div");b.className=`notification-icon ${n}`,d&&"info"===n?(b.className="notification-loading-container",b.innerHTML='<div class="notification-spinner"></div>',b.style.background="transparent",b.style.boxShadow="none",b.style.width="100px",b.style.height="100px"):b.innerHTML=this.getIcon(n);const g=document.createElement("h3");g.className="notification-title",g.textContent=e;const h=document.createElement("p");h.className="notification-message",h.textContent=o;let y=null;if(t.html||t.content)if(y=document.createElement("div"),y.className="notification-content",t.html)try{y.innerHTML=t.html}catch(n){y.textContent=t.html}else t.content&&t.content instanceof HTMLElement&&y.appendChild(t.content);const x=()=>this.close(r);let w=null,v=null;if(!d)if(Array.isArray(f)&&f.length)v=document.createElement("div"),v.className="notification-button-group",f.forEach(t=>{const e=document.createElement("button");e.className="notification-button",e.textContent=t.text||"OK";const o=t.color||this.getButtonGradient(n),i=t.shadowColor||this.getButtonShadow(n);e.style.background=o,e.style.boxShadow=`0 4px 12px ${i}`,e.addEventListener("click",n=>{n.stopPropagation(),n.preventDefault();try{x().then(()=>{if("function"==typeof t.onClick)try{const n=t.onClick();n&&"function"==typeof n.then&&n.catch(t=>console.error(t))}catch(t){console.error(t)}}).catch(()=>{})}catch(t){console.error(t)}}),e.addEventListener("mouseenter",()=>{e.style.boxShadow=`0 6px 16px ${i}`}),e.addEventListener("mouseleave",()=>{e.style.boxShadow=`0 4px 12px ${i}`}),v.appendChild(e)});else if(t.onConfirm||t.onCancel||t.confirmText||t.cancelText){v=document.createElement("div"),v.className="notification-button-group";const e=t.cancelText||"Cancelar",o=t.confirmText||"Aceptar",i=document.createElement("button");i.className="notification-button",i.textContent=e;const a=t.cancelColor||"linear-gradient(135deg, #9ca3af 0%, #6b7280 100%)",r=t.cancelShadow||"rgba(107,114,128,0.25)";i.style.background=a,i.style.boxShadow=`0 4px 12px ${r}`,i.addEventListener("click",n=>{n.stopPropagation(),n.preventDefault(),x().then(()=>{try{if("function"==typeof t.onCancel){const n=t.onCancel();n&&"function"==typeof n.then&&n.catch(t=>console.error(t))}}catch(t){console.error(t)}}).catch(()=>{})}),i.addEventListener("mouseenter",()=>{i.style.boxShadow=`0 6px 16px ${r}`}),i.addEventListener("mouseleave",()=>{i.style.boxShadow=`0 4px 12px ${r}`});const s=document.createElement("button");s.className="notification-button",s.textContent=o;const c=t.confirmColor||this.getButtonGradient(n),l=t.confirmShadow||this.getButtonShadow(n);s.style.background=c,s.style.boxShadow=`0 4px 12px ${l}`,s.addEventListener("click",async n=>{n.stopPropagation(),n.preventDefault();try{if(await x(),"function"==typeof t.onConfirm){const n=t.onConfirm();n&&"function"==typeof n.then&&await n}}catch(t){console.error(t)}}),s.addEventListener("mouseenter",()=>{s.style.boxShadow=`0 6px 16px ${l}`}),s.addEventListener("mouseleave",()=>{s.style.boxShadow=`0 4px 12px ${l}`}),v.appendChild(i),v.appendChild(s)}else if(i){w=document.createElement("button"),w.className="notification-button",w.textContent=i;const t=a||this.getButtonGradient(n),e=this.getButtonShadow(n);w.style.background=t,w.style.boxShadow=`0 4px 12px ${e}`}let k=null;if(u&&(k=document.createElement("button"),k.setAttribute("aria-label","Cerrar"),k.className="notification-close",k.innerHTML="×",k.addEventListener("click",t=>{t.stopPropagation(),x()})),m.appendChild(b),y){const t="notify-desc-"+Date.now();y.id=t,p.setAttribute("aria-describedby",t),m.appendChild(y)}else m.appendChild(g),m.appendChild(h);k&&m.appendChild(k),v?m.appendChild(v):w&&m.appendChild(w),p.appendChild(m),document.body.appendChild(p);const E=p,C=new Promise(t=>{try{E._externalResolve=t}catch(t){}});try{const t=document.getElementById("notify-live");t&&(t.textContent=`${e}: ${o}`)}catch(t){}try{this._lastActiveElement=document.activeElement}catch(t){this._lastActiveElement=null}this.currentNotification=p;try{const t=m.querySelectorAll('a[href], button:not([disabled]), textarea, input, select, [tabindex]:not([tabindex="-1"])');t&&t.length?t[0].focus():w?w.focus():p.focus()}catch(t){try{p.focus()}catch(t){}}const _=t=>{if("Tab"!==t.key)return;const n=Array.from(m.querySelectorAll('a[href], button:not([disabled]), textarea, input, select, [tabindex]:not([tabindex="-1"])')).filter(t=>t instanceof HTMLElement&&null!==t.offsetParent);if(!n.length)return void t.preventDefault();const e=n[0],o=n[n.length-1];t.shiftKey||document.activeElement!==o?t.shiftKey&&document.activeElement===e&&(t.preventDefault(),o.focus()):(t.preventDefault(),e.focus())};E._focusTrap=_,document.addEventListener("keydown",_);const N=t.anim||{},T="number"==typeof N.overlayDuration?N.overlayDuration:150,S=N.overlayEasing||"easeOutQuad",L="number"==typeof N.boxDuration?N.boxDuration:200,j="number"==typeof N.boxDelay?N.boxDelay:50,O=N.boxEasing||"easeOutBack",D="number"==typeof N.boxStartScale?N.boxStartScale:.8,A="number"==typeof N.iconDuration?N.iconDuration:250,$="number"==typeof N.iconDelay?N.iconDelay:100,P="number"==typeof N.iconRotate?N.iconRotate:"success"===n?-90:"error"===n?90:0;if("number"==typeof N.overlayOpacity&&(p.style.backgroundColor=`rgba(0,0,0,${N.overlayOpacity})`),anime({targets:p,opacity:[0,1],duration:T,easing:S}),anime({targets:m,scale:[D,1],opacity:[0,1],duration:L,easing:O,delay:j}),anime({targets:b,scale:[0,1],rotate:[P,0],duration:A,easing:O,delay:$}),w){const t=this.getButtonShadow(n);w.addEventListener("mouseenter",()=>{w.style.boxShadow=`0 6px 16px ${t}`}),w.addEventListener("mouseleave",()=>{w.style.boxShadow=`0 4px 12px ${t}`}),w.addEventListener("click",t=>{t.stopPropagation(),t.preventDefault(),x().catch(()=>{})})}if(c&&p.addEventListener("click",t=>{m.contains(t.target)||x()}),s&&setTimeout(()=>{x()},s),l){const t=n=>{"Escape"===n.key&&(x(),document.removeEventListener("keydown",t))};E._escHandler=t,document.addEventListener("keydown",t)}return C}close(t=null){if(!this.currentNotification)return Promise.resolve();const n=this.currentNotification,e=n,o=n.querySelector(".notification-box");return this.currentNotification=null,anime({targets:o,scale:.8,opacity:0,duration:100,easing:"easeInQuad"}),new Promise(o=>{anime({targets:n,opacity:0,duration:100,easing:"easeInQuad",complete:()=>{try{e&&e._escHandler&&(document.removeEventListener("keydown",e._escHandler),e._escHandler=void 0)}catch(t){}try{e&&e._focusTrap&&(document.removeEventListener("keydown",e._focusTrap),e._focusTrap=void 0)}catch(t){}try{if(e&&"function"==typeof e._externalResolve){try{e._externalResolve()}catch(t){}e._externalResolve=void 0}}catch(t){}try{n&&n.parentNode&&n.parentNode.removeChild(n)}catch(t){try{n.remove()}catch(t){}}if(!this.currentNotification){try{document.body.style.overflow=""}catch(t){}try{document.documentElement.style.overflow=""}catch(t){}}try{this._lastActiveElement&&"function"==typeof this._lastActiveElement.focus&&this._lastActiveElement.focus()}catch(t){}this._lastActiveElement=null,t&&t(),o()}})})}success(t,n=null,e={}){this.show(Object.assign({type:"success",title:n||this.getDefaultTitle("success"),message:t},e))}error(t,n=null,e={}){this.show(Object.assign({type:"error",title:n||this.getDefaultTitle("error"),message:t},e))}warning(t,n=null,e={}){this.show(Object.assign({type:"warning",title:n||this.getDefaultTitle("warning"),message:t},e))}question(t,n=null,e={}){this.show(Object.assign({type:"question",title:n||this.getDefaultTitle("question"),message:t},e))}info(t,n=null,e={}){this.show(Object.assign({type:"info",title:n||this.getDefaultTitle("info"),message:t},e))}loading(t="Cargando...",n="Espera",e={}){const o=Object.assign({type:"info",title:n,message:t,hideButton:!0,allowOutsideClick:!1,allowEscapeKey:!1},e),i=this.show(o);return this._currentLoadingPromise=i,i}closeLoading(t=null){return this._currentLoadingPromise=null,this.close(t)}hide(t=null){return this.close(t)}hiden(t=null){return this.close(t)}_formatTime(t){const n=Math.max(0,Math.floor(t));return`${Math.floor(n/60).toString().padStart(2,"0")}:${(n%60).toString().padStart(2,"0")}`}showToast(t,n={}){var e,o;const i=n.type||"info",a=null!==(e=n.title)&&void 0!==e?e:null,r="number"==typeof n.duration?n.duration:4e3,s=n.position||"top-right",c=!1!==n.showProgress,l=null!==(o=n.id)&&void 0!==o?o:null,d=!1!==n.closeable;if(null!==l){const t=this._toastInstances.get(l);if(t)return void t.reset(r)}let f=this._toastContainers.get(s);f&&document.body.contains(f)||(f=document.createElement("div"),f.className=`notify-toast-container notify-toast-${s}`,f.setAttribute("aria-label","Notificaciones"),document.body.appendChild(f),this._toastContainers.set(s,f));const u=s.startsWith("bottom"),p="top-center"===s,m=document.createElement("div");m.className="notify-toast",d||m.classList.add("notify-toast-no-close"),"error"===i||"warning"===i?m.setAttribute("role","alert"):m.setAttribute("role","status"),m.setAttribute("aria-atomic","true"),m.setAttribute("aria-live","error"===i||"warning"===i?"assertive":"polite");const b=document.createElement("div");b.className=`notify-toast-icon ${i}`,b.innerHTML=this.getIcon(i);const g=document.createElement("div");if(g.className="notify-toast-content",a){const t=document.createElement("div");t.className="notify-toast-title",t.textContent=a,g.appendChild(t)}const h=document.createElement("div");if(h.className="notify-toast-message",h.textContent=t,g.appendChild(h),m.appendChild(b),m.appendChild(g),d){const t=document.createElement("button");t.className="notify-toast-close",t.setAttribute("aria-label","Cerrar notificación"),t.innerHTML="×",t.addEventListener("click",E),m.appendChild(t)}let y=null;r>0&&c&&(y=document.createElement("div"),y.className=`notify-toast-progress ${i}`,y.setAttribute("role","progressbar"),y.setAttribute("aria-hidden","true"),m.appendChild(y)),u||p?f.appendChild(m):f.insertBefore(m,f.firstChild);let x=!1,w=null,v=r,k=0;function E(){if(x)return Promise.resolve();if(x=!0,m.contains(document.activeElement))try{document.activeElement.blur()}catch(t){}return m.classList.remove("notify-toast-visible"),new Promise(t=>{setTimeout(()=>{m.parentNode&&m.parentNode.removeChild(m),t()},300)})}const C=t=>{k=Date.now(),w=setTimeout(()=>{null!==l&&this._toastInstances.delete(l),E()},t),y&&(y.style.transition=`width ${t}ms linear`,y.style.width="0%")},_=t=>{x||(null!==w&&(clearTimeout(w),w=null),v=t,t>0?(y&&(y.style.transition="none",y.style.width="100%",y.offsetWidth),C(t)):y&&(y.style.transition="none",y.style.width="100%"))};if(null!==l){const t=()=>{if(!x){if(x=!0,null!==w&&clearTimeout(w),m.contains(document.activeElement))try{document.activeElement.blur()}catch(t){}m.parentNode&&m.parentNode.removeChild(m)}};this._toastInstances.set(l,{reset:_,dismiss:E,_silentDismiss:t})}requestAnimationFrame(()=>{requestAnimationFrame(()=>{m.classList.add("notify-toast-visible"),r>0&&C(r)})}),r>0&&d&&(m.addEventListener("mouseenter",()=>{if(x||null===w)return;clearTimeout(w),w=null;const t=Date.now()-k;if(v=Math.max(0,v-t),y){const t=v/r*100;y.style.transition="none",y.style.width=`${t}%`}}),m.addEventListener("mouseleave",()=>{x||v<=0||C(v)}))}toast(t,n={}){if("string"==typeof t)this.showToast(t,n);else{const{message:n=""}=t,e=__rest(t,["message"]);this.showToast(n,e)}}toastSuccess(t,n,e={}){this.showToast(t,Object.assign(Object.assign({},e),{type:"success",title:null!=n?n:e.title}))}toastError(t,n,e={}){this.showToast(t,Object.assign(Object.assign({},e),{type:"error",title:null!=n?n:e.title}))}toastWarning(t,n,e={}){this.showToast(t,Object.assign(Object.assign({},e),{type:"warning",title:null!=n?n:e.title}))}toastInfo(t,n,e={}){this.showToast(t,Object.assign(Object.assign({},e),{type:"info",title:null!=n?n:e.title}))}toastQuestion(t,n,e={}){this.showToast(t,Object.assign(Object.assign({},e),{type:"question",title:null!=n?n:e.title}))}toastLoading(t="Cargando...",n,e={}){this.showToast(t,Object.assign(Object.assign({position:"top-right"},e),{type:"loading",title:null!=n?n:e.title,id:"__loading__",closeable:!1,duration:0,showProgress:!1}))}closeToastLoading(){const t=this._toastInstances.get("__loading__");return t?(this._toastInstances.delete("__loading__"),t.dismiss()):Promise.resolve()}replaceToastLoading(t,n={}){const e=this._toastInstances.get("__loading__");e&&(this._toastInstances.delete("__loading__"),e._silentDismiss()),this.showToast(t,n)}},n=window;n.notify=t,n.Notification=t}}();const NotificationSystem=window.notify?.constructor||function(){throw new Error("NotificationSystem no se pudo cargar. Verifica que anime.js esté disponible.")};export default NotificationSystem;export{NotificationSystem};
|
|
@@ -30,6 +30,7 @@ var __rest = (this && this.__rest) || function (s, e) {
|
|
|
30
30
|
this._lastActiveElement = null;
|
|
31
31
|
this._currentLoadingPromise = null;
|
|
32
32
|
this._toastContainers = new Map();
|
|
33
|
+
this._toastInstances = new Map();
|
|
33
34
|
this.injectStyles();
|
|
34
35
|
this.loadBoxicons();
|
|
35
36
|
}
|
|
@@ -393,6 +394,7 @@ var __rest = (this && this.__rest) || function (s, e) {
|
|
|
393
394
|
.notify-toast-icon.warning { background: linear-gradient(135deg, #f59e0b 0%, #d97706 100%); }
|
|
394
395
|
.notify-toast-icon.info { background: linear-gradient(135deg, #3b82f6 0%, #2563eb 100%); }
|
|
395
396
|
.notify-toast-icon.question { background: linear-gradient(135deg, #8b5cf6 0%, #6366f1 100%); }
|
|
397
|
+
.notify-toast-icon.loading { background: linear-gradient(135deg, #6366f1 0%, #8b5cf6 100%); }
|
|
396
398
|
|
|
397
399
|
.notify-toast-content { flex: 1; min-width: 0; }
|
|
398
400
|
.notify-toast-title {
|
|
@@ -401,11 +403,13 @@ var __rest = (this && this.__rest) || function (s, e) {
|
|
|
401
403
|
color: #1f2937;
|
|
402
404
|
margin-bottom: 2px;
|
|
403
405
|
line-height: 1.3;
|
|
406
|
+
cursor: default;
|
|
404
407
|
}
|
|
405
408
|
.notify-toast-message {
|
|
406
409
|
font-size: 13px;
|
|
407
410
|
color: #6b7280;
|
|
408
411
|
line-height: 1.5;
|
|
412
|
+
cursor: default;
|
|
409
413
|
}
|
|
410
414
|
|
|
411
415
|
.notify-toast-close {
|
|
@@ -428,6 +432,9 @@ var __rest = (this && this.__rest) || function (s, e) {
|
|
|
428
432
|
}
|
|
429
433
|
.notify-toast-close:hover { background: rgba(0,0,0,0.1); color: #374151; }
|
|
430
434
|
|
|
435
|
+
/* Sin botón de cierre: reducir padding derecho */
|
|
436
|
+
.notify-toast.notify-toast-no-close { padding-right: 14px; }
|
|
437
|
+
|
|
431
438
|
.notify-toast-progress {
|
|
432
439
|
position: absolute;
|
|
433
440
|
bottom: 0;
|
|
@@ -441,12 +448,41 @@ var __rest = (this && this.__rest) || function (s, e) {
|
|
|
441
448
|
.notify-toast-progress.warning { background: #f59e0b; }
|
|
442
449
|
.notify-toast-progress.info { background: #3b82f6; }
|
|
443
450
|
.notify-toast-progress.question { background: #8b5cf6; }
|
|
451
|
+
.notify-toast-progress.loading { background: #6366f1; }
|
|
452
|
+
|
|
453
|
+
/* Spinner para toast de carga */
|
|
454
|
+
.notify-toast-spinner {
|
|
455
|
+
width: 18px;
|
|
456
|
+
height: 18px;
|
|
457
|
+
border: 2.5px solid rgba(255,255,255,0.35);
|
|
458
|
+
border-top-color: white;
|
|
459
|
+
border-radius: 50%;
|
|
460
|
+
animation: notification-spin 0.8s linear infinite;
|
|
461
|
+
flex-shrink: 0;
|
|
462
|
+
}
|
|
444
463
|
|
|
445
464
|
.dark .notify-toast { background: #0f1724; box-shadow: 0 4px 24px rgba(0,0,0,0.35); }
|
|
446
465
|
.dark .notify-toast-title { color: #e6eef8; }
|
|
447
466
|
.dark .notify-toast-message { color: #cbd5e1; }
|
|
448
467
|
.dark .notify-toast-close { background: rgba(255,255,255,0.06); color: #94a3b8; }
|
|
449
468
|
.dark .notify-toast-close:hover { background: rgba(255,255,255,0.1); color: #e2e8f0; }
|
|
469
|
+
|
|
470
|
+
/* Respeta la preferencia de movimiento reducido del sistema */
|
|
471
|
+
@media (prefers-reduced-motion: reduce) {
|
|
472
|
+
.notify-toast {
|
|
473
|
+
transition: opacity 0.1s ease !important;
|
|
474
|
+
transform: none !important;
|
|
475
|
+
}
|
|
476
|
+
.notify-toast.notify-toast-visible {
|
|
477
|
+
transform: none !important;
|
|
478
|
+
}
|
|
479
|
+
.notify-toast-spinner {
|
|
480
|
+
animation-duration: 1.5s !important;
|
|
481
|
+
}
|
|
482
|
+
.notify-toast-progress {
|
|
483
|
+
transition: none !important;
|
|
484
|
+
}
|
|
485
|
+
}
|
|
450
486
|
`;
|
|
451
487
|
document.head.appendChild(style);
|
|
452
488
|
}
|
|
@@ -456,7 +492,8 @@ var __rest = (this && this.__rest) || function (s, e) {
|
|
|
456
492
|
'error': '<i class="bx bx-x" aria-hidden="true"></i>',
|
|
457
493
|
'warning': '<i class="bx bx-error" aria-hidden="true"></i>',
|
|
458
494
|
'info': '<i class="bx bx-info-circle" aria-hidden="true"></i>',
|
|
459
|
-
'question': '<i class="bx bx-question-mark" aria-hidden="true"></i>'
|
|
495
|
+
'question': '<i class="bx bx-question-mark" aria-hidden="true"></i>',
|
|
496
|
+
'loading': '<div class="notify-toast-spinner" aria-hidden="true"></div>'
|
|
460
497
|
};
|
|
461
498
|
return icons[type] || icons.info;
|
|
462
499
|
}
|
|
@@ -954,16 +991,27 @@ var __rest = (this && this.__rest) || function (s, e) {
|
|
|
954
991
|
return `${mm}:${ss}`;
|
|
955
992
|
}
|
|
956
993
|
showToast(message, options = {}) {
|
|
957
|
-
var _a;
|
|
994
|
+
var _a, _b;
|
|
958
995
|
const type = options.type || 'info';
|
|
959
996
|
const title = (_a = options.title) !== null && _a !== void 0 ? _a : null;
|
|
960
997
|
const duration = typeof options.duration === 'number' ? options.duration : 4000;
|
|
961
998
|
const position = options.position || 'top-right';
|
|
962
999
|
const showProgress = options.showProgress !== false;
|
|
1000
|
+
const toastId = (_b = options.id) !== null && _b !== void 0 ? _b : null;
|
|
1001
|
+
const closeable = options.closeable !== false;
|
|
1002
|
+
// Deduplicación: si ya existe un toast con este ID, resetear su cuenta regresiva
|
|
1003
|
+
if (toastId !== null) {
|
|
1004
|
+
const existing = this._toastInstances.get(toastId);
|
|
1005
|
+
if (existing) {
|
|
1006
|
+
existing.reset(duration);
|
|
1007
|
+
return;
|
|
1008
|
+
}
|
|
1009
|
+
}
|
|
963
1010
|
let container = this._toastContainers.get(position);
|
|
964
1011
|
if (!container || !document.body.contains(container)) {
|
|
965
1012
|
container = document.createElement('div');
|
|
966
1013
|
container.className = `notify-toast-container notify-toast-${position}`;
|
|
1014
|
+
container.setAttribute('aria-label', 'Notificaciones');
|
|
967
1015
|
document.body.appendChild(container);
|
|
968
1016
|
this._toastContainers.set(position, container);
|
|
969
1017
|
}
|
|
@@ -971,6 +1019,20 @@ var __rest = (this && this.__rest) || function (s, e) {
|
|
|
971
1019
|
const isCenter = position === 'top-center';
|
|
972
1020
|
const toast = document.createElement('div');
|
|
973
1021
|
toast.className = 'notify-toast';
|
|
1022
|
+
if (!closeable) {
|
|
1023
|
+
toast.classList.add('notify-toast-no-close');
|
|
1024
|
+
}
|
|
1025
|
+
// Accesibilidad: role + aria-live según la urgencia del tipo
|
|
1026
|
+
// role="alert" implica aria-live="assertive" + aria-atomic="true" → lector lo interrumpe
|
|
1027
|
+
// role="status" implica aria-live="polite" + aria-atomic="true" → lector espera pausa
|
|
1028
|
+
if (type === 'error' || type === 'warning') {
|
|
1029
|
+
toast.setAttribute('role', 'alert');
|
|
1030
|
+
}
|
|
1031
|
+
else {
|
|
1032
|
+
toast.setAttribute('role', 'status');
|
|
1033
|
+
}
|
|
1034
|
+
toast.setAttribute('aria-atomic', 'true');
|
|
1035
|
+
toast.setAttribute('aria-live', type === 'error' || type === 'warning' ? 'assertive' : 'polite');
|
|
974
1036
|
const iconEl = document.createElement('div');
|
|
975
1037
|
iconEl.className = `notify-toast-icon ${type}`;
|
|
976
1038
|
iconEl.innerHTML = this.getIcon(type);
|
|
@@ -986,17 +1048,22 @@ var __rest = (this && this.__rest) || function (s, e) {
|
|
|
986
1048
|
msgEl.className = 'notify-toast-message';
|
|
987
1049
|
msgEl.textContent = message;
|
|
988
1050
|
contentEl.appendChild(msgEl);
|
|
989
|
-
const closeBtn = document.createElement('button');
|
|
990
|
-
closeBtn.className = 'notify-toast-close';
|
|
991
|
-
closeBtn.setAttribute('aria-label', 'Cerrar notificación');
|
|
992
|
-
closeBtn.innerHTML = '×';
|
|
993
1051
|
toast.appendChild(iconEl);
|
|
994
1052
|
toast.appendChild(contentEl);
|
|
995
|
-
|
|
1053
|
+
if (closeable) {
|
|
1054
|
+
const closeBtn = document.createElement('button');
|
|
1055
|
+
closeBtn.className = 'notify-toast-close';
|
|
1056
|
+
closeBtn.setAttribute('aria-label', 'Cerrar notificación');
|
|
1057
|
+
closeBtn.innerHTML = '×';
|
|
1058
|
+
closeBtn.addEventListener('click', removeToast);
|
|
1059
|
+
toast.appendChild(closeBtn);
|
|
1060
|
+
}
|
|
996
1061
|
let progressEl = null;
|
|
997
1062
|
if (duration > 0 && showProgress) {
|
|
998
1063
|
progressEl = document.createElement('div');
|
|
999
1064
|
progressEl.className = `notify-toast-progress ${type}`;
|
|
1065
|
+
progressEl.setAttribute('role', 'progressbar');
|
|
1066
|
+
progressEl.setAttribute('aria-hidden', 'true'); // decorativo: el timer no añade info que el usuario necesite leer
|
|
1000
1067
|
toast.appendChild(progressEl);
|
|
1001
1068
|
}
|
|
1002
1069
|
if (isBottom || isCenter) {
|
|
@@ -1009,19 +1076,34 @@ var __rest = (this && this.__rest) || function (s, e) {
|
|
|
1009
1076
|
let timerId = null;
|
|
1010
1077
|
let remaining = duration;
|
|
1011
1078
|
let timerStartedAt = 0;
|
|
1012
|
-
|
|
1079
|
+
function removeToast() {
|
|
1013
1080
|
if (dismissed)
|
|
1014
|
-
return;
|
|
1081
|
+
return Promise.resolve();
|
|
1015
1082
|
dismissed = true;
|
|
1083
|
+
// Si el foco estaba dentro del toast, sacarlo antes de que el nodo desaparezca
|
|
1084
|
+
// para evitar que el foco se pierda silenciosamente en el documento
|
|
1085
|
+
if (toast.contains(document.activeElement)) {
|
|
1086
|
+
try {
|
|
1087
|
+
document.activeElement.blur();
|
|
1088
|
+
}
|
|
1089
|
+
catch (e) { }
|
|
1090
|
+
}
|
|
1016
1091
|
toast.classList.remove('notify-toast-visible');
|
|
1017
|
-
|
|
1018
|
-
|
|
1019
|
-
toast.parentNode
|
|
1020
|
-
|
|
1021
|
-
|
|
1092
|
+
return new Promise(resolve => {
|
|
1093
|
+
setTimeout(() => {
|
|
1094
|
+
if (toast.parentNode)
|
|
1095
|
+
toast.parentNode.removeChild(toast);
|
|
1096
|
+
resolve();
|
|
1097
|
+
}, 300);
|
|
1098
|
+
});
|
|
1099
|
+
}
|
|
1022
1100
|
const startCountdown = (ms) => {
|
|
1023
1101
|
timerStartedAt = Date.now();
|
|
1024
|
-
timerId = setTimeout(
|
|
1102
|
+
timerId = setTimeout(() => {
|
|
1103
|
+
if (toastId !== null)
|
|
1104
|
+
this._toastInstances.delete(toastId);
|
|
1105
|
+
removeToast();
|
|
1106
|
+
}, ms);
|
|
1025
1107
|
if (progressEl) {
|
|
1026
1108
|
progressEl.style.transition = `width ${ms}ms linear`;
|
|
1027
1109
|
progressEl.style.width = '0%';
|
|
@@ -1045,7 +1127,46 @@ var __rest = (this && this.__rest) || function (s, e) {
|
|
|
1045
1127
|
return;
|
|
1046
1128
|
startCountdown(remaining);
|
|
1047
1129
|
};
|
|
1048
|
-
|
|
1130
|
+
const resetCountdown = (newDuration) => {
|
|
1131
|
+
if (dismissed)
|
|
1132
|
+
return;
|
|
1133
|
+
if (timerId !== null) {
|
|
1134
|
+
clearTimeout(timerId);
|
|
1135
|
+
timerId = null;
|
|
1136
|
+
}
|
|
1137
|
+
remaining = newDuration;
|
|
1138
|
+
if (newDuration > 0) {
|
|
1139
|
+
if (progressEl) {
|
|
1140
|
+
progressEl.style.transition = 'none';
|
|
1141
|
+
progressEl.style.width = '100%';
|
|
1142
|
+
// Forzar reflow para que la transición se aplique desde el inicio
|
|
1143
|
+
void progressEl.offsetWidth;
|
|
1144
|
+
}
|
|
1145
|
+
startCountdown(newDuration);
|
|
1146
|
+
}
|
|
1147
|
+
else if (progressEl) {
|
|
1148
|
+
progressEl.style.transition = 'none';
|
|
1149
|
+
progressEl.style.width = '100%';
|
|
1150
|
+
}
|
|
1151
|
+
};
|
|
1152
|
+
if (toastId !== null) {
|
|
1153
|
+
const silentDismiss = () => {
|
|
1154
|
+
if (dismissed)
|
|
1155
|
+
return;
|
|
1156
|
+
dismissed = true;
|
|
1157
|
+
if (timerId !== null)
|
|
1158
|
+
clearTimeout(timerId);
|
|
1159
|
+
if (toast.contains(document.activeElement)) {
|
|
1160
|
+
try {
|
|
1161
|
+
document.activeElement.blur();
|
|
1162
|
+
}
|
|
1163
|
+
catch (e) { }
|
|
1164
|
+
}
|
|
1165
|
+
if (toast.parentNode)
|
|
1166
|
+
toast.parentNode.removeChild(toast);
|
|
1167
|
+
};
|
|
1168
|
+
this._toastInstances.set(toastId, { reset: resetCountdown, dismiss: removeToast, _silentDismiss: silentDismiss });
|
|
1169
|
+
}
|
|
1049
1170
|
requestAnimationFrame(() => {
|
|
1050
1171
|
requestAnimationFrame(() => {
|
|
1051
1172
|
toast.classList.add('notify-toast-visible');
|
|
@@ -1054,7 +1175,7 @@ var __rest = (this && this.__rest) || function (s, e) {
|
|
|
1054
1175
|
}
|
|
1055
1176
|
});
|
|
1056
1177
|
});
|
|
1057
|
-
if (duration > 0) {
|
|
1178
|
+
if (duration > 0 && closeable) {
|
|
1058
1179
|
toast.addEventListener('mouseenter', pauseCountdown);
|
|
1059
1180
|
toast.addEventListener('mouseleave', resumeCountdown);
|
|
1060
1181
|
}
|
|
@@ -1083,6 +1204,38 @@ var __rest = (this && this.__rest) || function (s, e) {
|
|
|
1083
1204
|
toastQuestion(message, title, options = {}) {
|
|
1084
1205
|
this.showToast(message, Object.assign(Object.assign({}, options), { type: 'question', title: title !== null && title !== void 0 ? title : options.title }));
|
|
1085
1206
|
}
|
|
1207
|
+
/**
|
|
1208
|
+
* Muestra un toast de carga con spinner.
|
|
1209
|
+
* - No se puede cerrar manualmente (closeable: false por defecto).
|
|
1210
|
+
* - No tiene cuenta regresiva (duration: 0 por defecto).
|
|
1211
|
+
* - Solo puede existir uno a la vez (id '__loading__').
|
|
1212
|
+
* Ciérralo con notify.closeToastLoading().
|
|
1213
|
+
*/
|
|
1214
|
+
toastLoading(message = 'Cargando...', title, options = {}) {
|
|
1215
|
+
this.showToast(message, Object.assign(Object.assign({ position: 'top-right' }, options), { type: 'loading', title: title !== null && title !== void 0 ? title : options.title, id: '__loading__', closeable: false, duration: 0, showProgress: false }));
|
|
1216
|
+
}
|
|
1217
|
+
/** Cierra el toast de carga activo (si existe). Devuelve una Promise que resuelve cuando la animación de salida termina (≈300 ms). */
|
|
1218
|
+
closeToastLoading() {
|
|
1219
|
+
const entry = this._toastInstances.get('__loading__');
|
|
1220
|
+
if (entry) {
|
|
1221
|
+
this._toastInstances.delete('__loading__');
|
|
1222
|
+
return entry.dismiss();
|
|
1223
|
+
}
|
|
1224
|
+
return Promise.resolve();
|
|
1225
|
+
}
|
|
1226
|
+
/**
|
|
1227
|
+
* Reemplaza el toast de carga activo por un toast de resultado en el mismo lugar,
|
|
1228
|
+
* sin animación de salida/entrada — no hay solapamiento ni hueco visual.
|
|
1229
|
+
* Si no existe un toast de carga activo, simplemente muestra un toast normal.
|
|
1230
|
+
*/
|
|
1231
|
+
replaceToastLoading(message, options = {}) {
|
|
1232
|
+
const entry = this._toastInstances.get('__loading__');
|
|
1233
|
+
if (entry) {
|
|
1234
|
+
this._toastInstances.delete('__loading__');
|
|
1235
|
+
entry._silentDismiss();
|
|
1236
|
+
}
|
|
1237
|
+
this.showToast(message, options);
|
|
1238
|
+
}
|
|
1086
1239
|
}
|
|
1087
1240
|
const notifyInstance = new NotificationSystem();
|
|
1088
1241
|
const w = window;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
"use strict";var __rest=this&&this.__rest||function(t,n){var e={};for(var o in t)Object.prototype.hasOwnProperty.call(t,o)&&n.indexOf(o)<0&&(e[o]=t[o]);if(null!=t&&"function"==typeof Object.getOwnPropertySymbols){var i=0;for(o=Object.getOwnPropertySymbols(t);i<o.length;i++)n.indexOf(o[i])<0&&Object.prototype.propertyIsEnumerable.call(t,o[i])&&(e[o[i]]=t[o[i]])}return e};!function(){if("undefined"!=typeof anime)t();else{const n=document.createElement("script");n.src="https://cdnjs.cloudflare.com/ajax/libs/animejs/3.2.1/anime.min.js",n.onload=t,n.onerror=()=>{console.error("FerNotify: No se pudo cargar anime.js. Por favor, cargalo manualmente.")},document.head.appendChild(n)}function t(){const t=new class{constructor(){this.currentNotification=null,this._lastActiveElement=null,this._currentLoadingPromise=null,this._toastContainers=new Map,this.injectStyles(),this.loadBoxicons()}loadBoxicons(){if(!document.querySelector('link[href*="boxicons"]')){const t=document.createElement("link");t.rel="stylesheet",t.href="https://unpkg.com/boxicons@2.1.4/css/boxicons.min.css",document.head.appendChild(t)}}injectStyles(){const t=document.createElement("style");t.textContent="\n .notification-overlay {\n position: fixed;\n top: 0;\n left: 0;\n width: 100%;\n height: 100%;\n background-color: rgba(0, 0, 0, 0.4);\n backdrop-filter: blur(4px);\n -webkit-backdrop-filter: blur(4px);\n display: flex;\n align-items: center;\n justify-content: center;\n z-index: 9999;\n opacity: 0;\n overflow: hidden;\n }\n\n .notification-box {\n background: white;\n border-radius: 16px;\n padding: 40px 30px;\n max-width: 500px;\n width: 90%;\n max-height: 80vh;\n overflow: auto;\n position: relative;\n box-shadow: 0 20px 60px rgba(0, 0, 0, 0.3);\n text-align: center;\n transform: scale(0.7);\n opacity: 0;\n }\n\n .notification-content {\n text-align: left;\n margin-bottom: 18px;\n }\n\n .notification-close {\n position: absolute;\n top: 10px;\n right: 10px;\n width: 38px;\n height: 38px;\n border-radius: 8px;\n border: none;\n background: rgba(0,0,0,0.06);\n color: #111827;\n display: inline-flex;\n align-items: center;\n justify-content: center;\n cursor: pointer;\n font-size: 18px;\n }\n\n .notification-close:hover {\n background: rgba(0,0,0,0.09);\n }\n\n /* Form controls inside the modal */\n .notification-box input,\n .notification-box textarea,\n .notification-box select {\n width: 100%;\n padding: 10px 12px;\n border: 1px solid #e5e7eb;\n border-radius: 8px;\n background: #ffffff;\n color: #111827;\n font-size: 15px;\n box-sizing: border-box;\n transition: box-shadow 0.15s ease, border-color 0.15s ease;\n }\n\n .notification-box input:focus,\n .notification-box textarea:focus,\n .notification-box select:focus {\n outline: none;\n border-color: #6366f1;\n box-shadow: 0 6px 24px rgba(99,102,241,0.12), 0 0 0 4px rgba(99,102,241,0.06);\n }\n\n .notification-box label { display: block; margin-bottom: 6px; color: #374151; font-weight: 600; }\n\n /* Soporte para tema oscuro con clase .dark (Tailwind darkMode: 'class') */\n /* Esto tiene prioridad sobre prefers-color-scheme para respetar la elección del usuario en la web */\n .dark .notification-box { background: #0f1724 !important; color: #e6eef8 !important; }\n .dark .notification-box input,\n .dark .notification-box textarea,\n .dark .notification-box select {\n background: #0b1220 !important;\n border: 1px solid rgba(255,255,255,0.06) !important;\n color: #e6eef8 !important;\n }\n .dark .notification-box .notification-close { background: rgba(255,255,255,0.03) !important; color: #e6eef8 !important; }\n .dark .notification-overlay { background-color: rgba(0,0,0,0.6) !important; }\n .dark .notification-title { color: #e6eef8 !important; }\n .dark .notification-message { color: #cbd5e1 !important; }\n\n /* Forzar modo claro cuando NO hay clase .dark, ignorando prefers-color-scheme */\n html:not(.dark) .notification-box { background: white !important; color: #111827 !important; }\n html:not(.dark) .notification-box input,\n html:not(.dark) .notification-box textarea,\n html:not(.dark) .notification-box select {\n background: #ffffff !important;\n border: 1px solid #e5e7eb !important;\n color: #111827 !important;\n }\n html:not(.dark) .notification-box .notification-close { background: rgba(0,0,0,0.06) !important; color: #111827 !important; }\n html:not(.dark) .notification-overlay { background-color: rgba(0, 0, 0, 0.4) !important; }\n html:not(.dark) .notification-title { color: #1f2937 !important; }\n html:not(.dark) .notification-message { color: #6b7280 !important; }\n\n .notification-icon {\n width: 80px;\n height: 80px;\n border-radius: 50%;\n margin: 0 auto 25px;\n display: flex;\n align-items: center;\n justify-content: center;\n font-size: 40px;\n position: relative;\n }\n\n .notification-icon::before {\n content: '';\n position: absolute;\n width: 100%;\n height: 100%;\n border-radius: 50%;\n opacity: 0.2;\n }\n\n .notification-icon.success {\n background: linear-gradient(135deg, #10b981 0%, #059669 100%);\n color: white;\n }\n\n .notification-icon.success::before {\n background: #10b981;\n }\n\n .notification-icon.error {\n background: linear-gradient(135deg, #ef4444 0%, #dc2626 100%);\n color: white;\n }\n\n .notification-icon.error::before {\n background: #ef4444;\n }\n\n .notification-icon.warning {\n background: linear-gradient(135deg, #f59e0b 0%, #d97706 100%);\n color: white;\n }\n\n .notification-icon.warning::before {\n background: #f59e0b;\n }\n\n .notification-icon.info {\n background: linear-gradient(135deg, #3b82f6 0%, #2563eb 100%);\n color: white;\n }\n\n .notification-icon.info::before {\n background: #3b82f6;\n }\n .notification-icon.question {\n background: linear-gradient(135deg, #3b82f6 0%, #2563eb 100%);\n color: white;\n }\n\n .notification-icon.question::before {\n background: #3b82f6;\n }\n\n .notification-title {\n font-size: 24px;\n font-weight: 700;\n color: #1f2937;\n margin-bottom: 12px;\n line-height: 1.3;\n }\n\n .notification-message {\n font-size: 16px;\n color: #6b7280;\n line-height: 1.6;\n margin-bottom: 30px;\n }\n\n .notification-button {\n color: white;\n border: none;\n padding: 10px 14px;\n border-radius: 8px;\n font-size: 16px;\n font-weight: 600;\n cursor: pointer;\n transition: all 0.3s ease;\n }\n\n .notification-button:hover {\n transform: translateY(-2px);\n filter: brightness(1.1);\n }\n\n .notification-button:active {\n transform: translateY(0);\n }\n\n /* group container for multiple action buttons */\n .notification-button-group {\n display: flex;\n gap: 12px;\n justify-content: center;\n flex-wrap: wrap;\n margin-top: 10px;\n }\n\n .notification-icon-checkmark {\n animation: checkmark-draw 0.6s ease-in-out;\n }\n\n .notification-icon-cross {\n animation: cross-draw 0.5s ease-in-out;\n }\n\n @keyframes checkmark-draw {\n 0% {\n transform: scale(0) rotate(-45deg);\n opacity: 0;\n }\n 50% {\n transform: scale(1.2) rotate(-45deg);\n }\n 100% {\n transform: scale(1) rotate(0deg);\n opacity: 1;\n }\n }\n\n @keyframes cross-draw {\n 0% {\n transform: scale(0) rotate(-180deg);\n opacity: 0;\n }\n 50% {\n transform: scale(1.2) rotate(-90deg);\n }\n 100% {\n transform: scale(1) rotate(0deg);\n opacity: 1;\n }\n }\n\n /* Loading spinner styles */\n .notification-loading-container {\n display: flex;\n flex-direction: column;\n align-items: center;\n justify-content: center;\n margin: 0 auto;\n }\n\n .notification-spinner {\n width: 60px;\n height: 60px;\n border: 5px solid rgba(99, 102, 241, 0.15);\n border-top-color: #6366f1;\n border-radius: 50%;\n animation: notification-spin 1s linear infinite;\n margin: 0 auto;\n }\n\n @keyframes notification-spin {\n to {\n transform: rotate(360deg);\n }\n }\n\n .notification-loading-text {\n font-size: 14px;\n color: #6b7280;\n text-align: center;\n margin-top: 12px;\n }\n\n .dark .notification-loading-text {\n color: #cbd5e1;\n }\n\n /* ==================== Toast ==================== */\n .notify-toast-container {\n position: fixed;\n z-index: 10000;\n display: flex;\n flex-direction: column;\n gap: 10px;\n pointer-events: none;\n width: 360px;\n max-width: calc(100vw - 40px);\n }\n .notify-toast-top-right { top: 20px; right: 20px; }\n .notify-toast-top-left { top: 20px; left: 20px; }\n .notify-toast-top-center { top: 20px; left: 50%; transform: translateX(-50%); }\n .notify-toast-bottom-right { bottom: 20px; right: 20px; flex-direction: column-reverse; }\n .notify-toast-bottom-left { bottom: 20px; left: 20px; flex-direction: column-reverse; }\n\n .notify-toast {\n background: white;\n border-radius: 12px;\n padding: 14px 40px 14px 14px;\n box-shadow: 0 4px 24px rgba(0,0,0,0.12), 0 1px 4px rgba(0,0,0,0.06);\n display: flex;\n align-items: flex-start;\n gap: 12px;\n pointer-events: auto;\n position: relative;\n overflow: hidden;\n opacity: 0;\n transform: translateX(30px);\n transition: opacity 0.25s ease, transform 0.25s ease;\n }\n .notify-toast-top-left .notify-toast,\n .notify-toast-bottom-left .notify-toast { transform: translateX(-30px); }\n .notify-toast-top-center .notify-toast { transform: translateY(-20px); }\n .notify-toast.notify-toast-visible {\n opacity: 1;\n transform: translateX(0) translateY(0) !important;\n }\n\n .notify-toast-icon {\n width: 36px;\n height: 36px;\n border-radius: 50%;\n display: flex;\n align-items: center;\n justify-content: center;\n font-size: 20px;\n flex-shrink: 0;\n color: white;\n }\n .notify-toast-icon.success { background: linear-gradient(135deg, #10b981 0%, #059669 100%); }\n .notify-toast-icon.error { background: linear-gradient(135deg, #ef4444 0%, #dc2626 100%); }\n .notify-toast-icon.warning { background: linear-gradient(135deg, #f59e0b 0%, #d97706 100%); }\n .notify-toast-icon.info { background: linear-gradient(135deg, #3b82f6 0%, #2563eb 100%); }\n .notify-toast-icon.question { background: linear-gradient(135deg, #8b5cf6 0%, #6366f1 100%); }\n\n .notify-toast-content { flex: 1; min-width: 0; }\n .notify-toast-title {\n font-size: 14px;\n font-weight: 700;\n color: #1f2937;\n margin-bottom: 2px;\n line-height: 1.3;\n }\n .notify-toast-message {\n font-size: 13px;\n color: #6b7280;\n line-height: 1.5;\n }\n\n .notify-toast-close {\n position: absolute;\n top: 8px;\n right: 8px;\n width: 24px;\n height: 24px;\n border-radius: 6px;\n border: none;\n background: rgba(0,0,0,0.06);\n color: #6b7280;\n cursor: pointer;\n font-size: 16px;\n display: flex;\n align-items: center;\n justify-content: center;\n line-height: 1;\n padding: 0;\n }\n .notify-toast-close:hover { background: rgba(0,0,0,0.1); color: #374151; }\n\n .notify-toast-progress {\n position: absolute;\n bottom: 0;\n left: 0;\n height: 3px;\n width: 100%;\n border-radius: 0 0 0 12px;\n }\n .notify-toast-progress.success { background: #10b981; }\n .notify-toast-progress.error { background: #ef4444; }\n .notify-toast-progress.warning { background: #f59e0b; }\n .notify-toast-progress.info { background: #3b82f6; }\n .notify-toast-progress.question { background: #8b5cf6; }\n\n .dark .notify-toast { background: #0f1724; box-shadow: 0 4px 24px rgba(0,0,0,0.35); }\n .dark .notify-toast-title { color: #e6eef8; }\n .dark .notify-toast-message { color: #cbd5e1; }\n .dark .notify-toast-close { background: rgba(255,255,255,0.06); color: #94a3b8; }\n .dark .notify-toast-close:hover { background: rgba(255,255,255,0.1); color: #e2e8f0; }\n ",document.head.appendChild(t)}getIcon(t){const n={success:'<i class="bx bx-check" aria-hidden="true"></i>',error:'<i class="bx bx-x" aria-hidden="true"></i>',warning:'<i class="bx bx-error" aria-hidden="true"></i>',info:'<i class="bx bx-info-circle" aria-hidden="true"></i>',question:'<i class="bx bx-question-mark" aria-hidden="true"></i>'};return n[t]||n.info}getDefaultTitle(t){return{success:"¡Éxito!",error:"Error",warning:"Advertencia",info:"Información",question:"Pregunta"}[t]||"Notificación"}getButtonGradient(t){const n={success:"linear-gradient(135deg, #10b981 0%, #059669 100%)",error:"linear-gradient(135deg, #ef4444 0%, #dc2626 100%)",warning:"linear-gradient(135deg, #f59e0b 0%, #d97706 100%)",info:"linear-gradient(135deg, #3b82f6 0%, #2563eb 100%)",question:"linear-gradient(135deg, #8b5cf6 0%, #6366f1 100%)"};return n[t]||n.info}getButtonShadow(t){const n={success:"rgba(16, 185, 129, 0)",error:"rgba(239, 68, 68, 0)",warning:"rgba(245, 159, 11, 0)",info:"rgba(59, 131, 246, 0)",question:"rgba(108, 99, 245, 0)"};return n[t]||n.info}show(t={}){if(this.currentNotification){const t=this.currentNotification;this.currentNotification=null;try{t&&t.parentNode&&t.parentNode.removeChild(t)}catch(t){}}const{type:n="info",title:e=this.getDefaultTitle(n),message:o="",buttonText:i="OK",buttonColor:a=null,onClose:r=null,timer:s=null,allowOutsideClick:c=!0,allowEscapeKey:l=!0,hideButton:d=!1,buttons:f=null}=t,u=!0===t.showCloseButton;try{document.body.style.overflow="hidden"}catch(t){}try{document.documentElement.style.overflow="hidden"}catch(t){}const p=document.createElement("div");p.className="notification-overlay",p.tabIndex=-1,p.setAttribute("role","dialog"),p.setAttribute("aria-modal","true"),p.style.pointerEvents="auto";const m=document.createElement("div");m.className="notification-box";const b=document.createElement("div");b.className=`notification-icon ${n}`,d&&"info"===n?(b.className="notification-loading-container",b.innerHTML='<div class="notification-spinner"></div>',b.style.background="transparent",b.style.boxShadow="none",b.style.width="100px",b.style.height="100px"):b.innerHTML=this.getIcon(n);const h=document.createElement("h3");h.className="notification-title",h.textContent=e;const g=document.createElement("p");g.className="notification-message",g.textContent=o;let x=null;if(t.html||t.content)if(x=document.createElement("div"),x.className="notification-content",t.html)try{x.innerHTML=t.html}catch(n){x.textContent=t.html}else t.content&&t.content instanceof HTMLElement&&x.appendChild(t.content);const y=()=>this.close(r);let w=null,v=null;if(!d)if(Array.isArray(f)&&f.length)v=document.createElement("div"),v.className="notification-button-group",f.forEach(t=>{const e=document.createElement("button");e.className="notification-button",e.textContent=t.text||"OK";const o=t.color||this.getButtonGradient(n),i=t.shadowColor||this.getButtonShadow(n);e.style.background=o,e.style.boxShadow=`0 4px 12px ${i}`,e.addEventListener("click",n=>{n.stopPropagation(),n.preventDefault();try{y().then(()=>{if("function"==typeof t.onClick)try{const n=t.onClick();n&&"function"==typeof n.then&&n.catch(t=>console.error(t))}catch(t){console.error(t)}}).catch(()=>{})}catch(t){console.error(t)}}),e.addEventListener("mouseenter",()=>{e.style.boxShadow=`0 6px 16px ${i}`}),e.addEventListener("mouseleave",()=>{e.style.boxShadow=`0 4px 12px ${i}`}),v.appendChild(e)});else if(t.onConfirm||t.onCancel||t.confirmText||t.cancelText){v=document.createElement("div"),v.className="notification-button-group";const e=t.cancelText||"Cancelar",o=t.confirmText||"Aceptar",i=document.createElement("button");i.className="notification-button",i.textContent=e;const a=t.cancelColor||"linear-gradient(135deg, #9ca3af 0%, #6b7280 100%)",r=t.cancelShadow||"rgba(107,114,128,0.25)";i.style.background=a,i.style.boxShadow=`0 4px 12px ${r}`,i.addEventListener("click",n=>{n.stopPropagation(),n.preventDefault(),y().then(()=>{try{if("function"==typeof t.onCancel){const n=t.onCancel();n&&"function"==typeof n.then&&n.catch(t=>console.error(t))}}catch(t){console.error(t)}}).catch(()=>{})}),i.addEventListener("mouseenter",()=>{i.style.boxShadow=`0 6px 16px ${r}`}),i.addEventListener("mouseleave",()=>{i.style.boxShadow=`0 4px 12px ${r}`});const s=document.createElement("button");s.className="notification-button",s.textContent=o;const c=t.confirmColor||this.getButtonGradient(n),l=t.confirmShadow||this.getButtonShadow(n);s.style.background=c,s.style.boxShadow=`0 4px 12px ${l}`,s.addEventListener("click",async n=>{n.stopPropagation(),n.preventDefault();try{if(await y(),"function"==typeof t.onConfirm){const n=t.onConfirm();n&&"function"==typeof n.then&&await n}}catch(t){console.error(t)}}),s.addEventListener("mouseenter",()=>{s.style.boxShadow=`0 6px 16px ${l}`}),s.addEventListener("mouseleave",()=>{s.style.boxShadow=`0 4px 12px ${l}`}),v.appendChild(i),v.appendChild(s)}else if(i){w=document.createElement("button"),w.className="notification-button",w.textContent=i;const t=a||this.getButtonGradient(n),e=this.getButtonShadow(n);w.style.background=t,w.style.boxShadow=`0 4px 12px ${e}`}let k=null;if(u&&(k=document.createElement("button"),k.setAttribute("aria-label","Cerrar"),k.className="notification-close",k.innerHTML="×",k.addEventListener("click",t=>{t.stopPropagation(),y()})),m.appendChild(b),x){const t="notify-desc-"+Date.now();x.id=t,p.setAttribute("aria-describedby",t),m.appendChild(x)}else m.appendChild(h),m.appendChild(g);k&&m.appendChild(k),v?m.appendChild(v):w&&m.appendChild(w),p.appendChild(m),document.body.appendChild(p);const E=p,C=new Promise(t=>{try{E._externalResolve=t}catch(t){}});try{const t=document.getElementById("notify-live");t&&(t.textContent=`${e}: ${o}`)}catch(t){}try{this._lastActiveElement=document.activeElement}catch(t){this._lastActiveElement=null}this.currentNotification=p;try{const t=m.querySelectorAll('a[href], button:not([disabled]), textarea, input, select, [tabindex]:not([tabindex="-1"])');t&&t.length?t[0].focus():w?w.focus():p.focus()}catch(t){try{p.focus()}catch(t){}}const N=t=>{if("Tab"!==t.key)return;const n=Array.from(m.querySelectorAll('a[href], button:not([disabled]), textarea, input, select, [tabindex]:not([tabindex="-1"])')).filter(t=>t instanceof HTMLElement&&null!==t.offsetParent);if(!n.length)return void t.preventDefault();const e=n[0],o=n[n.length-1];t.shiftKey||document.activeElement!==o?t.shiftKey&&document.activeElement===e&&(t.preventDefault(),o.focus()):(t.preventDefault(),e.focus())};E._focusTrap=N,document.addEventListener("keydown",N);const T=t.anim||{},S="number"==typeof T.overlayDuration?T.overlayDuration:150,L=T.overlayEasing||"easeOutQuad",j="number"==typeof T.boxDuration?T.boxDuration:200,O="number"==typeof T.boxDelay?T.boxDelay:50,_=T.boxEasing||"easeOutBack",D="number"==typeof T.boxStartScale?T.boxStartScale:.8,$="number"==typeof T.iconDuration?T.iconDuration:250,A="number"==typeof T.iconDelay?T.iconDelay:100,q="number"==typeof T.iconRotate?T.iconRotate:"success"===n?-90:"error"===n?90:0;if("number"==typeof T.overlayOpacity&&(p.style.backgroundColor=`rgba(0,0,0,${T.overlayOpacity})`),anime({targets:p,opacity:[0,1],duration:S,easing:L}),anime({targets:m,scale:[D,1],opacity:[0,1],duration:j,easing:_,delay:O}),anime({targets:b,scale:[0,1],rotate:[q,0],duration:$,easing:_,delay:A}),w){const t=this.getButtonShadow(n);w.addEventListener("mouseenter",()=>{w.style.boxShadow=`0 6px 16px ${t}`}),w.addEventListener("mouseleave",()=>{w.style.boxShadow=`0 4px 12px ${t}`}),w.addEventListener("click",t=>{t.stopPropagation(),t.preventDefault(),y().catch(()=>{})})}if(c&&p.addEventListener("click",t=>{m.contains(t.target)||y()}),s&&setTimeout(()=>{y()},s),l){const t=n=>{"Escape"===n.key&&(y(),document.removeEventListener("keydown",t))};E._escHandler=t,document.addEventListener("keydown",t)}return C}close(t=null){if(!this.currentNotification)return Promise.resolve();const n=this.currentNotification,e=n,o=n.querySelector(".notification-box");return this.currentNotification=null,anime({targets:o,scale:.8,opacity:0,duration:100,easing:"easeInQuad"}),new Promise(o=>{anime({targets:n,opacity:0,duration:100,easing:"easeInQuad",complete:()=>{try{e&&e._escHandler&&(document.removeEventListener("keydown",e._escHandler),e._escHandler=void 0)}catch(t){}try{e&&e._focusTrap&&(document.removeEventListener("keydown",e._focusTrap),e._focusTrap=void 0)}catch(t){}try{if(e&&"function"==typeof e._externalResolve){try{e._externalResolve()}catch(t){}e._externalResolve=void 0}}catch(t){}try{n&&n.parentNode&&n.parentNode.removeChild(n)}catch(t){try{n.remove()}catch(t){}}if(!this.currentNotification){try{document.body.style.overflow=""}catch(t){}try{document.documentElement.style.overflow=""}catch(t){}}try{this._lastActiveElement&&"function"==typeof this._lastActiveElement.focus&&this._lastActiveElement.focus()}catch(t){}this._lastActiveElement=null,t&&t(),o()}})})}success(t,n=null,e={}){this.show(Object.assign({type:"success",title:n||this.getDefaultTitle("success"),message:t},e))}error(t,n=null,e={}){this.show(Object.assign({type:"error",title:n||this.getDefaultTitle("error"),message:t},e))}warning(t,n=null,e={}){this.show(Object.assign({type:"warning",title:n||this.getDefaultTitle("warning"),message:t},e))}question(t,n=null,e={}){this.show(Object.assign({type:"question",title:n||this.getDefaultTitle("question"),message:t},e))}info(t,n=null,e={}){this.show(Object.assign({type:"info",title:n||this.getDefaultTitle("info"),message:t},e))}loading(t="Cargando...",n="Espera",e={}){const o=Object.assign({type:"info",title:n,message:t,hideButton:!0,allowOutsideClick:!1,allowEscapeKey:!1},e),i=this.show(o);return this._currentLoadingPromise=i,i}closeLoading(t=null){return this._currentLoadingPromise=null,this.close(t)}hide(t=null){return this.close(t)}hiden(t=null){return this.close(t)}_formatTime(t){const n=Math.max(0,Math.floor(t));return`${Math.floor(n/60).toString().padStart(2,"0")}:${(n%60).toString().padStart(2,"0")}`}showToast(t,n={}){var e;const o=n.type||"info",i=null!==(e=n.title)&&void 0!==e?e:null,a="number"==typeof n.duration?n.duration:4e3,r=n.position||"top-right",s=!1!==n.showProgress;let c=this._toastContainers.get(r);c&&document.body.contains(c)||(c=document.createElement("div"),c.className=`notify-toast-container notify-toast-${r}`,document.body.appendChild(c),this._toastContainers.set(r,c));const l=r.startsWith("bottom"),d="top-center"===r,f=document.createElement("div");f.className="notify-toast";const u=document.createElement("div");u.className=`notify-toast-icon ${o}`,u.innerHTML=this.getIcon(o);const p=document.createElement("div");if(p.className="notify-toast-content",i){const t=document.createElement("div");t.className="notify-toast-title",t.textContent=i,p.appendChild(t)}const m=document.createElement("div");m.className="notify-toast-message",m.textContent=t,p.appendChild(m);const b=document.createElement("button");b.className="notify-toast-close",b.setAttribute("aria-label","Cerrar notificación"),b.innerHTML="×",f.appendChild(u),f.appendChild(p),f.appendChild(b);let h=null;a>0&&s&&(h=document.createElement("div"),h.className=`notify-toast-progress ${o}`,f.appendChild(h)),l||d?c.appendChild(f):c.insertBefore(f,c.firstChild);let g=!1,x=null,y=a,w=0;const v=()=>{g||(g=!0,f.classList.remove("notify-toast-visible"),setTimeout(()=>{f.parentNode&&f.parentNode.removeChild(f)},300))},k=t=>{w=Date.now(),x=setTimeout(v,t),h&&(h.style.transition=`width ${t}ms linear`,h.style.width="0%")};b.addEventListener("click",v),requestAnimationFrame(()=>{requestAnimationFrame(()=>{f.classList.add("notify-toast-visible"),a>0&&k(a)})}),a>0&&(f.addEventListener("mouseenter",()=>{if(g||null===x)return;clearTimeout(x),x=null;const t=Date.now()-w;if(y=Math.max(0,y-t),h){const t=y/a*100;h.style.transition="none",h.style.width=`${t}%`}}),f.addEventListener("mouseleave",()=>{g||y<=0||k(y)}))}toast(t,n={}){if("string"==typeof t)this.showToast(t,n);else{const{message:n=""}=t,e=__rest(t,["message"]);this.showToast(n,e)}}toastSuccess(t,n,e={}){this.showToast(t,Object.assign(Object.assign({},e),{type:"success",title:null!=n?n:e.title}))}toastError(t,n,e={}){this.showToast(t,Object.assign(Object.assign({},e),{type:"error",title:null!=n?n:e.title}))}toastWarning(t,n,e={}){this.showToast(t,Object.assign(Object.assign({},e),{type:"warning",title:null!=n?n:e.title}))}toastInfo(t,n,e={}){this.showToast(t,Object.assign(Object.assign({},e),{type:"info",title:null!=n?n:e.title}))}toastQuestion(t,n,e={}){this.showToast(t,Object.assign(Object.assign({},e),{type:"question",title:null!=n?n:e.title}))}},n=window;n.notify=t,n.Notification=t}}();
|
|
1
|
+
"use strict";var __rest=this&&this.__rest||function(t,n){var e={};for(var o in t)Object.prototype.hasOwnProperty.call(t,o)&&n.indexOf(o)<0&&(e[o]=t[o]);if(null!=t&&"function"==typeof Object.getOwnPropertySymbols){var i=0;for(o=Object.getOwnPropertySymbols(t);i<o.length;i++)n.indexOf(o[i])<0&&Object.prototype.propertyIsEnumerable.call(t,o[i])&&(e[o[i]]=t[o[i]])}return e};!function(){if("undefined"!=typeof anime)t();else{const n=document.createElement("script");n.src="https://cdnjs.cloudflare.com/ajax/libs/animejs/3.2.1/anime.min.js",n.onload=t,n.onerror=()=>{console.error("FerNotify: No se pudo cargar anime.js. Por favor, cargalo manualmente.")},document.head.appendChild(n)}function t(){const t=new class{constructor(){this.currentNotification=null,this._lastActiveElement=null,this._currentLoadingPromise=null,this._toastContainers=new Map,this._toastInstances=new Map,this.injectStyles(),this.loadBoxicons()}loadBoxicons(){if(!document.querySelector('link[href*="boxicons"]')){const t=document.createElement("link");t.rel="stylesheet",t.href="https://unpkg.com/boxicons@2.1.4/css/boxicons.min.css",document.head.appendChild(t)}}injectStyles(){const t=document.createElement("style");t.textContent="\n .notification-overlay {\n position: fixed;\n top: 0;\n left: 0;\n width: 100%;\n height: 100%;\n background-color: rgba(0, 0, 0, 0.4);\n backdrop-filter: blur(4px);\n -webkit-backdrop-filter: blur(4px);\n display: flex;\n align-items: center;\n justify-content: center;\n z-index: 9999;\n opacity: 0;\n overflow: hidden;\n }\n\n .notification-box {\n background: white;\n border-radius: 16px;\n padding: 40px 30px;\n max-width: 500px;\n width: 90%;\n max-height: 80vh;\n overflow: auto;\n position: relative;\n box-shadow: 0 20px 60px rgba(0, 0, 0, 0.3);\n text-align: center;\n transform: scale(0.7);\n opacity: 0;\n }\n\n .notification-content {\n text-align: left;\n margin-bottom: 18px;\n }\n\n .notification-close {\n position: absolute;\n top: 10px;\n right: 10px;\n width: 38px;\n height: 38px;\n border-radius: 8px;\n border: none;\n background: rgba(0,0,0,0.06);\n color: #111827;\n display: inline-flex;\n align-items: center;\n justify-content: center;\n cursor: pointer;\n font-size: 18px;\n }\n\n .notification-close:hover {\n background: rgba(0,0,0,0.09);\n }\n\n /* Form controls inside the modal */\n .notification-box input,\n .notification-box textarea,\n .notification-box select {\n width: 100%;\n padding: 10px 12px;\n border: 1px solid #e5e7eb;\n border-radius: 8px;\n background: #ffffff;\n color: #111827;\n font-size: 15px;\n box-sizing: border-box;\n transition: box-shadow 0.15s ease, border-color 0.15s ease;\n }\n\n .notification-box input:focus,\n .notification-box textarea:focus,\n .notification-box select:focus {\n outline: none;\n border-color: #6366f1;\n box-shadow: 0 6px 24px rgba(99,102,241,0.12), 0 0 0 4px rgba(99,102,241,0.06);\n }\n\n .notification-box label { display: block; margin-bottom: 6px; color: #374151; font-weight: 600; }\n\n /* Soporte para tema oscuro con clase .dark (Tailwind darkMode: 'class') */\n /* Esto tiene prioridad sobre prefers-color-scheme para respetar la elección del usuario en la web */\n .dark .notification-box { background: #0f1724 !important; color: #e6eef8 !important; }\n .dark .notification-box input,\n .dark .notification-box textarea,\n .dark .notification-box select {\n background: #0b1220 !important;\n border: 1px solid rgba(255,255,255,0.06) !important;\n color: #e6eef8 !important;\n }\n .dark .notification-box .notification-close { background: rgba(255,255,255,0.03) !important; color: #e6eef8 !important; }\n .dark .notification-overlay { background-color: rgba(0,0,0,0.6) !important; }\n .dark .notification-title { color: #e6eef8 !important; }\n .dark .notification-message { color: #cbd5e1 !important; }\n\n /* Forzar modo claro cuando NO hay clase .dark, ignorando prefers-color-scheme */\n html:not(.dark) .notification-box { background: white !important; color: #111827 !important; }\n html:not(.dark) .notification-box input,\n html:not(.dark) .notification-box textarea,\n html:not(.dark) .notification-box select {\n background: #ffffff !important;\n border: 1px solid #e5e7eb !important;\n color: #111827 !important;\n }\n html:not(.dark) .notification-box .notification-close { background: rgba(0,0,0,0.06) !important; color: #111827 !important; }\n html:not(.dark) .notification-overlay { background-color: rgba(0, 0, 0, 0.4) !important; }\n html:not(.dark) .notification-title { color: #1f2937 !important; }\n html:not(.dark) .notification-message { color: #6b7280 !important; }\n\n .notification-icon {\n width: 80px;\n height: 80px;\n border-radius: 50%;\n margin: 0 auto 25px;\n display: flex;\n align-items: center;\n justify-content: center;\n font-size: 40px;\n position: relative;\n }\n\n .notification-icon::before {\n content: '';\n position: absolute;\n width: 100%;\n height: 100%;\n border-radius: 50%;\n opacity: 0.2;\n }\n\n .notification-icon.success {\n background: linear-gradient(135deg, #10b981 0%, #059669 100%);\n color: white;\n }\n\n .notification-icon.success::before {\n background: #10b981;\n }\n\n .notification-icon.error {\n background: linear-gradient(135deg, #ef4444 0%, #dc2626 100%);\n color: white;\n }\n\n .notification-icon.error::before {\n background: #ef4444;\n }\n\n .notification-icon.warning {\n background: linear-gradient(135deg, #f59e0b 0%, #d97706 100%);\n color: white;\n }\n\n .notification-icon.warning::before {\n background: #f59e0b;\n }\n\n .notification-icon.info {\n background: linear-gradient(135deg, #3b82f6 0%, #2563eb 100%);\n color: white;\n }\n\n .notification-icon.info::before {\n background: #3b82f6;\n }\n .notification-icon.question {\n background: linear-gradient(135deg, #3b82f6 0%, #2563eb 100%);\n color: white;\n }\n\n .notification-icon.question::before {\n background: #3b82f6;\n }\n\n .notification-title {\n font-size: 24px;\n font-weight: 700;\n color: #1f2937;\n margin-bottom: 12px;\n line-height: 1.3;\n }\n\n .notification-message {\n font-size: 16px;\n color: #6b7280;\n line-height: 1.6;\n margin-bottom: 30px;\n }\n\n .notification-button {\n color: white;\n border: none;\n padding: 10px 14px;\n border-radius: 8px;\n font-size: 16px;\n font-weight: 600;\n cursor: pointer;\n transition: all 0.3s ease;\n }\n\n .notification-button:hover {\n transform: translateY(-2px);\n filter: brightness(1.1);\n }\n\n .notification-button:active {\n transform: translateY(0);\n }\n\n /* group container for multiple action buttons */\n .notification-button-group {\n display: flex;\n gap: 12px;\n justify-content: center;\n flex-wrap: wrap;\n margin-top: 10px;\n }\n\n .notification-icon-checkmark {\n animation: checkmark-draw 0.6s ease-in-out;\n }\n\n .notification-icon-cross {\n animation: cross-draw 0.5s ease-in-out;\n }\n\n @keyframes checkmark-draw {\n 0% {\n transform: scale(0) rotate(-45deg);\n opacity: 0;\n }\n 50% {\n transform: scale(1.2) rotate(-45deg);\n }\n 100% {\n transform: scale(1) rotate(0deg);\n opacity: 1;\n }\n }\n\n @keyframes cross-draw {\n 0% {\n transform: scale(0) rotate(-180deg);\n opacity: 0;\n }\n 50% {\n transform: scale(1.2) rotate(-90deg);\n }\n 100% {\n transform: scale(1) rotate(0deg);\n opacity: 1;\n }\n }\n\n /* Loading spinner styles */\n .notification-loading-container {\n display: flex;\n flex-direction: column;\n align-items: center;\n justify-content: center;\n margin: 0 auto;\n }\n\n .notification-spinner {\n width: 60px;\n height: 60px;\n border: 5px solid rgba(99, 102, 241, 0.15);\n border-top-color: #6366f1;\n border-radius: 50%;\n animation: notification-spin 1s linear infinite;\n margin: 0 auto;\n }\n\n @keyframes notification-spin {\n to {\n transform: rotate(360deg);\n }\n }\n\n .notification-loading-text {\n font-size: 14px;\n color: #6b7280;\n text-align: center;\n margin-top: 12px;\n }\n\n .dark .notification-loading-text {\n color: #cbd5e1;\n }\n\n /* ==================== Toast ==================== */\n .notify-toast-container {\n position: fixed;\n z-index: 10000;\n display: flex;\n flex-direction: column;\n gap: 10px;\n pointer-events: none;\n width: 360px;\n max-width: calc(100vw - 40px);\n }\n .notify-toast-top-right { top: 20px; right: 20px; }\n .notify-toast-top-left { top: 20px; left: 20px; }\n .notify-toast-top-center { top: 20px; left: 50%; transform: translateX(-50%); }\n .notify-toast-bottom-right { bottom: 20px; right: 20px; flex-direction: column-reverse; }\n .notify-toast-bottom-left { bottom: 20px; left: 20px; flex-direction: column-reverse; }\n\n .notify-toast {\n background: white;\n border-radius: 12px;\n padding: 14px 40px 14px 14px;\n box-shadow: 0 4px 24px rgba(0,0,0,0.12), 0 1px 4px rgba(0,0,0,0.06);\n display: flex;\n align-items: flex-start;\n gap: 12px;\n pointer-events: auto;\n position: relative;\n overflow: hidden;\n opacity: 0;\n transform: translateX(30px);\n transition: opacity 0.25s ease, transform 0.25s ease;\n }\n .notify-toast-top-left .notify-toast,\n .notify-toast-bottom-left .notify-toast { transform: translateX(-30px); }\n .notify-toast-top-center .notify-toast { transform: translateY(-20px); }\n .notify-toast.notify-toast-visible {\n opacity: 1;\n transform: translateX(0) translateY(0) !important;\n }\n\n .notify-toast-icon {\n width: 36px;\n height: 36px;\n border-radius: 50%;\n display: flex;\n align-items: center;\n justify-content: center;\n font-size: 20px;\n flex-shrink: 0;\n color: white;\n }\n .notify-toast-icon.success { background: linear-gradient(135deg, #10b981 0%, #059669 100%); }\n .notify-toast-icon.error { background: linear-gradient(135deg, #ef4444 0%, #dc2626 100%); }\n .notify-toast-icon.warning { background: linear-gradient(135deg, #f59e0b 0%, #d97706 100%); }\n .notify-toast-icon.info { background: linear-gradient(135deg, #3b82f6 0%, #2563eb 100%); }\n .notify-toast-icon.question { background: linear-gradient(135deg, #8b5cf6 0%, #6366f1 100%); }\n .notify-toast-icon.loading { background: linear-gradient(135deg, #6366f1 0%, #8b5cf6 100%); }\n\n .notify-toast-content { flex: 1; min-width: 0; }\n .notify-toast-title {\n font-size: 14px;\n font-weight: 700;\n color: #1f2937;\n margin-bottom: 2px;\n line-height: 1.3;\n cursor: default;\n }\n .notify-toast-message {\n font-size: 13px;\n color: #6b7280;\n line-height: 1.5;\n cursor: default;\n }\n\n .notify-toast-close {\n position: absolute;\n top: 8px;\n right: 8px;\n width: 24px;\n height: 24px;\n border-radius: 6px;\n border: none;\n background: rgba(0,0,0,0.06);\n color: #6b7280;\n cursor: pointer;\n font-size: 16px;\n display: flex;\n align-items: center;\n justify-content: center;\n line-height: 1;\n padding: 0;\n }\n .notify-toast-close:hover { background: rgba(0,0,0,0.1); color: #374151; }\n\n /* Sin botón de cierre: reducir padding derecho */\n .notify-toast.notify-toast-no-close { padding-right: 14px; }\n\n .notify-toast-progress {\n position: absolute;\n bottom: 0;\n left: 0;\n height: 3px;\n width: 100%;\n border-radius: 0 0 0 12px;\n }\n .notify-toast-progress.success { background: #10b981; }\n .notify-toast-progress.error { background: #ef4444; }\n .notify-toast-progress.warning { background: #f59e0b; }\n .notify-toast-progress.info { background: #3b82f6; }\n .notify-toast-progress.question { background: #8b5cf6; }\n .notify-toast-progress.loading { background: #6366f1; }\n\n /* Spinner para toast de carga */\n .notify-toast-spinner {\n width: 18px;\n height: 18px;\n border: 2.5px solid rgba(255,255,255,0.35);\n border-top-color: white;\n border-radius: 50%;\n animation: notification-spin 0.8s linear infinite;\n flex-shrink: 0;\n }\n\n .dark .notify-toast { background: #0f1724; box-shadow: 0 4px 24px rgba(0,0,0,0.35); }\n .dark .notify-toast-title { color: #e6eef8; }\n .dark .notify-toast-message { color: #cbd5e1; }\n .dark .notify-toast-close { background: rgba(255,255,255,0.06); color: #94a3b8; }\n .dark .notify-toast-close:hover { background: rgba(255,255,255,0.1); color: #e2e8f0; }\n\n /* Respeta la preferencia de movimiento reducido del sistema */\n @media (prefers-reduced-motion: reduce) {\n .notify-toast {\n transition: opacity 0.1s ease !important;\n transform: none !important;\n }\n .notify-toast.notify-toast-visible {\n transform: none !important;\n }\n .notify-toast-spinner {\n animation-duration: 1.5s !important;\n }\n .notify-toast-progress {\n transition: none !important;\n }\n }\n ",document.head.appendChild(t)}getIcon(t){const n={success:'<i class="bx bx-check" aria-hidden="true"></i>',error:'<i class="bx bx-x" aria-hidden="true"></i>',warning:'<i class="bx bx-error" aria-hidden="true"></i>',info:'<i class="bx bx-info-circle" aria-hidden="true"></i>',question:'<i class="bx bx-question-mark" aria-hidden="true"></i>',loading:'<div class="notify-toast-spinner" aria-hidden="true"></div>'};return n[t]||n.info}getDefaultTitle(t){return{success:"¡Éxito!",error:"Error",warning:"Advertencia",info:"Información",question:"Pregunta"}[t]||"Notificación"}getButtonGradient(t){const n={success:"linear-gradient(135deg, #10b981 0%, #059669 100%)",error:"linear-gradient(135deg, #ef4444 0%, #dc2626 100%)",warning:"linear-gradient(135deg, #f59e0b 0%, #d97706 100%)",info:"linear-gradient(135deg, #3b82f6 0%, #2563eb 100%)",question:"linear-gradient(135deg, #8b5cf6 0%, #6366f1 100%)"};return n[t]||n.info}getButtonShadow(t){const n={success:"rgba(16, 185, 129, 0)",error:"rgba(239, 68, 68, 0)",warning:"rgba(245, 159, 11, 0)",info:"rgba(59, 131, 246, 0)",question:"rgba(108, 99, 245, 0)"};return n[t]||n.info}show(t={}){if(this.currentNotification){const t=this.currentNotification;this.currentNotification=null;try{t&&t.parentNode&&t.parentNode.removeChild(t)}catch(t){}}const{type:n="info",title:e=this.getDefaultTitle(n),message:o="",buttonText:i="OK",buttonColor:a=null,onClose:r=null,timer:s=null,allowOutsideClick:c=!0,allowEscapeKey:l=!0,hideButton:d=!1,buttons:f=null}=t,u=!0===t.showCloseButton;try{document.body.style.overflow="hidden"}catch(t){}try{document.documentElement.style.overflow="hidden"}catch(t){}const p=document.createElement("div");p.className="notification-overlay",p.tabIndex=-1,p.setAttribute("role","dialog"),p.setAttribute("aria-modal","true"),p.style.pointerEvents="auto";const m=document.createElement("div");m.className="notification-box";const b=document.createElement("div");b.className=`notification-icon ${n}`,d&&"info"===n?(b.className="notification-loading-container",b.innerHTML='<div class="notification-spinner"></div>',b.style.background="transparent",b.style.boxShadow="none",b.style.width="100px",b.style.height="100px"):b.innerHTML=this.getIcon(n);const g=document.createElement("h3");g.className="notification-title",g.textContent=e;const h=document.createElement("p");h.className="notification-message",h.textContent=o;let x=null;if(t.html||t.content)if(x=document.createElement("div"),x.className="notification-content",t.html)try{x.innerHTML=t.html}catch(n){x.textContent=t.html}else t.content&&t.content instanceof HTMLElement&&x.appendChild(t.content);const y=()=>this.close(r);let w=null,v=null;if(!d)if(Array.isArray(f)&&f.length)v=document.createElement("div"),v.className="notification-button-group",f.forEach(t=>{const e=document.createElement("button");e.className="notification-button",e.textContent=t.text||"OK";const o=t.color||this.getButtonGradient(n),i=t.shadowColor||this.getButtonShadow(n);e.style.background=o,e.style.boxShadow=`0 4px 12px ${i}`,e.addEventListener("click",n=>{n.stopPropagation(),n.preventDefault();try{y().then(()=>{if("function"==typeof t.onClick)try{const n=t.onClick();n&&"function"==typeof n.then&&n.catch(t=>console.error(t))}catch(t){console.error(t)}}).catch(()=>{})}catch(t){console.error(t)}}),e.addEventListener("mouseenter",()=>{e.style.boxShadow=`0 6px 16px ${i}`}),e.addEventListener("mouseleave",()=>{e.style.boxShadow=`0 4px 12px ${i}`}),v.appendChild(e)});else if(t.onConfirm||t.onCancel||t.confirmText||t.cancelText){v=document.createElement("div"),v.className="notification-button-group";const e=t.cancelText||"Cancelar",o=t.confirmText||"Aceptar",i=document.createElement("button");i.className="notification-button",i.textContent=e;const a=t.cancelColor||"linear-gradient(135deg, #9ca3af 0%, #6b7280 100%)",r=t.cancelShadow||"rgba(107,114,128,0.25)";i.style.background=a,i.style.boxShadow=`0 4px 12px ${r}`,i.addEventListener("click",n=>{n.stopPropagation(),n.preventDefault(),y().then(()=>{try{if("function"==typeof t.onCancel){const n=t.onCancel();n&&"function"==typeof n.then&&n.catch(t=>console.error(t))}}catch(t){console.error(t)}}).catch(()=>{})}),i.addEventListener("mouseenter",()=>{i.style.boxShadow=`0 6px 16px ${r}`}),i.addEventListener("mouseleave",()=>{i.style.boxShadow=`0 4px 12px ${r}`});const s=document.createElement("button");s.className="notification-button",s.textContent=o;const c=t.confirmColor||this.getButtonGradient(n),l=t.confirmShadow||this.getButtonShadow(n);s.style.background=c,s.style.boxShadow=`0 4px 12px ${l}`,s.addEventListener("click",async n=>{n.stopPropagation(),n.preventDefault();try{if(await y(),"function"==typeof t.onConfirm){const n=t.onConfirm();n&&"function"==typeof n.then&&await n}}catch(t){console.error(t)}}),s.addEventListener("mouseenter",()=>{s.style.boxShadow=`0 6px 16px ${l}`}),s.addEventListener("mouseleave",()=>{s.style.boxShadow=`0 4px 12px ${l}`}),v.appendChild(i),v.appendChild(s)}else if(i){w=document.createElement("button"),w.className="notification-button",w.textContent=i;const t=a||this.getButtonGradient(n),e=this.getButtonShadow(n);w.style.background=t,w.style.boxShadow=`0 4px 12px ${e}`}let k=null;if(u&&(k=document.createElement("button"),k.setAttribute("aria-label","Cerrar"),k.className="notification-close",k.innerHTML="×",k.addEventListener("click",t=>{t.stopPropagation(),y()})),m.appendChild(b),x){const t="notify-desc-"+Date.now();x.id=t,p.setAttribute("aria-describedby",t),m.appendChild(x)}else m.appendChild(g),m.appendChild(h);k&&m.appendChild(k),v?m.appendChild(v):w&&m.appendChild(w),p.appendChild(m),document.body.appendChild(p);const E=p,C=new Promise(t=>{try{E._externalResolve=t}catch(t){}});try{const t=document.getElementById("notify-live");t&&(t.textContent=`${e}: ${o}`)}catch(t){}try{this._lastActiveElement=document.activeElement}catch(t){this._lastActiveElement=null}this.currentNotification=p;try{const t=m.querySelectorAll('a[href], button:not([disabled]), textarea, input, select, [tabindex]:not([tabindex="-1"])');t&&t.length?t[0].focus():w?w.focus():p.focus()}catch(t){try{p.focus()}catch(t){}}const _=t=>{if("Tab"!==t.key)return;const n=Array.from(m.querySelectorAll('a[href], button:not([disabled]), textarea, input, select, [tabindex]:not([tabindex="-1"])')).filter(t=>t instanceof HTMLElement&&null!==t.offsetParent);if(!n.length)return void t.preventDefault();const e=n[0],o=n[n.length-1];t.shiftKey||document.activeElement!==o?t.shiftKey&&document.activeElement===e&&(t.preventDefault(),o.focus()):(t.preventDefault(),e.focus())};E._focusTrap=_,document.addEventListener("keydown",_);const T=t.anim||{},N="number"==typeof T.overlayDuration?T.overlayDuration:150,L=T.overlayEasing||"easeOutQuad",S="number"==typeof T.boxDuration?T.boxDuration:200,j="number"==typeof T.boxDelay?T.boxDelay:50,O=T.boxEasing||"easeOutBack",D="number"==typeof T.boxStartScale?T.boxStartScale:.8,A="number"==typeof T.iconDuration?T.iconDuration:250,$="number"==typeof T.iconDelay?T.iconDelay:100,P="number"==typeof T.iconRotate?T.iconRotate:"success"===n?-90:"error"===n?90:0;if("number"==typeof T.overlayOpacity&&(p.style.backgroundColor=`rgba(0,0,0,${T.overlayOpacity})`),anime({targets:p,opacity:[0,1],duration:N,easing:L}),anime({targets:m,scale:[D,1],opacity:[0,1],duration:S,easing:O,delay:j}),anime({targets:b,scale:[0,1],rotate:[P,0],duration:A,easing:O,delay:$}),w){const t=this.getButtonShadow(n);w.addEventListener("mouseenter",()=>{w.style.boxShadow=`0 6px 16px ${t}`}),w.addEventListener("mouseleave",()=>{w.style.boxShadow=`0 4px 12px ${t}`}),w.addEventListener("click",t=>{t.stopPropagation(),t.preventDefault(),y().catch(()=>{})})}if(c&&p.addEventListener("click",t=>{m.contains(t.target)||y()}),s&&setTimeout(()=>{y()},s),l){const t=n=>{"Escape"===n.key&&(y(),document.removeEventListener("keydown",t))};E._escHandler=t,document.addEventListener("keydown",t)}return C}close(t=null){if(!this.currentNotification)return Promise.resolve();const n=this.currentNotification,e=n,o=n.querySelector(".notification-box");return this.currentNotification=null,anime({targets:o,scale:.8,opacity:0,duration:100,easing:"easeInQuad"}),new Promise(o=>{anime({targets:n,opacity:0,duration:100,easing:"easeInQuad",complete:()=>{try{e&&e._escHandler&&(document.removeEventListener("keydown",e._escHandler),e._escHandler=void 0)}catch(t){}try{e&&e._focusTrap&&(document.removeEventListener("keydown",e._focusTrap),e._focusTrap=void 0)}catch(t){}try{if(e&&"function"==typeof e._externalResolve){try{e._externalResolve()}catch(t){}e._externalResolve=void 0}}catch(t){}try{n&&n.parentNode&&n.parentNode.removeChild(n)}catch(t){try{n.remove()}catch(t){}}if(!this.currentNotification){try{document.body.style.overflow=""}catch(t){}try{document.documentElement.style.overflow=""}catch(t){}}try{this._lastActiveElement&&"function"==typeof this._lastActiveElement.focus&&this._lastActiveElement.focus()}catch(t){}this._lastActiveElement=null,t&&t(),o()}})})}success(t,n=null,e={}){this.show(Object.assign({type:"success",title:n||this.getDefaultTitle("success"),message:t},e))}error(t,n=null,e={}){this.show(Object.assign({type:"error",title:n||this.getDefaultTitle("error"),message:t},e))}warning(t,n=null,e={}){this.show(Object.assign({type:"warning",title:n||this.getDefaultTitle("warning"),message:t},e))}question(t,n=null,e={}){this.show(Object.assign({type:"question",title:n||this.getDefaultTitle("question"),message:t},e))}info(t,n=null,e={}){this.show(Object.assign({type:"info",title:n||this.getDefaultTitle("info"),message:t},e))}loading(t="Cargando...",n="Espera",e={}){const o=Object.assign({type:"info",title:n,message:t,hideButton:!0,allowOutsideClick:!1,allowEscapeKey:!1},e),i=this.show(o);return this._currentLoadingPromise=i,i}closeLoading(t=null){return this._currentLoadingPromise=null,this.close(t)}hide(t=null){return this.close(t)}hiden(t=null){return this.close(t)}_formatTime(t){const n=Math.max(0,Math.floor(t));return`${Math.floor(n/60).toString().padStart(2,"0")}:${(n%60).toString().padStart(2,"0")}`}showToast(t,n={}){var e,o;const i=n.type||"info",a=null!==(e=n.title)&&void 0!==e?e:null,r="number"==typeof n.duration?n.duration:4e3,s=n.position||"top-right",c=!1!==n.showProgress,l=null!==(o=n.id)&&void 0!==o?o:null,d=!1!==n.closeable;if(null!==l){const t=this._toastInstances.get(l);if(t)return void t.reset(r)}let f=this._toastContainers.get(s);f&&document.body.contains(f)||(f=document.createElement("div"),f.className=`notify-toast-container notify-toast-${s}`,f.setAttribute("aria-label","Notificaciones"),document.body.appendChild(f),this._toastContainers.set(s,f));const u=s.startsWith("bottom"),p="top-center"===s,m=document.createElement("div");m.className="notify-toast",d||m.classList.add("notify-toast-no-close"),"error"===i||"warning"===i?m.setAttribute("role","alert"):m.setAttribute("role","status"),m.setAttribute("aria-atomic","true"),m.setAttribute("aria-live","error"===i||"warning"===i?"assertive":"polite");const b=document.createElement("div");b.className=`notify-toast-icon ${i}`,b.innerHTML=this.getIcon(i);const g=document.createElement("div");if(g.className="notify-toast-content",a){const t=document.createElement("div");t.className="notify-toast-title",t.textContent=a,g.appendChild(t)}const h=document.createElement("div");if(h.className="notify-toast-message",h.textContent=t,g.appendChild(h),m.appendChild(b),m.appendChild(g),d){const t=document.createElement("button");t.className="notify-toast-close",t.setAttribute("aria-label","Cerrar notificación"),t.innerHTML="×",t.addEventListener("click",E),m.appendChild(t)}let x=null;r>0&&c&&(x=document.createElement("div"),x.className=`notify-toast-progress ${i}`,x.setAttribute("role","progressbar"),x.setAttribute("aria-hidden","true"),m.appendChild(x)),u||p?f.appendChild(m):f.insertBefore(m,f.firstChild);let y=!1,w=null,v=r,k=0;function E(){if(y)return Promise.resolve();if(y=!0,m.contains(document.activeElement))try{document.activeElement.blur()}catch(t){}return m.classList.remove("notify-toast-visible"),new Promise(t=>{setTimeout(()=>{m.parentNode&&m.parentNode.removeChild(m),t()},300)})}const C=t=>{k=Date.now(),w=setTimeout(()=>{null!==l&&this._toastInstances.delete(l),E()},t),x&&(x.style.transition=`width ${t}ms linear`,x.style.width="0%")},_=t=>{y||(null!==w&&(clearTimeout(w),w=null),v=t,t>0?(x&&(x.style.transition="none",x.style.width="100%",x.offsetWidth),C(t)):x&&(x.style.transition="none",x.style.width="100%"))};if(null!==l){const t=()=>{if(!y){if(y=!0,null!==w&&clearTimeout(w),m.contains(document.activeElement))try{document.activeElement.blur()}catch(t){}m.parentNode&&m.parentNode.removeChild(m)}};this._toastInstances.set(l,{reset:_,dismiss:E,_silentDismiss:t})}requestAnimationFrame(()=>{requestAnimationFrame(()=>{m.classList.add("notify-toast-visible"),r>0&&C(r)})}),r>0&&d&&(m.addEventListener("mouseenter",()=>{if(y||null===w)return;clearTimeout(w),w=null;const t=Date.now()-k;if(v=Math.max(0,v-t),x){const t=v/r*100;x.style.transition="none",x.style.width=`${t}%`}}),m.addEventListener("mouseleave",()=>{y||v<=0||C(v)}))}toast(t,n={}){if("string"==typeof t)this.showToast(t,n);else{const{message:n=""}=t,e=__rest(t,["message"]);this.showToast(n,e)}}toastSuccess(t,n,e={}){this.showToast(t,Object.assign(Object.assign({},e),{type:"success",title:null!=n?n:e.title}))}toastError(t,n,e={}){this.showToast(t,Object.assign(Object.assign({},e),{type:"error",title:null!=n?n:e.title}))}toastWarning(t,n,e={}){this.showToast(t,Object.assign(Object.assign({},e),{type:"warning",title:null!=n?n:e.title}))}toastInfo(t,n,e={}){this.showToast(t,Object.assign(Object.assign({},e),{type:"info",title:null!=n?n:e.title}))}toastQuestion(t,n,e={}){this.showToast(t,Object.assign(Object.assign({},e),{type:"question",title:null!=n?n:e.title}))}toastLoading(t="Cargando...",n,e={}){this.showToast(t,Object.assign(Object.assign({position:"top-right"},e),{type:"loading",title:null!=n?n:e.title,id:"__loading__",closeable:!1,duration:0,showProgress:!1}))}closeToastLoading(){const t=this._toastInstances.get("__loading__");return t?(this._toastInstances.delete("__loading__"),t.dismiss()):Promise.resolve()}replaceToastLoading(t,n={}){const e=this._toastInstances.get("__loading__");e&&(this._toastInstances.delete("__loading__"),e._silentDismiss()),this.showToast(t,n)}},n=window;n.notify=t,n.Notification=t}}();
|
package/package.json
CHANGED
|
@@ -1,17 +1,20 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "fernotify",
|
|
3
|
-
"version": "1.2.
|
|
3
|
+
"version": "1.2.11",
|
|
4
4
|
"description": "Sistema moderno de notificaciones con animaciones fluidas y soporte completo de Dark Mode",
|
|
5
5
|
"main": "dist/notification-system.js",
|
|
6
6
|
"module": "dist/notification-system.esm.js",
|
|
7
|
+
"types": "types/index.d.ts",
|
|
7
8
|
"exports": {
|
|
8
9
|
".": {
|
|
10
|
+
"types": "./types/index.d.ts",
|
|
9
11
|
"import": "./dist/notification-system.esm.js",
|
|
10
12
|
"require": "./dist/notification-system.js"
|
|
11
13
|
}
|
|
12
14
|
},
|
|
13
15
|
"files": [
|
|
14
16
|
"dist/",
|
|
17
|
+
"types/",
|
|
15
18
|
"LICENSE",
|
|
16
19
|
"README.md",
|
|
17
20
|
"NOTIFICATION_SYSTEM_GUIDE.md"
|
package/types/anime.d.ts
ADDED
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
// Tipos mínimos para anime.js 3.x cargado por CDN
|
|
2
|
+
// Solo cubre las propiedades usadas en notification-system.ts
|
|
3
|
+
|
|
4
|
+
type AnimeValue = number | number[];
|
|
5
|
+
|
|
6
|
+
interface AnimeParams {
|
|
7
|
+
targets: unknown;
|
|
8
|
+
duration?: number;
|
|
9
|
+
delay?: number;
|
|
10
|
+
easing?: string;
|
|
11
|
+
opacity?: AnimeValue;
|
|
12
|
+
scale?: AnimeValue;
|
|
13
|
+
rotate?: AnimeValue;
|
|
14
|
+
complete?: () => void;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
declare function anime(params: AnimeParams): void;
|
package/types/index.d.ts
ADDED
|
@@ -0,0 +1,173 @@
|
|
|
1
|
+
// Type definitions for fernotify
|
|
2
|
+
// Generated from notification-system.ts
|
|
3
|
+
|
|
4
|
+
export interface ButtonOptions {
|
|
5
|
+
text?: string;
|
|
6
|
+
color?: string;
|
|
7
|
+
shadowColor?: string;
|
|
8
|
+
onClick?: (() => void) | (() => Promise<unknown>);
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
export interface AnimationOptions {
|
|
12
|
+
overlayDuration?: number;
|
|
13
|
+
overlayEasing?: string;
|
|
14
|
+
boxDuration?: number;
|
|
15
|
+
boxDelay?: number;
|
|
16
|
+
boxEasing?: string;
|
|
17
|
+
boxStartScale?: number;
|
|
18
|
+
iconDuration?: number;
|
|
19
|
+
iconDelay?: number;
|
|
20
|
+
iconRotate?: number;
|
|
21
|
+
overlayOpacity?: number;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
export interface NotificationOptions {
|
|
25
|
+
type?: 'success' | 'error' | 'warning' | 'info' | 'question' | string;
|
|
26
|
+
title?: string;
|
|
27
|
+
message?: string;
|
|
28
|
+
html?: string;
|
|
29
|
+
content?: HTMLElement;
|
|
30
|
+
buttonText?: string;
|
|
31
|
+
buttonColor?: string | null;
|
|
32
|
+
onClose?: (() => void) | null;
|
|
33
|
+
timer?: number | null;
|
|
34
|
+
allowOutsideClick?: boolean;
|
|
35
|
+
allowEscapeKey?: boolean;
|
|
36
|
+
hideButton?: boolean;
|
|
37
|
+
buttons?: ButtonOptions[] | null;
|
|
38
|
+
onConfirm?: (() => void) | (() => Promise<unknown>) | null;
|
|
39
|
+
onCancel?: (() => void) | (() => Promise<unknown>) | null;
|
|
40
|
+
confirmText?: string;
|
|
41
|
+
cancelText?: string;
|
|
42
|
+
confirmColor?: string;
|
|
43
|
+
confirmShadow?: string;
|
|
44
|
+
cancelColor?: string;
|
|
45
|
+
cancelShadow?: string;
|
|
46
|
+
anim?: AnimationOptions;
|
|
47
|
+
showCloseButton?: boolean;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
export type ToastPosition =
|
|
51
|
+
| 'top-right'
|
|
52
|
+
| 'top-left'
|
|
53
|
+
| 'top-center'
|
|
54
|
+
| 'bottom-right'
|
|
55
|
+
| 'bottom-left';
|
|
56
|
+
|
|
57
|
+
export interface ToastOptions {
|
|
58
|
+
type?: 'success' | 'error' | 'warning' | 'info' | 'question' | string;
|
|
59
|
+
title?: string;
|
|
60
|
+
message?: string;
|
|
61
|
+
duration?: number;
|
|
62
|
+
position?: ToastPosition;
|
|
63
|
+
showProgress?: boolean;
|
|
64
|
+
/**
|
|
65
|
+
* Unique ID for deduplication: if a toast with this ID is already visible,
|
|
66
|
+
* its countdown is reset instead of creating a duplicate.
|
|
67
|
+
*/
|
|
68
|
+
id?: string;
|
|
69
|
+
/**
|
|
70
|
+
* When `false`, the close (×) button is hidden.
|
|
71
|
+
* `toastLoading()` sets this to `false` automatically.
|
|
72
|
+
* @default true
|
|
73
|
+
*/
|
|
74
|
+
closeable?: boolean;
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
export declare class NotificationSystem {
|
|
78
|
+
constructor();
|
|
79
|
+
|
|
80
|
+
// ── Modal notifications ──────────────────────────────────────────────────
|
|
81
|
+
|
|
82
|
+
/** Shows a modal notification. Returns a Promise that resolves when the modal is closed. */
|
|
83
|
+
show(options?: NotificationOptions): Promise<void>;
|
|
84
|
+
|
|
85
|
+
/** Closes the current modal. */
|
|
86
|
+
close(callback?: (() => void) | null): Promise<void>;
|
|
87
|
+
|
|
88
|
+
/** Alias for close(). */
|
|
89
|
+
hide(callback?: (() => void) | null): Promise<void>;
|
|
90
|
+
|
|
91
|
+
success(message: string, title?: string | null, options?: NotificationOptions): void;
|
|
92
|
+
error(message: string, title?: string | null, options?: NotificationOptions): void;
|
|
93
|
+
warning(message: string, title?: string | null, options?: NotificationOptions): void;
|
|
94
|
+
info(message: string, title?: string | null, options?: NotificationOptions): void;
|
|
95
|
+
question(message: string, title?: string | null, options?: NotificationOptions): void;
|
|
96
|
+
|
|
97
|
+
/** Shows a loading modal (no close button, blocks outside click / Escape). */
|
|
98
|
+
loading(message?: string, title?: string, options?: NotificationOptions): Promise<void>;
|
|
99
|
+
|
|
100
|
+
/** Closes the loading modal. */
|
|
101
|
+
closeLoading(callback?: (() => void) | null): Promise<void>;
|
|
102
|
+
|
|
103
|
+
// ── Toast notifications ──────────────────────────────────────────────────
|
|
104
|
+
|
|
105
|
+
/** Generic toast. Accepts a message string + options, or a single options object. */
|
|
106
|
+
toast(messageOrOptions: string | ToastOptions, options?: ToastOptions): void;
|
|
107
|
+
|
|
108
|
+
toastSuccess(message: string, title?: string, options?: ToastOptions): void;
|
|
109
|
+
toastError(message: string, title?: string, options?: ToastOptions): void;
|
|
110
|
+
toastWarning(message: string, title?: string, options?: ToastOptions): void;
|
|
111
|
+
toastInfo(message: string, title?: string, options?: ToastOptions): void;
|
|
112
|
+
toastQuestion(message: string, title?: string, options?: ToastOptions): void;
|
|
113
|
+
|
|
114
|
+
/**
|
|
115
|
+
* Shows a loading toast with a spinner.
|
|
116
|
+
* Only one loading toast can exist at a time (uses internal id `'__loading__'`).
|
|
117
|
+
* Close it with `closeToastLoading()`.
|
|
118
|
+
*/
|
|
119
|
+
toastLoading(message?: string, title?: string, options?: ToastOptions): void;
|
|
120
|
+
|
|
121
|
+
/**
|
|
122
|
+
* Closes the active loading toast and returns a Promise that resolves
|
|
123
|
+
* after the exit animation finishes (~300 ms).
|
|
124
|
+
*
|
|
125
|
+
* @example
|
|
126
|
+
* notify.toastLoading('Saving…');
|
|
127
|
+
* await doWork();
|
|
128
|
+
* await notify.closeToastLoading(); // wait for exit animation
|
|
129
|
+
* notify.toastSuccess('Done!');
|
|
130
|
+
*/
|
|
131
|
+
closeToastLoading(): Promise<void>;
|
|
132
|
+
|
|
133
|
+
/**
|
|
134
|
+
* Instantly replaces the loading toast with a new toast (no animation gap).
|
|
135
|
+
* If no loading toast is active, behaves like a normal `toast()` call.
|
|
136
|
+
*
|
|
137
|
+
* @example
|
|
138
|
+
* notify.toastLoading('Saving…');
|
|
139
|
+
* await doWork();
|
|
140
|
+
* notify.replaceToastLoading('Done!', { type: 'success' });
|
|
141
|
+
*/
|
|
142
|
+
replaceToastLoading(message: string, options?: ToastOptions): void;
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
/**
|
|
146
|
+
* Default export: the `NotificationSystem` class.
|
|
147
|
+
*
|
|
148
|
+
* @example – ESM / Vite / React
|
|
149
|
+
* ```ts
|
|
150
|
+
* import NotificationSystem from 'fernotify';
|
|
151
|
+
* const notify = new NotificationSystem();
|
|
152
|
+
* notify.toastSuccess('Hello!');
|
|
153
|
+
* ```
|
|
154
|
+
*
|
|
155
|
+
* @example – Singleton (recommended for apps)
|
|
156
|
+
* ```ts
|
|
157
|
+
* // src/lib/notify.ts
|
|
158
|
+
* import NotificationSystem from 'fernotify';
|
|
159
|
+
* const notify = new NotificationSystem();
|
|
160
|
+
* export default notify;
|
|
161
|
+
* ```
|
|
162
|
+
*/
|
|
163
|
+
export default NotificationSystem;
|
|
164
|
+
|
|
165
|
+
// Augment the global Window interface so window.notify is typed
|
|
166
|
+
// when the UMD/IIFE bundle is loaded via <script>.
|
|
167
|
+
declare global {
|
|
168
|
+
interface Window {
|
|
169
|
+
notify: NotificationSystem;
|
|
170
|
+
/** @deprecated Use window.notify instead. */
|
|
171
|
+
Notification: NotificationSystem;
|
|
172
|
+
}
|
|
173
|
+
}
|