ga-toasts 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/.htaccess +75 -0
- package/README.md +554 -0
- package/REDIRECT_SETUP.md +149 -0
- package/ga-toasts.d.ts +99 -0
- package/images/gennie_logo.png +0 -0
- package/index.html +1034 -0
- package/package.json +23 -0
- package/robots.txt +54 -0
- package/sitemap.xml +47 -0
- package/src/demo.css +698 -0
- package/src/demo.js +467 -0
- package/src/toasts.css +1644 -0
- package/src/toasts.js +1005 -0
- package/src/variables.css +480 -0
- package/vercel.json +50 -0
package/src/toasts.js
ADDED
|
@@ -0,0 +1,1005 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Toast component JavaScript for Genie AI Plugin
|
|
3
|
+
* Vanilla JS implementation - No jQuery dependency
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
(function () {
|
|
7
|
+
'use strict';
|
|
8
|
+
|
|
9
|
+
// Simple utility object for internal helpers (no global namespace)
|
|
10
|
+
const GaToastsUtils = {
|
|
11
|
+
generateId: function() {
|
|
12
|
+
return 'toast_' + Math.random().toString(36).substr(2, 9);
|
|
13
|
+
},
|
|
14
|
+
log: function(message, data) {
|
|
15
|
+
if (console && console.log) {
|
|
16
|
+
// console.log('[GaToasts]', message, data || '');
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
};
|
|
20
|
+
|
|
21
|
+
// Optional external logger hook
|
|
22
|
+
let GaToastsLogger = null;
|
|
23
|
+
|
|
24
|
+
function logEvent(eventName, payload) {
|
|
25
|
+
GaToastsUtils.log(eventName, payload);
|
|
26
|
+
if (typeof GaToastsLogger === 'function') {
|
|
27
|
+
try {
|
|
28
|
+
GaToastsLogger(eventName, payload);
|
|
29
|
+
} catch (e) {
|
|
30
|
+
// Swallow logger errors to avoid breaking the app
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
// Helper functions
|
|
36
|
+
const helpers = {
|
|
37
|
+
// Query selector wrapper
|
|
38
|
+
qs: function(selector, context) {
|
|
39
|
+
return (context || document).querySelector(selector);
|
|
40
|
+
},
|
|
41
|
+
|
|
42
|
+
// Query selector all wrapper
|
|
43
|
+
qsa: function(selector, context) {
|
|
44
|
+
return Array.from((context || document).querySelectorAll(selector));
|
|
45
|
+
},
|
|
46
|
+
|
|
47
|
+
// Add event listener with delegation
|
|
48
|
+
on: function(element, event, selector, handler) {
|
|
49
|
+
if (typeof selector === 'function') {
|
|
50
|
+
handler = selector;
|
|
51
|
+
element.addEventListener(event, handler);
|
|
52
|
+
} else {
|
|
53
|
+
element.addEventListener(event, function(e) {
|
|
54
|
+
const target = e.target.closest(selector);
|
|
55
|
+
if (target) {
|
|
56
|
+
handler.call(target, e);
|
|
57
|
+
}
|
|
58
|
+
});
|
|
59
|
+
}
|
|
60
|
+
},
|
|
61
|
+
|
|
62
|
+
// Remove event listener
|
|
63
|
+
off: function(element, event, handler) {
|
|
64
|
+
element.removeEventListener(event, handler);
|
|
65
|
+
},
|
|
66
|
+
|
|
67
|
+
// Add class
|
|
68
|
+
addClass: function(element, className) {
|
|
69
|
+
if (!element) return;
|
|
70
|
+
const classes = className.split(' ');
|
|
71
|
+
element.classList.add(...classes);
|
|
72
|
+
},
|
|
73
|
+
|
|
74
|
+
// Remove class
|
|
75
|
+
removeClass: function(element, className) {
|
|
76
|
+
if (!element) return;
|
|
77
|
+
const classes = className.split(' ');
|
|
78
|
+
element.classList.remove(...classes);
|
|
79
|
+
},
|
|
80
|
+
|
|
81
|
+
// Has class
|
|
82
|
+
hasClass: function(element, className) {
|
|
83
|
+
return element && element.classList.contains(className);
|
|
84
|
+
},
|
|
85
|
+
|
|
86
|
+
// Get/Set data attribute
|
|
87
|
+
data: function(element, key, value) {
|
|
88
|
+
if (value === undefined) {
|
|
89
|
+
return element.dataset[key];
|
|
90
|
+
}
|
|
91
|
+
element.dataset[key] = value;
|
|
92
|
+
},
|
|
93
|
+
|
|
94
|
+
// Get/Set attribute
|
|
95
|
+
attr: function(element, key, value) {
|
|
96
|
+
if (value === undefined) {
|
|
97
|
+
return element.getAttribute(key);
|
|
98
|
+
}
|
|
99
|
+
element.setAttribute(key, value);
|
|
100
|
+
},
|
|
101
|
+
|
|
102
|
+
// Remove element
|
|
103
|
+
remove: function(element) {
|
|
104
|
+
if (element && element.parentNode) {
|
|
105
|
+
element.parentNode.removeChild(element);
|
|
106
|
+
}
|
|
107
|
+
},
|
|
108
|
+
|
|
109
|
+
// Trigger custom event
|
|
110
|
+
trigger: function(element, eventName, detail) {
|
|
111
|
+
const event = new CustomEvent(eventName, {
|
|
112
|
+
detail: detail,
|
|
113
|
+
bubbles: true,
|
|
114
|
+
cancelable: true
|
|
115
|
+
});
|
|
116
|
+
element.dispatchEvent(event);
|
|
117
|
+
},
|
|
118
|
+
|
|
119
|
+
// Extend objects (simple deep merge)
|
|
120
|
+
extend: function() {
|
|
121
|
+
const extended = {};
|
|
122
|
+
const deep = arguments[0] === true;
|
|
123
|
+
const start = deep ? 1 : 0;
|
|
124
|
+
|
|
125
|
+
for (let i = start; i < arguments.length; i++) {
|
|
126
|
+
const obj = arguments[i];
|
|
127
|
+
if (!obj) continue;
|
|
128
|
+
|
|
129
|
+
for (let key in obj) {
|
|
130
|
+
if (obj.hasOwnProperty(key)) {
|
|
131
|
+
if (deep && typeof obj[key] === 'object' && !Array.isArray(obj[key])) {
|
|
132
|
+
extended[key] = this.extend(true, extended[key] || {}, obj[key]);
|
|
133
|
+
} else {
|
|
134
|
+
extended[key] = obj[key];
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
return extended;
|
|
140
|
+
},
|
|
141
|
+
|
|
142
|
+
// Create element from HTML string
|
|
143
|
+
createElement: function(html) {
|
|
144
|
+
const template = document.createElement('template');
|
|
145
|
+
template.innerHTML = html.trim();
|
|
146
|
+
return template.content.firstChild;
|
|
147
|
+
}
|
|
148
|
+
};
|
|
149
|
+
|
|
150
|
+
// Toast component
|
|
151
|
+
const Toast = {
|
|
152
|
+
/**
|
|
153
|
+
* Initialize toasts
|
|
154
|
+
*/
|
|
155
|
+
init: function () {
|
|
156
|
+
this.bindEvents();
|
|
157
|
+
this.initDefaultContainer();
|
|
158
|
+
},
|
|
159
|
+
|
|
160
|
+
/**
|
|
161
|
+
* Bind events
|
|
162
|
+
*/
|
|
163
|
+
bindEvents: function () {
|
|
164
|
+
const self = this;
|
|
165
|
+
|
|
166
|
+
// Close toast triggers
|
|
167
|
+
helpers.on(document, 'click', '[data-ga-toast-close], .ga-toast-close', function (e) {
|
|
168
|
+
e.preventDefault();
|
|
169
|
+
const toastId = helpers.data(this, 'gaToastClose');
|
|
170
|
+
if (toastId) {
|
|
171
|
+
self.close(helpers.qs('#' + toastId));
|
|
172
|
+
} else {
|
|
173
|
+
self.close(this.closest('.ga-toast'));
|
|
174
|
+
}
|
|
175
|
+
});
|
|
176
|
+
|
|
177
|
+
// Auto-close toasts with pause on hover
|
|
178
|
+
helpers.on(document, 'ga:toast:shown', '.ga-toast', function (e) {
|
|
179
|
+
const toast = e.target;
|
|
180
|
+
const autoClose = parseInt(helpers.data(toast, 'gaAutoClose'));
|
|
181
|
+
const pauseOnHover = helpers.data(toast, 'gaPauseOnHover') === 'true';
|
|
182
|
+
const progress = helpers.qs('.ga-toast-progress', toast);
|
|
183
|
+
|
|
184
|
+
if (autoClose && autoClose > 0) {
|
|
185
|
+
let timeoutId;
|
|
186
|
+
let startTime = Date.now();
|
|
187
|
+
let remainingTime = autoClose;
|
|
188
|
+
|
|
189
|
+
const startCountdown = function() {
|
|
190
|
+
startTime = Date.now();
|
|
191
|
+
timeoutId = setTimeout(function () {
|
|
192
|
+
if (toast && !helpers.hasClass(toast, 'hide')) {
|
|
193
|
+
self.close(toast);
|
|
194
|
+
}
|
|
195
|
+
}, remainingTime);
|
|
196
|
+
};
|
|
197
|
+
|
|
198
|
+
// Start initial countdown
|
|
199
|
+
startCountdown();
|
|
200
|
+
|
|
201
|
+
// Pause on hover if enabled
|
|
202
|
+
if (pauseOnHover) {
|
|
203
|
+
toast.addEventListener('mouseenter', function() {
|
|
204
|
+
const elapsed = Date.now() - startTime;
|
|
205
|
+
remainingTime = Math.max(0, remainingTime - elapsed);
|
|
206
|
+
clearTimeout(timeoutId);
|
|
207
|
+
helpers.addClass(toast, 'ga-toast-paused');
|
|
208
|
+
|
|
209
|
+
// Pause progress bar animation
|
|
210
|
+
if (progress) {
|
|
211
|
+
const currentTransform = getComputedStyle(progress).transform;
|
|
212
|
+
progress.style.transition = 'none';
|
|
213
|
+
helpers.attr(progress, 'data-paused-transform', currentTransform);
|
|
214
|
+
}
|
|
215
|
+
});
|
|
216
|
+
|
|
217
|
+
toast.addEventListener('mouseleave', function() {
|
|
218
|
+
helpers.removeClass(toast, 'ga-toast-paused');
|
|
219
|
+
|
|
220
|
+
// Resume progress bar animation
|
|
221
|
+
if (progress) {
|
|
222
|
+
progress.style.transition = 'transform ' + remainingTime + 'ms linear';
|
|
223
|
+
progress.style.transform = 'scaleX(0)';
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
startCountdown();
|
|
227
|
+
});
|
|
228
|
+
}
|
|
229
|
+
}
|
|
230
|
+
});
|
|
231
|
+
|
|
232
|
+
// Handle toast click to close (optional)
|
|
233
|
+
helpers.on(document, 'click', '.ga-toast', function (e) {
|
|
234
|
+
if (helpers.attr(this, 'data-ga-click-to-close') === 'true') {
|
|
235
|
+
if (!e.target.closest('.ga-toast-actions, .ga-toast-close')) {
|
|
236
|
+
self.close(this);
|
|
237
|
+
}
|
|
238
|
+
}
|
|
239
|
+
});
|
|
240
|
+
},
|
|
241
|
+
|
|
242
|
+
/**
|
|
243
|
+
* Initialize default container
|
|
244
|
+
*/
|
|
245
|
+
initDefaultContainer: function () {
|
|
246
|
+
if (helpers.qs('.ga-container')) {
|
|
247
|
+
// Create default container if it doesn't exist
|
|
248
|
+
this.getContainer('top-end');
|
|
249
|
+
}
|
|
250
|
+
},
|
|
251
|
+
|
|
252
|
+
/**
|
|
253
|
+
* Show toast
|
|
254
|
+
*/
|
|
255
|
+
show: function (options) {
|
|
256
|
+
const defaults = {
|
|
257
|
+
id: 'ga-toast-' + GaToastsUtils.generateId(),
|
|
258
|
+
title: '',
|
|
259
|
+
message: '',
|
|
260
|
+
meta: '',
|
|
261
|
+
type: 'info',
|
|
262
|
+
duration: 5000,
|
|
263
|
+
closable: true,
|
|
264
|
+
position: 'top-end',
|
|
265
|
+
icon: null,
|
|
266
|
+
actions: [],
|
|
267
|
+
size: '',
|
|
268
|
+
variant: '', // '', 'filled', 'light'
|
|
269
|
+
animation: 'slide', // 'fade', 'slide', 'bounce', 'scale'
|
|
270
|
+
clickToClose: false,
|
|
271
|
+
swipeToClose: true,
|
|
272
|
+
// Media / visual
|
|
273
|
+
avatar: null,
|
|
274
|
+
avatarAlt: '',
|
|
275
|
+
unread: false,
|
|
276
|
+
truncateTitle: false,
|
|
277
|
+
progress: true,
|
|
278
|
+
progressBackground: true,
|
|
279
|
+
pauseOnHover: true,
|
|
280
|
+
// UI niceties
|
|
281
|
+
compact: false,
|
|
282
|
+
showStatus: false,
|
|
283
|
+
statusText: '',
|
|
284
|
+
autoIcon: true
|
|
285
|
+
};
|
|
286
|
+
|
|
287
|
+
// Merge instance defaults (set via setDefaults) with per-call options
|
|
288
|
+
const mergedOptions = this.getMergedOptions
|
|
289
|
+
? this.getMergedOptions(options || {})
|
|
290
|
+
: (options || {});
|
|
291
|
+
|
|
292
|
+
const settings = helpers.extend({}, defaults, mergedOptions);
|
|
293
|
+
|
|
294
|
+
// Compact toasts: default to click-to-close if not explicitly set
|
|
295
|
+
if (settings.compact && mergedOptions && mergedOptions.clickToClose == null) {
|
|
296
|
+
settings.clickToClose = true;
|
|
297
|
+
}
|
|
298
|
+
|
|
299
|
+
const toast = this.create(settings);
|
|
300
|
+
|
|
301
|
+
// Enable swipe-to-close interaction if configured
|
|
302
|
+
if (settings.swipeToClose) {
|
|
303
|
+
this.attachSwipeHandlers(toast);
|
|
304
|
+
}
|
|
305
|
+
const container = this.getContainer(settings.position);
|
|
306
|
+
|
|
307
|
+
container.appendChild(toast);
|
|
308
|
+
|
|
309
|
+
// Trigger animation after a small delay to ensure DOM is ready
|
|
310
|
+
setTimeout(function () {
|
|
311
|
+
helpers.addClass(toast, 'show');
|
|
312
|
+
helpers.trigger(toast, 'ga:toast:shown');
|
|
313
|
+
}, 10);
|
|
314
|
+
|
|
315
|
+
logEvent('toast:shown', settings);
|
|
316
|
+
|
|
317
|
+
return toast;
|
|
318
|
+
},
|
|
319
|
+
|
|
320
|
+
/**
|
|
321
|
+
* Create toast element
|
|
322
|
+
*/
|
|
323
|
+
create: function (options) {
|
|
324
|
+
// Ensure options is an object
|
|
325
|
+
options = options || {};
|
|
326
|
+
|
|
327
|
+
const classes = ['ga-toast', 'ga-toast-' + options.type];
|
|
328
|
+
|
|
329
|
+
// Add size class
|
|
330
|
+
if (options.size) {
|
|
331
|
+
classes.push('ga-toast-' + options.size);
|
|
332
|
+
}
|
|
333
|
+
|
|
334
|
+
// Add variant class
|
|
335
|
+
if (options.variant) {
|
|
336
|
+
classes.push('ga-toast-' + options.type + '-' + options.variant);
|
|
337
|
+
}
|
|
338
|
+
|
|
339
|
+
// Add animation class
|
|
340
|
+
if (options.animation) {
|
|
341
|
+
classes.push(options.animation);
|
|
342
|
+
}
|
|
343
|
+
|
|
344
|
+
// Compact density
|
|
345
|
+
if (options.compact) {
|
|
346
|
+
classes.push('ga-toast-compact');
|
|
347
|
+
}
|
|
348
|
+
|
|
349
|
+
// Add modern styling classes
|
|
350
|
+
classes.push('ga-toast-modern');
|
|
351
|
+
|
|
352
|
+
// Add glassmorphism effect
|
|
353
|
+
if (options.glassmorphism !== false) {
|
|
354
|
+
classes.push('ga-toast-glass');
|
|
355
|
+
}
|
|
356
|
+
|
|
357
|
+
const toast = document.createElement('div');
|
|
358
|
+
toast.className = classes.join(' ');
|
|
359
|
+
toast.id = options.id;
|
|
360
|
+
|
|
361
|
+
// Add click to close attribute
|
|
362
|
+
if (options.clickToClose) {
|
|
363
|
+
helpers.attr(toast, 'data-ga-click-to-close', 'true');
|
|
364
|
+
}
|
|
365
|
+
|
|
366
|
+
// Add pause on hover attribute
|
|
367
|
+
if (options.pauseOnHover) {
|
|
368
|
+
helpers.attr(toast, 'data-ga-pause-on-hover', 'true');
|
|
369
|
+
}
|
|
370
|
+
|
|
371
|
+
// Create content wrapper
|
|
372
|
+
const content = document.createElement('div');
|
|
373
|
+
content.className = 'ga-toast-content';
|
|
374
|
+
|
|
375
|
+
// Add header if title, closable, or icon exists
|
|
376
|
+
if (options.title || options.closable || options.icon) {
|
|
377
|
+
const header = document.createElement('div');
|
|
378
|
+
header.className = 'ga-toast-header';
|
|
379
|
+
|
|
380
|
+
// Left section (avatar + icon + title)
|
|
381
|
+
const left = document.createElement('div');
|
|
382
|
+
left.className = 'ga-toast-header-left';
|
|
383
|
+
|
|
384
|
+
// Optional avatar (URL string)
|
|
385
|
+
if (options.avatar) {
|
|
386
|
+
const avatar = document.createElement('img');
|
|
387
|
+
avatar.className = 'ga-toast-avatar';
|
|
388
|
+
avatar.src = options.avatar;
|
|
389
|
+
if (options.avatarAlt) {
|
|
390
|
+
avatar.alt = options.avatarAlt;
|
|
391
|
+
} else if (options.title) {
|
|
392
|
+
avatar.alt = options.title;
|
|
393
|
+
} else {
|
|
394
|
+
avatar.alt = 'Notification avatar';
|
|
395
|
+
}
|
|
396
|
+
left.appendChild(avatar);
|
|
397
|
+
}
|
|
398
|
+
|
|
399
|
+
// Auto icon support
|
|
400
|
+
let resolvedIcon = options.icon;
|
|
401
|
+
if (!resolvedIcon && options.autoIcon !== false) {
|
|
402
|
+
const type = options.type || 'info';
|
|
403
|
+
const iconMap = {
|
|
404
|
+
success: '<svg width="20" height="20" viewBox="0 0 20 20" fill="currentColor"><path fill-rule="evenodd" d="M10 18a8 8 0 100-16 8 8 0 000 16zm3.707-9.293a1 1 0 00-1.414-1.414L9 10.586 7.707 9.293a1 1 0 00-1.414 1.414l2 2a1 1 0 001.414 0l4-4z" clip-rule="evenodd"></path></svg>',
|
|
405
|
+
error: '<svg width="20" height="20" viewBox="0 0 20 20" fill="currentColor"><path fill-rule="evenodd" d="M10 18a8 8 0 100-16 8 8 0 000 16zM8.707 7.293a1 1 0 00-1.414 1.414L8.586 10l-1.293 1.293a1 1 0 101.414 1.414L10 11.414l1.293 1.293a1 1 0 001.414-1.414L11.414 10l1.293-1.293a1 1 0 00-1.414-1.414L10 8.586 8.707 7.293z" clip-rule="evenodd"></path></svg>',
|
|
406
|
+
warning: '<svg width="20" height="20" viewBox="0 0 20 20" fill="currentColor"><path fill-rule="evenodd" d="M8.257 3.099c.765-1.36 2.722-1.36 3.486 0l5.58 9.92c.75 1.334-.213 2.98-1.742 2.98H4.42c-1.53 0-2.493-1.646-1.743-2.98l5.58-9.92zM11 13a1 1 0 11-2 0 1 1 0 012 0zm-1-8a1 1 0 00-1 1v3a1 1 0 002 0V6a1 1 0 00-1-1z" clip-rule="evenodd"></path></svg>',
|
|
407
|
+
info: '<svg width="20" height="20" viewBox="0 0 20 20" fill="currentColor"><path fill-rule="evenodd" d="M18 10a8 8 0 11-16 0 8 8 0 0116 0zm-7-4a1 1 0 11-2 0 1 1 0 012 0zM9 9a1 1 0 000 2v3a1 1 0 001 1h1a1 1 0 100-2v-3a1 1 0 00-1-1H9z" clip-rule="evenodd"></path></svg>',
|
|
408
|
+
primary: '<svg width="20" height="20" viewBox="0 0 20 20" fill="currentColor"><path d="M4 4h12v12H4z" opacity="0.2"></path><path d="M5 5h10v10H5z"></path></svg>',
|
|
409
|
+
secondary: '<svg width="20" height="20" viewBox="0 0 20 20" fill="currentColor"><circle cx="10" cy="10" r="6"></circle></svg>'
|
|
410
|
+
};
|
|
411
|
+
resolvedIcon = iconMap[type] || null;
|
|
412
|
+
}
|
|
413
|
+
|
|
414
|
+
if (resolvedIcon) {
|
|
415
|
+
const iconDiv = document.createElement('div');
|
|
416
|
+
iconDiv.className = 'ga-toast-icon';
|
|
417
|
+
iconDiv.innerHTML = resolvedIcon;
|
|
418
|
+
left.appendChild(iconDiv);
|
|
419
|
+
}
|
|
420
|
+
|
|
421
|
+
// Optional unread dot
|
|
422
|
+
if (options.unread) {
|
|
423
|
+
const unreadDot = document.createElement('span');
|
|
424
|
+
unreadDot.className = 'ga-toast-unread-dot';
|
|
425
|
+
left.appendChild(unreadDot);
|
|
426
|
+
}
|
|
427
|
+
|
|
428
|
+
if (options.title) {
|
|
429
|
+
const title = document.createElement('h4');
|
|
430
|
+
title.className = 'ga-toast-title';
|
|
431
|
+
title.textContent = options.title;
|
|
432
|
+
if (options.truncateTitle) {
|
|
433
|
+
title.classList.add('ga-toast-title-truncate');
|
|
434
|
+
}
|
|
435
|
+
left.appendChild(title);
|
|
436
|
+
}
|
|
437
|
+
|
|
438
|
+
// Optional status label
|
|
439
|
+
if (options.showStatus && options.type) {
|
|
440
|
+
const status = document.createElement('span');
|
|
441
|
+
status.className = 'ga-toast-status';
|
|
442
|
+
const label = (options.statusText || options.type)
|
|
443
|
+
.toString()
|
|
444
|
+
.replace(/^\w/, c => c.toUpperCase());
|
|
445
|
+
status.textContent = label;
|
|
446
|
+
header.appendChild(status);
|
|
447
|
+
}
|
|
448
|
+
|
|
449
|
+
header.appendChild(left);
|
|
450
|
+
|
|
451
|
+
// Right section (close button)
|
|
452
|
+
if (options.closable) {
|
|
453
|
+
const closeBtn = document.createElement('button');
|
|
454
|
+
closeBtn.type = 'button';
|
|
455
|
+
closeBtn.className = 'ga-toast-close';
|
|
456
|
+
helpers.attr(closeBtn, 'aria-label', 'Close');
|
|
457
|
+
helpers.attr(closeBtn, 'data-ga-toast-close', '');
|
|
458
|
+
header.appendChild(closeBtn);
|
|
459
|
+
}
|
|
460
|
+
|
|
461
|
+
content.appendChild(header);
|
|
462
|
+
}
|
|
463
|
+
|
|
464
|
+
// Add body if there is message or meta
|
|
465
|
+
// For compact toasts we still render the content, but keep the layout
|
|
466
|
+
// tight via CSS (see `.ga-toast-compact .ga-toast-body`).
|
|
467
|
+
if (options.message || options.meta) {
|
|
468
|
+
const body = document.createElement('div');
|
|
469
|
+
body.className = 'ga-toast-body';
|
|
470
|
+
|
|
471
|
+
if (options.meta) {
|
|
472
|
+
const meta = document.createElement('div');
|
|
473
|
+
meta.className = 'ga-toast-meta';
|
|
474
|
+
meta.textContent = options.meta;
|
|
475
|
+
body.appendChild(meta);
|
|
476
|
+
}
|
|
477
|
+
|
|
478
|
+
if (options.message) {
|
|
479
|
+
const messageEl = document.createElement('div');
|
|
480
|
+
messageEl.className = 'ga-toast-message';
|
|
481
|
+
messageEl.innerHTML = options.message;
|
|
482
|
+
body.appendChild(messageEl);
|
|
483
|
+
}
|
|
484
|
+
|
|
485
|
+
content.appendChild(body);
|
|
486
|
+
}
|
|
487
|
+
|
|
488
|
+
// Add actions if they exist (skip for compact toasts)
|
|
489
|
+
if (!options.compact && options.actions && options.actions.length > 0) {
|
|
490
|
+
const actionsDiv = document.createElement('div');
|
|
491
|
+
actionsDiv.className = 'ga-toast-actions';
|
|
492
|
+
const self = this;
|
|
493
|
+
|
|
494
|
+
options.actions.forEach(function (action) {
|
|
495
|
+
const btnClasses = ['ga-btn', 'ga-btn-sm'];
|
|
496
|
+
if (action.class) {
|
|
497
|
+
btnClasses.push(action.class);
|
|
498
|
+
} else {
|
|
499
|
+
btnClasses.push('ga-btn-secondary');
|
|
500
|
+
}
|
|
501
|
+
|
|
502
|
+
const btn = document.createElement('button');
|
|
503
|
+
btn.type = 'button';
|
|
504
|
+
btn.className = btnClasses.join(' ');
|
|
505
|
+
btn.textContent = action.text;
|
|
506
|
+
|
|
507
|
+
if (action.click) {
|
|
508
|
+
btn.addEventListener('click', function (e) {
|
|
509
|
+
action.click(e, toast);
|
|
510
|
+
});
|
|
511
|
+
}
|
|
512
|
+
|
|
513
|
+
actionsDiv.appendChild(btn);
|
|
514
|
+
});
|
|
515
|
+
|
|
516
|
+
content.appendChild(actionsDiv);
|
|
517
|
+
}
|
|
518
|
+
|
|
519
|
+
toast.appendChild(content);
|
|
520
|
+
|
|
521
|
+
// Add progress bar if enabled
|
|
522
|
+
if (options.progress && options.duration && options.duration > 0 && options.progressPosition !== 'none') {
|
|
523
|
+
const progress = document.createElement('div');
|
|
524
|
+
progress.className = 'ga-toast-progress ga-toast-progress-' + options.type;
|
|
525
|
+
if (options.progressPosition === 'top') {
|
|
526
|
+
helpers.addClass(progress, 'ga-toast-progress-top');
|
|
527
|
+
}
|
|
528
|
+
progress.style.width = '100%';
|
|
529
|
+
progress.style.transform = 'scaleX(1)';
|
|
530
|
+
progress.style.transformOrigin = 'left center';
|
|
531
|
+
toast.appendChild(progress);
|
|
532
|
+
|
|
533
|
+
// Animate progress bar with smooth countdown
|
|
534
|
+
setTimeout(function () {
|
|
535
|
+
progress.style.transition = 'transform ' + options.duration + 'ms linear';
|
|
536
|
+
progress.style.transform = 'scaleX(0)';
|
|
537
|
+
}, 10);
|
|
538
|
+
}
|
|
539
|
+
|
|
540
|
+
// Add background fill with opacity if enabled
|
|
541
|
+
if (options.progressBackground && options.duration && options.duration > 0) {
|
|
542
|
+
const backgroundFill = document.createElement('div');
|
|
543
|
+
backgroundFill.className = 'ga-toast-background-fill ga-toast-bg-' + options.type;
|
|
544
|
+
toast.appendChild(backgroundFill);
|
|
545
|
+
|
|
546
|
+
// Animate background fill
|
|
547
|
+
setTimeout(function () {
|
|
548
|
+
backgroundFill.style.transition = 'opacity ' + options.duration + 'ms linear';
|
|
549
|
+
backgroundFill.style.opacity = '0';
|
|
550
|
+
}, 10);
|
|
551
|
+
}
|
|
552
|
+
|
|
553
|
+
// Segmented steps indicator for multi-step flows
|
|
554
|
+
if (options.steps && options.steps > 1) {
|
|
555
|
+
const stepsContainer = document.createElement('div');
|
|
556
|
+
stepsContainer.className = 'ga-toast-steps';
|
|
557
|
+
const activeIndex = Math.min(
|
|
558
|
+
options.steps,
|
|
559
|
+
Math.max(1, options.currentStep || 1)
|
|
560
|
+
);
|
|
561
|
+
for (let i = 1; i <= options.steps; i++) {
|
|
562
|
+
const stepEl = document.createElement('div');
|
|
563
|
+
stepEl.className = 'ga-toast-step';
|
|
564
|
+
if (i <= activeIndex) {
|
|
565
|
+
helpers.addClass(stepEl, 'ga-toast-step-active');
|
|
566
|
+
}
|
|
567
|
+
stepsContainer.appendChild(stepEl);
|
|
568
|
+
}
|
|
569
|
+
content.appendChild(stepsContainer);
|
|
570
|
+
}
|
|
571
|
+
|
|
572
|
+
// Add auto-close data
|
|
573
|
+
if (options.duration && options.duration > 0) {
|
|
574
|
+
helpers.data(toast, 'gaAutoClose', options.duration.toString());
|
|
575
|
+
}
|
|
576
|
+
|
|
577
|
+
return toast;
|
|
578
|
+
},
|
|
579
|
+
|
|
580
|
+
/**
|
|
581
|
+
* Attach swipe-to-close interaction (primarily for touch devices)
|
|
582
|
+
*/
|
|
583
|
+
attachSwipeHandlers: function (toast) {
|
|
584
|
+
if (!toast) return;
|
|
585
|
+
|
|
586
|
+
const self = this;
|
|
587
|
+
let pointerDown = false;
|
|
588
|
+
let startX = 0;
|
|
589
|
+
let startY = 0;
|
|
590
|
+
let currentX = 0;
|
|
591
|
+
let startTime = 0;
|
|
592
|
+
|
|
593
|
+
const SWIPE_DISTANCE = 60;
|
|
594
|
+
|
|
595
|
+
function onPointerDown(e) {
|
|
596
|
+
if (e.pointerType === 'mouse' && e.button !== 0) return;
|
|
597
|
+
pointerDown = true;
|
|
598
|
+
startX = e.clientX;
|
|
599
|
+
startY = e.clientY;
|
|
600
|
+
currentX = startX;
|
|
601
|
+
startTime = Date.now();
|
|
602
|
+
toast.setPointerCapture && toast.setPointerCapture(e.pointerId);
|
|
603
|
+
}
|
|
604
|
+
|
|
605
|
+
function onPointerMove(e) {
|
|
606
|
+
if (!pointerDown) return;
|
|
607
|
+
currentX = e.clientX;
|
|
608
|
+
const dx = currentX - startX;
|
|
609
|
+
const dy = e.clientY - startY;
|
|
610
|
+
|
|
611
|
+
// Only treat mostly horizontal movement as swipe
|
|
612
|
+
if (Math.abs(dx) < 5 || Math.abs(dx) < Math.abs(dy)) {
|
|
613
|
+
return;
|
|
614
|
+
}
|
|
615
|
+
|
|
616
|
+
e.preventDefault();
|
|
617
|
+
|
|
618
|
+
const translateX = dx;
|
|
619
|
+
const opacity = Math.max(0.2, 1 - Math.abs(dx) / 200);
|
|
620
|
+
toast.style.transform = 'translateX(' + translateX + 'px)';
|
|
621
|
+
toast.style.opacity = String(opacity);
|
|
622
|
+
}
|
|
623
|
+
|
|
624
|
+
function onPointerUp(e) {
|
|
625
|
+
if (!pointerDown) return;
|
|
626
|
+
pointerDown = false;
|
|
627
|
+
|
|
628
|
+
const dx = currentX - startX;
|
|
629
|
+
const dy = e.clientY - startY;
|
|
630
|
+
const elapsed = Date.now() - startTime;
|
|
631
|
+
|
|
632
|
+
const isHorizontal = Math.abs(dx) > Math.abs(dy);
|
|
633
|
+
const passedDistance = Math.abs(dx) > SWIPE_DISTANCE;
|
|
634
|
+
|
|
635
|
+
// Reset visual state
|
|
636
|
+
toast.style.transform = '';
|
|
637
|
+
toast.style.opacity = '';
|
|
638
|
+
|
|
639
|
+
if (isHorizontal && passedDistance) {
|
|
640
|
+
self.close(toast);
|
|
641
|
+
}
|
|
642
|
+
}
|
|
643
|
+
|
|
644
|
+
toast.addEventListener('pointerdown', onPointerDown);
|
|
645
|
+
toast.addEventListener('pointermove', onPointerMove);
|
|
646
|
+
toast.addEventListener('pointerup', onPointerUp);
|
|
647
|
+
toast.addEventListener('pointercancel', onPointerUp);
|
|
648
|
+
},
|
|
649
|
+
|
|
650
|
+
/**
|
|
651
|
+
* Close toast
|
|
652
|
+
*/
|
|
653
|
+
close: function (toast) {
|
|
654
|
+
if (!toast || helpers.hasClass(toast, 'hide')) {
|
|
655
|
+
return;
|
|
656
|
+
}
|
|
657
|
+
|
|
658
|
+
// Remove any active event listeners
|
|
659
|
+
const newToast = toast.cloneNode(true);
|
|
660
|
+
toast.parentNode.replaceChild(newToast, toast);
|
|
661
|
+
toast = newToast;
|
|
662
|
+
|
|
663
|
+
helpers.removeClass(toast, 'show');
|
|
664
|
+
helpers.addClass(toast, 'hide');
|
|
665
|
+
|
|
666
|
+
const removeDelay = 300; // Match CSS transition duration
|
|
667
|
+
|
|
668
|
+
setTimeout(function () {
|
|
669
|
+
if (toast) {
|
|
670
|
+
helpers.trigger(toast, 'ga:toast:closed');
|
|
671
|
+
helpers.remove(toast);
|
|
672
|
+
}
|
|
673
|
+
}, removeDelay);
|
|
674
|
+
|
|
675
|
+
logEvent('toast:closed', toast.id);
|
|
676
|
+
},
|
|
677
|
+
|
|
678
|
+
/**
|
|
679
|
+
* Close all toasts
|
|
680
|
+
*/
|
|
681
|
+
closeAll: function () {
|
|
682
|
+
const self = this;
|
|
683
|
+
helpers.qsa('.ga-toast').forEach(function (toast) {
|
|
684
|
+
self.close(toast);
|
|
685
|
+
});
|
|
686
|
+
},
|
|
687
|
+
|
|
688
|
+
/**
|
|
689
|
+
* Get or create container by position
|
|
690
|
+
*/
|
|
691
|
+
getContainer: function (position) {
|
|
692
|
+
const containerId = 'ga-toast-container-' + position;
|
|
693
|
+
let container = helpers.qs('#' + containerId);
|
|
694
|
+
|
|
695
|
+
if (!container) {
|
|
696
|
+
container = document.createElement('div');
|
|
697
|
+
container.id = containerId;
|
|
698
|
+
container.className = 'ga-toast-container ga-toast-container-' + position;
|
|
699
|
+
|
|
700
|
+
// Try to append to ga-container first, then body
|
|
701
|
+
const gaContainer = helpers.qs('.ga-container');
|
|
702
|
+
if (gaContainer) {
|
|
703
|
+
gaContainer.appendChild(container);
|
|
704
|
+
} else {
|
|
705
|
+
document.body.appendChild(container);
|
|
706
|
+
}
|
|
707
|
+
}
|
|
708
|
+
|
|
709
|
+
return container;
|
|
710
|
+
},
|
|
711
|
+
|
|
712
|
+
/**
|
|
713
|
+
* Show success toast
|
|
714
|
+
*/
|
|
715
|
+
success: function (message, options) {
|
|
716
|
+
const settings = helpers.extend({}, options || {}, {
|
|
717
|
+
type: 'success',
|
|
718
|
+
message: message,
|
|
719
|
+
duration: (options && options.duration) || 5000,
|
|
720
|
+
icon: '<svg width="20" height="20" viewBox="0 0 20 20" fill="currentColor"><path fill-rule="evenodd" d="M10 18a8 8 0 100-16 8 8 0 000 16zm3.707-9.293a1 1 0 00-1.414-1.414L9 10.586 7.707 9.293a1 1 0 00-1.414 1.414l2 2a1 1 0 001.414 0l4-4z" clip-rule="evenodd"></path></svg>'
|
|
721
|
+
});
|
|
722
|
+
return this.show(settings);
|
|
723
|
+
},
|
|
724
|
+
|
|
725
|
+
/**
|
|
726
|
+
* Show error toast
|
|
727
|
+
*/
|
|
728
|
+
error: function (message, options) {
|
|
729
|
+
console.log('error', options);
|
|
730
|
+
const settings = helpers.extend({}, options || {}, {
|
|
731
|
+
type: 'error',
|
|
732
|
+
message: message,
|
|
733
|
+
duration: (options && options.duration) || 8000,
|
|
734
|
+
icon: '<svg width="20" height="20" viewBox="0 0 20 20" fill="currentColor"><path fill-rule="evenodd" d="M10 18a8 8 0 100-16 8 8 0 000 16zM8.707 7.293a1 1 0 00-1.414 1.414L8.586 10l-1.293 1.293a1 1 0 101.414 1.414L10 11.414l1.293 1.293a1 1 0 001.414-1.414L11.414 10l1.293-1.293a1 1 0 00-1.414-1.414L10 8.586 8.707 7.293z" clip-rule="evenodd"></path></svg>'
|
|
735
|
+
});
|
|
736
|
+
return this.show(settings);
|
|
737
|
+
},
|
|
738
|
+
|
|
739
|
+
/**
|
|
740
|
+
* Show warning toast
|
|
741
|
+
*/
|
|
742
|
+
warning: function (message, options) {
|
|
743
|
+
const settings = helpers.extend({}, options || {}, {
|
|
744
|
+
type: 'warning',
|
|
745
|
+
message: message,
|
|
746
|
+
duration: (options && options.duration) || 6000,
|
|
747
|
+
icon: '<svg width="20" height="20" viewBox="0 0 20 20" fill="currentColor"><path fill-rule="evenodd" d="M8.257 3.099c.765-1.36 2.722-1.36 3.486 0l5.58 9.92c.75 1.334-.213 2.98-1.742 2.98H4.42c-1.53 0-2.493-1.646-1.743-2.98l5.58-9.92zM11 13a1 1 0 11-2 0 1 1 0 012 0zm-1-8a1 1 0 00-1 1v3a1 1 0 002 0V6a1 1 0 00-1-1z" clip-rule="evenodd"></path></svg>'
|
|
748
|
+
});
|
|
749
|
+
return this.show(settings);
|
|
750
|
+
},
|
|
751
|
+
|
|
752
|
+
/**
|
|
753
|
+
* Show info toast
|
|
754
|
+
*/
|
|
755
|
+
info: function (message, options) {
|
|
756
|
+
const settings = helpers.extend({}, options || {}, {
|
|
757
|
+
type: 'info',
|
|
758
|
+
message: message,
|
|
759
|
+
duration: (options && options.duration) || 4000,
|
|
760
|
+
icon: '<svg width="20" height="20" viewBox="0 0 20 20" fill="currentColor"><path fill-rule="evenodd" d="M18 10a8 8 0 11-16 0 8 8 0 0116 0zm-7-4a1 1 0 11-2 0 1 1 0 012 0zM9 9a1 1 0 000 2v3a1 1 0 001 1h1a1 1 0 100-2v-3a1 1 0 00-1-1H9z" clip-rule="evenodd"></path></svg>'
|
|
761
|
+
});
|
|
762
|
+
return this.show(settings);
|
|
763
|
+
},
|
|
764
|
+
|
|
765
|
+
/**
|
|
766
|
+
* Show confirmation toast
|
|
767
|
+
*/
|
|
768
|
+
confirm: function (message, options) {
|
|
769
|
+
const self = this;
|
|
770
|
+
const defaults = {
|
|
771
|
+
type: 'warning',
|
|
772
|
+
message: message,
|
|
773
|
+
duration: 0,
|
|
774
|
+
closable: false,
|
|
775
|
+
actions: [
|
|
776
|
+
{
|
|
777
|
+
text: 'Cancel',
|
|
778
|
+
class: 'ga-btn-secondary',
|
|
779
|
+
click: function (e, toast) {
|
|
780
|
+
self.close(toast);
|
|
781
|
+
if (options && options.onCancel) {
|
|
782
|
+
options.onCancel();
|
|
783
|
+
}
|
|
784
|
+
}
|
|
785
|
+
},
|
|
786
|
+
{
|
|
787
|
+
text: 'Confirm',
|
|
788
|
+
class: 'ga-btn-primary',
|
|
789
|
+
click: function (e, toast) {
|
|
790
|
+
self.close(toast);
|
|
791
|
+
if (options && options.onConfirm) {
|
|
792
|
+
options.onConfirm();
|
|
793
|
+
}
|
|
794
|
+
}
|
|
795
|
+
}
|
|
796
|
+
]
|
|
797
|
+
};
|
|
798
|
+
|
|
799
|
+
const settings = helpers.extend(true, {}, defaults, options);
|
|
800
|
+
return this.show(settings);
|
|
801
|
+
},
|
|
802
|
+
|
|
803
|
+
/**
|
|
804
|
+
* Show loading toast
|
|
805
|
+
*/
|
|
806
|
+
loading: function (message, options) {
|
|
807
|
+
const defaults = {
|
|
808
|
+
type: 'info',
|
|
809
|
+
message: message || 'Loading...',
|
|
810
|
+
closable: false,
|
|
811
|
+
duration: 0,
|
|
812
|
+
icon: '<svg width="20" height="20" viewBox="0 0 24 24" fill="none"><circle cx="12" cy="12" r="10" stroke="currentColor" stroke-width="2"/><path d="M12 2a10 10 0 0 1 10 10" stroke="currentColor" stroke-width="2" stroke-linecap="round" class="ga-spin"/></svg>'
|
|
813
|
+
};
|
|
814
|
+
|
|
815
|
+
const settings = helpers.extend({}, defaults, options);
|
|
816
|
+
const toast = this.show(settings);
|
|
817
|
+
helpers.addClass(toast, 'ga-toast-loading');
|
|
818
|
+
return toast;
|
|
819
|
+
},
|
|
820
|
+
|
|
821
|
+
/**
|
|
822
|
+
* Update toast content
|
|
823
|
+
*/
|
|
824
|
+
update: function (toastId, options) {
|
|
825
|
+
options = options || {};
|
|
826
|
+
|
|
827
|
+
const toast = helpers.qs('#' + toastId);
|
|
828
|
+
if (!toast) {
|
|
829
|
+
logEvent('toast:update:not-found', toastId);
|
|
830
|
+
return false;
|
|
831
|
+
}
|
|
832
|
+
|
|
833
|
+
if (options.title !== undefined) {
|
|
834
|
+
const title = helpers.qs('.ga-toast-title', toast);
|
|
835
|
+
if (title) {
|
|
836
|
+
title.textContent = options.title;
|
|
837
|
+
} else if (options.title) {
|
|
838
|
+
let header = helpers.qs('.ga-toast-header', toast);
|
|
839
|
+
if (!header) {
|
|
840
|
+
header = document.createElement('div');
|
|
841
|
+
header.className = 'ga-toast-header';
|
|
842
|
+
const content = helpers.qs('.ga-toast-content', toast);
|
|
843
|
+
content.insertBefore(header, content.firstChild);
|
|
844
|
+
}
|
|
845
|
+
const titleEl = document.createElement('h4');
|
|
846
|
+
titleEl.className = 'ga-toast-title';
|
|
847
|
+
titleEl.textContent = options.title;
|
|
848
|
+
header.insertBefore(titleEl, header.firstChild);
|
|
849
|
+
}
|
|
850
|
+
}
|
|
851
|
+
|
|
852
|
+
if (options.message !== undefined) {
|
|
853
|
+
const messageEl = helpers.qs('.ga-toast-body', toast);
|
|
854
|
+
if (messageEl) {
|
|
855
|
+
messageEl.innerHTML = options.message;
|
|
856
|
+
}
|
|
857
|
+
}
|
|
858
|
+
|
|
859
|
+
if (options.type) {
|
|
860
|
+
helpers.removeClass(toast, 'ga-toast-success ga-toast-error ga-toast-warning ga-toast-info ga-toast-primary ga-toast-secondary');
|
|
861
|
+
helpers.addClass(toast, 'ga-toast-' + options.type);
|
|
862
|
+
}
|
|
863
|
+
|
|
864
|
+
if (options.icon !== undefined) {
|
|
865
|
+
const icon = helpers.qs('.ga-toast-icon', toast);
|
|
866
|
+
if (options.icon) {
|
|
867
|
+
if (icon) {
|
|
868
|
+
icon.innerHTML = options.icon;
|
|
869
|
+
} else {
|
|
870
|
+
const iconDiv = document.createElement('div');
|
|
871
|
+
iconDiv.className = 'ga-toast-icon';
|
|
872
|
+
iconDiv.innerHTML = options.icon;
|
|
873
|
+
toast.insertBefore(iconDiv, toast.firstChild);
|
|
874
|
+
}
|
|
875
|
+
} else if (icon) {
|
|
876
|
+
helpers.remove(icon);
|
|
877
|
+
}
|
|
878
|
+
}
|
|
879
|
+
|
|
880
|
+
return true;
|
|
881
|
+
},
|
|
882
|
+
|
|
883
|
+
/**
|
|
884
|
+
* Get toast count
|
|
885
|
+
*/
|
|
886
|
+
getCount: function (type) {
|
|
887
|
+
if (type) {
|
|
888
|
+
return helpers.qsa('.ga-toast.ga-toast-' + type).length;
|
|
889
|
+
}
|
|
890
|
+
return helpers.qsa('.ga-toast').length;
|
|
891
|
+
},
|
|
892
|
+
|
|
893
|
+
/**
|
|
894
|
+
* Clear all toasts
|
|
895
|
+
*/
|
|
896
|
+
clear: function (type) {
|
|
897
|
+
const self = this;
|
|
898
|
+
if (type) {
|
|
899
|
+
helpers.qsa('.ga-toast.ga-toast-' + type).forEach(function (toast) {
|
|
900
|
+
self.close(toast);
|
|
901
|
+
});
|
|
902
|
+
} else {
|
|
903
|
+
this.closeAll();
|
|
904
|
+
}
|
|
905
|
+
},
|
|
906
|
+
|
|
907
|
+
/**
|
|
908
|
+
* Check if toast exists
|
|
909
|
+
*/
|
|
910
|
+
exists: function (toastId) {
|
|
911
|
+
return !!helpers.qs('#' + toastId);
|
|
912
|
+
},
|
|
913
|
+
|
|
914
|
+
/**
|
|
915
|
+
* Get toast by ID
|
|
916
|
+
*/
|
|
917
|
+
get: function (toastId) {
|
|
918
|
+
return helpers.qs('#' + toastId);
|
|
919
|
+
},
|
|
920
|
+
|
|
921
|
+
/**
|
|
922
|
+
* Set global defaults
|
|
923
|
+
*/
|
|
924
|
+
setDefaults: function (defaults) {
|
|
925
|
+
this.globalDefaults = helpers.extend({}, this.globalDefaults || {}, defaults);
|
|
926
|
+
},
|
|
927
|
+
|
|
928
|
+
/**
|
|
929
|
+
* Get merged options with global defaults
|
|
930
|
+
*/
|
|
931
|
+
getMergedOptions: function (options) {
|
|
932
|
+
return helpers.extend({}, this.globalDefaults || {}, options);
|
|
933
|
+
},
|
|
934
|
+
|
|
935
|
+
/**
|
|
936
|
+
* Set external logger callback
|
|
937
|
+
*/
|
|
938
|
+
setLogger: function (logger) {
|
|
939
|
+
GaToastsLogger = typeof logger === 'function' ? logger : null;
|
|
940
|
+
},
|
|
941
|
+
|
|
942
|
+
/**
|
|
943
|
+
* Show modern toast with enhanced styling
|
|
944
|
+
*/
|
|
945
|
+
modern: function (message, options) {
|
|
946
|
+
const defaults = {
|
|
947
|
+
message: message,
|
|
948
|
+
progress: true,
|
|
949
|
+
progressBackground: true,
|
|
950
|
+
pauseOnHover: true,
|
|
951
|
+
glassmorphism: true,
|
|
952
|
+
animation: 'slide',
|
|
953
|
+
size: 'md',
|
|
954
|
+
variant: 'light'
|
|
955
|
+
};
|
|
956
|
+
|
|
957
|
+
const settings = helpers.extend({}, defaults, options);
|
|
958
|
+
return this.show(settings);
|
|
959
|
+
},
|
|
960
|
+
|
|
961
|
+
/**
|
|
962
|
+
* Show notification with modern styling
|
|
963
|
+
*/
|
|
964
|
+
notification: function (title, message, options) {
|
|
965
|
+
const defaults = {
|
|
966
|
+
title: title,
|
|
967
|
+
message: message,
|
|
968
|
+
type: 'info',
|
|
969
|
+
progress: true,
|
|
970
|
+
progressBackground: true,
|
|
971
|
+
pauseOnHover: true,
|
|
972
|
+
glassmorphism: true,
|
|
973
|
+
animation: 'slide',
|
|
974
|
+
size: 'md',
|
|
975
|
+
duration: 4000
|
|
976
|
+
};
|
|
977
|
+
|
|
978
|
+
const settings = helpers.extend({}, defaults, options);
|
|
979
|
+
return this.show(settings);
|
|
980
|
+
}
|
|
981
|
+
};
|
|
982
|
+
|
|
983
|
+
// Initialize when document is ready
|
|
984
|
+
if (document.readyState === 'loading') {
|
|
985
|
+
document.addEventListener('DOMContentLoaded', function() {
|
|
986
|
+
Toast.init();
|
|
987
|
+
});
|
|
988
|
+
} else {
|
|
989
|
+
Toast.init();
|
|
990
|
+
}
|
|
991
|
+
|
|
992
|
+
// Primary global export
|
|
993
|
+
var GaToasts = Toast;
|
|
994
|
+
window.GaToasts = GaToasts;
|
|
995
|
+
|
|
996
|
+
// Framework / module support (CommonJS, AMD)
|
|
997
|
+
if (typeof module !== 'undefined' && module.exports) {
|
|
998
|
+
module.exports = GaToasts;
|
|
999
|
+
} else if (typeof define === 'function' && define.amd) {
|
|
1000
|
+
define([], function () {
|
|
1001
|
+
return GaToasts;
|
|
1002
|
+
});
|
|
1003
|
+
}
|
|
1004
|
+
|
|
1005
|
+
})();
|