fernotify 1.2.8 → 1.2.10

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
@@ -212,6 +212,79 @@ notify.loading('Cargando...', 'Espera', {
212
212
  });
213
213
  ```
214
214
 
215
+ ### Toast — Notificaciones no bloqueantes 🍞
216
+
217
+ Los toasts aparecen en la esquina de la pantalla **sin interrumpir al usuario**, se apilan automáticamente y se cierran solos (o con el botón ×).
218
+
219
+ ```javascript
220
+ // Métodos abreviados (mismo patrón que los modales)
221
+ notify.toastSuccess('Cambios guardados.', 'Título opcional');
222
+ notify.toastError('Error al procesar.', 'Error');
223
+ notify.toastWarning('Revisa los datos.');
224
+ notify.toastInfo('Nueva versión disponible.');
225
+ notify.toastQuestion('Nueva solicitud pendiente.');
226
+
227
+ // Método genérico con opciones completas
228
+ notify.toast('Mensaje aquí', {
229
+ type: 'success', // 'success' | 'error' | 'warning' | 'info' | 'question' | 'loading'
230
+ title: 'Título opcional',
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'
233
+ id: 'my-toast', // ID para deduplicación (optional)
234
+ closeable: true // false = oculta el botón × (default: true)
235
+ });
236
+ ```
237
+
238
+ **Características:**
239
+ - Sin overlay — no bloquea la UI
240
+ - Apilables: se acumulan uno bajo otro
241
+ - Progress bar que indica el tiempo restante
242
+ - Botón × para cierre manual
243
+ - Animación de entrada/salida suave
244
+ - Soporte completo de dark mode
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
+
215
288
  ### Personalización de Animaciones
216
289
 
217
290
  ```javascript
@@ -14,6 +14,17 @@
14
14
 
15
15
  // Importar el código UMD y ejecutarlo para generar window.notify
16
16
  "use strict";
17
+ var __rest = (this && this.__rest) || function (s, e) {
18
+ var t = {};
19
+ for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p) && e.indexOf(p) < 0)
20
+ t[p] = s[p];
21
+ if (s != null && typeof Object.getOwnPropertySymbols === "function")
22
+ for (var i = 0, p = Object.getOwnPropertySymbols(s); i < p.length; i++) {
23
+ if (e.indexOf(p[i]) < 0 && Object.prototype.propertyIsEnumerable.call(s, p[i]))
24
+ t[p[i]] = s[p[i]];
25
+ }
26
+ return t;
27
+ };
17
28
  (function ensureAnimeDependency() {
18
29
  if (typeof anime !== 'undefined') {
19
30
  initFerNotify();
@@ -33,6 +44,8 @@
33
44
  this.currentNotification = null;
34
45
  this._lastActiveElement = null;
35
46
  this._currentLoadingPromise = null;
47
+ this._toastContainers = new Map();
48
+ this._toastInstances = new Map();
36
49
  this.injectStyles();
37
50
  this.loadBoxicons();
38
51
  }
@@ -339,6 +352,152 @@
339
352
  .dark .notification-loading-text {
340
353
  color: #cbd5e1;
341
354
  }
355
+
356
+ /* ==================== Toast ==================== */
357
+ .notify-toast-container {
358
+ position: fixed;
359
+ z-index: 10000;
360
+ display: flex;
361
+ flex-direction: column;
362
+ gap: 10px;
363
+ pointer-events: none;
364
+ width: 360px;
365
+ max-width: calc(100vw - 40px);
366
+ }
367
+ .notify-toast-top-right { top: 20px; right: 20px; }
368
+ .notify-toast-top-left { top: 20px; left: 20px; }
369
+ .notify-toast-top-center { top: 20px; left: 50%; transform: translateX(-50%); }
370
+ .notify-toast-bottom-right { bottom: 20px; right: 20px; flex-direction: column-reverse; }
371
+ .notify-toast-bottom-left { bottom: 20px; left: 20px; flex-direction: column-reverse; }
372
+
373
+ .notify-toast {
374
+ background: white;
375
+ border-radius: 12px;
376
+ padding: 14px 40px 14px 14px;
377
+ box-shadow: 0 4px 24px rgba(0,0,0,0.12), 0 1px 4px rgba(0,0,0,0.06);
378
+ display: flex;
379
+ align-items: flex-start;
380
+ gap: 12px;
381
+ pointer-events: auto;
382
+ position: relative;
383
+ overflow: hidden;
384
+ opacity: 0;
385
+ transform: translateX(30px);
386
+ transition: opacity 0.25s ease, transform 0.25s ease;
387
+ }
388
+ .notify-toast-top-left .notify-toast,
389
+ .notify-toast-bottom-left .notify-toast { transform: translateX(-30px); }
390
+ .notify-toast-top-center .notify-toast { transform: translateY(-20px); }
391
+ .notify-toast.notify-toast-visible {
392
+ opacity: 1;
393
+ transform: translateX(0) translateY(0) !important;
394
+ }
395
+
396
+ .notify-toast-icon {
397
+ width: 36px;
398
+ height: 36px;
399
+ border-radius: 50%;
400
+ display: flex;
401
+ align-items: center;
402
+ justify-content: center;
403
+ font-size: 20px;
404
+ flex-shrink: 0;
405
+ color: white;
406
+ }
407
+ .notify-toast-icon.success { background: linear-gradient(135deg, #10b981 0%, #059669 100%); }
408
+ .notify-toast-icon.error { background: linear-gradient(135deg, #ef4444 0%, #dc2626 100%); }
409
+ .notify-toast-icon.warning { background: linear-gradient(135deg, #f59e0b 0%, #d97706 100%); }
410
+ .notify-toast-icon.info { background: linear-gradient(135deg, #3b82f6 0%, #2563eb 100%); }
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%); }
413
+
414
+ .notify-toast-content { flex: 1; min-width: 0; }
415
+ .notify-toast-title {
416
+ font-size: 14px;
417
+ font-weight: 700;
418
+ color: #1f2937;
419
+ margin-bottom: 2px;
420
+ line-height: 1.3;
421
+ cursor: default;
422
+ }
423
+ .notify-toast-message {
424
+ font-size: 13px;
425
+ color: #6b7280;
426
+ line-height: 1.5;
427
+ cursor: default;
428
+ }
429
+
430
+ .notify-toast-close {
431
+ position: absolute;
432
+ top: 8px;
433
+ right: 8px;
434
+ width: 24px;
435
+ height: 24px;
436
+ border-radius: 6px;
437
+ border: none;
438
+ background: rgba(0,0,0,0.06);
439
+ color: #6b7280;
440
+ cursor: pointer;
441
+ font-size: 16px;
442
+ display: flex;
443
+ align-items: center;
444
+ justify-content: center;
445
+ line-height: 1;
446
+ padding: 0;
447
+ }
448
+ .notify-toast-close:hover { background: rgba(0,0,0,0.1); color: #374151; }
449
+
450
+ /* Sin botón de cierre: reducir padding derecho */
451
+ .notify-toast.notify-toast-no-close { padding-right: 14px; }
452
+
453
+ .notify-toast-progress {
454
+ position: absolute;
455
+ bottom: 0;
456
+ left: 0;
457
+ height: 3px;
458
+ width: 100%;
459
+ border-radius: 0 0 0 12px;
460
+ }
461
+ .notify-toast-progress.success { background: #10b981; }
462
+ .notify-toast-progress.error { background: #ef4444; }
463
+ .notify-toast-progress.warning { background: #f59e0b; }
464
+ .notify-toast-progress.info { background: #3b82f6; }
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
+ }
478
+
479
+ .dark .notify-toast { background: #0f1724; box-shadow: 0 4px 24px rgba(0,0,0,0.35); }
480
+ .dark .notify-toast-title { color: #e6eef8; }
481
+ .dark .notify-toast-message { color: #cbd5e1; }
482
+ .dark .notify-toast-close { background: rgba(255,255,255,0.06); color: #94a3b8; }
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
+ }
342
501
  `;
343
502
  document.head.appendChild(style);
344
503
  }
@@ -348,7 +507,8 @@
348
507
  'error': '<i class="bx bx-x" aria-hidden="true"></i>',
349
508
  'warning': '<i class="bx bx-error" aria-hidden="true"></i>',
350
509
  'info': '<i class="bx bx-info-circle" aria-hidden="true"></i>',
351
- '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>'
352
512
  };
353
513
  return icons[type] || icons.info;
354
514
  }
@@ -845,6 +1005,252 @@
845
1005
  const ss = (s % 60).toString().padStart(2, '0');
846
1006
  return `${mm}:${ss}`;
847
1007
  }
1008
+ showToast(message, options = {}) {
1009
+ var _a, _b;
1010
+ const type = options.type || 'info';
1011
+ const title = (_a = options.title) !== null && _a !== void 0 ? _a : null;
1012
+ const duration = typeof options.duration === 'number' ? options.duration : 4000;
1013
+ const position = options.position || 'top-right';
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
+ }
1025
+ let container = this._toastContainers.get(position);
1026
+ if (!container || !document.body.contains(container)) {
1027
+ container = document.createElement('div');
1028
+ container.className = `notify-toast-container notify-toast-${position}`;
1029
+ container.setAttribute('aria-label', 'Notificaciones');
1030
+ document.body.appendChild(container);
1031
+ this._toastContainers.set(position, container);
1032
+ }
1033
+ const isBottom = position.startsWith('bottom');
1034
+ const isCenter = position === 'top-center';
1035
+ const toast = document.createElement('div');
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');
1051
+ const iconEl = document.createElement('div');
1052
+ iconEl.className = `notify-toast-icon ${type}`;
1053
+ iconEl.innerHTML = this.getIcon(type);
1054
+ const contentEl = document.createElement('div');
1055
+ contentEl.className = 'notify-toast-content';
1056
+ if (title) {
1057
+ const titleEl = document.createElement('div');
1058
+ titleEl.className = 'notify-toast-title';
1059
+ titleEl.textContent = title;
1060
+ contentEl.appendChild(titleEl);
1061
+ }
1062
+ const msgEl = document.createElement('div');
1063
+ msgEl.className = 'notify-toast-message';
1064
+ msgEl.textContent = message;
1065
+ contentEl.appendChild(msgEl);
1066
+ toast.appendChild(iconEl);
1067
+ toast.appendChild(contentEl);
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
+ }
1076
+ let progressEl = null;
1077
+ if (duration > 0 && showProgress) {
1078
+ progressEl = document.createElement('div');
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
1082
+ toast.appendChild(progressEl);
1083
+ }
1084
+ if (isBottom || isCenter) {
1085
+ container.appendChild(toast);
1086
+ }
1087
+ else {
1088
+ container.insertBefore(toast, container.firstChild);
1089
+ }
1090
+ let dismissed = false;
1091
+ let timerId = null;
1092
+ let remaining = duration;
1093
+ let timerStartedAt = 0;
1094
+ function removeToast() {
1095
+ if (dismissed)
1096
+ return Promise.resolve();
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
+ }
1106
+ toast.classList.remove('notify-toast-visible');
1107
+ return new Promise(resolve => {
1108
+ setTimeout(() => {
1109
+ if (toast.parentNode)
1110
+ toast.parentNode.removeChild(toast);
1111
+ resolve();
1112
+ }, 300);
1113
+ });
1114
+ }
1115
+ const startCountdown = (ms) => {
1116
+ timerStartedAt = Date.now();
1117
+ timerId = setTimeout(() => {
1118
+ if (toastId !== null)
1119
+ this._toastInstances.delete(toastId);
1120
+ removeToast();
1121
+ }, ms);
1122
+ if (progressEl) {
1123
+ progressEl.style.transition = `width ${ms}ms linear`;
1124
+ progressEl.style.width = '0%';
1125
+ }
1126
+ };
1127
+ const pauseCountdown = () => {
1128
+ if (dismissed || timerId === null)
1129
+ return;
1130
+ clearTimeout(timerId);
1131
+ timerId = null;
1132
+ const elapsed = Date.now() - timerStartedAt;
1133
+ remaining = Math.max(0, remaining - elapsed);
1134
+ if (progressEl) {
1135
+ const pct = (remaining / duration) * 100;
1136
+ progressEl.style.transition = 'none';
1137
+ progressEl.style.width = `${pct}%`;
1138
+ }
1139
+ };
1140
+ const resumeCountdown = () => {
1141
+ if (dismissed || remaining <= 0)
1142
+ return;
1143
+ startCountdown(remaining);
1144
+ };
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
+ }
1185
+ requestAnimationFrame(() => {
1186
+ requestAnimationFrame(() => {
1187
+ toast.classList.add('notify-toast-visible');
1188
+ if (duration > 0) {
1189
+ startCountdown(duration);
1190
+ }
1191
+ });
1192
+ });
1193
+ if (duration > 0 && closeable) {
1194
+ toast.addEventListener('mouseenter', pauseCountdown);
1195
+ toast.addEventListener('mouseleave', resumeCountdown);
1196
+ }
1197
+ }
1198
+ toast(messageOrOptions, options = {}) {
1199
+ if (typeof messageOrOptions === 'string') {
1200
+ this.showToast(messageOrOptions, options);
1201
+ }
1202
+ else {
1203
+ const { message = '' } = messageOrOptions, rest = __rest(messageOrOptions, ["message"]);
1204
+ this.showToast(message, rest);
1205
+ }
1206
+ }
1207
+ toastSuccess(message, title, options = {}) {
1208
+ this.showToast(message, Object.assign(Object.assign({}, options), { type: 'success', title: title !== null && title !== void 0 ? title : options.title }));
1209
+ }
1210
+ toastError(message, title, options = {}) {
1211
+ this.showToast(message, Object.assign(Object.assign({}, options), { type: 'error', title: title !== null && title !== void 0 ? title : options.title }));
1212
+ }
1213
+ toastWarning(message, title, options = {}) {
1214
+ this.showToast(message, Object.assign(Object.assign({}, options), { type: 'warning', title: title !== null && title !== void 0 ? title : options.title }));
1215
+ }
1216
+ toastInfo(message, title, options = {}) {
1217
+ this.showToast(message, Object.assign(Object.assign({}, options), { type: 'info', title: title !== null && title !== void 0 ? title : options.title }));
1218
+ }
1219
+ toastQuestion(message, title, options = {}) {
1220
+ this.showToast(message, Object.assign(Object.assign({}, options), { type: 'question', title: title !== null && title !== void 0 ? title : options.title }));
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
+ }
848
1254
  }
849
1255
  const notifyInstance = new NotificationSystem();
850
1256
  const w = window;
@@ -1 +1 @@
1
- "use strict";!function(){if("undefined"!=typeof anime)n();else{const t=document.createElement("script");t.src="https://cdnjs.cloudflare.com/ajax/libs/animejs/3.2.1/anime.min.js",t.onload=n,t.onerror=()=>{console.error("FerNotify: No se pudo cargar anime.js. Por favor, cargalo manualmente.")},document.head.appendChild(t)}function n(){const n=new class{constructor(){this.currentNotification=null,this._lastActiveElement=null,this._currentLoadingPromise=null,this.injectStyles(),this.loadBoxicons()}loadBoxicons(){if(!document.querySelector('link[href*="boxicons"]')){const n=document.createElement("link");n.rel="stylesheet",n.href="https://unpkg.com/boxicons@2.1.4/css/boxicons.min.css",document.head.appendChild(n)}}injectStyles(){const n=document.createElement("style");n.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 ",document.head.appendChild(n)}getIcon(n){const t={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 t[n]||t.info}getDefaultTitle(n){return{success:"¡Éxito!",error:"Error",warning:"Advertencia",info:"Información",question:"Pregunta"}[n]||"Notificación"}getButtonGradient(n){const t={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 t[n]||t.info}getButtonShadow(n){const t={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 t[n]||t.info}show(n={}){if(this.currentNotification){const n=this.currentNotification;this.currentNotification=null;try{n&&n.parentNode&&n.parentNode.removeChild(n)}catch(n){}}const{type:t="info",title:e=this.getDefaultTitle(t),message:o="",buttonText:i="OK",buttonColor:a=null,onClose:r=null,timer:c=null,allowOutsideClick:s=!0,allowEscapeKey:l=!0,hideButton:d=!1,buttons:u=null}=n,f=!0===n.showCloseButton;try{document.body.style.overflow="hidden"}catch(n){}try{document.documentElement.style.overflow="hidden"}catch(n){}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 ${t}`,d&&"info"===t?(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(t);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(n.html||n.content)if(x=document.createElement("div"),x.className="notification-content",n.html)try{x.innerHTML=n.html}catch(t){x.textContent=n.html}else n.content&&n.content instanceof HTMLElement&&x.appendChild(n.content);const y=()=>this.close(r);let w=null,v=null;if(!d)if(Array.isArray(u)&&u.length)v=document.createElement("div"),v.className="notification-button-group",u.forEach(n=>{const e=document.createElement("button");e.className="notification-button",e.textContent=n.text||"OK";const o=n.color||this.getButtonGradient(t),i=n.shadowColor||this.getButtonShadow(t);e.style.background=o,e.style.boxShadow=`0 4px 12px ${i}`,e.addEventListener("click",t=>{t.stopPropagation(),t.preventDefault();try{y().then(()=>{if("function"==typeof n.onClick)try{const t=n.onClick();t&&"function"==typeof t.then&&t.catch(n=>console.error(n))}catch(n){console.error(n)}}).catch(()=>{})}catch(n){console.error(n)}}),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(n.onConfirm||n.onCancel||n.confirmText||n.cancelText){v=document.createElement("div"),v.className="notification-button-group";const e=n.cancelText||"Cancelar",o=n.confirmText||"Aceptar",i=document.createElement("button");i.className="notification-button",i.textContent=e;const a=n.cancelColor||"linear-gradient(135deg, #9ca3af 0%, #6b7280 100%)",r=n.cancelShadow||"rgba(107,114,128,0.25)";i.style.background=a,i.style.boxShadow=`0 4px 12px ${r}`,i.addEventListener("click",t=>{t.stopPropagation(),t.preventDefault(),y().then(()=>{try{if("function"==typeof n.onCancel){const t=n.onCancel();t&&"function"==typeof t.then&&t.catch(n=>console.error(n))}}catch(n){console.error(n)}}).catch(()=>{})}),i.addEventListener("mouseenter",()=>{i.style.boxShadow=`0 6px 16px ${r}`}),i.addEventListener("mouseleave",()=>{i.style.boxShadow=`0 4px 12px ${r}`});const c=document.createElement("button");c.className="notification-button",c.textContent=o;const s=n.confirmColor||this.getButtonGradient(t),l=n.confirmShadow||this.getButtonShadow(t);c.style.background=s,c.style.boxShadow=`0 4px 12px ${l}`,c.addEventListener("click",async t=>{t.stopPropagation(),t.preventDefault();try{if(await y(),"function"==typeof n.onConfirm){const t=n.onConfirm();t&&"function"==typeof t.then&&await t}}catch(n){console.error(n)}}),c.addEventListener("mouseenter",()=>{c.style.boxShadow=`0 6px 16px ${l}`}),c.addEventListener("mouseleave",()=>{c.style.boxShadow=`0 4px 12px ${l}`}),v.appendChild(i),v.appendChild(c)}else if(i){w=document.createElement("button"),w.className="notification-button",w.textContent=i;const n=a||this.getButtonGradient(t),e=this.getButtonShadow(t);w.style.background=n,w.style.boxShadow=`0 4px 12px ${e}`}let k=null;if(f&&(k=document.createElement("button"),k.setAttribute("aria-label","Cerrar"),k.className="notification-close",k.innerHTML="&times;",k.addEventListener("click",n=>{n.stopPropagation(),y()})),m.appendChild(b),x){const n="notify-desc-"+Date.now();x.id=n,p.setAttribute("aria-describedby",n),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(n=>{try{E._externalResolve=n}catch(n){}});try{const n=document.getElementById("notify-live");n&&(n.textContent=`${e}: ${o}`)}catch(n){}try{this._lastActiveElement=document.activeElement}catch(n){this._lastActiveElement=null}this.currentNotification=p;try{const n=m.querySelectorAll('a[href], button:not([disabled]), textarea, input, select, [tabindex]:not([tabindex="-1"])');n&&n.length?n[0].focus():w?w.focus():p.focus()}catch(n){try{p.focus()}catch(n){}}const S=n=>{if("Tab"!==n.key)return;const t=Array.from(m.querySelectorAll('a[href], button:not([disabled]), textarea, input, select, [tabindex]:not([tabindex="-1"])')).filter(n=>n instanceof HTMLElement&&null!==n.offsetParent);if(!t.length)return void n.preventDefault();const e=t[0],o=t[t.length-1];n.shiftKey||document.activeElement!==o?n.shiftKey&&document.activeElement===e&&(n.preventDefault(),o.focus()):(n.preventDefault(),e.focus())};E._focusTrap=S,document.addEventListener("keydown",S);const N=n.anim||{},L="number"==typeof N.overlayDuration?N.overlayDuration:150,T=N.overlayEasing||"easeOutQuad",D="number"==typeof N.boxDuration?N.boxDuration:200,_="number"==typeof N.boxDelay?N.boxDelay:50,j=N.boxEasing||"easeOutBack",A="number"==typeof N.boxStartScale?N.boxStartScale:.8,$="number"==typeof N.iconDuration?N.iconDuration:250,B="number"==typeof N.iconDelay?N.iconDelay:100,q="number"==typeof N.iconRotate?N.iconRotate:"success"===t?-90:"error"===t?90:0;if("number"==typeof N.overlayOpacity&&(p.style.backgroundColor=`rgba(0,0,0,${N.overlayOpacity})`),anime({targets:p,opacity:[0,1],duration:L,easing:T}),anime({targets:m,scale:[A,1],opacity:[0,1],duration:D,easing:j,delay:_}),anime({targets:b,scale:[0,1],rotate:[q,0],duration:$,easing:j,delay:B}),w){const n=this.getButtonShadow(t);w.addEventListener("mouseenter",()=>{w.style.boxShadow=`0 6px 16px ${n}`}),w.addEventListener("mouseleave",()=>{w.style.boxShadow=`0 4px 12px ${n}`}),w.addEventListener("click",n=>{n.stopPropagation(),n.preventDefault(),y().catch(()=>{})})}if(s&&p.addEventListener("click",n=>{m.contains(n.target)||y()}),c&&setTimeout(()=>{y()},c),l){const n=t=>{"Escape"===t.key&&(y(),document.removeEventListener("keydown",n))};E._escHandler=n,document.addEventListener("keydown",n)}return C}close(n=null){if(!this.currentNotification)return Promise.resolve();const t=this.currentNotification,e=t,o=t.querySelector(".notification-box");return this.currentNotification=null,anime({targets:o,scale:.8,opacity:0,duration:100,easing:"easeInQuad"}),new Promise(o=>{anime({targets:t,opacity:0,duration:100,easing:"easeInQuad",complete:()=>{try{e&&e._escHandler&&(document.removeEventListener("keydown",e._escHandler),e._escHandler=void 0)}catch(n){}try{e&&e._focusTrap&&(document.removeEventListener("keydown",e._focusTrap),e._focusTrap=void 0)}catch(n){}try{if(e&&"function"==typeof e._externalResolve){try{e._externalResolve()}catch(n){}e._externalResolve=void 0}}catch(n){}try{t&&t.parentNode&&t.parentNode.removeChild(t)}catch(n){try{t.remove()}catch(n){}}if(!this.currentNotification){try{document.body.style.overflow=""}catch(n){}try{document.documentElement.style.overflow=""}catch(n){}}try{this._lastActiveElement&&"function"==typeof this._lastActiveElement.focus&&this._lastActiveElement.focus()}catch(n){}this._lastActiveElement=null,n&&n(),o()}})})}success(n,t=null,e={}){this.show(Object.assign({type:"success",title:t||this.getDefaultTitle("success"),message:n},e))}error(n,t=null,e={}){this.show(Object.assign({type:"error",title:t||this.getDefaultTitle("error"),message:n},e))}warning(n,t=null,e={}){this.show(Object.assign({type:"warning",title:t||this.getDefaultTitle("warning"),message:n},e))}question(n,t=null,e={}){this.show(Object.assign({type:"question",title:t||this.getDefaultTitle("question"),message:n},e))}info(n,t=null,e={}){this.show(Object.assign({type:"info",title:t||this.getDefaultTitle("info"),message:n},e))}loading(n="Cargando...",t="Espera",e={}){const o=Object.assign({type:"info",title:t,message:n,hideButton:!0,allowOutsideClick:!1,allowEscapeKey:!1},e),i=this.show(o);return this._currentLoadingPromise=i,i}closeLoading(n=null){return this._currentLoadingPromise=null,this.close(n)}hide(n=null){return this.close(n)}hiden(n=null){return this.close(n)}_formatTime(n){const t=Math.max(0,Math.floor(n));return`${Math.floor(t/60).toString().padStart(2,"0")}:${(t%60).toString().padStart(2,"0")}`}},t=window;t.notify=n,t.Notification=n}}();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};
@@ -1,4 +1,15 @@
1
1
  "use strict";
2
+ var __rest = (this && this.__rest) || function (s, e) {
3
+ var t = {};
4
+ for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p) && e.indexOf(p) < 0)
5
+ t[p] = s[p];
6
+ if (s != null && typeof Object.getOwnPropertySymbols === "function")
7
+ for (var i = 0, p = Object.getOwnPropertySymbols(s); i < p.length; i++) {
8
+ if (e.indexOf(p[i]) < 0 && Object.prototype.propertyIsEnumerable.call(s, p[i]))
9
+ t[p[i]] = s[p[i]];
10
+ }
11
+ return t;
12
+ };
2
13
  (function ensureAnimeDependency() {
3
14
  if (typeof anime !== 'undefined') {
4
15
  initFerNotify();
@@ -18,6 +29,8 @@
18
29
  this.currentNotification = null;
19
30
  this._lastActiveElement = null;
20
31
  this._currentLoadingPromise = null;
32
+ this._toastContainers = new Map();
33
+ this._toastInstances = new Map();
21
34
  this.injectStyles();
22
35
  this.loadBoxicons();
23
36
  }
@@ -324,6 +337,152 @@
324
337
  .dark .notification-loading-text {
325
338
  color: #cbd5e1;
326
339
  }
340
+
341
+ /* ==================== Toast ==================== */
342
+ .notify-toast-container {
343
+ position: fixed;
344
+ z-index: 10000;
345
+ display: flex;
346
+ flex-direction: column;
347
+ gap: 10px;
348
+ pointer-events: none;
349
+ width: 360px;
350
+ max-width: calc(100vw - 40px);
351
+ }
352
+ .notify-toast-top-right { top: 20px; right: 20px; }
353
+ .notify-toast-top-left { top: 20px; left: 20px; }
354
+ .notify-toast-top-center { top: 20px; left: 50%; transform: translateX(-50%); }
355
+ .notify-toast-bottom-right { bottom: 20px; right: 20px; flex-direction: column-reverse; }
356
+ .notify-toast-bottom-left { bottom: 20px; left: 20px; flex-direction: column-reverse; }
357
+
358
+ .notify-toast {
359
+ background: white;
360
+ border-radius: 12px;
361
+ padding: 14px 40px 14px 14px;
362
+ box-shadow: 0 4px 24px rgba(0,0,0,0.12), 0 1px 4px rgba(0,0,0,0.06);
363
+ display: flex;
364
+ align-items: flex-start;
365
+ gap: 12px;
366
+ pointer-events: auto;
367
+ position: relative;
368
+ overflow: hidden;
369
+ opacity: 0;
370
+ transform: translateX(30px);
371
+ transition: opacity 0.25s ease, transform 0.25s ease;
372
+ }
373
+ .notify-toast-top-left .notify-toast,
374
+ .notify-toast-bottom-left .notify-toast { transform: translateX(-30px); }
375
+ .notify-toast-top-center .notify-toast { transform: translateY(-20px); }
376
+ .notify-toast.notify-toast-visible {
377
+ opacity: 1;
378
+ transform: translateX(0) translateY(0) !important;
379
+ }
380
+
381
+ .notify-toast-icon {
382
+ width: 36px;
383
+ height: 36px;
384
+ border-radius: 50%;
385
+ display: flex;
386
+ align-items: center;
387
+ justify-content: center;
388
+ font-size: 20px;
389
+ flex-shrink: 0;
390
+ color: white;
391
+ }
392
+ .notify-toast-icon.success { background: linear-gradient(135deg, #10b981 0%, #059669 100%); }
393
+ .notify-toast-icon.error { background: linear-gradient(135deg, #ef4444 0%, #dc2626 100%); }
394
+ .notify-toast-icon.warning { background: linear-gradient(135deg, #f59e0b 0%, #d97706 100%); }
395
+ .notify-toast-icon.info { background: linear-gradient(135deg, #3b82f6 0%, #2563eb 100%); }
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%); }
398
+
399
+ .notify-toast-content { flex: 1; min-width: 0; }
400
+ .notify-toast-title {
401
+ font-size: 14px;
402
+ font-weight: 700;
403
+ color: #1f2937;
404
+ margin-bottom: 2px;
405
+ line-height: 1.3;
406
+ cursor: default;
407
+ }
408
+ .notify-toast-message {
409
+ font-size: 13px;
410
+ color: #6b7280;
411
+ line-height: 1.5;
412
+ cursor: default;
413
+ }
414
+
415
+ .notify-toast-close {
416
+ position: absolute;
417
+ top: 8px;
418
+ right: 8px;
419
+ width: 24px;
420
+ height: 24px;
421
+ border-radius: 6px;
422
+ border: none;
423
+ background: rgba(0,0,0,0.06);
424
+ color: #6b7280;
425
+ cursor: pointer;
426
+ font-size: 16px;
427
+ display: flex;
428
+ align-items: center;
429
+ justify-content: center;
430
+ line-height: 1;
431
+ padding: 0;
432
+ }
433
+ .notify-toast-close:hover { background: rgba(0,0,0,0.1); color: #374151; }
434
+
435
+ /* Sin botón de cierre: reducir padding derecho */
436
+ .notify-toast.notify-toast-no-close { padding-right: 14px; }
437
+
438
+ .notify-toast-progress {
439
+ position: absolute;
440
+ bottom: 0;
441
+ left: 0;
442
+ height: 3px;
443
+ width: 100%;
444
+ border-radius: 0 0 0 12px;
445
+ }
446
+ .notify-toast-progress.success { background: #10b981; }
447
+ .notify-toast-progress.error { background: #ef4444; }
448
+ .notify-toast-progress.warning { background: #f59e0b; }
449
+ .notify-toast-progress.info { background: #3b82f6; }
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
+ }
463
+
464
+ .dark .notify-toast { background: #0f1724; box-shadow: 0 4px 24px rgba(0,0,0,0.35); }
465
+ .dark .notify-toast-title { color: #e6eef8; }
466
+ .dark .notify-toast-message { color: #cbd5e1; }
467
+ .dark .notify-toast-close { background: rgba(255,255,255,0.06); color: #94a3b8; }
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
+ }
327
486
  `;
328
487
  document.head.appendChild(style);
329
488
  }
@@ -333,7 +492,8 @@
333
492
  'error': '<i class="bx bx-x" aria-hidden="true"></i>',
334
493
  'warning': '<i class="bx bx-error" aria-hidden="true"></i>',
335
494
  'info': '<i class="bx bx-info-circle" aria-hidden="true"></i>',
336
- '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>'
337
497
  };
338
498
  return icons[type] || icons.info;
339
499
  }
@@ -830,6 +990,252 @@
830
990
  const ss = (s % 60).toString().padStart(2, '0');
831
991
  return `${mm}:${ss}`;
832
992
  }
993
+ showToast(message, options = {}) {
994
+ var _a, _b;
995
+ const type = options.type || 'info';
996
+ const title = (_a = options.title) !== null && _a !== void 0 ? _a : null;
997
+ const duration = typeof options.duration === 'number' ? options.duration : 4000;
998
+ const position = options.position || 'top-right';
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
+ }
1010
+ let container = this._toastContainers.get(position);
1011
+ if (!container || !document.body.contains(container)) {
1012
+ container = document.createElement('div');
1013
+ container.className = `notify-toast-container notify-toast-${position}`;
1014
+ container.setAttribute('aria-label', 'Notificaciones');
1015
+ document.body.appendChild(container);
1016
+ this._toastContainers.set(position, container);
1017
+ }
1018
+ const isBottom = position.startsWith('bottom');
1019
+ const isCenter = position === 'top-center';
1020
+ const toast = document.createElement('div');
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');
1036
+ const iconEl = document.createElement('div');
1037
+ iconEl.className = `notify-toast-icon ${type}`;
1038
+ iconEl.innerHTML = this.getIcon(type);
1039
+ const contentEl = document.createElement('div');
1040
+ contentEl.className = 'notify-toast-content';
1041
+ if (title) {
1042
+ const titleEl = document.createElement('div');
1043
+ titleEl.className = 'notify-toast-title';
1044
+ titleEl.textContent = title;
1045
+ contentEl.appendChild(titleEl);
1046
+ }
1047
+ const msgEl = document.createElement('div');
1048
+ msgEl.className = 'notify-toast-message';
1049
+ msgEl.textContent = message;
1050
+ contentEl.appendChild(msgEl);
1051
+ toast.appendChild(iconEl);
1052
+ toast.appendChild(contentEl);
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
+ }
1061
+ let progressEl = null;
1062
+ if (duration > 0 && showProgress) {
1063
+ progressEl = document.createElement('div');
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
1067
+ toast.appendChild(progressEl);
1068
+ }
1069
+ if (isBottom || isCenter) {
1070
+ container.appendChild(toast);
1071
+ }
1072
+ else {
1073
+ container.insertBefore(toast, container.firstChild);
1074
+ }
1075
+ let dismissed = false;
1076
+ let timerId = null;
1077
+ let remaining = duration;
1078
+ let timerStartedAt = 0;
1079
+ function removeToast() {
1080
+ if (dismissed)
1081
+ return Promise.resolve();
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
+ }
1091
+ toast.classList.remove('notify-toast-visible');
1092
+ return new Promise(resolve => {
1093
+ setTimeout(() => {
1094
+ if (toast.parentNode)
1095
+ toast.parentNode.removeChild(toast);
1096
+ resolve();
1097
+ }, 300);
1098
+ });
1099
+ }
1100
+ const startCountdown = (ms) => {
1101
+ timerStartedAt = Date.now();
1102
+ timerId = setTimeout(() => {
1103
+ if (toastId !== null)
1104
+ this._toastInstances.delete(toastId);
1105
+ removeToast();
1106
+ }, ms);
1107
+ if (progressEl) {
1108
+ progressEl.style.transition = `width ${ms}ms linear`;
1109
+ progressEl.style.width = '0%';
1110
+ }
1111
+ };
1112
+ const pauseCountdown = () => {
1113
+ if (dismissed || timerId === null)
1114
+ return;
1115
+ clearTimeout(timerId);
1116
+ timerId = null;
1117
+ const elapsed = Date.now() - timerStartedAt;
1118
+ remaining = Math.max(0, remaining - elapsed);
1119
+ if (progressEl) {
1120
+ const pct = (remaining / duration) * 100;
1121
+ progressEl.style.transition = 'none';
1122
+ progressEl.style.width = `${pct}%`;
1123
+ }
1124
+ };
1125
+ const resumeCountdown = () => {
1126
+ if (dismissed || remaining <= 0)
1127
+ return;
1128
+ startCountdown(remaining);
1129
+ };
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
+ }
1170
+ requestAnimationFrame(() => {
1171
+ requestAnimationFrame(() => {
1172
+ toast.classList.add('notify-toast-visible');
1173
+ if (duration > 0) {
1174
+ startCountdown(duration);
1175
+ }
1176
+ });
1177
+ });
1178
+ if (duration > 0 && closeable) {
1179
+ toast.addEventListener('mouseenter', pauseCountdown);
1180
+ toast.addEventListener('mouseleave', resumeCountdown);
1181
+ }
1182
+ }
1183
+ toast(messageOrOptions, options = {}) {
1184
+ if (typeof messageOrOptions === 'string') {
1185
+ this.showToast(messageOrOptions, options);
1186
+ }
1187
+ else {
1188
+ const { message = '' } = messageOrOptions, rest = __rest(messageOrOptions, ["message"]);
1189
+ this.showToast(message, rest);
1190
+ }
1191
+ }
1192
+ toastSuccess(message, title, options = {}) {
1193
+ this.showToast(message, Object.assign(Object.assign({}, options), { type: 'success', title: title !== null && title !== void 0 ? title : options.title }));
1194
+ }
1195
+ toastError(message, title, options = {}) {
1196
+ this.showToast(message, Object.assign(Object.assign({}, options), { type: 'error', title: title !== null && title !== void 0 ? title : options.title }));
1197
+ }
1198
+ toastWarning(message, title, options = {}) {
1199
+ this.showToast(message, Object.assign(Object.assign({}, options), { type: 'warning', title: title !== null && title !== void 0 ? title : options.title }));
1200
+ }
1201
+ toastInfo(message, title, options = {}) {
1202
+ this.showToast(message, Object.assign(Object.assign({}, options), { type: 'info', title: title !== null && title !== void 0 ? title : options.title }));
1203
+ }
1204
+ toastQuestion(message, title, options = {}) {
1205
+ this.showToast(message, Object.assign(Object.assign({}, options), { type: 'question', title: title !== null && title !== void 0 ? title : options.title }));
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
+ }
833
1239
  }
834
1240
  const notifyInstance = new NotificationSystem();
835
1241
  const w = window;
@@ -1 +1 @@
1
- "use strict";!function(){if("undefined"!=typeof anime)n();else{const t=document.createElement("script");t.src="https://cdnjs.cloudflare.com/ajax/libs/animejs/3.2.1/anime.min.js",t.onload=n,t.onerror=()=>{console.error("FerNotify: No se pudo cargar anime.js. Por favor, cargalo manualmente.")},document.head.appendChild(t)}function n(){const n=new class{constructor(){this.currentNotification=null,this._lastActiveElement=null,this._currentLoadingPromise=null,this.injectStyles(),this.loadBoxicons()}loadBoxicons(){if(!document.querySelector('link[href*="boxicons"]')){const n=document.createElement("link");n.rel="stylesheet",n.href="https://unpkg.com/boxicons@2.1.4/css/boxicons.min.css",document.head.appendChild(n)}}injectStyles(){const n=document.createElement("style");n.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 ",document.head.appendChild(n)}getIcon(n){const t={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 t[n]||t.info}getDefaultTitle(n){return{success:"¡Éxito!",error:"Error",warning:"Advertencia",info:"Información",question:"Pregunta"}[n]||"Notificación"}getButtonGradient(n){const t={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 t[n]||t.info}getButtonShadow(n){const t={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 t[n]||t.info}show(n={}){if(this.currentNotification){const n=this.currentNotification;this.currentNotification=null;try{n&&n.parentNode&&n.parentNode.removeChild(n)}catch(n){}}const{type:t="info",title:e=this.getDefaultTitle(t),message:o="",buttonText:i="OK",buttonColor:a=null,onClose:r=null,timer:c=null,allowOutsideClick:s=!0,allowEscapeKey:l=!0,hideButton:d=!1,buttons:u=null}=n,f=!0===n.showCloseButton;try{document.body.style.overflow="hidden"}catch(n){}try{document.documentElement.style.overflow="hidden"}catch(n){}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 ${t}`,d&&"info"===t?(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(t);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(n.html||n.content)if(x=document.createElement("div"),x.className="notification-content",n.html)try{x.innerHTML=n.html}catch(t){x.textContent=n.html}else n.content&&n.content instanceof HTMLElement&&x.appendChild(n.content);const y=()=>this.close(r);let w=null,v=null;if(!d)if(Array.isArray(u)&&u.length)v=document.createElement("div"),v.className="notification-button-group",u.forEach(n=>{const e=document.createElement("button");e.className="notification-button",e.textContent=n.text||"OK";const o=n.color||this.getButtonGradient(t),i=n.shadowColor||this.getButtonShadow(t);e.style.background=o,e.style.boxShadow=`0 4px 12px ${i}`,e.addEventListener("click",t=>{t.stopPropagation(),t.preventDefault();try{y().then(()=>{if("function"==typeof n.onClick)try{const t=n.onClick();t&&"function"==typeof t.then&&t.catch(n=>console.error(n))}catch(n){console.error(n)}}).catch(()=>{})}catch(n){console.error(n)}}),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(n.onConfirm||n.onCancel||n.confirmText||n.cancelText){v=document.createElement("div"),v.className="notification-button-group";const e=n.cancelText||"Cancelar",o=n.confirmText||"Aceptar",i=document.createElement("button");i.className="notification-button",i.textContent=e;const a=n.cancelColor||"linear-gradient(135deg, #9ca3af 0%, #6b7280 100%)",r=n.cancelShadow||"rgba(107,114,128,0.25)";i.style.background=a,i.style.boxShadow=`0 4px 12px ${r}`,i.addEventListener("click",t=>{t.stopPropagation(),t.preventDefault(),y().then(()=>{try{if("function"==typeof n.onCancel){const t=n.onCancel();t&&"function"==typeof t.then&&t.catch(n=>console.error(n))}}catch(n){console.error(n)}}).catch(()=>{})}),i.addEventListener("mouseenter",()=>{i.style.boxShadow=`0 6px 16px ${r}`}),i.addEventListener("mouseleave",()=>{i.style.boxShadow=`0 4px 12px ${r}`});const c=document.createElement("button");c.className="notification-button",c.textContent=o;const s=n.confirmColor||this.getButtonGradient(t),l=n.confirmShadow||this.getButtonShadow(t);c.style.background=s,c.style.boxShadow=`0 4px 12px ${l}`,c.addEventListener("click",async t=>{t.stopPropagation(),t.preventDefault();try{if(await y(),"function"==typeof n.onConfirm){const t=n.onConfirm();t&&"function"==typeof t.then&&await t}}catch(n){console.error(n)}}),c.addEventListener("mouseenter",()=>{c.style.boxShadow=`0 6px 16px ${l}`}),c.addEventListener("mouseleave",()=>{c.style.boxShadow=`0 4px 12px ${l}`}),v.appendChild(i),v.appendChild(c)}else if(i){w=document.createElement("button"),w.className="notification-button",w.textContent=i;const n=a||this.getButtonGradient(t),e=this.getButtonShadow(t);w.style.background=n,w.style.boxShadow=`0 4px 12px ${e}`}let k=null;if(f&&(k=document.createElement("button"),k.setAttribute("aria-label","Cerrar"),k.className="notification-close",k.innerHTML="&times;",k.addEventListener("click",n=>{n.stopPropagation(),y()})),m.appendChild(b),x){const n="notify-desc-"+Date.now();x.id=n,p.setAttribute("aria-describedby",n),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(n=>{try{E._externalResolve=n}catch(n){}});try{const n=document.getElementById("notify-live");n&&(n.textContent=`${e}: ${o}`)}catch(n){}try{this._lastActiveElement=document.activeElement}catch(n){this._lastActiveElement=null}this.currentNotification=p;try{const n=m.querySelectorAll('a[href], button:not([disabled]), textarea, input, select, [tabindex]:not([tabindex="-1"])');n&&n.length?n[0].focus():w?w.focus():p.focus()}catch(n){try{p.focus()}catch(n){}}const S=n=>{if("Tab"!==n.key)return;const t=Array.from(m.querySelectorAll('a[href], button:not([disabled]), textarea, input, select, [tabindex]:not([tabindex="-1"])')).filter(n=>n instanceof HTMLElement&&null!==n.offsetParent);if(!t.length)return void n.preventDefault();const e=t[0],o=t[t.length-1];n.shiftKey||document.activeElement!==o?n.shiftKey&&document.activeElement===e&&(n.preventDefault(),o.focus()):(n.preventDefault(),e.focus())};E._focusTrap=S,document.addEventListener("keydown",S);const N=n.anim||{},L="number"==typeof N.overlayDuration?N.overlayDuration:150,T=N.overlayEasing||"easeOutQuad",D="number"==typeof N.boxDuration?N.boxDuration:200,_="number"==typeof N.boxDelay?N.boxDelay:50,j=N.boxEasing||"easeOutBack",A="number"==typeof N.boxStartScale?N.boxStartScale:.8,$="number"==typeof N.iconDuration?N.iconDuration:250,B="number"==typeof N.iconDelay?N.iconDelay:100,O="number"==typeof N.iconRotate?N.iconRotate:"success"===t?-90:"error"===t?90:0;if("number"==typeof N.overlayOpacity&&(p.style.backgroundColor=`rgba(0,0,0,${N.overlayOpacity})`),anime({targets:p,opacity:[0,1],duration:L,easing:T}),anime({targets:m,scale:[A,1],opacity:[0,1],duration:D,easing:j,delay:_}),anime({targets:b,scale:[0,1],rotate:[O,0],duration:$,easing:j,delay:B}),w){const n=this.getButtonShadow(t);w.addEventListener("mouseenter",()=>{w.style.boxShadow=`0 6px 16px ${n}`}),w.addEventListener("mouseleave",()=>{w.style.boxShadow=`0 4px 12px ${n}`}),w.addEventListener("click",n=>{n.stopPropagation(),n.preventDefault(),y().catch(()=>{})})}if(s&&p.addEventListener("click",n=>{m.contains(n.target)||y()}),c&&setTimeout(()=>{y()},c),l){const n=t=>{"Escape"===t.key&&(y(),document.removeEventListener("keydown",n))};E._escHandler=n,document.addEventListener("keydown",n)}return C}close(n=null){if(!this.currentNotification)return Promise.resolve();const t=this.currentNotification,e=t,o=t.querySelector(".notification-box");return this.currentNotification=null,anime({targets:o,scale:.8,opacity:0,duration:100,easing:"easeInQuad"}),new Promise(o=>{anime({targets:t,opacity:0,duration:100,easing:"easeInQuad",complete:()=>{try{e&&e._escHandler&&(document.removeEventListener("keydown",e._escHandler),e._escHandler=void 0)}catch(n){}try{e&&e._focusTrap&&(document.removeEventListener("keydown",e._focusTrap),e._focusTrap=void 0)}catch(n){}try{if(e&&"function"==typeof e._externalResolve){try{e._externalResolve()}catch(n){}e._externalResolve=void 0}}catch(n){}try{t&&t.parentNode&&t.parentNode.removeChild(t)}catch(n){try{t.remove()}catch(n){}}if(!this.currentNotification){try{document.body.style.overflow=""}catch(n){}try{document.documentElement.style.overflow=""}catch(n){}}try{this._lastActiveElement&&"function"==typeof this._lastActiveElement.focus&&this._lastActiveElement.focus()}catch(n){}this._lastActiveElement=null,n&&n(),o()}})})}success(n,t=null,e={}){this.show(Object.assign({type:"success",title:t||this.getDefaultTitle("success"),message:n},e))}error(n,t=null,e={}){this.show(Object.assign({type:"error",title:t||this.getDefaultTitle("error"),message:n},e))}warning(n,t=null,e={}){this.show(Object.assign({type:"warning",title:t||this.getDefaultTitle("warning"),message:n},e))}question(n,t=null,e={}){this.show(Object.assign({type:"question",title:t||this.getDefaultTitle("question"),message:n},e))}info(n,t=null,e={}){this.show(Object.assign({type:"info",title:t||this.getDefaultTitle("info"),message:n},e))}loading(n="Cargando...",t="Espera",e={}){const o=Object.assign({type:"info",title:t,message:n,hideButton:!0,allowOutsideClick:!1,allowEscapeKey:!1},e),i=this.show(o);return this._currentLoadingPromise=i,i}closeLoading(n=null){return this._currentLoadingPromise=null,this.close(n)}hide(n=null){return this.close(n)}hiden(n=null){return this.close(n)}_formatTime(n){const t=Math.max(0,Math.floor(n));return`${Math.floor(t/60).toString().padStart(2,"0")}:${(t%60).toString().padStart(2,"0")}`}},t=window;t.notify=n,t.Notification=n}}();
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,6 +1,6 @@
1
1
  {
2
2
  "name": "fernotify",
3
- "version": "1.2.8",
3
+ "version": "1.2.10",
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",