rojeru-toast 1.0.0

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/src/index.js ADDED
@@ -0,0 +1,569 @@
1
+ /**
2
+ * rojeru-toast - Librería moderna de mensajes toast
3
+ * @version 2.6.1
4
+ * @license MIT
5
+ * @author Rogelio Urieta Camacho (RojeruSan)
6
+ */
7
+ /**
8
+ * MIT License
9
+ *
10
+ * Copyright (c) 2025 Rogelio Urieta Camacho (RojeruSan)
11
+ *
12
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
13
+ * of this software and associated documentation files (the "Software"), to deal
14
+ * in the Software without restriction, including without limitation the rights
15
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
16
+ * copies of the Software, and to permit persons to whom the Software is
17
+ * furnished to do so, subject to the following conditions:
18
+ *
19
+ * The above copyright notice and this permission notice shall be included in all
20
+ * copies or substantial portions of the Software.
21
+ *
22
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
23
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
24
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
25
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
26
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
27
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
28
+ * SOFTWARE.
29
+ */
30
+ class RojeruToast {
31
+ constructor() {
32
+ this.defaultOptions = {
33
+ duration: 3000,
34
+ position: 'top-right',
35
+ type: 'info',
36
+ dismissible: true,
37
+ pauseOnHover: true,
38
+ theme: 'light',
39
+ animation: 'slide',
40
+ closeOnClick: false
41
+ };
42
+
43
+ this.container = null;
44
+ this.toastInstances = new Map();
45
+ this.stylesInjected = false;
46
+ this.init();
47
+ }
48
+
49
+ init() {
50
+ this.createContainer();
51
+ this.injectStyles();
52
+ }
53
+
54
+ injectStyles() {
55
+ if (this.stylesInjected || document.querySelector('#rojeru-toast-styles')) {
56
+ this.stylesInjected = true;
57
+ return;
58
+ }
59
+
60
+ const link = document.createElement('link');
61
+ link.id = 'rojeru-toast-styles';
62
+ link.rel = 'stylesheet';
63
+ link.href = 'https://cdn.jsdelivr.net/npm/rojeru-toast@latest/dist/rojeru-toast.min.css';
64
+ link.onerror = () => {
65
+ console.warn('No se pudieron cargar los estilos CSS desde CDN. Los estilos se inyectarán inline.');
66
+ this.injectInlineStyles();
67
+ };
68
+ document.head.appendChild(link);
69
+ this.stylesInjected = true;
70
+ }
71
+
72
+ injectInlineStyles() {
73
+ if (document.querySelector('#rojeru-toast-inline-styles')) return;
74
+
75
+ const style = document.createElement('style');
76
+ style.id = 'rojeru-toast-inline-styles';
77
+ style.textContent = `
78
+ .rojeru-toast-container {
79
+ position: fixed;
80
+ z-index: 10000;
81
+ pointer-events: none;
82
+ display: flex;
83
+ flex-direction: column;
84
+ gap: 12px;
85
+ max-width: 420px;
86
+ padding: 20px;
87
+ }
88
+ .rojeru-toast-container.top-right { top: 0; right: 0; align-items: flex-end; }
89
+ .rojeru-toast-container.top-left { top: 0; left: 0; align-items: flex-start; }
90
+ .rojeru-toast-container.top-center { top: 0; left: 50%; transform: translateX(-50%); align-items: center; }
91
+ .rojeru-toast-container.bottom-right { bottom: 0; right: 0; align-items: flex-end; }
92
+ .rojeru-toast-container.bottom-left { bottom: 0; left: 0; align-items: flex-start; }
93
+ .rojeru-toast-container.bottom-center { bottom: 0; left: 50%; transform: translateX(-50%); align-items: center; }
94
+ .rojeru-toast {
95
+ pointer-events: all;
96
+ padding: 16px 20px;
97
+ border-radius: 12px;
98
+ backdrop-filter: blur(10px);
99
+ -webkit-backdrop-filter: blur(10px);
100
+ font-family: 'Inter', -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
101
+ font-size: 14px;
102
+ font-weight: 500;
103
+ line-height: 1.5;
104
+ max-width: 380px;
105
+ display: flex;
106
+ align-items: flex-start;
107
+ gap: 12px;
108
+ transition: all 0.4s cubic-bezier(0.16, 1, 0.3, 1);
109
+ position: relative;
110
+ overflow: hidden;
111
+ border: 1px solid;
112
+ }
113
+ .rojeru-toast.rojeru-toast-clickable { cursor: pointer; transition: all 0.3s cubic-bezier(0.16, 1, 0.3, 1); }
114
+ .rojeru-toast.rojeru-toast-clickable:hover { transform: translateY(-2px); box-shadow: 0 12px 40px rgba(0,0,0,0.15); }
115
+ .rojeru-toast.rojeru-toast-clickable:active { transform: translateY(0); }
116
+ .rojeru-toast::before { content: ''; position: absolute; top: 0; left: 0; right: 0; height: 1px; background: linear-gradient(90deg, transparent, currentColor, transparent); opacity: 0.3; }
117
+ .rojeru-toast.hiding { transform: scale(0.9) translateX(100px); opacity: 0; pointer-events: none; }
118
+ .rojeru-toast.light { background: rgba(255,255,255,0.95); color: #1a1a1a; border-color: rgba(0,0,0,0.1); box-shadow: 0 8px 32px rgba(0,0,0,0.1), 0 2px 8px rgba(0,0,0,0.05), inset 0 1px 0 rgba(255,255,255,0.8); }
119
+ .rojeru-toast.light.info { color: #0066CC; } .rojeru-toast.light.success { color: #00A86B; } .rojeru-toast.light.warning { color: #FF6B00; } .rojeru-toast.light.error { color: #FF3B30; } .rojeru-toast.light.loading { color: #666666; }
120
+ .rojeru-toast.dark { background: rgba(28,28,30,0.95); color: #ffffff; border-color: rgba(255,255,255,0.1); box-shadow: 0 8px 32px rgba(0,0,0,0.3), 0 2px 8px rgba(0,0,0,0.2), inset 0 1px 0 rgba(255,255,255,0.1); }
121
+ .rojeru-toast.dark.info { color: #5AC8FA; } .rojeru-toast.dark.success { color: #30D158; } .rojeru-toast.dark.warning { color: #FF9F0A; } .rojeru-toast.dark.error { color: #FF453A; } .rojeru-toast.dark.loading { color: #8E8E93; }
122
+ .rojeru-toast.colored { color: white; border: none; box-shadow: 0 8px 32px rgba(0,0,0,0.25), 0 4px 16px rgba(0,0,0,0.15); text-shadow: 0 1px 2px rgba(0,0,0,0.1); }
123
+ .rojeru-toast.colored.info { background: linear-gradient(135deg, #0055AA, #0077CC); }
124
+ .rojeru-toast.colored.success { background: linear-gradient(135deg, #008755, #00AA66); }
125
+ .rojeru-toast.colored.warning { background: linear-gradient(135deg, #CC5500, #EE7700); }
126
+ .rojeru-toast.colored.error { background: linear-gradient(135deg, #CC2200, #EE4422); }
127
+ .rojeru-toast-colored::before { background: linear-gradient(90deg, transparent, rgba(255,255,255,0.4), transparent); }
128
+ .rojeru-toast-colored .rojeru-toast-close { background: rgba(255,255,255,0.25); color: white; text-shadow: 0 1px 2px rgba(0,0,0,0.2); }
129
+ .rojeru-toast-colored .rojeru-toast-close:hover { background: rgba(255,255,255,0.35); }
130
+ .rojeru-toast-colored .rojeru-toast-progress { background: rgba(255,255,255,0.6); box-shadow: 0 0 8px rgba(255,255,255,0.3); }
131
+ .rojeru-toast-loading-icon { animation: rojeru-toast-spin 1s linear infinite; }
132
+ @keyframes rojeru-toast-spin { 0% { transform: rotate(0deg); } 100% { transform: rotate(360deg); } }
133
+ .rojeru-toast-icon { width: 20px; height: 20px; flex-shrink: 0; margin-top: 1px; }
134
+ .rojeru-toast-icon svg { width: 100%; height: 100%; display: block; }
135
+ .rojeru-toast-content { flex: 1; padding-right: 8px; display: flex; flex-direction: column; gap: 2px; }
136
+ .rojeru-toast-title { font-weight: 600; font-size: 15px; display: block; }
137
+ .rojeru-toast-message { opacity: 0.95; font-weight: 400; line-height: 1.4; }
138
+ .rojeru-toast.colored .rojeru-toast-title { opacity: 0.95; font-weight: 700; }
139
+ .rojeru-toast.colored .rojeru-toast-message { opacity: 0.9; }
140
+ .rojeru-toast:not(:has(.rojeru-toast-title)) .rojeru-toast-content { gap: 0; }
141
+ .rojeru-toast:not(:has(.rojeru-toast-title)) .rojeru-toast-message { margin-top: 1px; }
142
+ .rojeru-toast-close { background: rgba(0,0,0,0.1); border: none; width: 24px; height: 24px; border-radius: 6px; cursor: pointer; opacity: 0.7; display: flex; align-items: center; justify-content: center; transition: all 0.2s ease; flex-shrink: 0; margin-top: -2px; font-size: 16px; font-weight: bold; }
143
+ .rojeru-toast-close:hover { opacity: 1; background: rgba(0,0,0,0.15); transform: scale(1.1); }
144
+ .rojeru-toast.dark .rojeru-toast-close { background: rgba(255,255,255,0.15); color: #ffffff; }
145
+ .rojeru-toast.dark .rojeru-toast-close:hover { background: rgba(255,255,255,0.25); }
146
+ .rojeru-toast-progress { position: absolute; bottom: 0; left: 0; height: 3px; background: currentColor; opacity: 0.3; width: 100%; transform-origin: left; transform: scaleX(1); }
147
+ .rojeru-toast.slide { animation: rojeru-toast-slide-in 0.5s cubic-bezier(0.16, 1, 0.3, 1); }
148
+ .rojeru-toast.fade { animation: rojeru-toast-fade-in 0.4s ease-out; }
149
+ .rojeru-toast.scale { animation: rojeru-toast-scale-in 0.3s cubic-bezier(0.16, 1, 0.3, 1); }
150
+ @keyframes rojeru-toast-slide-in { from { transform: translateX(100%); opacity: 0; } to { transform: translateX(0); opacity: 1; } }
151
+ @keyframes rojeru-toast-slide-in-left { from { transform: translateX(-100%); opacity: 0; } to { transform: translateX(0); opacity: 1; } }
152
+ @keyframes rojeru-toast-fade-in { from { opacity: 0; transform: translateY(-20px); } to { opacity: 1; transform: translateY(0); } }
153
+ @keyframes rojeru-toast-scale-in { from { opacity: 0; transform: scale(0.8); } to { opacity: 1; transform: scale(1); } }
154
+ @keyframes rojeru-toast-update { 0% { transform: scale(1); } 50% { transform: scale(1.02); } 100% { transform: scale(1); } }
155
+ @keyframes rojeru-toast-type-change { 0% { transform: scale(1); opacity: 1; } 50% { transform: scale(0.95); opacity: 0.7; } 100% { transform: scale(1); opacity: 1; } }
156
+ @media (max-width: 768px) {
157
+ .rojeru-toast-container { max-width: 100vw; padding: 10px; }
158
+ .rojeru-toast { max-width: calc(100vw - 20px); font-size: 15px; padding: 14px 16px; }
159
+ .rojeru-toast-container.top-center, .rojeru-toast-container.bottom-center { width: 100%; }
160
+ .rojeru-toast-icon { width: 18px; height: 18px; }
161
+ }
162
+ `;
163
+ document.head.appendChild(style);
164
+ }
165
+
166
+ createContainer() {
167
+ const existingContainer = document.querySelector('.rojeru-toast-container');
168
+ if (existingContainer) {
169
+ existingContainer.remove();
170
+ }
171
+
172
+ this.container = document.createElement('div');
173
+ this.container.className = 'rojeru-toast-container';
174
+ document.body.appendChild(this.container);
175
+ }
176
+
177
+ getIcon(type) {
178
+ const icons = {
179
+ info: '<svg viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg"><circle cx="12" cy="12" r="10" stroke="currentColor" stroke-width="2"/><path d="M12 16V12" stroke="currentColor" stroke-width="2" stroke-linecap="round"/><circle cx="12" cy="8" r="1" fill="currentColor"/></svg>',
180
+ success: '<svg viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg"><circle cx="12" cy="12" r="10" stroke="currentColor" stroke-width="2"/><path d="M8 12L11 15L16 9" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/></svg>',
181
+ warning: '<svg viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg"><path d="M12 9V13" stroke="currentColor" stroke-width="2" stroke-linecap="round"/><path d="M12 17H12.01" stroke="currentColor" stroke-width="2" stroke-linecap="round"/><path d="M10.29 3.86L1.82 18C1.645 18.302 1.553 18.649 1.553 19C1.553 19.351 1.645 19.698 1.82 20C2.08 20.47 2.591 20.79 3.17 20.79H20.83C21.409 20.79 21.92 20.47 22.18 20C22.355 19.698 22.447 19.351 22.447 19C22.447 18.649 22.355 18.302 22.18 18L13.71 3.86C13.45 3.39 12.939 3.07 12.36 3.07C11.781 3.07 11.27 3.39 11.01 3.86V3.86Z" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/></svg>',
182
+ error: '<svg viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg"><circle cx="12" cy="12" r="10" stroke="currentColor" stroke-width="2"/><path d="M15 9L9 15" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/><path d="M9 9L15 15" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/></svg>',
183
+ loading: '<svg viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg" class="rojeru-toast-loading-icon"><circle cx="12" cy="12" r="10" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-dasharray="31.4 31.4" opacity="0.3"/><circle cx="12" cy="12" r="10" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-dasharray="15.7 31.4"/></svg>'
184
+ };
185
+ return icons[type] || icons.info;
186
+ }
187
+
188
+ show(title, message, options = {}) {
189
+ const config = { ...this.defaultOptions, ...options };
190
+
191
+ const isSimpleToast = message === undefined && typeof title === 'string';
192
+
193
+ if (isSimpleToast) {
194
+ message = title;
195
+ title = null;
196
+ }
197
+
198
+ this.updateContainerPosition(config.position);
199
+
200
+ const toast = document.createElement('div');
201
+
202
+ if (config.type === 'loading') {
203
+ const baseType = config.loadingType || 'info';
204
+ toast.className = `rojeru-toast ${config.theme} loading ${baseType} ${config.animation}`;
205
+ } else {
206
+ toast.className = `rojeru-toast ${config.theme} ${config.type} ${config.animation}`;
207
+ }
208
+
209
+ const iconType = config.type === 'loading' ? 'loading' : config.type;
210
+ const icon = this.getIcon(iconType);
211
+ const hasTitle = title && title !== '';
212
+
213
+ toast.innerHTML = `
214
+ <div class="rojeru-toast-icon">${icon}</div>
215
+ <div class="rojeru-toast-content">
216
+ ${hasTitle ? `<span class="rojeru-toast-title">${title}</span>` : ''}
217
+ <span class="rojeru-toast-message">${message}</span>
218
+ </div>
219
+ ${config.dismissible && config.type !== 'loading' ? '<button class="rojeru-toast-close" aria-label="Cerrar">&times;</button>' : ''}
220
+ ${config.duration > 0 && config.type !== 'loading' ? '<div class="rojeru-toast-progress"></div>' : ''}
221
+ `;
222
+
223
+ if (config.type === 'loading') {
224
+ config.dismissible = false;
225
+ config.closeOnClick = false;
226
+ }
227
+
228
+ if (!config.dismissible && config.duration === 0 && config.closeOnClick) {
229
+ toast.classList.add('rojeru-toast-clickable');
230
+ }
231
+
232
+ this.addSlideAnimation(toast, config.position);
233
+ this.container.appendChild(toast);
234
+
235
+ const toastId = 'toast-' + Date.now() + '-' + Math.random().toString(36).substr(2, 9);
236
+ toast.setAttribute('data-toast-id', toastId);
237
+
238
+ let timeoutId = null;
239
+
240
+ if (config.duration > 0 && config.type !== 'loading') {
241
+ const progressBar = toast.querySelector('.rojeru-toast-progress');
242
+ if (progressBar) {
243
+ setTimeout(() => {
244
+ progressBar.style.transition = `transform ${config.duration}ms linear`;
245
+ progressBar.style.transform = 'scaleX(0)';
246
+ }, 50);
247
+ }
248
+
249
+ timeoutId = setTimeout(() => {
250
+ this.hide(toast);
251
+ }, config.duration);
252
+ }
253
+
254
+ if (config.dismissible && config.type !== 'loading') {
255
+ const closeBtn = toast.querySelector('.rojeru-toast-close');
256
+ closeBtn.addEventListener('click', () => {
257
+ if (timeoutId) {
258
+ clearTimeout(timeoutId);
259
+ }
260
+ this.hide(toast);
261
+ });
262
+ }
263
+
264
+ if (config.closeOnClick && config.type !== 'loading') {
265
+ toast.style.cursor = 'pointer';
266
+ toast.addEventListener('click', (e) => {
267
+ if (!e.target.closest('.rojeru-toast-close')) {
268
+ if (timeoutId) {
269
+ clearTimeout(timeoutId);
270
+ }
271
+ this.hide(toast);
272
+ }
273
+ });
274
+ }
275
+
276
+ if (config.pauseOnHover && config.duration > 0 && config.type !== 'loading') {
277
+ let remainingTime = config.duration;
278
+ let startTime = Date.now();
279
+
280
+ toast.addEventListener('mouseenter', () => {
281
+ if (timeoutId) {
282
+ clearTimeout(timeoutId);
283
+ timeoutId = null;
284
+
285
+ const elapsed = Date.now() - startTime;
286
+ remainingTime = config.duration - elapsed;
287
+
288
+ const progressBar = toast.querySelector('.rojeru-toast-progress');
289
+ if (progressBar) {
290
+ const computedStyle = window.getComputedStyle(progressBar);
291
+ const currentScale = new DOMMatrixReadOnly(computedStyle.transform).m11;
292
+ progressBar.style.transition = 'none';
293
+ progressBar.style.transform = `scaleX(${currentScale})`;
294
+ }
295
+ }
296
+ });
297
+
298
+ toast.addEventListener('mouseleave', () => {
299
+ if (!timeoutId && remainingTime > 0) {
300
+ startTime = Date.now();
301
+
302
+ const progressBar = toast.querySelector('.rojeru-toast-progress');
303
+ if (progressBar) {
304
+ const computedStyle = window.getComputedStyle(progressBar);
305
+ const currentScale = new DOMMatrixReadOnly(computedStyle.transform).m11;
306
+ const remainingDuration = remainingTime;
307
+
308
+ progressBar.style.transition = `transform ${remainingDuration}ms linear`;
309
+ progressBar.style.transform = 'scaleX(0)';
310
+ }
311
+
312
+ timeoutId = setTimeout(() => {
313
+ this.hide(toast);
314
+ }, remainingTime);
315
+ }
316
+ });
317
+ }
318
+
319
+ const toastInstance = {
320
+ hide: () => {
321
+ if (timeoutId) {
322
+ clearTimeout(timeoutId);
323
+ }
324
+ this.hide(toast);
325
+ },
326
+ update: (newTitle, newMessage, newOptions = {}) => {
327
+ return this.updateToast(toast, newTitle, newMessage, newOptions);
328
+ },
329
+ changeType: (newType, newMessage = null, newTitle = null) => {
330
+ return this.changeToastType(toast, newType, newMessage, newTitle);
331
+ },
332
+ complete: (successMessage = null, options = {}) => {
333
+ if (toast.classList.contains('loading')) {
334
+ const message = successMessage || 'Proceso completado';
335
+ const type = options.type || 'success';
336
+ return this.changeToastType(toast, type, message);
337
+ }
338
+ return this;
339
+ },
340
+ getId: () => toastId,
341
+ element: toast
342
+ };
343
+
344
+ this.toastInstances.set(toastId, toastInstance);
345
+
346
+ return toastInstance;
347
+ }
348
+
349
+ updateToast(toast, newTitle, newMessage, newOptions = {}) {
350
+ const isSimpleUpdate = newMessage === undefined && typeof newTitle === 'string';
351
+
352
+ if (isSimpleUpdate) {
353
+ newMessage = newTitle;
354
+ newTitle = null;
355
+ }
356
+
357
+ if (newOptions.type) {
358
+ this.changeToastType(toast, newOptions.type, newMessage, newTitle);
359
+ return;
360
+ }
361
+
362
+ const titleEl = toast.querySelector('.rojeru-toast-title');
363
+ const messageEl = toast.querySelector('.rojeru-toast-message');
364
+
365
+ if (titleEl) {
366
+ if (newTitle) {
367
+ titleEl.textContent = newTitle;
368
+ titleEl.style.display = 'block';
369
+ } else {
370
+ titleEl.style.display = 'none';
371
+ }
372
+ }
373
+
374
+ if (messageEl && newMessage !== null) {
375
+ messageEl.innerHTML = newMessage;
376
+ }
377
+
378
+ if (newOptions.theme) {
379
+ toast.classList.remove('light', 'dark', 'colored');
380
+ toast.classList.add(newOptions.theme);
381
+ }
382
+
383
+ toast.style.animation = 'none';
384
+ setTimeout(() => {
385
+ toast.style.animation = 'rojeru-toast-update 0.3s ease-out';
386
+ }, 10);
387
+
388
+ return this.toastInstances.get(toast.getAttribute('data-toast-id'));
389
+ }
390
+
391
+ changeToastType(toast, newType, newMessage = null, newTitle = null) {
392
+ toast.classList.remove('info', 'success', 'warning', 'error', 'loading');
393
+
394
+ if (newType === 'loading') {
395
+ const baseType = 'info';
396
+ toast.classList.add('loading', baseType);
397
+ } else {
398
+ toast.classList.add(newType);
399
+ }
400
+
401
+ const iconEl = toast.querySelector('.rojeru-toast-icon');
402
+ if (iconEl) {
403
+ const iconType = newType === 'loading' ? 'loading' : newType;
404
+ iconEl.innerHTML = this.getIcon(iconType);
405
+ }
406
+
407
+ if (newType !== 'loading') {
408
+ const hasCloseBtn = toast.querySelector('.rojeru-toast-close');
409
+ if (!hasCloseBtn && this.defaultOptions.dismissible) {
410
+ const closeBtn = document.createElement('button');
411
+ closeBtn.className = 'rojeru-toast-close';
412
+ closeBtn.setAttribute('aria-label', 'Cerrar');
413
+ closeBtn.innerHTML = '&times;';
414
+ closeBtn.addEventListener('click', () => {
415
+ this.hide(toast);
416
+ });
417
+ toast.appendChild(closeBtn);
418
+ }
419
+
420
+ const existingProgress = toast.querySelector('.rojeru-toast-progress');
421
+ if (existingProgress) {
422
+ existingProgress.remove();
423
+ }
424
+ } else {
425
+ const closeBtn = toast.querySelector('.rojeru-toast-close');
426
+ if (closeBtn) {
427
+ closeBtn.remove();
428
+ }
429
+ const progressBar = toast.querySelector('.rojeru-toast-progress');
430
+ if (progressBar) {
431
+ progressBar.remove();
432
+ }
433
+ }
434
+
435
+ if (newMessage !== null) {
436
+ const messageEl = toast.querySelector('.rojeru-toast-message');
437
+ if (messageEl) {
438
+ messageEl.innerHTML = newMessage;
439
+ }
440
+ }
441
+
442
+ if (newTitle !== null) {
443
+ const titleEl = toast.querySelector('.rojeru-toast-title');
444
+ if (titleEl) {
445
+ if (newTitle) {
446
+ titleEl.textContent = newTitle;
447
+ titleEl.style.display = 'block';
448
+ } else {
449
+ titleEl.style.display = 'none';
450
+ }
451
+ }
452
+ }
453
+
454
+ toast.style.animation = 'none';
455
+ setTimeout(() => {
456
+ toast.style.animation = 'rojeru-toast-type-change 0.4s ease-out';
457
+ }, 10);
458
+
459
+ return this.toastInstances.get(toast.getAttribute('data-toast-id'));
460
+ }
461
+
462
+ addSlideAnimation(toast, position) {
463
+ const isLeft = position.includes('left');
464
+ const isTop = position.includes('top');
465
+ const isCenter = position.includes('center');
466
+
467
+ if (isCenter) {
468
+ toast.style.animation = isTop ?
469
+ 'rojeru-toast-fade-in 0.4s ease-out' :
470
+ 'rojeru-toast-fade-in 0.4s ease-out';
471
+ } else if (isLeft) {
472
+ toast.style.animation = 'rojeru-toast-slide-in-left 0.5s cubic-bezier(0.16, 1, 0.3, 1)';
473
+ }
474
+ }
475
+
476
+ updateContainerPosition(position) {
477
+ this.container.className = `rojeru-toast-container ${position}`;
478
+ }
479
+
480
+ hide(toast) {
481
+ if (toast && toast.parentNode) {
482
+ const toastId = toast.getAttribute('data-toast-id');
483
+ this.toastInstances.delete(toastId);
484
+
485
+ toast.classList.add('hiding');
486
+ setTimeout(() => {
487
+ if (toast.parentNode) {
488
+ toast.parentNode.removeChild(toast);
489
+ }
490
+ }, 400);
491
+ }
492
+ }
493
+
494
+ hideById(toastId) {
495
+ const toastInstance = this.toastInstances.get(toastId);
496
+ if (toastInstance) {
497
+ toastInstance.hide();
498
+ }
499
+ }
500
+
501
+ hideLast() {
502
+ const toasts = Array.from(this.container.querySelectorAll('.rojeru-toast'));
503
+ if (toasts.length > 0) {
504
+ this.hide(toasts[toasts.length - 1]);
505
+ }
506
+ }
507
+
508
+ hideByType(type) {
509
+ const toasts = this.container.querySelectorAll(`.rojeru-toast.${type}`);
510
+ toasts.forEach(toast => {
511
+ this.hide(toast);
512
+ });
513
+ }
514
+
515
+ info(message, options = {}) {
516
+ return this.show(null, message, { ...options, type: 'info' });
517
+ }
518
+
519
+ success(message, options = {}) {
520
+ return this.show(null, message, { ...options, type: 'success' });
521
+ }
522
+
523
+ warning(message, options = {}) {
524
+ return this.show(null, message, { ...options, type: 'warning' });
525
+ }
526
+
527
+ error(message, options = {}) {
528
+ return this.show(null, message, { ...options, type: 'error' });
529
+ }
530
+
531
+ loading(message, options = {}) {
532
+ const baseType = options.loadingType || 'info';
533
+
534
+ return this.show(null, message, {
535
+ ...options,
536
+ type: 'loading',
537
+ loadingType: baseType,
538
+ dismissible: false,
539
+ duration: 0
540
+ });
541
+ }
542
+
543
+ withTitle(title, message, options = {}) {
544
+ return this.show(title, message, options);
545
+ }
546
+
547
+ clear() {
548
+ const toasts = this.container.querySelectorAll('.rojeru-toast');
549
+ toasts.forEach(toast => {
550
+ this.hide(toast);
551
+ });
552
+ this.toastInstances.clear();
553
+ }
554
+ }
555
+
556
+ // Exportaciones para diferentes entornos
557
+ export default RojeruToast;
558
+
559
+ // Para uso global en navegador
560
+ if (typeof window !== 'undefined') {
561
+ window.RojeruToast = RojeruToast;
562
+ window.rojeruToast = new RojeruToast();
563
+ }
564
+
565
+ // Para CommonJS/Node.js
566
+ if (typeof module !== 'undefined' && module.exports) {
567
+ module.exports = RojeruToast;
568
+ module.exports.default = RojeruToast;
569
+ }