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,478 @@
1
+ /**
2
+ * Constructeur de requêtes pour filtrage, tri et pagination de données
3
+ * @class
4
+ * @property {Array} data - Données source
5
+ * @property {Array} filters - Filtres appliqués
6
+ * @property {Array} sorts - Tris appliqués
7
+ * @property {number} offset - Offset de pagination
8
+ * @property {number} limit - Limite de résultats
9
+ */
10
+ class QueryBuilder {
11
+ /**
12
+ * Crée une instance de QueryBuilder
13
+ * @param {Array} data - Données à interroger
14
+ */
15
+ constructor(data = []) {
16
+ this.data = data;
17
+ this.filters = [];
18
+ this.sorts = [];
19
+ this.offset = 0;
20
+ this.limit = null;
21
+ this.grouped = null;
22
+ }
23
+
24
+ /**
25
+ * Crée une nouvelle instance
26
+ * @param {Array} data - Données
27
+ * @returns {QueryBuilder} Nouvelle instance
28
+ */
29
+ static from(data) {
30
+ return new QueryBuilder(data);
31
+ }
32
+
33
+ /**
34
+ * Filtre où la valeur est égale
35
+ * @param {string} field - Champ
36
+ * @param {*} value - Valeur
37
+ * @returns {QueryBuilder} Instance
38
+ */
39
+ where(field, value) {
40
+ this.filters.push({ type: 'equals', field, value });
41
+ return this;
42
+ }
43
+
44
+ /**
45
+ * Filtre où la valeur est différente
46
+ * @param {string} field - Champ
47
+ * @param {*} value - Valeur
48
+ * @returns {QueryBuilder} Instance
49
+ */
50
+ whereNot(field, value) {
51
+ this.filters.push({ type: 'not', field, value });
52
+ return this;
53
+ }
54
+
55
+ /**
56
+ * Filtre avec opérateur personnalisé
57
+ * @param {string} field - Champ
58
+ * @param {string} operator - Opérateur (>, <, >=, <=, contains, startsWith, endsWith)
59
+ * @param {*} value - Valeur
60
+ * @returns {QueryBuilder} Instance
61
+ */
62
+ whereOperator(field, operator, value) {
63
+ this.filters.push({ type: 'operator', field, operator, value });
64
+ return this;
65
+ }
66
+
67
+ /**
68
+ * Filtre où la valeur est dans un tableau
69
+ * @param {string} field - Champ
70
+ * @param {Array} values - Valeurs
71
+ * @returns {QueryBuilder} Instance
72
+ */
73
+ whereIn(field, values) {
74
+ this.filters.push({ type: 'in', field, values });
75
+ return this;
76
+ }
77
+
78
+ /**
79
+ * Filtre où la valeur n'est pas dans un tableau
80
+ * @param {string} field - Champ
81
+ * @param {Array} values - Valeurs
82
+ * @returns {QueryBuilder} Instance
83
+ */
84
+ whereNotIn(field, values) {
85
+ this.filters.push({ type: 'notIn', field, values });
86
+ return this;
87
+ }
88
+
89
+ /**
90
+ * Filtre avec une fonction personnalisée
91
+ * @param {Function} predicate - Fonction de filtre
92
+ * @returns {QueryBuilder} Instance
93
+ */
94
+ whereCustom(predicate) {
95
+ this.filters.push({ type: 'custom', predicate });
96
+ return this;
97
+ }
98
+
99
+ /**
100
+ * Filtre where null
101
+ * @param {string} field - Champ
102
+ * @returns {QueryBuilder} Instance
103
+ */
104
+ whereNull(field) {
105
+ this.filters.push({ type: 'null', field });
106
+ return this;
107
+ }
108
+
109
+ /**
110
+ * Filtre where not null
111
+ * @param {string} field - Champ
112
+ * @returns {QueryBuilder} Instance
113
+ */
114
+ whereNotNull(field) {
115
+ this.filters.push({ type: 'notNull', field });
116
+ return this;
117
+ }
118
+
119
+ /**
120
+ * Recherche textuelle (like)
121
+ * @param {string} field - Champ
122
+ * @param {string} search - Texte à chercher
123
+ * @param {boolean} [caseSensitive=false] - Sensible à la casse
124
+ * @returns {QueryBuilder} Instance
125
+ */
126
+ search(field, search, caseSensitive = false) {
127
+ this.filters.push({ type: 'search', field, search, caseSensitive });
128
+ return this;
129
+ }
130
+
131
+ /**
132
+ * Tri ascendant
133
+ * @param {string} field - Champ
134
+ * @returns {QueryBuilder} Instance
135
+ */
136
+ orderBy(field) {
137
+ this.sorts.push({ field, direction: 'asc' });
138
+ return this;
139
+ }
140
+
141
+ /**
142
+ * Tri descendant
143
+ * @param {string} field - Champ
144
+ * @returns {QueryBuilder} Instance
145
+ */
146
+ orderByDesc(field) {
147
+ this.sorts.push({ field, direction: 'desc' });
148
+ return this;
149
+ }
150
+
151
+ /**
152
+ * Tri avec comparateur personnalisé
153
+ * @param {Function} compareFn - Fonction de comparaison
154
+ * @returns {QueryBuilder} Instance
155
+ */
156
+ orderByCustom(compareFn) {
157
+ this.sorts.push({ type: 'custom', compareFn });
158
+ return this;
159
+ }
160
+
161
+ /**
162
+ * Limite le nombre de résultats
163
+ * @param {number} limit - Nombre max de résultats
164
+ * @returns {QueryBuilder} Instance
165
+ */
166
+ take(limit) {
167
+ this.limit = limit;
168
+ return this;
169
+ }
170
+
171
+ /**
172
+ * Saute les N premiers résultats
173
+ * @param {number} offset - Nombre à sauter
174
+ * @returns {QueryBuilder} Instance
175
+ */
176
+ skip(offset) {
177
+ this.offset = offset;
178
+ return this;
179
+ }
180
+
181
+ /**
182
+ * Pagination
183
+ * @param {number} page - Numéro de page (1-indexed)
184
+ * @param {number} perPage - Éléments par page
185
+ * @returns {QueryBuilder} Instance
186
+ */
187
+ paginate(page, perPage) {
188
+ this.offset = (page - 1) * perPage;
189
+ this.limit = perPage;
190
+ return this;
191
+ }
192
+
193
+ /**
194
+ * Groupe par champ
195
+ * @param {string} field - Champ de groupement
196
+ * @returns {QueryBuilder} Instance
197
+ */
198
+ groupBy(field) {
199
+ this.grouped = field;
200
+ return this;
201
+ }
202
+
203
+ /**
204
+ * Applique les filtres
205
+ * @param {Array} data - Données
206
+ * @returns {Array} Données filtrées
207
+ * @private
208
+ */
209
+ applyFilters(data) {
210
+ let result = [...data];
211
+
212
+ for (let filter of this.filters) {
213
+ result = result.filter(item => {
214
+ switch (filter.type) {
215
+ case 'equals':
216
+ return item[filter.field] === filter.value;
217
+
218
+ case 'not':
219
+ return item[filter.field] !== filter.value;
220
+
221
+ case 'operator':
222
+ return this.applyOperator(item[filter.field], filter.operator, filter.value);
223
+
224
+ case 'in':
225
+ return filter.values.includes(item[filter.field]);
226
+
227
+ case 'notIn':
228
+ return !filter.values.includes(item[filter.field]);
229
+
230
+ case 'null':
231
+ return item[filter.field] == null;
232
+
233
+ case 'notNull':
234
+ return item[filter.field] != null;
235
+
236
+ case 'search':
237
+ const fieldValue = String(item[filter.field]);
238
+ const searchValue = filter.search;
239
+
240
+ if (filter.caseSensitive) {
241
+ return fieldValue.includes(searchValue);
242
+ } else {
243
+ return fieldValue.toLowerCase().includes(searchValue.toLowerCase());
244
+ }
245
+
246
+ case 'custom':
247
+ return filter.predicate(item);
248
+
249
+ default:
250
+ return true;
251
+ }
252
+ });
253
+ }
254
+
255
+ return result;
256
+ }
257
+
258
+ /**
259
+ * Applique un opérateur
260
+ * @param {*} fieldValue - Valeur du champ
261
+ * @param {string} operator - Opérateur
262
+ * @param {*} value - Valeur de comparaison
263
+ * @returns {boolean} Résultat
264
+ * @private
265
+ */
266
+ applyOperator(fieldValue, operator, value) {
267
+ switch (operator) {
268
+ case '>': return fieldValue > value;
269
+ case '<': return fieldValue < value;
270
+ case '>=': return fieldValue >= value;
271
+ case '<=': return fieldValue <= value;
272
+ case 'contains': return String(fieldValue).includes(value);
273
+ case 'startsWith': return String(fieldValue).startsWith(value);
274
+ case 'endsWith': return String(fieldValue).endsWith(value);
275
+ default: return false;
276
+ }
277
+ }
278
+
279
+ /**
280
+ * Applique les tris
281
+ * @param {Array} data - Données
282
+ * @returns {Array} Données triées
283
+ * @private
284
+ */
285
+ applySorts(data) {
286
+ if (this.sorts.length === 0) return data;
287
+
288
+ return [...data].sort((a, b) => {
289
+ for (let sort of this.sorts) {
290
+ if (sort.type === 'custom') {
291
+ return sort.compareFn(a, b);
292
+ }
293
+
294
+ const aVal = a[sort.field];
295
+ const bVal = b[sort.field];
296
+
297
+ let comparison = 0;
298
+
299
+ if (aVal == null && bVal == null) continue;
300
+ if (aVal == null) return 1;
301
+ if (bVal == null) return -1;
302
+
303
+ if (typeof aVal === 'number' && typeof bVal === 'number') {
304
+ comparison = aVal - bVal;
305
+ } else {
306
+ comparison = String(aVal).localeCompare(String(bVal));
307
+ }
308
+
309
+ if (comparison !== 0) {
310
+ return sort.direction === 'asc' ? comparison : -comparison;
311
+ }
312
+ }
313
+
314
+ return 0;
315
+ });
316
+ }
317
+
318
+ /**
319
+ * Applique la pagination
320
+ * @param {Array} data - Données
321
+ * @returns {Array} Données paginées
322
+ * @private
323
+ */
324
+ applyPagination(data) {
325
+ const start = this.offset;
326
+ const end = this.limit ? start + this.limit : data.length;
327
+ return data.slice(start, end);
328
+ }
329
+
330
+ /**
331
+ * Applique le groupement
332
+ * @param {Array} data - Données
333
+ * @returns {Object} Données groupées
334
+ * @private
335
+ */
336
+ applyGrouping(data) {
337
+ const groups = {};
338
+
339
+ for (let item of data) {
340
+ const key = item[this.grouped];
341
+
342
+ if (!groups[key]) {
343
+ groups[key] = [];
344
+ }
345
+
346
+ groups[key].push(item);
347
+ }
348
+
349
+ return groups;
350
+ }
351
+
352
+ /**
353
+ * Exécute la requête et retourne les résultats
354
+ * @returns {Array|Object} Résultats
355
+ */
356
+ get() {
357
+ let result = this.applyFilters(this.data);
358
+ result = this.applySorts(result);
359
+
360
+ if (this.grouped) {
361
+ return this.applyGrouping(result);
362
+ }
363
+
364
+ result = this.applyPagination(result);
365
+ return result;
366
+ }
367
+
368
+ /**
369
+ * Retourne le premier résultat
370
+ * @returns {*} Premier élément ou null
371
+ */
372
+ first() {
373
+ const results = this.take(1).get();
374
+ return results.length > 0 ? results[0] : null;
375
+ }
376
+
377
+ /**
378
+ * Compte les résultats
379
+ * @returns {number} Nombre de résultats
380
+ */
381
+ count() {
382
+ return this.applyFilters(this.data).length;
383
+ }
384
+
385
+ /**
386
+ * Vérifie si des résultats existent
387
+ * @returns {boolean} True si résultats
388
+ */
389
+ exists() {
390
+ return this.count() > 0;
391
+ }
392
+
393
+ /**
394
+ * Obtient des valeurs uniques d'un champ
395
+ * @param {string} field - Champ
396
+ * @returns {Array} Valeurs uniques
397
+ */
398
+ pluck(field) {
399
+ return this.get().map(item => item[field]);
400
+ }
401
+
402
+ /**
403
+ * Obtient des valeurs uniques
404
+ * @param {string} field - Champ
405
+ * @returns {Array} Valeurs uniques
406
+ */
407
+ unique(field) {
408
+ return [...new Set(this.pluck(field))];
409
+ }
410
+
411
+ /**
412
+ * Calcule la somme d'un champ
413
+ * @param {string} field - Champ
414
+ * @returns {number} Somme
415
+ */
416
+ sum(field) {
417
+ return this.get().reduce((sum, item) => sum + (Number(item[field]) || 0), 0);
418
+ }
419
+
420
+ /**
421
+ * Calcule la moyenne d'un champ
422
+ * @param {string} field - Champ
423
+ * @returns {number} Moyenne
424
+ */
425
+ avg(field) {
426
+ const items = this.get();
427
+ return items.length > 0 ? this.sum(field) / items.length : 0;
428
+ }
429
+
430
+ /**
431
+ * Trouve le minimum d'un champ
432
+ * @param {string} field - Champ
433
+ * @returns {number} Minimum
434
+ */
435
+ min(field) {
436
+ const values = this.pluck(field).filter(v => v != null);
437
+ return values.length > 0 ? Math.min(...values) : null;
438
+ }
439
+
440
+ /**
441
+ * Trouve le maximum d'un champ
442
+ * @param {string} field - Champ
443
+ * @returns {number} Maximum
444
+ */
445
+ max(field) {
446
+ const values = this.pluck(field).filter(v => v != null);
447
+ return values.length > 0 ? Math.max(...values) : null;
448
+ }
449
+
450
+ /**
451
+ * Clone la requête
452
+ * @returns {QueryBuilder} Nouveau builder
453
+ */
454
+ clone() {
455
+ const builder = new QueryBuilder(this.data);
456
+ builder.filters = [...this.filters];
457
+ builder.sorts = [...this.sorts];
458
+ builder.offset = this.offset;
459
+ builder.limit = this.limit;
460
+ builder.grouped = this.grouped;
461
+ return builder;
462
+ }
463
+
464
+ /**
465
+ * Réinitialise la requête
466
+ * @returns {QueryBuilder} Instance
467
+ */
468
+ reset() {
469
+ this.filters = [];
470
+ this.sorts = [];
471
+ this.offset = 0;
472
+ this.limit = null;
473
+ this.grouped = null;
474
+ return this;
475
+ }
476
+ }
477
+
478
+ export default QueryBuilder;
@@ -0,0 +1,64 @@
1
+ /**
2
+ * Gestion des zones sûres (safe area) pour les appareils avec encoches ou barres de navigation
3
+ * @class
4
+ * @static
5
+ * @example
6
+ * SafeArea.detect();
7
+ * SafeArea.applyToComponent(myComponent);
8
+ */
9
+ class SafeArea {
10
+ /** @type {number} */
11
+ static top = 0;
12
+ /** @type {number} */
13
+ static bottom = 0;
14
+ /** @type {number} */
15
+ static left = 0;
16
+ /** @type {number} */
17
+ static right = 0;
18
+
19
+ /**
20
+ * Détecte et définit les marges de safe area en fonction de l'appareil
21
+ * @static
22
+ */
23
+ static detect() {
24
+ // Détecter l'iPhone X et supérieur (notch)
25
+ const isIPhoneX = /iPhone/.test(navigator.userAgent) && window.screen.height >= 812;
26
+
27
+ if (isIPhoneX) {
28
+ // Portrait
29
+ if (window.innerHeight > window.innerWidth) {
30
+ this.top = 44; // Status bar + notch
31
+ this.bottom = 34; // Home indicator
32
+ } else {
33
+ // Landscape
34
+ this.top = 0;
35
+ this.bottom = 21;
36
+ this.left = 44;
37
+ this.right = 44;
38
+ }
39
+ }
40
+
41
+ // Détecter Android avec notch via CSS
42
+ if (CSS.supports('padding-top: env(safe-area-inset-top)')) {
43
+ const style = getComputedStyle(document.documentElement);
44
+ this.top = parseInt(style.getPropertyValue('env(safe-area-inset-top)')) || 0;
45
+ this.bottom = parseInt(style.getPropertyValue('env(safe-area-inset-bottom)')) || 0;
46
+ this.left = parseInt(style.getPropertyValue('env(safe-area-inset-left)')) || 0;
47
+ this.right = parseInt(style.getPropertyValue('env(safe-area-inset-right)')) || 0;
48
+ }
49
+ }
50
+
51
+ /**
52
+ * Applique les marges de safe area à un composant
53
+ * @static
54
+ * @param {Component} component - Composant à ajuster
55
+ */
56
+ static applyToComponent(component) {
57
+ component.y += this.top;
58
+ component.x += this.left;
59
+ component.width -= (this.left + this.right);
60
+ component.height -= (this.top + this.bottom);
61
+ }
62
+ }
63
+
64
+ export default SafeArea;