canvasframework 0.5.18 → 0.5.20

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 (113) hide show
  1. package/README.md +30 -0
  2. package/components/Accordion.js +265 -0
  3. package/components/AndroidDatePickerDialog.js +406 -0
  4. package/components/AppBar.js +398 -0
  5. package/components/AudioPlayer.js +611 -0
  6. package/components/Avatar.js +202 -0
  7. package/components/Banner.js +342 -0
  8. package/components/BottomNavigationBar.js +433 -0
  9. package/components/BottomSheet.js +234 -0
  10. package/components/Button.js +358 -0
  11. package/components/Camera.js +644 -0
  12. package/components/Card.js +193 -0
  13. package/components/Chart.js +700 -0
  14. package/components/Checkbox.js +166 -0
  15. package/components/Chip.js +212 -0
  16. package/components/CircularProgress.js +327 -0
  17. package/components/ContextMenu.js +116 -0
  18. package/components/DatePicker.js +298 -0
  19. package/components/Dialog.js +337 -0
  20. package/components/Divider.js +125 -0
  21. package/components/Drawer.js +276 -0
  22. package/components/FAB.js +270 -0
  23. package/components/FileUpload.js +315 -0
  24. package/components/FloatedCamera.js +644 -0
  25. package/components/IOSDatePickerWheel.js +430 -0
  26. package/components/ImageCarousel.js +219 -0
  27. package/components/ImageComponent.js +223 -0
  28. package/components/Input.js +831 -0
  29. package/components/InputDatalist.js +723 -0
  30. package/components/InputTags.js +624 -0
  31. package/components/List.js +95 -0
  32. package/components/ListItem.js +269 -0
  33. package/components/Modal.js +364 -0
  34. package/components/MorphingFAB.js +428 -0
  35. package/components/MultiSelectDialog.js +206 -0
  36. package/components/NumberInput.js +271 -0
  37. package/components/PasswordInput.js +462 -0
  38. package/components/ProgressBar.js +88 -0
  39. package/components/QRCodeReader.js +539 -0
  40. package/components/RadioButton.js +151 -0
  41. package/components/SearchInput.js +315 -0
  42. package/components/SegmentedControl.js +357 -0
  43. package/components/Select.js +199 -0
  44. package/components/SelectDialog.js +255 -0
  45. package/components/Slider.js +113 -0
  46. package/components/SliverAppBar.js +139 -0
  47. package/components/Snackbar.js +243 -0
  48. package/components/SpeedDialFAB.js +397 -0
  49. package/components/Stepper.js +281 -0
  50. package/components/SwipeableListItem.js +327 -0
  51. package/components/Switch.js +147 -0
  52. package/components/Table.js +492 -0
  53. package/components/Tabs.js +423 -0
  54. package/components/Text.js +141 -0
  55. package/components/TextField.js +151 -0
  56. package/components/TimePicker.js +934 -0
  57. package/components/Toast.js +236 -0
  58. package/components/TreeView.js +420 -0
  59. package/components/Video.js +397 -0
  60. package/components/View.js +140 -0
  61. package/components/VirtualList.js +120 -0
  62. package/core/CanvasFramework.js +3045 -0
  63. package/core/Component.js +243 -0
  64. package/core/ThemeManager.js +358 -0
  65. package/core/UIBuilder.js +267 -0
  66. package/core/WebGLCanvasAdapter.js +782 -0
  67. package/features/Column.js +43 -0
  68. package/features/Grid.js +47 -0
  69. package/features/LayoutComponent.js +43 -0
  70. package/features/OpenStreetMap.js +310 -0
  71. package/features/Positioned.js +33 -0
  72. package/features/PullToRefresh.js +328 -0
  73. package/features/Row.js +40 -0
  74. package/features/SignaturePad.js +257 -0
  75. package/features/Skeleton.js +193 -0
  76. package/features/Stack.js +21 -0
  77. package/index.js +119 -0
  78. package/manager/AccessibilityManager.js +107 -0
  79. package/manager/ErrorHandler.js +59 -0
  80. package/manager/FeatureFlags.js +60 -0
  81. package/manager/MemoryManager.js +107 -0
  82. package/manager/PerformanceMonitor.js +84 -0
  83. package/manager/SecurityManager.js +54 -0
  84. package/package.json +22 -16
  85. package/utils/AnimationEngine.js +734 -0
  86. package/utils/CryptoManager.js +303 -0
  87. package/utils/DataStore.js +403 -0
  88. package/utils/DevTools.js +1618 -0
  89. package/utils/DevToolsConsole.js +201 -0
  90. package/utils/EventBus.js +407 -0
  91. package/utils/FetchClient.js +74 -0
  92. package/utils/FirebaseAuth.js +653 -0
  93. package/utils/FirebaseCore.js +246 -0
  94. package/utils/FirebaseFirestore.js +581 -0
  95. package/utils/FirebaseFunctions.js +97 -0
  96. package/utils/FirebaseRealtimeDB.js +498 -0
  97. package/utils/FirebaseStorage.js +612 -0
  98. package/utils/FormValidator.js +355 -0
  99. package/utils/GeoLocationService.js +62 -0
  100. package/utils/I18n.js +207 -0
  101. package/utils/IndexedDBManager.js +273 -0
  102. package/utils/InspectionOverlay.js +308 -0
  103. package/utils/NotificationManager.js +60 -0
  104. package/utils/OfflineSyncManager.js +342 -0
  105. package/utils/PayPalPayment.js +678 -0
  106. package/utils/QueryBuilder.js +478 -0
  107. package/utils/SafeArea.js +64 -0
  108. package/utils/SecureStorage.js +289 -0
  109. package/utils/StateManager.js +207 -0
  110. package/utils/StripePayment.js +552 -0
  111. package/utils/WebSocketClient.js +66 -0
  112. package/dist/canvasframework.js +0 -2
  113. package/dist/canvasframework.js.LICENSE.txt +0 -1
@@ -0,0 +1,398 @@
1
+ import Component from '../core/Component.js';
2
+
3
+ /**
4
+ * Barre d'application supérieure (Material & Cupertino)
5
+ * @class
6
+ * @extends Component
7
+ */
8
+ class AppBar extends Component {
9
+ /**
10
+ * Crée une instance de AppBar
11
+ * @param {CanvasFramework} framework - Framework parent
12
+ * @param {Object} [options={}] - Options de configuration
13
+ * @param {string} [options.title=''] - Titre
14
+ * @param {string} [options.leftIcon] - Icône gauche ('menu' ou 'back')
15
+ * @param {string} [options.rightIcon] - Icône droite ('search' ou 'more')
16
+ * @param {Function} [options.onLeftClick] - Callback gauche
17
+ * @param {Function} [options.onRightClick] - Callback droit
18
+ * @param {number} [options.height] - Hauteur (auto selon platform)
19
+ * @param {string} [options.bgColor] - Couleur de fond
20
+ * @param {string} [options.textColor] - Couleur du texte
21
+ * @param {number} [options.elevation=4] - Élévation (Material)
22
+ */
23
+ constructor(framework, options = {}) {
24
+ super(framework, {
25
+ x: 0,
26
+ y: 0,
27
+ width: framework.width,
28
+ height: options.height || (framework.platform === 'material' ? 56 : 44),
29
+ ...options
30
+ });
31
+
32
+ this.title = options.title || '';
33
+ this.leftIcon = options.leftIcon || null;
34
+ this.rightIcon = options.rightIcon || null;
35
+ this.onLeftClick = options.onLeftClick;
36
+ this.onRightClick = options.onRightClick;
37
+ this.platform = framework.platform;
38
+
39
+ // Couleurs selon la plateforme
40
+ if (this.platform === 'material') {
41
+ this.bgColor = options.bgColor || '#6200EE';
42
+ this.textColor = options.textColor || '#FFFFFF';
43
+ this.elevation = options.elevation !== undefined ? options.elevation : 4;
44
+ } else {
45
+ // iOS : Transparent ou blanc avec blur effect (simulé)
46
+ this.bgColor = options.bgColor || 'rgba(248, 248, 248, 0.95)';
47
+ this.textColor = options.textColor || '#000000';
48
+ this.elevation = 0;
49
+ }
50
+
51
+ // Ripple effect (Material uniquement)
52
+ this.ripples = [];
53
+ this.animationFrame = null;
54
+ this.lastAnimationTime = 0;
55
+
56
+ // États pressed pour iOS
57
+ this.leftPressed = false;
58
+ this.rightPressed = false;
59
+
60
+ this.onPress = this.handlePress.bind(this);
61
+ }
62
+
63
+ /**
64
+ * Démarrer l'animation des ripples
65
+ * @private
66
+ */
67
+ startRippleAnimation() {
68
+ const animate = (timestamp) => {
69
+ if (!this.lastAnimationTime) this.lastAnimationTime = timestamp;
70
+ const deltaTime = timestamp - this.lastAnimationTime;
71
+ this.lastAnimationTime = timestamp;
72
+
73
+ let needsUpdate = false;
74
+
75
+ // Mettre à jour chaque ripple
76
+ for (let i = this.ripples.length - 1; i >= 0; i--) {
77
+ const ripple = this.ripples[i];
78
+
79
+ // Animer le rayon (expansion)
80
+ if (ripple.radius < ripple.maxRadius) {
81
+ ripple.radius += (ripple.maxRadius / 250) * deltaTime;
82
+ needsUpdate = true;
83
+ }
84
+
85
+ // Animer l'opacité (fade out) - commencer plus tôt
86
+ if (ripple.radius >= ripple.maxRadius * 0.4) {
87
+ ripple.opacity -= (0.003 * deltaTime);
88
+ if (ripple.opacity < 0) ripple.opacity = 0;
89
+ needsUpdate = true;
90
+ }
91
+
92
+ // Supprimer les ripples terminés
93
+ if (ripple.opacity <= 0 && ripple.radius >= ripple.maxRadius * 0.95) {
94
+ this.ripples.splice(i, 1);
95
+ needsUpdate = true;
96
+ }
97
+ }
98
+
99
+ // Redessiner si nécessaire
100
+ if (needsUpdate) {
101
+ this.requestRender();
102
+ }
103
+
104
+ // Continuer l'animation
105
+ if (this.ripples.length > 0) {
106
+ this.animationFrame = requestAnimationFrame(animate);
107
+ } else {
108
+ this.animationFrame = null;
109
+ this.lastAnimationTime = 0;
110
+ }
111
+ };
112
+
113
+ if (this.ripples.length > 0 && !this.animationFrame) {
114
+ this.animationFrame = requestAnimationFrame(animate);
115
+ }
116
+ }
117
+
118
+ /**
119
+ * Demander un redessin
120
+ * @private
121
+ */
122
+ requestRender() {
123
+ if (this.framework && this.framework.requestRender) {
124
+ this.framework.requestRender();
125
+ }
126
+ }
127
+
128
+ /**
129
+ * Nettoyer l'animation lors de la destruction
130
+ */
131
+ destroy() {
132
+ if (this.animationFrame) {
133
+ cancelAnimationFrame(this.animationFrame);
134
+ this.animationFrame = null;
135
+ }
136
+ super.destroy();
137
+ }
138
+
139
+ /**
140
+ * Dessine l'AppBar
141
+ * @param {CanvasRenderingContext2D} ctx - Contexte de dessin
142
+ */
143
+ draw(ctx) {
144
+ ctx.save();
145
+
146
+ // Ombre (Material uniquement)
147
+ if (this.platform === 'material' && this.elevation > 0) {
148
+ ctx.shadowColor = 'rgba(0, 0, 0, 0.2)';
149
+ ctx.shadowBlur = this.elevation * 2;
150
+ ctx.shadowOffsetY = this.elevation / 2;
151
+ }
152
+
153
+ // Background
154
+ ctx.fillStyle = this.bgColor;
155
+ ctx.fillRect(this.x, this.y, this.width, this.height);
156
+
157
+ ctx.shadowColor = 'transparent';
158
+ ctx.shadowBlur = 0;
159
+ ctx.shadowOffsetY = 0;
160
+
161
+ // Bordure inférieure (iOS uniquement)
162
+ if (this.platform === 'cupertino') {
163
+ ctx.strokeStyle = 'rgba(0, 0, 0, 0.1)';
164
+ ctx.lineWidth = 0.5;
165
+ ctx.beginPath();
166
+ ctx.moveTo(this.x, this.y + this.height - 0.5);
167
+ ctx.lineTo(this.x + this.width, this.y + this.height - 0.5);
168
+ ctx.stroke();
169
+ }
170
+
171
+ // Ripples pour les boutons (Material)
172
+ if (this.platform === 'material') {
173
+ this.drawRipples(ctx);
174
+ }
175
+
176
+ // Overlay pressed pour iOS
177
+ if (this.platform === 'cupertino') {
178
+ if (this.leftPressed && this.leftIcon) {
179
+ ctx.fillStyle = 'rgba(0, 0, 0, 0.1)';
180
+ ctx.beginPath();
181
+ ctx.arc(this.x + 28, this.y + this.height / 2, 20, 0, Math.PI * 2);
182
+ ctx.fill();
183
+ }
184
+ if (this.rightPressed && this.rightIcon) {
185
+ ctx.fillStyle = 'rgba(0, 0, 0, 0.1)';
186
+ ctx.beginPath();
187
+ ctx.arc(this.x + this.width - 28, this.y + this.height / 2, 20, 0, Math.PI * 2);
188
+ ctx.fill();
189
+ }
190
+ }
191
+
192
+ // Titre
193
+ ctx.fillStyle = this.textColor;
194
+ const titleAlign = this.platform === 'material' && this.leftIcon ? 'left' : 'center';
195
+ const titleX = titleAlign === 'left' ? this.x + 72 : this.x + this.width / 2;
196
+ ctx.font = `${this.platform === 'material' ? 'bold ' : ''}20px -apple-system, Roboto, sans-serif`;
197
+ ctx.textAlign = titleAlign;
198
+ ctx.textBaseline = 'middle';
199
+ ctx.fillText(this.title, titleX, this.y + this.height / 2);
200
+
201
+ // Icône gauche
202
+ if (this.leftIcon) {
203
+ const iconColor = this.platform === 'cupertino' ? this.textColor : this.textColor;
204
+ if (this.leftIcon === 'menu') {
205
+ this.drawMenuIcon(ctx, this.x + 16, this.y + this.height / 2, iconColor);
206
+ } else if (this.leftIcon === 'back') {
207
+ this.drawBackIcon(ctx, this.x + 16, this.y + this.height / 2, iconColor);
208
+ }
209
+ }
210
+
211
+ // Icône droite
212
+ if (this.rightIcon) {
213
+ const iconColor = this.platform === 'cupertino' ? '#007AFF' : this.textColor;
214
+ if (this.rightIcon === 'search') {
215
+ this.drawSearchIcon(ctx, this.x + this.width - 36, this.y + this.height / 2, iconColor);
216
+ } else if (this.rightIcon === 'more') {
217
+ this.drawMoreIcon(ctx, this.x + this.width - 36, this.y + this.height / 2, iconColor);
218
+ }
219
+ }
220
+
221
+ ctx.restore();
222
+ }
223
+
224
+ /**
225
+ * Dessine les ripples
226
+ * @private
227
+ */
228
+ drawRipples(ctx) {
229
+ for (let ripple of this.ripples) {
230
+ ctx.save();
231
+ ctx.globalAlpha = ripple.opacity;
232
+ ctx.fillStyle = 'rgba(255, 255, 255, 0.3)';
233
+ ctx.beginPath();
234
+ ctx.arc(ripple.x, ripple.y, ripple.radius, 0, Math.PI * 2);
235
+ ctx.fill();
236
+ ctx.restore();
237
+ }
238
+ }
239
+
240
+ /**
241
+ * Dessine l'icône menu (hamburger)
242
+ * @private
243
+ */
244
+ drawMenuIcon(ctx, x, y, color) {
245
+ ctx.strokeStyle = color;
246
+ ctx.lineWidth = 2;
247
+ ctx.lineCap = 'round';
248
+ for (let i = 0; i < 3; i++) {
249
+ ctx.beginPath();
250
+ ctx.moveTo(x, y - 8 + i * 8);
251
+ ctx.lineTo(x + 24, y - 8 + i * 8);
252
+ ctx.stroke();
253
+ }
254
+ }
255
+
256
+ /**
257
+ * Dessine l'icône retour
258
+ * @private
259
+ */
260
+ drawBackIcon(ctx, x, y, color) {
261
+ ctx.strokeStyle = color;
262
+ ctx.lineWidth = 2;
263
+ ctx.lineCap = 'round';
264
+ ctx.lineJoin = 'round';
265
+ ctx.beginPath();
266
+ ctx.moveTo(x + 16, y - 10);
267
+ ctx.lineTo(x + 6, y);
268
+ ctx.lineTo(x + 16, y + 10);
269
+ ctx.stroke();
270
+ }
271
+
272
+ /**
273
+ * Dessine l'icône recherche
274
+ * @private
275
+ */
276
+ drawSearchIcon(ctx, x, y, color) {
277
+ ctx.strokeStyle = color;
278
+ ctx.lineWidth = 2;
279
+ ctx.lineCap = 'round';
280
+ ctx.beginPath();
281
+ ctx.arc(x + 8, y - 2, 8, 0, Math.PI * 2);
282
+ ctx.stroke();
283
+ ctx.beginPath();
284
+ ctx.moveTo(x + 14, y + 4);
285
+ ctx.lineTo(x + 20, y + 10);
286
+ ctx.stroke();
287
+ }
288
+
289
+ /**
290
+ * Dessine l'icône plus (3 dots)
291
+ * @private
292
+ */
293
+ drawMoreIcon(ctx, x, y, color) {
294
+ ctx.fillStyle = color;
295
+ const spacing = this.platform === 'material' ? 10 : 8;
296
+ for (let i = 0; i < 3; i++) {
297
+ ctx.beginPath();
298
+ ctx.arc(x + 12, y - spacing + i * spacing, 2, 0, Math.PI * 2);
299
+ ctx.fill();
300
+ }
301
+ }
302
+
303
+ /**
304
+ * Vérifie si un point est dans les zones cliquables
305
+ */
306
+ isPointInside(x, y) {
307
+ // Les coordonnées x, y sont absolues, on les compare avec nos coordonnées absolues
308
+ const inY = y >= this.y && y <= this.y + this.height;
309
+
310
+ if (!inY) return false;
311
+
312
+ if (this.leftIcon && x >= this.x && x <= this.x + 56) return true;
313
+ if (this.rightIcon && x >= this.x + this.width - 56 && x <= this.x + this.width) return true;
314
+
315
+ return false;
316
+ }
317
+
318
+ /**
319
+ * Gère la pression (clic)
320
+ * @private
321
+ */
322
+ handlePress(x, y) {
323
+ const adjustedY = y;
324
+
325
+ if (adjustedY >= this.y && adjustedY <= this.y + this.height) {
326
+ // Bouton gauche
327
+ if (this.leftIcon && x >= this.x && x <= this.x + 56) {
328
+ // Ripple effect (Material)
329
+ if (this.platform === 'material') {
330
+ this.ripples.push({
331
+ x: this.x + 28,
332
+ y: this.y + this.height / 2,
333
+ radius: 0,
334
+ maxRadius: 28,
335
+ opacity: 1,
336
+ createdAt: performance.now()
337
+ });
338
+
339
+ // Démarrer l'animation si elle n'est pas en cours
340
+ if (!this.animationFrame) {
341
+ this.startRippleAnimation();
342
+ }
343
+
344
+ // Forcer un redessin
345
+ this.requestRender();
346
+ } else {
347
+ // iOS pressed state
348
+ this.leftPressed = true;
349
+ setTimeout(() => {
350
+ this.leftPressed = false;
351
+ this.requestRender();
352
+ }, 150);
353
+ }
354
+
355
+ if (this.onLeftClick) this.onLeftClick();
356
+ this.requestRender();
357
+ return true;
358
+ }
359
+
360
+ // Bouton droit
361
+ if (this.rightIcon && x >= this.x + this.width - 56 && x <= this.x + this.width) {
362
+ // Ripple effect (Material)
363
+ if (this.platform === 'material') {
364
+ this.ripples.push({
365
+ x: this.x + this.width - 28,
366
+ y: this.y + this.height / 2,
367
+ radius: 0,
368
+ maxRadius: 28,
369
+ opacity: 1,
370
+ createdAt: performance.now()
371
+ });
372
+
373
+ // Démarrer l'animation si elle n'est pas en cours
374
+ if (!this.animationFrame) {
375
+ this.startRippleAnimation();
376
+ }
377
+
378
+ // Forcer un redessin
379
+ this.requestRender();
380
+ } else {
381
+ // iOS pressed state
382
+ this.rightPressed = true;
383
+ setTimeout(() => {
384
+ this.rightPressed = false;
385
+ this.requestRender();
386
+ }, 150);
387
+ }
388
+
389
+ if (this.onRightClick) this.onRightClick();
390
+ this.requestRender();
391
+ return true;
392
+ }
393
+ }
394
+ return false;
395
+ }
396
+ }
397
+
398
+ export default AppBar;