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,276 +0,0 @@
1
- import Component from '../core/Component.js';
2
-
3
- /**
4
- * Tiroir latéral (navigation)
5
- * @class
6
- * @extends Component
7
- * @property {number} targetX - Position X cible
8
- * @property {Array} items - Items du drawer
9
- * @property {Object|null} header - En-tête
10
- * @property {Function} onItemClick - Callback au clic sur item
11
- * @property {string} platform - Plateforme
12
- * @property {boolean} animating - En cours d'animation
13
- * @property {number} hoveredIndex - Index survolé
14
- */
15
- class Drawer extends Component {
16
- /**
17
- * Crée une instance de Drawer
18
- * @param {CanvasFramework} framework - Framework parent
19
- * @param {Object} [options={}] - Options de configuration
20
- * @param {Array} [options.items=[]] - Items [{label, icon, divider}]
21
- * @param {Object} [options.header] - En-tête {title}
22
- * @param {Function} [options.onItemClick] - Callback au clic sur item
23
- */
24
- constructor(framework, options = {}) {
25
- super(framework, {
26
- x: -framework.width * 0.8,
27
- y: 0,
28
- width: framework.width * 0.8,
29
- height: framework.height,
30
- visible: false,
31
- ...options
32
- });
33
- this.targetX = -this.width;
34
- this.items = options.items || [];
35
- this.header = options.header || null;
36
- this.onItemClick = options.onItemClick;
37
- this.platform = framework.platform;
38
- this.animating = false;
39
- this.hoveredIndex = -1;
40
-
41
- // Bind des méthodes
42
- this.handlePress = this.handlePress.bind(this);
43
- this.handleMove = this.handleMove.bind(this);
44
-
45
- // IMPORTANT: Définir les callbacks
46
- this.onPress = this.handlePress;
47
- this.onMove = this.handleMove;
48
- // ✅ Se mettre automatiquement au-dessus de tous les composants
49
- this.bringToFront();
50
- }
51
-
52
- /**
53
- * Met le drawer au-dessus de tous les composants
54
- * @private
55
- */
56
- bringToFront() {
57
- const index = this.framework.components.indexOf(this);
58
- if (index > -1) {
59
- this.framework.components.splice(index, 1);
60
- this.framework.components.push(this);
61
- }
62
- }
63
-
64
- /**
65
- * Ouvre le drawer
66
- */
67
- open() {
68
- this.bringToFront(); // ✅ Se remettre au-dessus à chaque ouverture
69
- this.visible = true;
70
- this.targetX = 0;
71
- this.animate();
72
- }
73
-
74
- /**
75
- * Ferme le drawer
76
- */
77
- close() {
78
- this.targetX = -this.width;
79
- this.animate();
80
- }
81
-
82
- /**
83
- * Anime le drawer
84
- * @private
85
- */
86
- animate() {
87
- if (this.animating) return;
88
- this.animating = true;
89
-
90
- const step = () => {
91
- const diff = this.targetX - this.x;
92
- if (Math.abs(diff) < 1) {
93
- this.x = this.targetX;
94
- this.animating = false;
95
- if (this.targetX < 0) {
96
- this.visible = false;
97
- }
98
- return;
99
- }
100
- this.x += diff * 0.2;
101
- requestAnimationFrame(step);
102
- };
103
- step();
104
- }
105
-
106
- /**
107
- * Vérifie dans quelle zone se trouve un point
108
- * @param {number} x - Coordonnée X
109
- * @param {number} y - Coordonnée Y
110
- * @returns {string|null} Zone ('overlay', 'item', 'drawer', null si en dehors)
111
- * @private
112
- */
113
- getZoneAtPoint(x, y) {
114
- if (!this.visible) return null;
115
-
116
- // Vérifier si le point est dans l'overlay (toute la zone de l'écran)
117
- // Mais on ne veut pas capturer les clics sur le drawer lui-même pour les items
118
- if (x >= this.x && x <= this.x + this.width) {
119
- // Le point est dans le drawer
120
- const startY = this.header ? 150 : 0;
121
- const index = Math.floor((y - startY) / 56);
122
- if (index >= 0 && index < this.items.length) {
123
- const itemY = startY + index * 56;
124
- if (y >= itemY && y <= itemY + 56) {
125
- return 'item';
126
- }
127
- }
128
- return 'drawer';
129
- }
130
-
131
- // Le point est dans l'overlay (zone sombre autour du drawer)
132
- return 'overlay';
133
- }
134
-
135
- /**
136
- * Gère la pression (clic)
137
- * @param {number} x - Coordonnée X
138
- * @param {number} y - Coordonnée Y
139
- * @private
140
- */
141
- handlePress(x, y) {
142
- const zone = this.getZoneAtPoint(x, y);
143
-
144
- if (zone === 'overlay') {
145
- // Clic sur l'overlay - fermer le drawer
146
- this.close();
147
- return true; // On a géré le clic
148
- } else if (zone === 'item') {
149
- // Clic sur un item
150
- const startY = this.header ? 150 : 0;
151
- const index = Math.floor((y - startY) / 56);
152
- if (index >= 0 && index < this.items.length) {
153
- if (this.onItemClick) {
154
- this.onItemClick(index, this.items[index]);
155
- }
156
- this.close();
157
- }
158
- return true; // On a géré le clic
159
- }
160
-
161
- // Clic sur le drawer (mais pas sur un item) - on ne fait rien mais on capture le clic
162
- return true;
163
- }
164
-
165
- /**
166
- * Gère le mouvement (hover)
167
- * @param {number} x - Coordonnée X
168
- * @param {number} y - Coordonnée Y
169
- * @private
170
- */
171
- handleMove(x, y) {
172
- if (!this.visible) return;
173
-
174
- const zone = this.getZoneAtPoint(x, y);
175
- if (zone === 'item') {
176
- const startY = this.header ? 150 : 0;
177
- const index = Math.floor((y - startY) / 56);
178
- this.hoveredIndex = index;
179
- } else {
180
- this.hoveredIndex = -1;
181
- }
182
- }
183
-
184
- /**
185
- * Vérifie si un point est dans les limites du drawer (inclut l'overlay)
186
- * @param {number} x - Coordonnée X
187
- * @param {number} y - Coordonnée Y
188
- * @returns {boolean} True si le point est dans le drawer ou l'overlay
189
- */
190
- isPointInside(x, y) {
191
- if (!this.visible) return false;
192
-
193
- // Quand le drawer est ouvert, il capture TOUS les clics sur l'écran
194
- // car il a un overlay qui couvre tout
195
- return true;
196
- }
197
-
198
- /**
199
- * Dessine le drawer
200
- * @param {CanvasRenderingContext2D} ctx - Contexte de dessin
201
- */
202
- draw(ctx) {
203
- if (!this.visible) return;
204
-
205
- ctx.save();
206
-
207
- // Overlay sombre avec opacité progressive
208
- const overlayOpacity = Math.min(0.5, (this.x + this.width) / this.width * 0.5);
209
- ctx.fillStyle = `rgba(0, 0, 0, ${overlayOpacity})`;
210
- ctx.fillRect(0, 0, this.framework.width, this.framework.height);
211
-
212
- // Drawer
213
- ctx.fillStyle = '#FFFFFF';
214
- ctx.fillRect(this.x, this.y, this.width, this.height);
215
-
216
- // Ombre droite
217
- const gradient = ctx.createLinearGradient(this.x + this.width, 0, this.x + this.width + 10, 0);
218
- gradient.addColorStop(0, 'rgba(0, 0, 0, 0.2)');
219
- gradient.addColorStop(1, 'rgba(0, 0, 0, 0)');
220
- ctx.fillStyle = gradient;
221
- ctx.fillRect(this.x + this.width, 0, 10, this.height);
222
-
223
- // Header
224
- if (this.header) {
225
- ctx.fillStyle = this.platform === 'material' ? '#6200EE' : '#F8F8F8';
226
- ctx.fillRect(this.x, this.y, this.width, 150);
227
-
228
- ctx.fillStyle = this.platform === 'material' ? '#FFFFFF' : '#000000';
229
- ctx.font = 'bold 24px -apple-system, Roboto, sans-serif';
230
- ctx.textAlign = 'left';
231
- ctx.textBaseline = 'bottom';
232
- ctx.fillText(this.header.title || '', this.x + 20, this.y + 130);
233
- }
234
-
235
- // Items
236
- const startY = this.header ? 150 : 0;
237
- for (let i = 0; i < this.items.length; i++) {
238
- const item = this.items[i];
239
- const itemY = this.y + startY + i * 56;
240
-
241
- // Hover effect
242
- if (this.hoveredIndex === i) {
243
- ctx.fillStyle = '#F5F5F5';
244
- ctx.fillRect(this.x, itemY, this.width, 56);
245
- }
246
-
247
- // Icon
248
- if (item.icon) {
249
- ctx.fillStyle = '#757575';
250
- ctx.font = '20px -apple-system, Roboto, sans-serif';
251
- ctx.textAlign = 'left';
252
- ctx.textBaseline = 'middle';
253
- ctx.fillText(item.icon, this.x + 20, itemY + 28);
254
- }
255
-
256
- // Label
257
- ctx.fillStyle = '#000000';
258
- ctx.font = '16px -apple-system, Roboto, sans-serif';
259
- ctx.fillText(item.label, this.x + (item.icon ? 72 : 20), itemY + 28);
260
-
261
- // Divider
262
- if (item.divider) {
263
- ctx.strokeStyle = '#E0E0E0';
264
- ctx.lineWidth = 1;
265
- ctx.beginPath();
266
- ctx.moveTo(this.x, itemY + 56);
267
- ctx.lineTo(this.x + this.width, itemY + 56);
268
- ctx.stroke();
269
- }
270
- }
271
-
272
- ctx.restore();
273
- }
274
- }
275
-
276
- export default Drawer;
package/components/FAB.js DELETED
@@ -1,270 +0,0 @@
1
- import Component from '../core/Component.js';
2
-
3
- /**
4
- * Bouton d'action flottant (Material Design 3)
5
- * @class
6
- * @extends Component
7
- * @property {string} icon - Icône du bouton
8
- * @property {boolean} extended - Mode étendu (avec texte)
9
- * @property {string} text - Texte (en mode étendu)
10
- * @property {string} platform - Plateforme
11
- * @property {string} variant - Variante Material 3: 'small', 'medium', 'large', 'extended'
12
- * @property {number} size - Taille du bouton
13
- * @property {string} bgColor - Couleur de fond
14
- * @property {string} iconColor - Couleur de l'icône
15
- * @property {Array} ripples - Effets ripple
16
- */
17
- class FAB extends Component {
18
- /**
19
- * Crée une instance de FAB
20
- * @param {CanvasFramework} framework - Framework parent
21
- * @param {Object} [options={}] - Options de configuration
22
- * @param {string} [options.icon='+'] - Icône
23
- * @param {boolean} [options.extended=false] - Mode étendu
24
- * @param {string} [options.text=''] - Texte (mode étendu)
25
- * @param {string} [options.variant='medium'] - Variante: 'small', 'medium', 'large', 'extended'
26
- * @param {string} [options.bgColor] - Couleur (auto selon platform)
27
- * @param {string} [options.iconColor='#FFFFFF'] - Couleur de l'icône
28
- */
29
- constructor(framework, options = {}) {
30
- super(framework, options);
31
-
32
- this.icon = options.icon || '+';
33
- this.extended = options.extended || false;
34
- this.text = options.text || '';
35
- this.platform = framework.platform;
36
- this.variant = options.variant || 'medium';
37
-
38
- // Tailles selon Material Design 3
39
- const sizes = {
40
- small: 40,
41
- medium: 56,
42
- large: 96
43
- };
44
-
45
- this.size = options.size || sizes[this.variant] || 56;
46
-
47
- // Couleurs Material 3
48
- this.bgColor = options.bgColor || (framework.platform === 'material' ? '#6750A4' : '#007AFF');
49
- this.iconColor = options.iconColor || '#FFFFFF';
50
-
51
- // Border radius selon Material 3 (pas circulaire!)
52
- this.borderRadius = {
53
- small: 12,
54
- medium: 16,
55
- large: 28,
56
- extended: 16
57
- }[this.variant] || 16;
58
-
59
- // Position par défaut en bas à droite
60
- this.x = options.x !== undefined ? options.x : framework.width - this.size - 16;
61
- this.y = options.y !== undefined ? options.y : framework.height - this.size - 80;
62
-
63
- // Si extended, ajuster la largeur
64
- if (this.extended && this.text) {
65
- const ctx = framework.ctx;
66
- ctx.save();
67
- ctx.font = 'bold 14px -apple-system, sans-serif';
68
- const textWidth = ctx.measureText(this.text).width;
69
- ctx.restore();
70
- this.width = this.size + textWidth + 24;
71
- this.borderRadius = 16;
72
- } else {
73
- this.width = this.size;
74
- }
75
- this.height = this.size;
76
-
77
- // Effet ripple
78
- this.ripples = [];
79
-
80
- // ✅ CORRECTION : Binder onPress comme dans Button
81
- this.onPress = this.handlePress.bind(this);
82
- }
83
-
84
- /**
85
- * Gère la pression sur le FAB
86
- * @param {number} x - Coordonnée X
87
- * @param {number} y - Coordonnée Y
88
- * @private
89
- */
90
- handlePress(x, y) {
91
- // Créer un ripple au point de clic (Material uniquement)
92
- if (this.platform === 'material') {
93
- const adjustedY = y - this.framework.scrollOffset;
94
- this.ripples.push({
95
- x: x - this.x,
96
- y: adjustedY - this.y,
97
- radius: 0,
98
- maxRadius: Math.max(this.width, this.height) * 1.5,
99
- opacity: 1
100
- });
101
- this.animateRipple();
102
- }
103
- }
104
-
105
- /**
106
- * Anime l'effet ripple
107
- * @private
108
- */
109
- animateRipple() {
110
- const animate = () => {
111
- let hasActiveRipples = false;
112
-
113
- for (let ripple of this.ripples) {
114
- if (ripple.radius < ripple.maxRadius) {
115
- ripple.radius += ripple.maxRadius / 15;
116
- hasActiveRipples = true;
117
- }
118
-
119
- // Fade out après 50% de l'expansion
120
- if (ripple.radius >= ripple.maxRadius * 0.5) {
121
- ripple.opacity -= 0.05;
122
- }
123
- }
124
-
125
- // Nettoyer les ripples terminés
126
- this.ripples = this.ripples.filter(r => r.opacity > 0);
127
-
128
- if (hasActiveRipples) {
129
- requestAnimationFrame(animate);
130
- }
131
- };
132
-
133
- animate();
134
- }
135
-
136
- /**
137
- * Dessine le FAB
138
- * @param {CanvasRenderingContext2D} ctx - Contexte de dessin
139
- */
140
- draw(ctx) {
141
- ctx.save();
142
-
143
- // Ombre (elevation)
144
- if (!this.pressed) {
145
- ctx.shadowColor = 'rgba(0, 0, 0, 0.3)';
146
- ctx.shadowBlur = this.platform === 'material' ? 8 : 12;
147
- ctx.shadowOffsetY = this.platform === 'material' ? 4 : 6;
148
- }
149
-
150
- // Background - Material 3: rectangles arrondis, pas cercles!
151
- ctx.fillStyle = this.pressed ? this.darkenColor(this.bgColor) : this.bgColor;
152
- ctx.beginPath();
153
- this.roundRect(ctx, this.x, this.y, this.width, this.height, this.borderRadius);
154
- ctx.fill();
155
-
156
- ctx.shadowColor = 'transparent';
157
- ctx.shadowBlur = 0;
158
- ctx.shadowOffsetY = 0;
159
-
160
- // Clipping pour les ripples (Material uniquement)
161
- if (this.platform === 'material') {
162
- ctx.save();
163
- ctx.beginPath();
164
- this.roundRect(ctx, this.x, this.y, this.width, this.height, this.borderRadius);
165
- ctx.clip();
166
-
167
- // Dessiner les ripples
168
- for (let ripple of this.ripples) {
169
- ctx.globalAlpha = ripple.opacity;
170
- ctx.fillStyle = 'rgba(255, 255, 255, 0.3)';
171
- ctx.beginPath();
172
- ctx.arc(this.x + ripple.x, this.y + ripple.y, ripple.radius, 0, Math.PI * 2);
173
- ctx.fill();
174
- }
175
-
176
- ctx.restore();
177
- }
178
-
179
- // Overlay si pressed (iOS)
180
- if (this.pressed && this.platform === 'cupertino') {
181
- ctx.fillStyle = 'rgba(0, 0, 0, 0.1)';
182
- ctx.beginPath();
183
- this.roundRect(ctx, this.x, this.y, this.width, this.height, this.borderRadius);
184
- ctx.fill();
185
- }
186
-
187
- // Icône
188
- ctx.fillStyle = this.iconColor;
189
- const iconSize = this.variant === 'large' ? 36 : 24;
190
- ctx.font = `bold ${iconSize}px sans-serif`;
191
- ctx.textAlign = 'center';
192
- ctx.textBaseline = 'middle';
193
-
194
- if (this.extended && this.text) {
195
- // Icône à gauche
196
- ctx.fillText(this.icon, this.x + this.size / 2, this.y + this.size / 2);
197
-
198
- // Texte à droite
199
- ctx.font = 'bold 14px -apple-system, sans-serif';
200
- ctx.fillText(this.text, this.x + this.size + 12, this.y + this.size / 2);
201
- } else {
202
- // Icône centrée
203
- ctx.fillText(this.icon, this.x + this.width / 2, this.y + this.height / 2);
204
- }
205
-
206
- ctx.restore();
207
- }
208
-
209
- /**
210
- * Dessine un rectangle avec coins arrondis
211
- * @param {CanvasRenderingContext2D} ctx - Contexte de dessin
212
- * @param {number} x - Position X
213
- * @param {number} y - Position Y
214
- * @param {number} width - Largeur
215
- * @param {number} height - Hauteur
216
- * @param {number} radius - Rayon des coins
217
- * @private
218
- */
219
- roundRect(ctx, x, y, width, height, radius) {
220
- ctx.moveTo(x + radius, y);
221
- ctx.lineTo(x + width - radius, y);
222
- ctx.quadraticCurveTo(x + width, y, x + width, y + radius);
223
- ctx.lineTo(x + width, y + height - radius);
224
- ctx.quadraticCurveTo(x + width, y + height, x + width - radius, y + height);
225
- ctx.lineTo(x + radius, y + height);
226
- ctx.quadraticCurveTo(x, y + height, x, y + height - radius);
227
- ctx.lineTo(x, y + radius);
228
- ctx.quadraticCurveTo(x, y, x + radius, y);
229
- }
230
-
231
- /**
232
- * Assombrit une couleur
233
- * @param {string} color - Couleur hexadécimale
234
- * @returns {string} Couleur assombrie
235
- * @private
236
- */
237
- darkenColor(color) {
238
- const rgb = this.hexToRgb(color);
239
- return `rgb(${Math.max(0, rgb.r - 30)}, ${Math.max(0, rgb.g - 30)}, ${Math.max(0, rgb.b - 30)})`;
240
- }
241
-
242
- /**
243
- * Convertit une couleur hex en RGB
244
- * @param {string} hex - Couleur hexadécimale
245
- * @returns {{r: number, g: number, b: number}} Objet RGB
246
- * @private
247
- */
248
- hexToRgb(hex) {
249
- const result = /^#?([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})$/i.exec(hex);
250
- return result ? {
251
- r: parseInt(result[1], 16),
252
- g: parseInt(result[2], 16),
253
- b: parseInt(result[3], 16)
254
- } : { r: 0, g: 0, b: 0 };
255
- }
256
-
257
- /**
258
- * Vérifie si un point est dans les limites
259
- * @param {number} x - Coordonnée X
260
- * @param {number} y - Coordonnée Y
261
- * @returns {boolean} True si le point est dans le FAB
262
- */
263
- isPointInside(x, y) {
264
- // Material 3: toujours des rectangles arrondis, plus de cercles
265
- return x >= this.x && x <= this.x + this.width &&
266
- y >= this.y && y <= this.y + this.height;
267
- }
268
- }
269
-
270
- export default FAB;