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 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' // 'top-right' | 'top-left' | 'top-center' | 'bottom-right' | 'bottom-left'
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 = '&times;';
1008
1066
  toast.appendChild(iconEl);
1009
1067
  toast.appendChild(contentEl);
1010
- toast.appendChild(closeBtn);
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 = '&times;';
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
- const removeToast = () => {
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
- setTimeout(() => {
1033
- if (toast.parentNode)
1034
- toast.parentNode.removeChild(toast);
1035
- }, 300);
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(removeToast, ms);
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
- closeBtn.addEventListener('click', removeToast);
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="&times;",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="&times;",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="&times;",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="&times;",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 = '&times;';
993
1051
  toast.appendChild(iconEl);
994
1052
  toast.appendChild(contentEl);
995
- toast.appendChild(closeBtn);
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 = '&times;';
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
- const removeToast = () => {
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
- setTimeout(() => {
1018
- if (toast.parentNode)
1019
- toast.parentNode.removeChild(toast);
1020
- }, 300);
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(removeToast, ms);
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
- closeBtn.addEventListener('click', removeToast);
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="&times;",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="&times;",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="&times;",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="&times;",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.9",
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"
@@ -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;
@@ -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
+ }