vanillaforge 1.9.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/CHANGELOG.md +466 -0
- package/README.md +198 -0
- package/package.json +91 -0
- package/src/components/base-component.js +925 -0
- package/src/core/component-manager.js +306 -0
- package/src/core/dom-morph.js +234 -0
- package/src/core/event-bus.js +229 -0
- package/src/core/router.js +487 -0
- package/src/core/signal.js +114 -0
- package/src/framework.js +323 -0
- package/src/plugins/alerts/alerts-plugin.js +427 -0
- package/src/plugins/fonts/files/inter.js +4 -0
- package/src/plugins/fonts/files/jetbrains-mono.js +4 -0
- package/src/plugins/fonts/font-manifests.js +53 -0
- package/src/plugins/fonts/fonts-plugin.js +246 -0
- package/src/plugins/icons/default-icons.js +51 -0
- package/src/plugins/icons/icons-plugin.js +130 -0
- package/src/plugins/store/store-plugin.js +127 -0
- package/src/plugins/theme/base-styles.js +58 -0
- package/src/plugins/theme/theme-plugin.js +160 -0
- package/src/utils/decorators.js +51 -0
- package/src/utils/dom.js +40 -0
- package/src/utils/error-handler.js +442 -0
- package/src/utils/framework-debug.js +375 -0
- package/src/utils/logger.js +324 -0
- package/src/utils/notification.js +123 -0
- package/src/utils/performance.js +281 -0
- package/src/utils/storage.js +86 -0
- package/src/utils/sweet-alert.js +84 -0
- package/src/utils/validation.js +70 -0
- package/src/utils/validators.js +129 -0
- package/types/index.d.ts +524 -0
|
@@ -0,0 +1,123 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Notification Utility
|
|
3
|
+
*
|
|
4
|
+
* Handles displaying messages to the user, such as toasts and modals.
|
|
5
|
+
*/
|
|
6
|
+
export class Notification {
|
|
7
|
+
/**
|
|
8
|
+
* Show a toast notification
|
|
9
|
+
*
|
|
10
|
+
* @param {string} message - The message to display
|
|
11
|
+
* @param {string} type - The type of toast ('error', 'warning', 'success', 'info')
|
|
12
|
+
*/
|
|
13
|
+
showToast(message, type = 'info') {
|
|
14
|
+
const toast = document.createElement('div');
|
|
15
|
+
toast.className = `toast toast-${type}`;
|
|
16
|
+
toast.textContent = message;
|
|
17
|
+
|
|
18
|
+
// Basic styling, can be moved to a CSS file
|
|
19
|
+
toast.style.cssText = `
|
|
20
|
+
position: fixed;
|
|
21
|
+
top: 20px;
|
|
22
|
+
right: 20px;
|
|
23
|
+
padding: 16px 20px;
|
|
24
|
+
background: #333;
|
|
25
|
+
color: #fff;
|
|
26
|
+
border-radius: 6px;
|
|
27
|
+
box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1);
|
|
28
|
+
z-index: 9999;
|
|
29
|
+
max-width: 300px;
|
|
30
|
+
font-size: 14px;
|
|
31
|
+
line-height: 1.4;
|
|
32
|
+
`;
|
|
33
|
+
|
|
34
|
+
switch (type) {
|
|
35
|
+
case 'error':
|
|
36
|
+
toast.style.background = '#fee2e2';
|
|
37
|
+
toast.style.color = '#991b1b';
|
|
38
|
+
toast.style.border = '1px solid #fecaca';
|
|
39
|
+
break;
|
|
40
|
+
case 'warning':
|
|
41
|
+
toast.style.background = '#fef3cd';
|
|
42
|
+
toast.style.color = '#92400e';
|
|
43
|
+
toast.style.border = '1px solid #fde68a';
|
|
44
|
+
break;
|
|
45
|
+
case 'success':
|
|
46
|
+
toast.style.background = '#dcfce7';
|
|
47
|
+
toast.style.color = '#166534';
|
|
48
|
+
toast.style.border = '1px solid #bbf7d0';
|
|
49
|
+
break;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
document.body.appendChild(toast);
|
|
53
|
+
|
|
54
|
+
setTimeout(() => {
|
|
55
|
+
if (toast.parentNode) {
|
|
56
|
+
toast.remove();
|
|
57
|
+
}
|
|
58
|
+
}, 5000);
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
/**
|
|
62
|
+
* Show a modal dialog
|
|
63
|
+
*
|
|
64
|
+
* @param {string} title - The title of the modal
|
|
65
|
+
* @param {string} message - The message to display in the modal body
|
|
66
|
+
* @param {Object} [options={}] - Options for the modal (e.g., buttons)
|
|
67
|
+
*/
|
|
68
|
+
showModal(title, message, options = {}) {
|
|
69
|
+
const modal = document.createElement('div');
|
|
70
|
+
modal.className = 'modal-overlay';
|
|
71
|
+
modal.innerHTML = `
|
|
72
|
+
<div class="modal">
|
|
73
|
+
<div class="modal-header">
|
|
74
|
+
<h3>${title}</h3>
|
|
75
|
+
<button class="modal-close">×</button>
|
|
76
|
+
</div>
|
|
77
|
+
<div class="modal-body">
|
|
78
|
+
<p>${message}</p>
|
|
79
|
+
${options.details ? `
|
|
80
|
+
<details style="margin-top: 16px;">
|
|
81
|
+
<summary>Technical Details</summary>
|
|
82
|
+
<pre style="font-size: 12px; margin-top: 8px;">${options.details}</pre>
|
|
83
|
+
</details>
|
|
84
|
+
` : ''}
|
|
85
|
+
</div>
|
|
86
|
+
<div class="modal-footer">
|
|
87
|
+
${(options.buttons || [{ label: 'Close', action: 'close' }]).map(btn => `<button class="modal-btn-${btn.action}">${btn.label}</button>`).join('')}
|
|
88
|
+
</div>
|
|
89
|
+
</div>
|
|
90
|
+
`;
|
|
91
|
+
|
|
92
|
+
modal.style.cssText = `
|
|
93
|
+
position: fixed;
|
|
94
|
+
top: 0;
|
|
95
|
+
left: 0;
|
|
96
|
+
width: 100%;
|
|
97
|
+
height: 100%;
|
|
98
|
+
background: rgba(0, 0, 0, 0.5);
|
|
99
|
+
display: flex;
|
|
100
|
+
justify-content: center;
|
|
101
|
+
align-items: center;
|
|
102
|
+
z-index: 10000;
|
|
103
|
+
`;
|
|
104
|
+
|
|
105
|
+
modal.querySelector('.modal-close').onclick = () => modal.remove();
|
|
106
|
+
|
|
107
|
+
if (options.buttons) {
|
|
108
|
+
options.buttons.forEach(btn => {
|
|
109
|
+
modal.querySelector(`.modal-btn-${btn.action}`).onclick = () => {
|
|
110
|
+
if (btn.onClick) {
|
|
111
|
+
btn.onClick();
|
|
112
|
+
}
|
|
113
|
+
modal.remove();
|
|
114
|
+
};
|
|
115
|
+
});
|
|
116
|
+
} else {
|
|
117
|
+
modal.querySelector('.modal-btn-close').onclick = () => modal.remove();
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
|
|
121
|
+
document.body.appendChild(modal);
|
|
122
|
+
}
|
|
123
|
+
}
|
|
@@ -0,0 +1,281 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Performance optimization utilities for VanillaForge
|
|
3
|
+
*
|
|
4
|
+
* Provides tools for optimizing application performance including
|
|
5
|
+
* lazy loading, caching, and resource management.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* Performance utilities class
|
|
10
|
+
*/
|
|
11
|
+
import { Logger } from './logger.js';
|
|
12
|
+
|
|
13
|
+
export class PerformanceUtils {
|
|
14
|
+
constructor() {
|
|
15
|
+
this.cache = new Map();
|
|
16
|
+
this.observers = new Map();
|
|
17
|
+
this.loadingStates = new Map();
|
|
18
|
+
this.logger = new Logger('PerformanceUtils');
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* Debounce function execution
|
|
23
|
+
* @param {Function} func - Function to debounce
|
|
24
|
+
* @param {number} wait - Delay in milliseconds
|
|
25
|
+
* @param {boolean} immediate - Execute immediately on first call
|
|
26
|
+
* @returns {Function} Debounced function
|
|
27
|
+
*/
|
|
28
|
+
debounce(func, wait, immediate = false) {
|
|
29
|
+
let timeout;
|
|
30
|
+
return function executedFunction(...args) {
|
|
31
|
+
const later = () => {
|
|
32
|
+
timeout = null;
|
|
33
|
+
if (!immediate) func.apply(this, args);
|
|
34
|
+
};
|
|
35
|
+
const callNow = immediate && !timeout;
|
|
36
|
+
clearTimeout(timeout);
|
|
37
|
+
timeout = setTimeout(later, wait);
|
|
38
|
+
if (callNow) func.apply(this, args);
|
|
39
|
+
};
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
/**
|
|
43
|
+
* Throttle function execution
|
|
44
|
+
* @param {Function} func - Function to throttle
|
|
45
|
+
* @param {number} limit - Minimum time between executions
|
|
46
|
+
* @returns {Function} Throttled function
|
|
47
|
+
*/
|
|
48
|
+
throttle(func, limit) {
|
|
49
|
+
let inThrottle;
|
|
50
|
+
return function(...args) {
|
|
51
|
+
if (!inThrottle) {
|
|
52
|
+
func.apply(this, args);
|
|
53
|
+
inThrottle = true;
|
|
54
|
+
setTimeout(() => inThrottle = false, limit);
|
|
55
|
+
}
|
|
56
|
+
};
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
/**
|
|
60
|
+
* Lazy load a component with intersection observer
|
|
61
|
+
* @param {HTMLElement} element - Element to observe
|
|
62
|
+
* @param {Function} loadCallback - Function to call when element is visible
|
|
63
|
+
* @param {Object} options - Intersection observer options
|
|
64
|
+
*/
|
|
65
|
+
lazyLoad(element, loadCallback, options = {}) {
|
|
66
|
+
const defaultOptions = {
|
|
67
|
+
root: null,
|
|
68
|
+
rootMargin: '50px',
|
|
69
|
+
threshold: 0.1
|
|
70
|
+
};
|
|
71
|
+
|
|
72
|
+
const observerOptions = { ...defaultOptions, ...options };
|
|
73
|
+
|
|
74
|
+
if ('IntersectionObserver' in window) {
|
|
75
|
+
const observer = new IntersectionObserver((entries) => {
|
|
76
|
+
entries.forEach(entry => {
|
|
77
|
+
if (entry.isIntersecting) {
|
|
78
|
+
loadCallback(entry.target);
|
|
79
|
+
observer.unobserve(entry.target);
|
|
80
|
+
}
|
|
81
|
+
});
|
|
82
|
+
}, observerOptions);
|
|
83
|
+
|
|
84
|
+
observer.observe(element);
|
|
85
|
+
this.observers.set(element, observer);
|
|
86
|
+
} else {
|
|
87
|
+
// Fallback for browsers without IntersectionObserver
|
|
88
|
+
loadCallback(element);
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
/**
|
|
93
|
+
* Cache data with expiration
|
|
94
|
+
* @param {string} key - Cache key
|
|
95
|
+
* @param {*} data - Data to cache
|
|
96
|
+
* @param {number} ttl - Time to live in milliseconds
|
|
97
|
+
*/
|
|
98
|
+
setCache(key, data, ttl = 300000) { // 5 minutes default
|
|
99
|
+
const expiry = Date.now() + ttl;
|
|
100
|
+
this.cache.set(key, { data, expiry });
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
/**
|
|
104
|
+
* Get cached data
|
|
105
|
+
* @param {string} key - Cache key
|
|
106
|
+
* @returns {*} Cached data or null if expired/not found
|
|
107
|
+
*/
|
|
108
|
+
getCache(key) {
|
|
109
|
+
const cached = this.cache.get(key);
|
|
110
|
+
if (!cached) return null;
|
|
111
|
+
|
|
112
|
+
if (Date.now() > cached.expiry) {
|
|
113
|
+
this.cache.delete(key);
|
|
114
|
+
return null;
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
return cached.data;
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
/**
|
|
121
|
+
* Clear expired cache entries
|
|
122
|
+
*/
|
|
123
|
+
clearExpiredCache() {
|
|
124
|
+
const now = Date.now();
|
|
125
|
+
for (const [key, value] of this.cache.entries()) {
|
|
126
|
+
if (now > value.expiry) {
|
|
127
|
+
this.cache.delete(key);
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
/**
|
|
133
|
+
* Measure function execution time
|
|
134
|
+
* @param {Function} func - Function to measure
|
|
135
|
+
* @param {string} label - Label for the measurement
|
|
136
|
+
* @returns {Promise|*} Function result
|
|
137
|
+
*/
|
|
138
|
+
measure(func, label = 'Function') {
|
|
139
|
+
const start = performance.now();
|
|
140
|
+
const result = func();
|
|
141
|
+
|
|
142
|
+
if (result instanceof Promise) {
|
|
143
|
+
return result.finally(() => {
|
|
144
|
+
const end = performance.now();
|
|
145
|
+
this.logger.info(`${label} execution time`, { duration: `${(end - start).toFixed(2)}ms` });
|
|
146
|
+
});
|
|
147
|
+
} else {
|
|
148
|
+
const end = performance.now();
|
|
149
|
+
this.logger.info(`${label} execution time`, { duration: `${(end - start).toFixed(2)}ms` });
|
|
150
|
+
return result;
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
/**
|
|
155
|
+
* Batch DOM operations to avoid layout thrashing
|
|
156
|
+
* @param {Function[]} operations - Array of DOM operations
|
|
157
|
+
*/
|
|
158
|
+
batchDOMOperations(operations) {
|
|
159
|
+
requestAnimationFrame(() => {
|
|
160
|
+
operations.forEach(operation => operation());
|
|
161
|
+
});
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
/**
|
|
165
|
+
* Preload resources
|
|
166
|
+
* @param {string[]} urls - Array of resource URLs
|
|
167
|
+
* @param {string} type - Resource type ('image', 'script', 'style')
|
|
168
|
+
* @returns {Promise} Promise that resolves when all resources are loaded
|
|
169
|
+
*/
|
|
170
|
+
async preloadResources(resources) {
|
|
171
|
+
const promises = resources.map(resource => {
|
|
172
|
+
return new Promise((resolve, reject) => {
|
|
173
|
+
let element;
|
|
174
|
+
const { url, type } = resource;
|
|
175
|
+
|
|
176
|
+
switch (type) {
|
|
177
|
+
case 'image':
|
|
178
|
+
element = new Image();
|
|
179
|
+
element.src = url;
|
|
180
|
+
break;
|
|
181
|
+
case 'script':
|
|
182
|
+
element = document.createElement('script');
|
|
183
|
+
element.src = url;
|
|
184
|
+
element.async = true;
|
|
185
|
+
document.head.appendChild(element);
|
|
186
|
+
break;
|
|
187
|
+
case 'style':
|
|
188
|
+
element = document.createElement('link');
|
|
189
|
+
element.rel = 'stylesheet';
|
|
190
|
+
element.href = url;
|
|
191
|
+
document.head.appendChild(element);
|
|
192
|
+
break;
|
|
193
|
+
default:
|
|
194
|
+
return reject(new Error(`Unsupported resource type: ${type}`));
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
element.onload = () => resolve({ url, type, status: 'loaded' });
|
|
198
|
+
element.onerror = () => reject({ url, type, status: 'failed' });
|
|
199
|
+
});
|
|
200
|
+
});
|
|
201
|
+
|
|
202
|
+
return Promise.allSettled(promises);
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
|
|
206
|
+
/**
|
|
207
|
+
* Monitor memory usage
|
|
208
|
+
* @param {Function} callback - Callback function to receive memory stats
|
|
209
|
+
* @param {number} interval - Monitoring interval in milliseconds
|
|
210
|
+
* @returns {number} Interval ID
|
|
211
|
+
*/
|
|
212
|
+
monitorMemory(callback, interval = 5000) {
|
|
213
|
+
if (!performance.memory) {
|
|
214
|
+
console.warn('Memory monitoring not available in this browser');
|
|
215
|
+
return null;
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
return setInterval(() => {
|
|
219
|
+
const memory = {
|
|
220
|
+
used: Math.round(performance.memory.usedJSHeapSize / 1024 / 1024),
|
|
221
|
+
total: Math.round(performance.memory.totalJSHeapSize / 1024 / 1024),
|
|
222
|
+
limit: Math.round(performance.memory.jsHeapSizeLimit / 1024 / 1024)
|
|
223
|
+
};
|
|
224
|
+
callback(memory);
|
|
225
|
+
}, interval);
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
/**
|
|
229
|
+
* Cleanup observers and resources
|
|
230
|
+
*/
|
|
231
|
+
cleanup() {
|
|
232
|
+
// Clear all intersection observers
|
|
233
|
+
this.observers.forEach((observer, element) => {
|
|
234
|
+
observer.unobserve(element);
|
|
235
|
+
observer.disconnect();
|
|
236
|
+
});
|
|
237
|
+
this.observers.clear();
|
|
238
|
+
|
|
239
|
+
// Clear cache
|
|
240
|
+
this.cache.clear();
|
|
241
|
+
|
|
242
|
+
// Clear loading states
|
|
243
|
+
this.loadingStates.clear();
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
/**
|
|
247
|
+
* Create a performance mark
|
|
248
|
+
* @param {string} name - Mark name
|
|
249
|
+
*/
|
|
250
|
+
mark(name) {
|
|
251
|
+
if (performance.mark) {
|
|
252
|
+
performance.mark(name);
|
|
253
|
+
}
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
/**
|
|
257
|
+
* Measure performance between two marks
|
|
258
|
+
* @param {string} name - Measure name
|
|
259
|
+
* @param {string} startMark - Start mark name
|
|
260
|
+
* @param {string} endMark - End mark name
|
|
261
|
+
*/
|
|
262
|
+
measureBetween(name, startMark, endMark) {
|
|
263
|
+
if (performance.measure) {
|
|
264
|
+
performance.measure(name, startMark, endMark);
|
|
265
|
+
}
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
/**
|
|
269
|
+
* Get performance entries
|
|
270
|
+
* @param {string} type - Entry type (mark, measure, navigation, etc.)
|
|
271
|
+
* @returns {Array} Performance entries
|
|
272
|
+
*/
|
|
273
|
+
getPerformanceEntries(type) {
|
|
274
|
+
if (performance.getEntriesByType) {
|
|
275
|
+
return performance.getEntriesByType(type);
|
|
276
|
+
}
|
|
277
|
+
return [];
|
|
278
|
+
}
|
|
279
|
+
}
|
|
280
|
+
|
|
281
|
+
export const performanceUtils = new PerformanceUtils();
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Storage Adapters
|
|
3
|
+
*
|
|
4
|
+
* Provides a consistent interface for key-value storage, abstracting
|
|
5
|
+
* away the underlying implementation (e.g., localStorage, in-memory).
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* Base class for storage adapters
|
|
10
|
+
*/
|
|
11
|
+
class StorageAdapter {
|
|
12
|
+
getItem(_key) {
|
|
13
|
+
throw new Error('Not implemented');
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
setItem(_key, _value) {
|
|
17
|
+
throw new Error('Not implemented');
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
removeItem(_key) {
|
|
21
|
+
throw new Error('Not implemented');
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
getLogs(key) {
|
|
25
|
+
try {
|
|
26
|
+
const logs = this.getItem(key) || '[]';
|
|
27
|
+
return JSON.parse(logs);
|
|
28
|
+
} catch (error) {
|
|
29
|
+
console.error('Failed to retrieve logs:', error);
|
|
30
|
+
return [];
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
saveLogs(key, logs, maxLogs = 100) {
|
|
35
|
+
try {
|
|
36
|
+
if (logs.length > maxLogs) {
|
|
37
|
+
logs.splice(0, logs.length - maxLogs);
|
|
38
|
+
}
|
|
39
|
+
this.setItem(key, JSON.stringify(logs));
|
|
40
|
+
} catch (error) {
|
|
41
|
+
console.error('Failed to save logs:', error);
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
/**
|
|
47
|
+
* localStorage adapter (for browser environments)
|
|
48
|
+
*/
|
|
49
|
+
export class LocalStorageAdapter extends StorageAdapter {
|
|
50
|
+
getItem(key) {
|
|
51
|
+
if (typeof localStorage === 'undefined') return null;
|
|
52
|
+
return localStorage.getItem(key);
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
setItem(key, value) {
|
|
56
|
+
if (typeof localStorage === 'undefined') return;
|
|
57
|
+
localStorage.setItem(key, value);
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
removeItem(key) {
|
|
61
|
+
if (typeof localStorage === 'undefined') return;
|
|
62
|
+
localStorage.removeItem(key);
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
/**
|
|
67
|
+
* In-memory storage adapter (for testing or non-browser environments)
|
|
68
|
+
*/
|
|
69
|
+
export class InMemoryStorageAdapter extends StorageAdapter {
|
|
70
|
+
constructor() {
|
|
71
|
+
super();
|
|
72
|
+
this.storage = new Map();
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
getItem(key) {
|
|
76
|
+
return this.storage.get(key) || null;
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
setItem(key, value) {
|
|
80
|
+
this.storage.set(key, value);
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
removeItem(key) {
|
|
84
|
+
this.storage.delete(key);
|
|
85
|
+
}
|
|
86
|
+
}
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* SweetAlert2 Utility
|
|
3
|
+
*
|
|
4
|
+
* Wrapper for SweetAlert2 to ensure it's available and provide consistent styling
|
|
5
|
+
*
|
|
6
|
+
* @author VanillaForge Team
|
|
7
|
+
* @version 3.0.0
|
|
8
|
+
* @since 2025-06-14
|
|
9
|
+
*/
|
|
10
|
+
|
|
11
|
+
export class SweetAlert {
|
|
12
|
+
constructor(swalInstance) {
|
|
13
|
+
if (!swalInstance) {
|
|
14
|
+
throw new Error('SweetAlert2 instance is required.');
|
|
15
|
+
}
|
|
16
|
+
this.swal = swalInstance;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
fire(options) {
|
|
20
|
+
const defaultOptions = {
|
|
21
|
+
customClass: {
|
|
22
|
+
popup: 'swal-custom-popup',
|
|
23
|
+
title: 'swal-custom-title',
|
|
24
|
+
content: 'swal-custom-content',
|
|
25
|
+
confirmButton: 'swal-custom-confirm',
|
|
26
|
+
cancelButton: 'swal-custom-cancel'
|
|
27
|
+
},
|
|
28
|
+
buttonsStyling: false
|
|
29
|
+
};
|
|
30
|
+
|
|
31
|
+
return this.swal.fire({
|
|
32
|
+
...defaultOptions,
|
|
33
|
+
...options
|
|
34
|
+
});
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
success(title, text = '', options = {}) {
|
|
38
|
+
return this.fire({
|
|
39
|
+
icon: 'success',
|
|
40
|
+
title,
|
|
41
|
+
text,
|
|
42
|
+
...options
|
|
43
|
+
});
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
error(title, text = '', options = {}) {
|
|
47
|
+
return this.fire({
|
|
48
|
+
icon: 'error',
|
|
49
|
+
title,
|
|
50
|
+
text,
|
|
51
|
+
...options
|
|
52
|
+
});
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
warning(title, text = '', options = {}) {
|
|
56
|
+
return this.fire({
|
|
57
|
+
icon: 'warning',
|
|
58
|
+
title,
|
|
59
|
+
text,
|
|
60
|
+
...options
|
|
61
|
+
});
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
info(title, text = '', options = {}) {
|
|
65
|
+
return this.fire({
|
|
66
|
+
icon: 'info',
|
|
67
|
+
title,
|
|
68
|
+
text,
|
|
69
|
+
...options
|
|
70
|
+
});
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
confirm(title, text = '', options = {}) {
|
|
74
|
+
return this.fire({
|
|
75
|
+
icon: 'question',
|
|
76
|
+
title,
|
|
77
|
+
text,
|
|
78
|
+
showCancelButton: true,
|
|
79
|
+
confirmButtonText: 'Yes',
|
|
80
|
+
cancelButtonText: 'No',
|
|
81
|
+
...options
|
|
82
|
+
});
|
|
83
|
+
}
|
|
84
|
+
}
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Validation Utilities
|
|
3
|
+
*
|
|
4
|
+
* This module provides comprehensive validation functions for the VanillaForge.
|
|
5
|
+
* It includes validation for user input, data integrity, business rules, and security.
|
|
6
|
+
*
|
|
7
|
+
* Features:
|
|
8
|
+
* - Email validation
|
|
9
|
+
* - Phone number validation
|
|
10
|
+
* - Currency amount validation
|
|
11
|
+
* - Date validation
|
|
12
|
+
* - String sanitization
|
|
13
|
+
* - Business rule validation
|
|
14
|
+
* - Security validation
|
|
15
|
+
*
|
|
16
|
+
* @author VanillaForge Team
|
|
17
|
+
* @version 3.0.0
|
|
18
|
+
* @since 2024-06-14
|
|
19
|
+
*/
|
|
20
|
+
|
|
21
|
+
import { Logger } from './logger.js';
|
|
22
|
+
import * as Validators from './validators.js';
|
|
23
|
+
|
|
24
|
+
export class ValidationUtils {
|
|
25
|
+
constructor(logger) {
|
|
26
|
+
this.logger = logger || new Logger('ValidationUtils');
|
|
27
|
+
this.validators = {
|
|
28
|
+
email: Validators.validateEmail,
|
|
29
|
+
phone: Validators.validatePhoneNumber,
|
|
30
|
+
currency: Validators.validateCurrencyAmount,
|
|
31
|
+
// Add other validators here
|
|
32
|
+
};
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
validate(type, value, options) {
|
|
36
|
+
const validator = this.validators[type];
|
|
37
|
+
if (!validator) {
|
|
38
|
+
this.logger.warn(`Validator for type "${type}" not found.`);
|
|
39
|
+
return { isValid: true, sanitized: value, errors: [] };
|
|
40
|
+
}
|
|
41
|
+
try {
|
|
42
|
+
return validator(value, options);
|
|
43
|
+
} catch (error) {
|
|
44
|
+
this.logger.error(`Validation failed for type "${type}"`, { error, value });
|
|
45
|
+
return { isValid: false, errors: ['Validation failed due to an unexpected error.'] };
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
validateFields(data, rules) {
|
|
50
|
+
const result = {
|
|
51
|
+
isValid: true,
|
|
52
|
+
errors: {},
|
|
53
|
+
sanitized: {}
|
|
54
|
+
};
|
|
55
|
+
|
|
56
|
+
for (const [field, rule] of Object.entries(rules)) {
|
|
57
|
+
const value = data[field];
|
|
58
|
+
const fieldResult = this.validate(rule.type, value, rule.options);
|
|
59
|
+
|
|
60
|
+
if (!fieldResult.isValid) {
|
|
61
|
+
result.isValid = false;
|
|
62
|
+
result.errors[field] = fieldResult.errors;
|
|
63
|
+
} else {
|
|
64
|
+
result.sanitized[field] = fieldResult.sanitized;
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
return result;
|
|
69
|
+
}
|
|
70
|
+
}
|