canvasframework 0.5.16 → 0.5.18

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 (112) hide show
  1. package/dist/canvasframework.js +2 -0
  2. package/dist/canvasframework.js.LICENSE.txt +1 -0
  3. package/package.json +18 -17
  4. package/components/Accordion.js +0 -265
  5. package/components/AndroidDatePickerDialog.js +0 -406
  6. package/components/AppBar.js +0 -398
  7. package/components/AudioPlayer.js +0 -611
  8. package/components/Avatar.js +0 -202
  9. package/components/Banner.js +0 -342
  10. package/components/BottomNavigationBar.js +0 -433
  11. package/components/BottomSheet.js +0 -234
  12. package/components/Button.js +0 -360
  13. package/components/Camera.js +0 -644
  14. package/components/Card.js +0 -193
  15. package/components/Chart.js +0 -700
  16. package/components/Checkbox.js +0 -166
  17. package/components/Chip.js +0 -212
  18. package/components/CircularProgress.js +0 -327
  19. package/components/ContextMenu.js +0 -116
  20. package/components/DatePicker.js +0 -298
  21. package/components/Dialog.js +0 -337
  22. package/components/Divider.js +0 -125
  23. package/components/Drawer.js +0 -276
  24. package/components/FAB.js +0 -270
  25. package/components/FileUpload.js +0 -315
  26. package/components/FloatedCamera.js +0 -644
  27. package/components/IOSDatePickerWheel.js +0 -430
  28. package/components/ImageCarousel.js +0 -219
  29. package/components/ImageComponent.js +0 -223
  30. package/components/Input.js +0 -831
  31. package/components/InputDatalist.js +0 -723
  32. package/components/InputTags.js +0 -624
  33. package/components/List.js +0 -95
  34. package/components/ListItem.js +0 -269
  35. package/components/Modal.js +0 -364
  36. package/components/MorphingFAB.js +0 -428
  37. package/components/MultiSelectDialog.js +0 -206
  38. package/components/NumberInput.js +0 -271
  39. package/components/PasswordInput.js +0 -462
  40. package/components/ProgressBar.js +0 -88
  41. package/components/QRCodeReader.js +0 -539
  42. package/components/RadioButton.js +0 -151
  43. package/components/SearchInput.js +0 -315
  44. package/components/SegmentedControl.js +0 -357
  45. package/components/Select.js +0 -199
  46. package/components/SelectDialog.js +0 -255
  47. package/components/Slider.js +0 -113
  48. package/components/SliverAppBar.js +0 -139
  49. package/components/Snackbar.js +0 -243
  50. package/components/SpeedDialFAB.js +0 -397
  51. package/components/Stepper.js +0 -281
  52. package/components/SwipeableListItem.js +0 -327
  53. package/components/Switch.js +0 -147
  54. package/components/Table.js +0 -492
  55. package/components/Tabs.js +0 -423
  56. package/components/Text.js +0 -141
  57. package/components/TextField.js +0 -151
  58. package/components/TimePicker.js +0 -934
  59. package/components/Toast.js +0 -236
  60. package/components/TreeView.js +0 -420
  61. package/components/Video.js +0 -397
  62. package/components/View.js +0 -140
  63. package/components/VirtualList.js +0 -120
  64. package/core/CanvasFramework.js +0 -3034
  65. package/core/Component.js +0 -243
  66. package/core/ThemeManager.js +0 -358
  67. package/core/UIBuilder.js +0 -267
  68. package/core/WebGLCanvasAdapter.js +0 -782
  69. package/features/Column.js +0 -43
  70. package/features/Grid.js +0 -47
  71. package/features/LayoutComponent.js +0 -43
  72. package/features/OpenStreetMap.js +0 -310
  73. package/features/Positioned.js +0 -33
  74. package/features/PullToRefresh.js +0 -328
  75. package/features/Row.js +0 -40
  76. package/features/SignaturePad.js +0 -257
  77. package/features/Skeleton.js +0 -193
  78. package/features/Stack.js +0 -21
  79. package/index.js +0 -119
  80. package/manager/AccessibilityManager.js +0 -107
  81. package/manager/ErrorHandler.js +0 -59
  82. package/manager/FeatureFlags.js +0 -60
  83. package/manager/MemoryManager.js +0 -107
  84. package/manager/PerformanceMonitor.js +0 -84
  85. package/manager/SecurityManager.js +0 -54
  86. package/utils/AnimationEngine.js +0 -734
  87. package/utils/CryptoManager.js +0 -303
  88. package/utils/DataStore.js +0 -403
  89. package/utils/DevTools.js +0 -1618
  90. package/utils/DevToolsConsole.js +0 -201
  91. package/utils/EventBus.js +0 -407
  92. package/utils/FetchClient.js +0 -74
  93. package/utils/FirebaseAuth.js +0 -653
  94. package/utils/FirebaseCore.js +0 -246
  95. package/utils/FirebaseFirestore.js +0 -581
  96. package/utils/FirebaseFunctions.js +0 -97
  97. package/utils/FirebaseRealtimeDB.js +0 -498
  98. package/utils/FirebaseStorage.js +0 -612
  99. package/utils/FormValidator.js +0 -355
  100. package/utils/GeoLocationService.js +0 -62
  101. package/utils/I18n.js +0 -207
  102. package/utils/IndexedDBManager.js +0 -273
  103. package/utils/InspectionOverlay.js +0 -308
  104. package/utils/NotificationManager.js +0 -60
  105. package/utils/OfflineSyncManager.js +0 -342
  106. package/utils/PayPalPayment.js +0 -678
  107. package/utils/QueryBuilder.js +0 -478
  108. package/utils/SafeArea.js +0 -64
  109. package/utils/SecureStorage.js +0 -289
  110. package/utils/StateManager.js +0 -207
  111. package/utils/StripePayment.js +0 -552
  112. package/utils/WebSocketClient.js +0 -66
@@ -1,166 +0,0 @@
1
- import Component from '../core/Component.js';
2
-
3
- /**
4
- * Checkbox Material & Cupertino (iOS-like)
5
- */
6
- class Checkbox extends Component {
7
- constructor(framework, options = {}) {
8
- super(framework, options);
9
-
10
- this.checked = !!options.checked;
11
- this.label = options.label || '';
12
- this.platform = framework.platform;
13
- this.onChange = options.onChange;
14
-
15
- this.boxSize = 22;
16
- this.padding = 10;
17
-
18
- this.textWidth = this.label
19
- ? this.getTextWidth(this.label)
20
- : 0;
21
-
22
- // Largeur totale
23
- this.width =
24
- this.platform === 'material'
25
- ? this.boxSize + this.padding + this.textWidth
26
- : this.textWidth + 28; // place pour checkmark iOS
27
-
28
- this.height = 28;
29
-
30
- this.onClick = () => {
31
- this.checked = !this.checked;
32
- this.onChange?.(this.checked);
33
- };
34
- }
35
-
36
- getTextWidth(text) {
37
- const ctx = this.framework.ctx;
38
- ctx.save();
39
- ctx.font = '16px -apple-system, system-ui, sans-serif';
40
- const w = ctx.measureText(text).width;
41
- ctx.restore();
42
- return w;
43
- }
44
-
45
- draw(ctx) {
46
- ctx.save();
47
- ctx.font = '16px -apple-system, system-ui, sans-serif';
48
- ctx.textBaseline = 'middle';
49
-
50
- const centerY = this.y + this.height / 2;
51
-
52
- if (this.platform === 'material') {
53
- this.drawMaterial(ctx, centerY);
54
- } else {
55
- this.drawCupertino(ctx, centerY);
56
- }
57
-
58
- ctx.restore();
59
- }
60
-
61
- /* ---------------- MATERIAL ---------------- */
62
-
63
- drawMaterial(ctx, centerY) {
64
- const x = this.x;
65
- const y = centerY - this.boxSize / 2;
66
-
67
- // Box
68
- ctx.lineWidth = 2;
69
- ctx.strokeStyle = this.checked ? '#6200EE' : '#757575';
70
- ctx.fillStyle = this.checked ? '#6200EE' : 'transparent';
71
-
72
- this.roundRect(ctx, x, y, this.boxSize, this.boxSize, 3);
73
- if (this.checked) ctx.fill();
74
- ctx.stroke();
75
-
76
- // Check
77
- if (this.checked) {
78
- ctx.strokeStyle = '#FFF';
79
- ctx.lineWidth = 2.4;
80
- ctx.beginPath();
81
- ctx.moveTo(x + 5, y + 12);
82
- ctx.lineTo(x + 9, y + 16);
83
- ctx.lineTo(x + 17, y + 7);
84
- ctx.stroke();
85
- }
86
-
87
- // Label
88
- ctx.fillStyle = '#000';
89
- ctx.fillText(
90
- this.label,
91
- x + this.boxSize + this.padding,
92
- centerY
93
- );
94
- }
95
-
96
- /* ---------------- CUPERTINO ---------------- */
97
-
98
- /* ---------------- CUPERTINO ---------------- */
99
-
100
- drawCupertino(ctx, centerY) {
101
- const radius = 10;
102
- const circleX = this.x + radius;
103
- const circleY = centerY;
104
-
105
- // Cercle
106
- if (this.checked) {
107
- ctx.fillStyle = '#007AFF'; // Apple blue
108
- ctx.beginPath();
109
- ctx.arc(circleX, circleY, radius, 0, Math.PI * 2);
110
- ctx.fill();
111
- } else {
112
- ctx.strokeStyle = '#C7C7CC'; // iOS gray
113
- ctx.lineWidth = 2;
114
- ctx.beginPath();
115
- ctx.arc(circleX, circleY, radius, 0, Math.PI * 2);
116
- ctx.stroke();
117
- }
118
-
119
- // Checkmark
120
- if (this.checked) {
121
- ctx.strokeStyle = '#FFFFFF';
122
- ctx.lineWidth = 2.2;
123
- ctx.lineCap = 'round';
124
- ctx.lineJoin = 'round';
125
-
126
- ctx.beginPath();
127
- ctx.moveTo(circleX - 4, circleY);
128
- ctx.lineTo(circleX - 1, circleY + 3);
129
- ctx.lineTo(circleX + 5, circleY - 4);
130
- ctx.stroke();
131
- }
132
-
133
- // Label
134
- ctx.fillStyle = '#000';
135
- ctx.fillText(
136
- this.label,
137
- this.x + radius * 2 + this.padding,
138
- centerY
139
- );
140
- }
141
-
142
- roundRect(ctx, x, y, w, h, r) {
143
- ctx.beginPath();
144
- ctx.moveTo(x + r, y);
145
- ctx.lineTo(x + w - r, y);
146
- ctx.quadraticCurveTo(x + w, y, x + w, y + r);
147
- ctx.lineTo(x + w, y + h - r);
148
- ctx.quadraticCurveTo(x + w, y + h, x + w - r, y + h);
149
- ctx.lineTo(x + r, y + h);
150
- ctx.quadraticCurveTo(x, y + h, x, y + h - r);
151
- ctx.lineTo(x, y + r);
152
- ctx.quadraticCurveTo(x, y, x + r, y);
153
- ctx.closePath();
154
- }
155
-
156
- isPointInside(x, y) {
157
- return (
158
- x >= this.x &&
159
- x <= this.x + this.width &&
160
- y >= this.y &&
161
- y <= this.y + this.height
162
- );
163
- }
164
- }
165
-
166
- export default Checkbox;
@@ -1,212 +0,0 @@
1
- import Component from '../core/Component.js';
2
- /**
3
- * Chip (étiquette cliquable)
4
- * @class
5
- * @extends Component
6
- * @property {string} text - Texte
7
- * @property {string|null} icon - Icône
8
- * @property {boolean} closable - Peut être fermé
9
- * @property {string} platform - Plateforme
10
- * @property {string} bgColor - Couleur de fond
11
- * @property {string} textColor - Couleur du texte
12
- * @property {Function} onClose - Callback à la fermeture
13
- * @property {number} borderRadius - Rayon des coins
14
- * @property {Object|null} closeButtonRect - Rectangle du bouton fermer
15
- */
16
- class Chip extends Component {
17
- /**
18
- * Crée une instance de Chip
19
- * @param {CanvasFramework} framework - Framework parent
20
- * @param {Object} [options={}] - Options de configuration
21
- * @param {string} [options.text=''] - Texte
22
- * @param {string} [options.icon] - Icône
23
- * @param {boolean} [options.closable=true] - Peut être fermé
24
- * @param {string} [options.bgColor] - Couleur de fond (auto selon platform)
25
- * @param {string} [options.textColor='#000000'] - Couleur du texte
26
- * @param {Function} [options.onClose] - Callback à la fermeture
27
- * @param {number} [options.height=32] - Hauteur
28
- */
29
- constructor(framework, options = {}) {
30
- super(framework, options);
31
- this.text = options.text || '';
32
- this.icon = options.icon || null;
33
- this.closable = options.closable !== false;
34
- this.platform = framework.platform;
35
- this.bgColor = options.bgColor || (framework.platform === 'material' ? '#E0E0E0' : '#F0F0F0');
36
- this.textColor = options.textColor || '#000000';
37
- this.onClose = options.onClose;
38
-
39
- // Calculer la largeur en fonction du contenu
40
- const ctx = framework.ctx;
41
- ctx.font = '14px -apple-system, sans-serif';
42
- const textWidth = ctx.measureText(this.text).width;
43
- const iconWidth = this.icon ? 24 : 0;
44
- const closeWidth = this.closable ? 24 : 0;
45
- this.width = iconWidth + textWidth + closeWidth + 24; // padding
46
- this.height = options.height || 32;
47
- this.borderRadius = this.height / 2;
48
-
49
- this.closeButtonRect = null;
50
- this.onPress = this.handlePress.bind(this);
51
- }
52
-
53
- /**
54
- * Dessine le chip
55
- * @param {CanvasRenderingContext2D} ctx - Contexte de dessin
56
- */
57
- draw(ctx) {
58
- ctx.save();
59
-
60
- // Background
61
- ctx.fillStyle = this.pressed ? this.darkenColor(this.bgColor) : this.bgColor;
62
- ctx.beginPath();
63
- this.roundRect(ctx, this.x, this.y, this.width, this.height, this.borderRadius);
64
- ctx.fill();
65
-
66
- let currentX = this.x + 12;
67
-
68
- // Icône
69
- if (this.icon) {
70
- ctx.font = '16px sans-serif';
71
- ctx.textAlign = 'left';
72
- ctx.textBaseline = 'middle';
73
- ctx.fillStyle = this.textColor;
74
- ctx.fillText(this.icon, currentX, this.y + this.height / 2);
75
- currentX += 20;
76
- }
77
-
78
- // Texte
79
- ctx.font = '14px -apple-system, sans-serif';
80
- ctx.fillStyle = this.textColor;
81
- ctx.textAlign = 'left';
82
- ctx.textBaseline = 'middle';
83
- ctx.fillText(this.text, currentX, this.y + this.height / 2);
84
-
85
- // Bouton de fermeture
86
- if (this.closable) {
87
- const closeX = this.x + this.width - 20;
88
- const closeY = this.y + this.height / 2;
89
-
90
- this.closeButtonRect = {
91
- x: closeX - 8,
92
- y: closeY - 8,
93
- width: 16,
94
- height: 16
95
- };
96
-
97
- // Cercle du bouton (optionnel)
98
- ctx.fillStyle = 'rgba(0, 0, 0, 0.1)';
99
- ctx.beginPath();
100
- ctx.arc(closeX, closeY, 8, 0, Math.PI * 2);
101
- ctx.fill();
102
-
103
- // Croix (X)
104
- ctx.strokeStyle = this.textColor;
105
- ctx.lineWidth = 1.5;
106
- ctx.lineCap = 'round';
107
- ctx.beginPath();
108
- ctx.moveTo(closeX - 4, closeY - 4);
109
- ctx.lineTo(closeX + 4, closeY + 4);
110
- ctx.stroke();
111
-
112
- ctx.beginPath();
113
- ctx.moveTo(closeX + 4, closeY - 4);
114
- ctx.lineTo(closeX - 4, closeY + 4);
115
- ctx.stroke();
116
- }
117
-
118
- ctx.restore();
119
- }
120
-
121
- /**
122
- * Gère la pression (clic)
123
- * @param {number} x - Coordonnée X
124
- * @param {number} y - Coordonnée Y
125
- * @private
126
- */
127
- handlePress(x, y) {
128
- const adjustedY = y - this.framework.scrollOffset;
129
-
130
- // Vérifier si on clique sur le bouton de fermeture
131
- if (this.closable && this.closeButtonRect) {
132
- if (x >= this.closeButtonRect.x &&
133
- x <= this.closeButtonRect.x + this.closeButtonRect.width &&
134
- adjustedY >= this.closeButtonRect.y &&
135
- adjustedY <= this.closeButtonRect.y + this.closeButtonRect.height) {
136
- if (this.onClose) this.onClose();
137
- return;
138
- }
139
- }
140
-
141
- // Sinon, déclencher onClick normal
142
- if (this.onClick) this.onClick();
143
- }
144
-
145
- /**
146
- * Dessine un rectangle avec coins arrondis
147
- * @param {CanvasRenderingContext2D} ctx - Contexte de dessin
148
- * @param {number} x - Position X
149
- * @param {number} y - Position Y
150
- * @param {number} width - Largeur
151
- * @param {number} height - Hauteur
152
- * @param {number} radius - Rayon des coins
153
- * @private
154
- */
155
- roundRect(ctx, x, y, width, height, radius) {
156
- ctx.beginPath();
157
- ctx.moveTo(x + radius, y);
158
- ctx.lineTo(x + width - radius, y);
159
- ctx.quadraticCurveTo(x + width, y, x + width, y + radius);
160
- ctx.lineTo(x + width, y + height - radius);
161
- ctx.quadraticCurveTo(x + width, y + height, x + width - radius, y + height);
162
- ctx.lineTo(x + radius, y + height);
163
- ctx.quadraticCurveTo(x, y + height, x, y + height - radius);
164
- ctx.lineTo(x, y + radius);
165
- ctx.quadraticCurveTo(x, y, x + radius, y);
166
- ctx.closePath();
167
- }
168
-
169
- /**
170
- * Assombrit une couleur
171
- * @param {string} color - Couleur
172
- * @returns {string} Couleur assombrie
173
- * @private
174
- */
175
- darkenColor(color) {
176
- // Utiliser la même méthode que Button
177
- if (color.startsWith('#')) {
178
- const rgb = this.hexToRgb(color);
179
- return `rgb(${Math.max(0, rgb.r - 20)}, ${Math.max(0, rgb.g - 20)}, ${Math.max(0, rgb.b - 20)})`;
180
- }
181
- return color;
182
- }
183
-
184
- /**
185
- * Convertit une couleur hex en RGB
186
- * @param {string} hex - Couleur hexadécimale
187
- * @returns {{r: number, g: number, b: number}} Objet RGB
188
- * @private
189
- */
190
- hexToRgb(hex) {
191
- const result = /^#?([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})$/i.exec(hex);
192
- return result ? {
193
- r: parseInt(result[1], 16),
194
- g: parseInt(result[2], 16),
195
- b: parseInt(result[3], 16)
196
- } : { r: 0, g: 0, b: 0 };
197
- }
198
-
199
- /**
200
- * Vérifie si un point est dans les limites
201
- * @param {number} x - Coordonnée X
202
- * @param {number} y - Coordonnée Y
203
- * @returns {boolean} True si le point est dans le chip
204
- */
205
- isPointInside(x, y) {
206
- const adjustedY = y - this.framework.scrollOffset;
207
- return x >= this.x && x <= this.x + this.width &&
208
- adjustedY >= this.y && adjustedY <= this.y + this.height;
209
- }
210
- }
211
-
212
- export default Chip;
@@ -1,327 +0,0 @@
1
- import Component from '../core/Component.js';
2
-
3
- /**
4
- * Spinner de chargement circulaire avec support Material et Cupertino
5
- * @class
6
- * @extends Component
7
- * @property {number} size - Taille du spinner
8
- * @property {boolean} indeterminate - Mode indéterminé
9
- * @property {number} progress - Progression (0-100)
10
- * @property {string} platform - Plateforme
11
- * @property {string} color - Couleur
12
- * @property {number} lineWidth - Épaisseur de la ligne
13
- * @property {number} rotation - Rotation actuelle
14
- * @property {number} animationSpeed - Vitesse d'animation
15
- */
16
- class CircularProgress extends Component {
17
- /**
18
- * Crée une instance de CircularProgress
19
- * @param {CanvasFramework} framework - Framework parent
20
- * @param {Object} [options={}] - Options de configuration
21
- * @param {number} [options.size=40] - Taille
22
- * @param {boolean} [options.indeterminate=true] - Mode indéterminé
23
- * @param {number} [options.progress=0] - Progression (0-100)
24
- * @param {string} [options.color] - Couleur (auto selon platform)
25
- * @param {number} [options.lineWidth] - Épaisseur (auto selon platform)
26
- * @param {number} [options.animationSpeed] - Vitesse d'animation (auto selon platform)
27
- */
28
- constructor(framework, options = {}) {
29
- super(framework, options);
30
- this.size = options.size || 40;
31
- this.indeterminate = options.indeterminate !== false;
32
- this.progress = options.progress || 0; // 0-100
33
- this.platform = framework.platform;
34
-
35
- // Couleurs selon la plateforme
36
- this.color = options.color || (
37
- this.platform === 'material' ? '#6200EE' : '#8E8E93'
38
- );
39
-
40
- // Épaisseur selon la plateforme
41
- this.lineWidth = options.lineWidth || (
42
- this.platform === 'material' ? 4 : 2.5
43
- );
44
-
45
- // Vitesse d'animation selon la plateforme
46
- this.animationSpeed = options.animationSpeed || (
47
- this.platform === 'material' ? 0.05 : 0.08
48
- );
49
-
50
- this.rotation = 0;
51
-
52
- // Pour l'animation Material (arc qui s'agrandit/rétrécit)
53
- this.arcStart = 0;
54
- this.arcEnd = 0;
55
- this.arcGrowing = true;
56
-
57
- // Pour l'animation Cupertino (12 traits qui tournent)
58
- this.cupertinoLines = 12;
59
- this.cupertinoOpacity = Array(this.cupertinoLines).fill(0);
60
-
61
- this.width = this.size;
62
- this.height = this.size;
63
-
64
- // Démarrer l'animation pour indeterminate
65
- if (this.indeterminate) {
66
- this.startAnimation();
67
- }
68
- }
69
-
70
- /**
71
- * Démarre l'animation du spinner
72
- * @private
73
- */
74
- startAnimation() {
75
- let lastTime = performance.now();
76
-
77
- const animate = (currentTime) => {
78
- if (!this.visible || !this.indeterminate) return;
79
-
80
- const deltaTime = currentTime - lastTime;
81
- lastTime = currentTime;
82
-
83
- if (this.platform === 'material') {
84
- this.animateMaterial(deltaTime);
85
- } else {
86
- this.animateCupertino(deltaTime);
87
- }
88
-
89
- this.markDirty();
90
- requestAnimationFrame(animate);
91
- };
92
-
93
- requestAnimationFrame(animate);
94
- }
95
-
96
- /**
97
- * Animation Material (arc qui tourne et change de taille)
98
- * @private
99
- */
100
- animateMaterial(deltaTime) {
101
- // Rotation globale
102
- this.rotation += this.animationSpeed;
103
- if (this.rotation > Math.PI * 2) {
104
- this.rotation -= Math.PI * 2;
105
- }
106
-
107
- // Animation de l'arc (grossit puis rétrécit)
108
- const arcSpeed = 0.03;
109
-
110
- if (this.arcGrowing) {
111
- this.arcEnd += arcSpeed;
112
- if (this.arcEnd > Math.PI * 1.5) {
113
- this.arcGrowing = false;
114
- }
115
- } else {
116
- this.arcStart += arcSpeed;
117
- if (this.arcStart >= this.arcEnd) {
118
- this.arcGrowing = true;
119
- this.arcStart = 0;
120
- this.arcEnd = 0;
121
- }
122
- }
123
- }
124
-
125
- /**
126
- * Animation Cupertino (traits qui s'estompent)
127
- * @private
128
- */
129
- animateCupertino(deltaTime) {
130
- this.rotation += this.animationSpeed;
131
- if (this.rotation > Math.PI * 2) {
132
- this.rotation -= Math.PI * 2;
133
- }
134
-
135
- // Calculer l'opacité de chaque trait (fade progressif)
136
- const activeIndex = Math.floor((this.rotation / (Math.PI * 2)) * this.cupertinoLines);
137
-
138
- for (let i = 0; i < this.cupertinoLines; i++) {
139
- const distance = Math.abs(i - activeIndex);
140
- const minDistance = Math.min(distance, this.cupertinoLines - distance);
141
- this.cupertinoOpacity[i] = 1 - (minDistance / this.cupertinoLines) * 0.8;
142
- }
143
- }
144
-
145
- /**
146
- * Dessine le spinner Material
147
- * @private
148
- */
149
- drawMaterial(ctx, centerX, centerY, radius) {
150
- if (this.indeterminate) {
151
- // Track (cercle de base - très léger)
152
- ctx.strokeStyle = 'rgba(98, 0, 238, 0.1)';
153
- ctx.lineWidth = this.lineWidth;
154
- ctx.beginPath();
155
- ctx.arc(centerX, centerY, radius, 0, Math.PI * 2);
156
- ctx.stroke();
157
-
158
- // Arc animé qui tourne
159
- ctx.save();
160
- ctx.translate(centerX, centerY);
161
- ctx.rotate(this.rotation);
162
-
163
- ctx.strokeStyle = this.color;
164
- ctx.lineCap = 'round';
165
- ctx.lineWidth = this.lineWidth;
166
- ctx.beginPath();
167
- ctx.arc(0, 0, radius, this.arcStart, this.arcEnd);
168
- ctx.stroke();
169
-
170
- ctx.restore();
171
- } else {
172
- // Progress circulaire déterminé
173
- // Track
174
- ctx.strokeStyle = '#E0E0E0';
175
- ctx.lineWidth = this.lineWidth;
176
- ctx.beginPath();
177
- ctx.arc(centerX, centerY, radius, 0, Math.PI * 2);
178
- ctx.stroke();
179
-
180
- // Progress
181
- const angle = (this.progress / 100) * Math.PI * 2;
182
- ctx.strokeStyle = this.color;
183
- ctx.lineCap = 'round';
184
- ctx.beginPath();
185
- ctx.arc(centerX, centerY, radius, -Math.PI / 2, -Math.PI / 2 + angle);
186
- ctx.stroke();
187
-
188
- // Pourcentage au centre
189
- if (this.progress > 0) {
190
- ctx.fillStyle = this.color;
191
- ctx.font = `bold ${this.size / 3}px -apple-system, sans-serif`;
192
- ctx.textAlign = 'center';
193
- ctx.textBaseline = 'middle';
194
- ctx.fillText(`${Math.round(this.progress)}%`, centerX, centerY);
195
- }
196
- }
197
- }
198
-
199
- /**
200
- * Dessine le spinner Cupertino (style iOS)
201
- * @private
202
- */
203
- drawCupertino(ctx, centerX, centerY, radius) {
204
- if (this.indeterminate) {
205
- // Spinner iOS avec 12 traits
206
- ctx.lineCap = 'round';
207
- ctx.lineWidth = this.lineWidth;
208
-
209
- for (let i = 0; i < this.cupertinoLines; i++) {
210
- const angle = (i / this.cupertinoLines) * Math.PI * 2;
211
- const opacity = this.cupertinoOpacity[i];
212
-
213
- ctx.save();
214
- ctx.translate(centerX, centerY);
215
- ctx.rotate(angle);
216
-
217
- // Trait avec opacité variable
218
- const startRadius = radius * 0.6;
219
- const endRadius = radius;
220
-
221
- ctx.strokeStyle = this.hexToRgba(this.color, opacity);
222
- ctx.beginPath();
223
- ctx.moveTo(0, -startRadius);
224
- ctx.lineTo(0, -endRadius);
225
- ctx.stroke();
226
-
227
- ctx.restore();
228
- }
229
- } else {
230
- // Progress circulaire iOS (plus fin et élégant)
231
- // Track
232
- ctx.strokeStyle = '#E5E5EA';
233
- ctx.lineWidth = this.lineWidth;
234
- ctx.beginPath();
235
- ctx.arc(centerX, centerY, radius, 0, Math.PI * 2);
236
- ctx.stroke();
237
-
238
- // Progress
239
- const angle = (this.progress / 100) * Math.PI * 2;
240
- ctx.strokeStyle = this.color;
241
- ctx.lineCap = 'round';
242
- ctx.beginPath();
243
- ctx.arc(centerX, centerY, radius, -Math.PI / 2, -Math.PI / 2 + angle);
244
- ctx.stroke();
245
-
246
- // Pas de texte au centre pour iOS (plus minimaliste)
247
- // Mais si tu veux, décommente :
248
- /*
249
- if (this.progress > 0) {
250
- ctx.fillStyle = this.color;
251
- ctx.font = `${this.size / 4}px -apple-system, sans-serif`;
252
- ctx.textAlign = 'center';
253
- ctx.textBaseline = 'middle';
254
- ctx.fillText(`${Math.round(this.progress)}%`, centerX, centerY);
255
- }
256
- */
257
- }
258
- }
259
-
260
- /**
261
- * Dessine le spinner
262
- * @param {CanvasRenderingContext2D} ctx - Contexte de dessin
263
- */
264
- draw(ctx) {
265
- ctx.save();
266
-
267
- const centerX = this.x + this.size / 2;
268
- const centerY = this.y + this.size / 2;
269
- const radius = (this.size - this.lineWidth * 2) / 2;
270
-
271
- if (this.platform === 'material') {
272
- this.drawMaterial(ctx, centerX, centerY, radius);
273
- } else {
274
- this.drawCupertino(ctx, centerX, centerY, radius);
275
- }
276
-
277
- ctx.restore();
278
- }
279
-
280
- /**
281
- * Convertit une couleur hex en rgba avec opacité
282
- * @private
283
- */
284
- hexToRgba(hex, alpha) {
285
- // Si c'est déjà rgba, le retourner
286
- if (hex.startsWith('rgba')) return hex;
287
- if (hex.startsWith('rgb')) {
288
- return hex.replace('rgb', 'rgba').replace(')', `, ${alpha})`);
289
- }
290
-
291
- // Convertir hex en rgba
292
- const r = parseInt(hex.slice(1, 3), 16);
293
- const g = parseInt(hex.slice(3, 5), 16);
294
- const b = parseInt(hex.slice(5, 7), 16);
295
- return `rgba(${r}, ${g}, ${b}, ${alpha})`;
296
- }
297
-
298
- /**
299
- * Définit la progression
300
- * @param {number} value - Valeur de progression (0-100)
301
- */
302
- setProgress(value) {
303
- this.progress = Math.max(0, Math.min(100, value));
304
- this.indeterminate = false;
305
- this.markDirty();
306
- }
307
-
308
- /**
309
- * Active le mode indéterminé
310
- */
311
- setIndeterminate() {
312
- this.indeterminate = true;
313
- if (!this._animating) {
314
- this.startAnimation();
315
- }
316
- }
317
-
318
- /**
319
- * Vérifie si un point est dans les limites
320
- * @returns {boolean} False (non cliquable)
321
- */
322
- isPointInside() {
323
- return false; // Non cliquable
324
- }
325
- }
326
-
327
- export default CircularProgress;