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,337 @@
1
+ import Component from '../core/Component.js';
2
+
3
+ /**
4
+ * Dialog Material (Android) & Cupertino (iOS)
5
+ */
6
+ class Dialog extends Component {
7
+ constructor(framework, options = {}) {
8
+ super(framework, {
9
+ x: 0,
10
+ y: 0,
11
+ width: framework.width,
12
+ height: framework.height,
13
+ visible: false
14
+ });
15
+
16
+ this.platform = framework.platform;
17
+
18
+ this.title = options.title || '';
19
+ this.message = options.message || '';
20
+ this.buttons = options.buttons || ['OK'];
21
+ this.onButtonClick = options.onButtonClick;
22
+
23
+ this.dialogWidth = Math.min(320, framework.width - 40);
24
+ this.dialogHeight = 160;
25
+ this.opacity = 0;
26
+
27
+ this.isVisible = false;
28
+ this.buttonRects = [];
29
+
30
+ /* Ripple Android uniquement */
31
+ this.ripples = [];
32
+ this._animating = false;
33
+
34
+ this.onPress = this.handlePress.bind(this);
35
+
36
+ this.messageLines = this.wrapText(
37
+ this.message,
38
+ this.dialogWidth - 48,
39
+ '16px -apple-system, Roboto, sans-serif'
40
+ );
41
+
42
+ if (this.messageLines.length > 2) {
43
+ this.dialogHeight += (this.messageLines.length - 2) * 22;
44
+ }
45
+ }
46
+
47
+ /* ---------------- DRAW ---------------- */
48
+
49
+ draw(ctx) {
50
+ if (!this.isVisible || this.opacity <= 0) return;
51
+
52
+ ctx.save();
53
+ ctx.globalAlpha = this.opacity;
54
+
55
+ // Overlay
56
+ ctx.fillStyle = 'rgba(0,0,0,0.5)';
57
+ ctx.fillRect(0, 0, this.framework.width, this.framework.height);
58
+
59
+ if (this.platform === 'material') {
60
+ this.drawMaterial(ctx);
61
+ } else {
62
+ this.drawCupertino(ctx); // 👈 TON DESIGN INITIAL
63
+ }
64
+
65
+ ctx.restore();
66
+ }
67
+
68
+ /* ---------------- MATERIAL (ANDROID) ---------------- */
69
+
70
+ drawMaterial(ctx) {
71
+ const x = (this.framework.width - this.dialogWidth) / 2;
72
+ const y = (this.framework.height - this.dialogHeight) / 2;
73
+
74
+ // Card
75
+ ctx.fillStyle = '#FFF';
76
+ ctx.shadowColor = 'rgba(0,0,0,0.25)';
77
+ ctx.shadowBlur = 12;
78
+ this.roundRect(ctx, x, y, this.dialogWidth, this.dialogHeight, 6);
79
+ ctx.fill();
80
+ ctx.shadowColor = 'transparent';
81
+
82
+ // Title
83
+ ctx.fillStyle = '#000';
84
+ ctx.font = '500 20px Roboto, sans-serif';
85
+ ctx.textAlign = 'left';
86
+ ctx.fillText(this.title, x + 24, y + 36);
87
+
88
+ // Message
89
+ ctx.fillStyle = '#444';
90
+ ctx.font = '16px Roboto, sans-serif';
91
+ for (let i = 0; i < this.messageLines.length; i++) {
92
+ ctx.fillText(this.messageLines[i], x + 24, y + 70 + i * 22);
93
+ }
94
+
95
+ // Buttons
96
+ this.buttonRects = [];
97
+ let btnX = x + this.dialogWidth - 16;
98
+ const btnY = y + this.dialogHeight - 28;
99
+
100
+ ctx.font = '500 14px Roboto, sans-serif';
101
+
102
+ for (let i = this.buttons.length - 1; i >= 0; i--) {
103
+ const text = this.buttons[i];
104
+ const w = ctx.measureText(text).width + 24;
105
+
106
+ btnX -= w;
107
+
108
+ const rect = {
109
+ x: btnX,
110
+ y: btnY - 18,
111
+ width: w,
112
+ height: 36,
113
+ index: i
114
+ };
115
+ this.buttonRects[i] = rect;
116
+
117
+ // Ripple
118
+ ctx.save();
119
+ ctx.beginPath();
120
+ ctx.rect(rect.x, rect.y, rect.width, rect.height);
121
+ ctx.clip();
122
+
123
+ for (const r of this.ripples) {
124
+ if (r.index === i) {
125
+ ctx.globalAlpha = r.alpha;
126
+ ctx.fillStyle = 'rgba(98,0,238,0.25)';
127
+ ctx.beginPath();
128
+ ctx.arc(r.x, r.y, r.radius, 0, Math.PI * 2);
129
+ ctx.fill();
130
+ }
131
+ }
132
+ ctx.restore();
133
+
134
+ ctx.fillStyle = '#6200EE';
135
+ ctx.textAlign = 'center';
136
+ ctx.fillText(text, btnX + w / 2, btnY);
137
+
138
+ btnX -= 8;
139
+ }
140
+ }
141
+
142
+ /* ---------------- CUPERTINO (iOS) — DESIGN ORIGINAL ---------------- */
143
+
144
+ drawCupertino(ctx) {
145
+ const x = (this.framework.width - this.dialogWidth) / 2;
146
+ const y = (this.framework.height - this.dialogHeight) / 2;
147
+
148
+ // Dialog
149
+ ctx.fillStyle = '#FFF';
150
+ ctx.shadowColor = 'rgba(0,0,0,0.3)';
151
+ ctx.shadowBlur = 20;
152
+ this.roundRect(ctx, x, y, this.dialogWidth, this.dialogHeight, 12);
153
+ ctx.fill();
154
+ ctx.shadowColor = 'transparent';
155
+
156
+ // Title
157
+ ctx.fillStyle = '#000';
158
+ ctx.font = '600 18px -apple-system, sans-serif';
159
+ ctx.textAlign = 'center';
160
+ ctx.fillText(this.title, x + this.dialogWidth / 2, y + 38);
161
+
162
+ // Message
163
+ ctx.fillStyle = '#666';
164
+ ctx.font = '16px -apple-system, sans-serif';
165
+ for (let i = 0; i < this.messageLines.length; i++) {
166
+ ctx.fillText(
167
+ this.messageLines[i],
168
+ x + this.dialogWidth / 2,
169
+ y + 72 + i * 22
170
+ );
171
+ }
172
+
173
+ // Divider
174
+ const dividerY = y + this.dialogHeight - 54;
175
+ ctx.strokeStyle = '#E5E5EA';
176
+ ctx.beginPath();
177
+ ctx.moveTo(x, dividerY);
178
+ ctx.lineTo(x + this.dialogWidth, dividerY);
179
+ ctx.stroke();
180
+
181
+ // Buttons
182
+ this.buttonRects = [];
183
+ const btnW = this.dialogWidth / this.buttons.length;
184
+ const btnH = 54;
185
+
186
+ for (let i = 0; i < this.buttons.length; i++) {
187
+ const bx = x + i * btnW;
188
+ const by = dividerY;
189
+
190
+ this.buttonRects.push({
191
+ x: bx,
192
+ y: by,
193
+ width: btnW,
194
+ height: btnH
195
+ });
196
+
197
+ ctx.fillStyle =
198
+ i === this.buttons.length - 1 ? '#007AFF' : '#8E8E93';
199
+
200
+ ctx.font = '600 17px -apple-system, sans-serif';
201
+ ctx.textAlign = 'center';
202
+ ctx.fillText(this.buttons[i], bx + btnW / 2, by + btnH / 2);
203
+
204
+ if (i < this.buttons.length - 1) {
205
+ ctx.strokeStyle = '#E5E5EA';
206
+ ctx.beginPath();
207
+ ctx.moveTo(bx + btnW, by);
208
+ ctx.lineTo(bx + btnW, by + btnH);
209
+ ctx.stroke();
210
+ }
211
+ }
212
+ }
213
+
214
+ /* ---------------- INTERACTION ---------------- */
215
+
216
+ handlePress(x, y) {
217
+ for (let i = 0; i < this.buttonRects.length; i++) {
218
+ const r = this.buttonRects[i];
219
+ if (
220
+ x >= r.x && x <= r.x + r.width &&
221
+ y >= r.y && y <= r.y + r.height
222
+ ) {
223
+ if (this.platform === 'material') {
224
+ const max = Math.max(r.width, r.height) * 1.4;
225
+ this.ripples.push({
226
+ index: i,
227
+ x,
228
+ y,
229
+ radius: 0,
230
+ alpha: 0.35,
231
+ max
232
+ });
233
+ this.animateRipples();
234
+ }
235
+
236
+ setTimeout(() => {
237
+ this.onButtonClick?.(i, this.buttons[i]);
238
+ this.hide();
239
+ }, 120);
240
+ return;
241
+ }
242
+ }
243
+
244
+ this.hide();
245
+ }
246
+
247
+ animateRipples() {
248
+ if (this._animating) return;
249
+ this._animating = true;
250
+
251
+ const step = () => {
252
+ let active = false;
253
+
254
+ for (const r of this.ripples) {
255
+ r.radius += r.max * 0.12;
256
+ r.alpha -= 0.04;
257
+ if (r.alpha > 0) active = true;
258
+ }
259
+
260
+ this.ripples = this.ripples.filter(r => r.alpha > 0);
261
+
262
+ if (active) requestAnimationFrame(step);
263
+ else this._animating = false;
264
+ };
265
+
266
+ requestAnimationFrame(step);
267
+ }
268
+
269
+ /* ---------------- SHOW / HIDE ---------------- */
270
+
271
+ show() {
272
+ this.isVisible = true;
273
+ this.visible = true;
274
+ this.opacity = 0;
275
+
276
+ const fade = () => {
277
+ this.opacity += 0.1;
278
+ if (this.opacity < 1) requestAnimationFrame(fade);
279
+ };
280
+ fade();
281
+ }
282
+
283
+ hide() {
284
+ const fade = () => {
285
+ this.opacity -= 0.1;
286
+ if (this.opacity > 0) requestAnimationFrame(fade);
287
+ else {
288
+ this.isVisible = false;
289
+ this.visible = false;
290
+ this.framework.remove(this);
291
+ }
292
+ };
293
+ fade();
294
+ }
295
+
296
+ /* ---------------- UTILS ---------------- */
297
+
298
+ wrapText(text, maxWidth, font) {
299
+ const ctx = this.framework.ctx;
300
+ ctx.font = font;
301
+
302
+ const words = text.split(' ');
303
+ const lines = [];
304
+ let line = words[0];
305
+
306
+ for (let i = 1; i < words.length; i++) {
307
+ const test = line + ' ' + words[i];
308
+ if (ctx.measureText(test).width < maxWidth) line = test;
309
+ else {
310
+ lines.push(line);
311
+ line = words[i];
312
+ }
313
+ }
314
+ lines.push(line);
315
+ return lines;
316
+ }
317
+
318
+ roundRect(ctx, x, y, w, h, r) {
319
+ ctx.beginPath();
320
+ ctx.moveTo(x + r, y);
321
+ ctx.lineTo(x + w - r, y);
322
+ ctx.quadraticCurveTo(x + w, y, x + w, y + r);
323
+ ctx.lineTo(x + w, y + h - r);
324
+ ctx.quadraticCurveTo(x + w, y + h, x + w - r, y + h);
325
+ ctx.lineTo(x + r, y + h);
326
+ ctx.quadraticCurveTo(x, y + h, x, y + h - r);
327
+ ctx.lineTo(x, y + r);
328
+ ctx.quadraticCurveTo(x, y, x + r, y);
329
+ ctx.closePath();
330
+ }
331
+
332
+ isPointInside() {
333
+ return this.isVisible;
334
+ }
335
+ }
336
+
337
+ export default Dialog;
@@ -0,0 +1,125 @@
1
+ import Component from '../core/Component.js';
2
+
3
+ /**
4
+ * Séparateur visuel horizontal ou vertical
5
+ * @class
6
+ * @extends Component
7
+ * @property {string} orientation - 'horizontal' ou 'vertical'
8
+ * @property {number} thickness - Épaisseur du divider
9
+ * @property {string} color - Couleur du divider
10
+ * @property {number} margin - Marge autour du divider
11
+ * @property {string} style - 'solid', 'dashed', ou 'dotted'
12
+ * @property {boolean} hasInset - Si true, ajoute un inset pour les listes
13
+ * @property {number} insetStart - Début de l'inset (px)
14
+ * @property {string} variant - 'full', 'inset', 'middle'
15
+ */
16
+ class Divider extends Component {
17
+ /**
18
+ * Crée une instance de Divider
19
+ * @param {CanvasFramework} framework - Framework parent
20
+ * @param {Object} [options={}] - Options de configuration
21
+ * @param {string} [options.orientation='horizontal'] - Orientation
22
+ * @param {number} [options.thickness] - Épaisseur (auto selon platform)
23
+ * @param {string} [options.color] - Couleur (auto selon platform)
24
+ * @param {number} [options.margin=0] - Marge
25
+ * @param {string} [options.style='solid'] - Style de ligne
26
+ * @param {string} [options.variant='full'] - Variante
27
+ * @param {number} [options.insetStart=16] - Début de l'inset
28
+ */
29
+ constructor(framework, options = {}) {
30
+ super(framework, options);
31
+
32
+ this.orientation = options.orientation || 'horizontal';
33
+ this.margin = options.margin || 0;
34
+ this.style = options.style || 'solid';
35
+ this.variant = options.variant || 'full';
36
+ this.insetStart = options.insetStart || 16;
37
+
38
+ const platform = framework.platform;
39
+
40
+ // Styles selon la plateforme
41
+ if (platform === 'material') {
42
+ this.thickness = options.thickness || 1;
43
+ this.color = options.color || 'rgba(0, 0, 0, 0.12)';
44
+ } else {
45
+ this.thickness = options.thickness || 0.5;
46
+ this.color = options.color || 'rgba(60, 60, 67, 0.29)';
47
+ }
48
+
49
+ // Ajuster les dimensions selon l'orientation
50
+ if (this.orientation === 'horizontal') {
51
+ this.height = this.thickness + (this.margin * 2);
52
+ } else {
53
+ this.width = this.thickness + (this.margin * 2);
54
+ }
55
+ }
56
+
57
+ /**
58
+ * Dessine le divider
59
+ * @param {CanvasRenderingContext2D} ctx - Contexte de dessin
60
+ */
61
+ draw(ctx) {
62
+ ctx.save();
63
+
64
+ let startX = this.x;
65
+ let startY = this.y;
66
+ let endX = this.x;
67
+ let endY = this.y;
68
+
69
+ if (this.orientation === 'horizontal') {
70
+ startX = this.x;
71
+ endX = this.x + this.width;
72
+ startY = endY = this.y + this.margin + this.thickness / 2;
73
+
74
+ // Variantes
75
+ if (this.variant === 'inset') {
76
+ startX += this.insetStart;
77
+ } else if (this.variant === 'middle') {
78
+ const inset = this.width * 0.1;
79
+ startX += inset;
80
+ endX -= inset;
81
+ }
82
+ } else {
83
+ startY = this.y;
84
+ endY = this.y + this.height;
85
+ startX = endX = this.x + this.margin + this.thickness / 2;
86
+
87
+ // Variantes
88
+ if (this.variant === 'inset') {
89
+ startY += this.insetStart;
90
+ } else if (this.variant === 'middle') {
91
+ const inset = this.height * 0.1;
92
+ startY += inset;
93
+ endY -= inset;
94
+ }
95
+ }
96
+
97
+ ctx.strokeStyle = this.color;
98
+ ctx.lineWidth = this.thickness;
99
+
100
+ // Styles de ligne
101
+ if (this.style === 'dashed') {
102
+ ctx.setLineDash([8, 4]);
103
+ } else if (this.style === 'dotted') {
104
+ ctx.setLineDash([2, 4]);
105
+ }
106
+
107
+ ctx.beginPath();
108
+ ctx.moveTo(startX, startY);
109
+ ctx.lineTo(endX, endY);
110
+ ctx.stroke();
111
+
112
+ ctx.setLineDash([]);
113
+ ctx.restore();
114
+ }
115
+
116
+ /**
117
+ * Vérifie si un point est dans les limites (non cliquable)
118
+ * @returns {boolean} Toujours false
119
+ */
120
+ isPointInside() {
121
+ return false;
122
+ }
123
+ }
124
+
125
+ export default Divider;