canvasframework 0.3.6

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.
Files changed (85) hide show
  1. package/README.md +554 -0
  2. package/components/Accordion.js +252 -0
  3. package/components/AndroidDatePickerDialog.js +398 -0
  4. package/components/AppBar.js +225 -0
  5. package/components/Avatar.js +202 -0
  6. package/components/BottomNavigationBar.js +205 -0
  7. package/components/BottomSheet.js +374 -0
  8. package/components/Button.js +225 -0
  9. package/components/Card.js +193 -0
  10. package/components/Checkbox.js +180 -0
  11. package/components/Chip.js +212 -0
  12. package/components/CircularProgress.js +143 -0
  13. package/components/ContextMenu.js +116 -0
  14. package/components/DatePicker.js +257 -0
  15. package/components/Dialog.js +367 -0
  16. package/components/Divider.js +125 -0
  17. package/components/Drawer.js +261 -0
  18. package/components/FAB.js +270 -0
  19. package/components/FileUpload.js +315 -0
  20. package/components/IOSDatePickerWheel.js +268 -0
  21. package/components/ImageCarousel.js +193 -0
  22. package/components/ImageComponent.js +223 -0
  23. package/components/Input.js +309 -0
  24. package/components/List.js +94 -0
  25. package/components/ListItem.js +223 -0
  26. package/components/Modal.js +364 -0
  27. package/components/MultiSelectDialog.js +206 -0
  28. package/components/NumberInput.js +271 -0
  29. package/components/ProgressBar.js +88 -0
  30. package/components/RadioButton.js +142 -0
  31. package/components/SearchInput.js +315 -0
  32. package/components/SegmentedControl.js +202 -0
  33. package/components/Select.js +199 -0
  34. package/components/SelectDialog.js +255 -0
  35. package/components/Slider.js +113 -0
  36. package/components/Snackbar.js +243 -0
  37. package/components/Stepper.js +281 -0
  38. package/components/SwipeableListItem.js +179 -0
  39. package/components/Switch.js +147 -0
  40. package/components/Table.js +492 -0
  41. package/components/Tabs.js +125 -0
  42. package/components/Text.js +141 -0
  43. package/components/TextField.js +331 -0
  44. package/components/Toast.js +236 -0
  45. package/components/TreeView.js +420 -0
  46. package/components/Video.js +397 -0
  47. package/components/View.js +140 -0
  48. package/components/VirtualList.js +120 -0
  49. package/core/CanvasFramework.js +1271 -0
  50. package/core/CanvasWork.js +32 -0
  51. package/core/Component.js +153 -0
  52. package/core/LogicWorker.js +25 -0
  53. package/core/WebGLCanvasAdapter.js +1369 -0
  54. package/features/Column.js +43 -0
  55. package/features/Grid.js +47 -0
  56. package/features/LayoutComponent.js +43 -0
  57. package/features/OpenStreetMap.js +310 -0
  58. package/features/Positioned.js +33 -0
  59. package/features/PullToRefresh.js +328 -0
  60. package/features/Row.js +40 -0
  61. package/features/SignaturePad.js +257 -0
  62. package/features/Skeleton.js +84 -0
  63. package/features/Stack.js +21 -0
  64. package/index.js +101 -0
  65. package/manager/AccessibilityManager.js +107 -0
  66. package/manager/ErrorHandler.js +59 -0
  67. package/manager/FeatureFlags.js +60 -0
  68. package/manager/MemoryManager.js +107 -0
  69. package/manager/PerformanceMonitor.js +84 -0
  70. package/manager/SecurityManager.js +54 -0
  71. package/package.json +28 -0
  72. package/utils/AnimationEngine.js +428 -0
  73. package/utils/DataStore.js +403 -0
  74. package/utils/EventBus.js +407 -0
  75. package/utils/FetchClient.js +74 -0
  76. package/utils/FormValidator.js +355 -0
  77. package/utils/GeoLocationService.js +62 -0
  78. package/utils/I18n.js +207 -0
  79. package/utils/IndexedDBManager.js +273 -0
  80. package/utils/OfflineSyncManager.js +342 -0
  81. package/utils/QueryBuilder.js +478 -0
  82. package/utils/SafeArea.js +64 -0
  83. package/utils/SecureStorage.js +289 -0
  84. package/utils/StateManager.js +207 -0
  85. package/utils/WebSocketClient.js +66 -0
@@ -0,0 +1,407 @@
1
+ /**
2
+ * Bus d'événements pour communication entre composants
3
+ * Pattern: Publish/Subscribe (Pub/Sub)
4
+ * @class
5
+ * @property {Map} events - Événements enregistrés
6
+ * @property {Array} history - Historique des événements
7
+ * @property {boolean} enableHistory - Activer l'historique
8
+ * @property {number} maxHistory - Taille max de l'historique
9
+ */
10
+ class EventBus {
11
+ /**
12
+ * Crée une instance de EventBus
13
+ * @param {Object} [options={}] - Options
14
+ * @param {boolean} [options.enableHistory=false] - Activer l'historique
15
+ * @param {number} [options.maxHistory=100] - Taille max historique
16
+ * @param {boolean} [options.enableWildcard=true] - Activer wildcards
17
+ */
18
+ constructor(options = {}) {
19
+ this.events = new Map();
20
+ this.history = [];
21
+ this.enableHistory = options.enableHistory || false;
22
+ this.maxHistory = options.maxHistory || 100;
23
+ this.enableWildcard = options.enableWildcard !== false;
24
+ this.middlewares = [];
25
+ }
26
+
27
+ /**
28
+ * Souscrit à un événement
29
+ * @param {string} event - Nom de l'événement
30
+ * @param {Function} callback - Fonction callback
31
+ * @param {Object} [options={}] - Options
32
+ * @param {boolean} [options.once=false] - Exécuter une seule fois
33
+ * @param {number} [options.priority=0] - Priorité (plus haut = exécuté avant)
34
+ * @returns {Function} Fonction de désinscription
35
+ */
36
+ on(event, callback, options = {}) {
37
+ if (typeof callback !== 'function') {
38
+ throw new Error('Callback must be a function');
39
+ }
40
+
41
+ if (!this.events.has(event)) {
42
+ this.events.set(event, []);
43
+ }
44
+
45
+ const listener = {
46
+ callback,
47
+ once: options.once || false,
48
+ priority: options.priority || 0,
49
+ id: this.generateId()
50
+ };
51
+
52
+ const listeners = this.events.get(event);
53
+ listeners.push(listener);
54
+
55
+ // Trier par priorité (descendant)
56
+ listeners.sort((a, b) => b.priority - a.priority);
57
+
58
+ // Retourner une fonction de désinscription
59
+ return () => this.off(event, listener.id);
60
+ }
61
+
62
+ /**
63
+ * Souscrit à un événement (une seule fois)
64
+ * @param {string} event - Nom de l'événement
65
+ * @param {Function} callback - Fonction callback
66
+ * @param {Object} [options={}] - Options
67
+ * @returns {Function} Fonction de désinscription
68
+ */
69
+ once(event, callback, options = {}) {
70
+ return this.on(event, callback, { ...options, once: true });
71
+ }
72
+
73
+ /**
74
+ * Se désinscrit d'un événement
75
+ * @param {string} event - Nom de l'événement
76
+ * @param {string|Function} [callbackOrId] - Callback ou ID du listener
77
+ * @returns {boolean} True si désabonné
78
+ */
79
+ off(event, callbackOrId) {
80
+ const listeners = this.events.get(event);
81
+
82
+ if (!listeners) return false;
83
+
84
+ if (!callbackOrId) {
85
+ // Supprimer tous les listeners de cet événement
86
+ this.events.delete(event);
87
+ return true;
88
+ }
89
+
90
+ const index = listeners.findIndex(listener =>
91
+ listener.callback === callbackOrId || listener.id === callbackOrId
92
+ );
93
+
94
+ if (index > -1) {
95
+ listeners.splice(index, 1);
96
+
97
+ // Supprimer l'événement s'il n'y a plus de listeners
98
+ if (listeners.length === 0) {
99
+ this.events.delete(event);
100
+ }
101
+
102
+ return true;
103
+ }
104
+
105
+ return false;
106
+ }
107
+
108
+ /**
109
+ * Émet un événement
110
+ * @param {string} event - Nom de l'événement
111
+ * @param {...*} args - Arguments à passer
112
+ * @returns {number} Nombre de listeners exécutés
113
+ */
114
+ emit(event, ...args) {
115
+ // Historique
116
+ if (this.enableHistory) {
117
+ this.addToHistory(event, args);
118
+ }
119
+
120
+ // Middlewares
121
+ let shouldContinue = true;
122
+ for (let middleware of this.middlewares) {
123
+ const result = middleware(event, args);
124
+ if (result === false) {
125
+ shouldContinue = false;
126
+ break;
127
+ }
128
+ }
129
+
130
+ if (!shouldContinue) return 0;
131
+
132
+ let count = 0;
133
+
134
+ // Listeners exacts
135
+ const listeners = this.events.get(event);
136
+ if (listeners) {
137
+ count += this.executeListeners(listeners, event, args);
138
+ }
139
+
140
+ // Wildcards (si activé)
141
+ if (this.enableWildcard) {
142
+ count += this.emitWildcard(event, args);
143
+ }
144
+
145
+ return count;
146
+ }
147
+
148
+ /**
149
+ * Émet un événement avec wildcard
150
+ * @param {string} event - Nom de l'événement
151
+ * @param {Array} args - Arguments
152
+ * @returns {number} Nombre de listeners exécutés
153
+ * @private
154
+ */
155
+ emitWildcard(event, args) {
156
+ let count = 0;
157
+
158
+ for (let [eventPattern, listeners] of this.events.entries()) {
159
+ if (eventPattern.includes('*')) {
160
+ if (this.matchWildcard(event, eventPattern)) {
161
+ count += this.executeListeners(listeners, event, args);
162
+ }
163
+ }
164
+ }
165
+
166
+ return count;
167
+ }
168
+
169
+ /**
170
+ * Vérifie si un événement match un pattern wildcard
171
+ * @param {string} event - Événement
172
+ * @param {string} pattern - Pattern avec *
173
+ * @returns {boolean} True si match
174
+ * @private
175
+ */
176
+ matchWildcard(event, pattern) {
177
+ const regex = new RegExp('^' + pattern.replace(/\*/g, '.*') + '$');
178
+ return regex.test(event);
179
+ }
180
+
181
+ /**
182
+ * Exécute les listeners
183
+ * @param {Array} listeners - Liste des listeners
184
+ * @param {string} event - Nom de l'événement
185
+ * @param {Array} args - Arguments
186
+ * @returns {number} Nombre de listeners exécutés
187
+ * @private
188
+ */
189
+ executeListeners(listeners, event, args) {
190
+ let count = 0;
191
+ const toRemove = [];
192
+
193
+ for (let listener of listeners) {
194
+ try {
195
+ listener.callback(...args);
196
+ count++;
197
+
198
+ if (listener.once) {
199
+ toRemove.push(listener.id);
200
+ }
201
+ } catch (error) {
202
+ console.error(`Error in event listener for "${event}":`, error);
203
+ }
204
+ }
205
+
206
+ // Supprimer les listeners "once"
207
+ for (let id of toRemove) {
208
+ this.off(event, id);
209
+ }
210
+
211
+ return count;
212
+ }
213
+
214
+ /**
215
+ * Émet un événement de manière asynchrone
216
+ * @param {string} event - Nom de l'événement
217
+ * @param {...*} args - Arguments
218
+ * @returns {Promise<number>} Nombre de listeners exécutés
219
+ */
220
+ async emitAsync(event, ...args) {
221
+ if (this.enableHistory) {
222
+ this.addToHistory(event, args);
223
+ }
224
+
225
+ let count = 0;
226
+ const listeners = this.events.get(event);
227
+
228
+ if (listeners) {
229
+ for (let listener of listeners) {
230
+ try {
231
+ await listener.callback(...args);
232
+ count++;
233
+
234
+ if (listener.once) {
235
+ this.off(event, listener.id);
236
+ }
237
+ } catch (error) {
238
+ console.error(`Error in async event listener for "${event}":`, error);
239
+ }
240
+ }
241
+ }
242
+
243
+ return count;
244
+ }
245
+
246
+ /**
247
+ * Attend qu'un événement soit émis
248
+ * @param {string} event - Nom de l'événement
249
+ * @param {number} [timeout=0] - Timeout en ms (0 = infini)
250
+ * @returns {Promise} Promise qui se résout avec les args
251
+ */
252
+ waitFor(event, timeout = 0) {
253
+ return new Promise((resolve, reject) => {
254
+ let timeoutId;
255
+
256
+ const unsubscribe = this.once(event, (...args) => {
257
+ if (timeoutId) clearTimeout(timeoutId);
258
+ resolve(args);
259
+ });
260
+
261
+ if (timeout > 0) {
262
+ timeoutId = setTimeout(() => {
263
+ unsubscribe();
264
+ reject(new Error(`Timeout waiting for event "${event}"`));
265
+ }, timeout);
266
+ }
267
+ });
268
+ }
269
+
270
+ /**
271
+ * Ajoute un middleware
272
+ * @param {Function} middleware - Fonction middleware (event, args) => boolean
273
+ */
274
+ use(middleware) {
275
+ if (typeof middleware !== 'function') {
276
+ throw new Error('Middleware must be a function');
277
+ }
278
+ this.middlewares.push(middleware);
279
+ }
280
+
281
+ /**
282
+ * Supprime un middleware
283
+ * @param {Function} middleware - Middleware à supprimer
284
+ */
285
+ removeMiddleware(middleware) {
286
+ const index = this.middlewares.indexOf(middleware);
287
+ if (index > -1) {
288
+ this.middlewares.splice(index, 1);
289
+ }
290
+ }
291
+
292
+ /**
293
+ * Obtient tous les événements enregistrés
294
+ * @returns {Array<string>} Liste des événements
295
+ */
296
+ eventNames() {
297
+ return Array.from(this.events.keys());
298
+ }
299
+
300
+ /**
301
+ * Obtient le nombre de listeners pour un événement
302
+ * @param {string} event - Nom de l'événement
303
+ * @returns {number} Nombre de listeners
304
+ */
305
+ listenerCount(event) {
306
+ const listeners = this.events.get(event);
307
+ return listeners ? listeners.length : 0;
308
+ }
309
+
310
+ /**
311
+ * Supprime tous les listeners
312
+ */
313
+ clear() {
314
+ this.events.clear();
315
+ }
316
+
317
+ /**
318
+ * Ajoute à l'historique
319
+ * @param {string} event - Événement
320
+ * @param {Array} args - Arguments
321
+ * @private
322
+ */
323
+ addToHistory(event, args) {
324
+ this.history.push({
325
+ event,
326
+ args,
327
+ timestamp: Date.now()
328
+ });
329
+
330
+ // Limiter la taille de l'historique
331
+ if (this.history.length > this.maxHistory) {
332
+ this.history.shift();
333
+ }
334
+ }
335
+
336
+ /**
337
+ * Obtient l'historique
338
+ * @param {string} [event] - Filtrer par événement
339
+ * @returns {Array} Historique
340
+ */
341
+ getHistory(event) {
342
+ if (event) {
343
+ return this.history.filter(item => item.event === event);
344
+ }
345
+ return this.history;
346
+ }
347
+
348
+ /**
349
+ * Vide l'historique
350
+ */
351
+ clearHistory() {
352
+ this.history = [];
353
+ }
354
+
355
+ /**
356
+ * Rejoue l'historique
357
+ * @param {string} [event] - Filtrer par événement
358
+ */
359
+ replay(event) {
360
+ const items = this.getHistory(event);
361
+
362
+ for (let item of items) {
363
+ this.emit(item.event, ...item.args);
364
+ }
365
+ }
366
+
367
+ /**
368
+ * Génère un ID unique
369
+ * @returns {string} ID
370
+ * @private
371
+ */
372
+ generateId() {
373
+ return `${Date.now()}-${Math.random().toString(36).substr(2, 9)}`;
374
+ }
375
+
376
+ /**
377
+ * Debug: affiche tous les listeners
378
+ */
379
+ debug() {
380
+ console.log('EventBus Debug:');
381
+ console.log('Events:', this.eventNames());
382
+
383
+ for (let [event, listeners] of this.events.entries()) {
384
+ console.log(` ${event}: ${listeners.length} listener(s)`);
385
+ listeners.forEach((listener, i) => {
386
+ console.log(` [${i}] Priority: ${listener.priority}, Once: ${listener.once}`);
387
+ });
388
+ }
389
+
390
+ if (this.enableHistory) {
391
+ console.log('History:', this.history.length, 'events');
392
+ }
393
+ }
394
+ }
395
+
396
+ // Instance globale par défaut
397
+ EventBus.global = new EventBus();
398
+
399
+ // Raccourcis globaux
400
+ EventBus.on = (...args) => EventBus.global.on(...args);
401
+ EventBus.once = (...args) => EventBus.global.once(...args);
402
+ EventBus.off = (...args) => EventBus.global.off(...args);
403
+ EventBus.emit = (...args) => EventBus.global.emit(...args);
404
+ EventBus.emitAsync = (...args) => EventBus.global.emitAsync(...args);
405
+ EventBus.waitFor = (...args) => EventBus.global.waitFor(...args);
406
+
407
+ export default EventBus;
@@ -0,0 +1,74 @@
1
+ /**
2
+ * Client HTTP avec cache interne TTL
3
+ * @class
4
+ * @property {Map<string, {data:any, expiresAt:number}>} cache - Cache interne
5
+ * @property {Object} defaultHeaders - Headers par défaut
6
+ * @property {number} defaultTTL - TTL par défaut pour le cache (ms)
7
+ */
8
+ class FetchClient {
9
+ /**
10
+ * Crée une instance de FetchClient
11
+ * @param {Object} [options={}]
12
+ * @param {Object} [options.defaultHeaders] - Headers par défaut
13
+ * @param {number} [options.defaultTTL=0] - TTL par défaut en ms (0 = pas de cache)
14
+ */
15
+ constructor(options = {}) {
16
+ this.defaultHeaders = options.defaultHeaders || { 'Content-Type': 'application/json' };
17
+ this.defaultTTL = options.defaultTTL || 0;
18
+ this.cache = new Map();
19
+ }
20
+
21
+ /**
22
+ * GET avec cache TTL optionnel
23
+ * @param {string} url - URL de la requête
24
+ * @param {Object} [options] - Options supplémentaires
25
+ * @param {number} [options.ttl] - TTL pour cette requête (ms)
26
+ * @returns {Promise<any>} - Données JSON
27
+ */
28
+ async get(url, options = {}) {
29
+ const ttl = options.ttl ?? this.defaultTTL;
30
+
31
+ const cached = this.cache.get(url);
32
+ if (cached && (ttl === 0 || cached.expiresAt > Date.now())) {
33
+ return cached.data;
34
+ }
35
+
36
+ const res = await fetch(url, { method: 'GET', headers: this.defaultHeaders });
37
+ if (!res.ok) throw new Error(`Fetch error ${res.status}: ${res.statusText}`);
38
+
39
+ const data = await res.json();
40
+
41
+ if (ttl > 0) {
42
+ this.cache.set(url, { data, expiresAt: Date.now() + ttl });
43
+ }
44
+
45
+ return data;
46
+ }
47
+
48
+ /**
49
+ * POST
50
+ * @param {string} url - URL
51
+ * @param {Object} body - Corps de la requête
52
+ * @param {Object} [options] - Options supplémentaires
53
+ * @param {Object} [options.headers] - Headers supplémentaires
54
+ * @returns {Promise<any>} - Données JSON
55
+ */
56
+ async post(url, body, options = {}) {
57
+ const res = await fetch(url, {
58
+ method: 'POST',
59
+ headers: { ...this.defaultHeaders, ...(options.headers || {}) },
60
+ body: JSON.stringify(body)
61
+ });
62
+ if (!res.ok) throw new Error(`Fetch error ${res.status}: ${res.statusText}`);
63
+ return res.json();
64
+ }
65
+
66
+ /**
67
+ * Vide le cache
68
+ */
69
+ clearCache() {
70
+ this.cache.clear();
71
+ }
72
+ }
73
+
74
+ export default FetchClient;