canvasframework 0.5.39 → 0.5.40

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 (2) hide show
  1. package/components/Card.js +115 -344
  2. package/package.json +1 -1
@@ -43,216 +43,8 @@ class Card extends Component {
43
43
  // Stocker les positions relatives des enfants
44
44
  this.childPositions = new Map();
45
45
 
46
- // NOUVEAU: Gestion des événements
47
- this._canvas = null;
48
- this._isTouchDevice = false;
49
- this._clickListeners = new Map();
50
- this._touchListeners = new Map();
51
- this._enabled = options.enabled !== undefined ? options.enabled : true;
52
- this._interactive = options.interactive !== undefined ? options.interactive : true;
53
-
54
- // NOUVEAU: Pour le hot reload, nettoyer les anciens événements
55
- if (typeof window !== 'undefined') {
56
- this._setupEventCleanup();
57
- }
58
- }
59
-
60
- // NOUVEAU: Initialisation des événements
61
- _initEvents() {
62
- if (!this.framework || !this.framework.canvas) return;
63
-
64
- this._canvas = this.framework.canvas;
65
-
66
- // Détecter si on est sur mobile/touch
67
- this._isTouchDevice = 'ontouchstart' in window ||
68
- navigator.maxTouchPoints > 0 ||
69
- navigator.msMaxTouchPoints > 0;
70
-
71
- // Écouter les événements sur le canvas
72
- this._canvas.addEventListener('click', this._handleCanvasClick.bind(this));
73
- this._canvas.addEventListener('touchstart', this._handleCanvasTouch.bind(this), { passive: false });
74
- this._canvas.addEventListener('touchend', this._handleCanvasTouchEnd.bind(this), { passive: false });
75
- }
76
-
77
- // NOUVEAU: Nettoyage pour hot reload Vite
78
- _setupEventCleanup() {
79
- // Avant que la page ne soit déchargée (pour hot reload)
80
- window.addEventListener('beforeunload', () => {
81
- this._cleanupEvents();
82
- });
83
- }
84
-
85
- // NOUVEAU: Nettoyer les événements
86
- _cleanupEvents() {
87
- if (this._canvas) {
88
- this._canvas.removeEventListener('click', this._handleCanvasClick);
89
- this._canvas.removeEventListener('touchstart', this._handleCanvasTouch);
90
- this._canvas.removeEventListener('touchend', this._handleCanvasTouchEnd);
91
- }
92
- this._clickListeners.clear();
93
- this._touchListeners.clear();
94
- }
95
-
96
- // NOUVEAU: Convertir les coordonnées du DOM vers canvas
97
- _getCanvasCoordinates(clientX, clientY) {
98
- if (!this._canvas) return { x: 0, y: 0 };
99
-
100
- const rect = this._canvas.getBoundingClientRect();
101
- const scaleX = this._canvas.width / rect.width;
102
- const scaleY = this._canvas.height / rect.height;
103
-
104
- return {
105
- x: (clientX - rect.left) * scaleX,
106
- y: (clientY - rect.top) * scaleY
107
- };
108
- }
109
-
110
- // NOUVEAU: Gérer les clics sur le canvas
111
- _handleCanvasClick(event) {
112
- if (!this._enabled || !this.visible || !this._interactive) return;
113
-
114
- const coords = this._getCanvasCoordinates(event.clientX, event.clientY);
115
-
116
- // Vérifier si le clic est sur cette carte
117
- if (this.isPointInside(coords.x, coords.y)) {
118
- // Empêcher le comportement par défaut
119
- event.preventDefault();
120
- event.stopPropagation();
121
-
122
- // Déclencher l'événement onClick de la carte
123
- if (this.onClick) {
124
- const localCoords = {
125
- x: coords.x - this.x,
126
- y: coords.y - this.y
127
- };
128
- this.onClick({
129
- type: 'click',
130
- x: localCoords.x,
131
- y: localCoords.y,
132
- clientX: event.clientX,
133
- clientY: event.clientY,
134
- originalEvent: event,
135
- target: this
136
- });
137
- }
138
-
139
- // Vérifier si un enfant est cliqué
140
- this._propagateClickToChildren(coords.x, coords.y, event);
141
- }
142
- }
143
-
144
- // NOUVEAU: Gérer les touches tactiles
145
- _handleCanvasTouch(event) {
146
- if (!this._enabled || !this.visible || !this._interactive) return;
147
-
148
- if (event.touches.length > 0) {
149
- event.preventDefault();
150
- event.stopPropagation();
151
-
152
- const touch = event.touches[0];
153
- const coords = this._getCanvasCoordinates(touch.clientX, touch.clientY);
154
-
155
- // Stocker la position de départ du touch
156
- this._lastTouchStart = { x: coords.x, y: coords.y };
157
-
158
- // Vérifier si le touch est sur cette carte
159
- if (this.isPointInside(coords.x, coords.y)) {
160
- // Déclencher l'événement onTouchStart de la carte
161
- if (this.onTouchStart) {
162
- const localCoords = {
163
- x: coords.x - this.x,
164
- y: coords.y - this.y
165
- };
166
- this.onTouchStart({
167
- type: 'touchstart',
168
- x: localCoords.x,
169
- y: localCoords.y,
170
- clientX: touch.clientX,
171
- clientY: touch.clientY,
172
- originalEvent: event,
173
- target: this
174
- });
175
- }
176
- }
177
- }
178
- }
179
-
180
- // NOUVEAU: Gérer la fin des touches tactiles
181
- _handleCanvasTouchEnd(event) {
182
- if (!this._enabled || !this.visible || !this._interactive) return;
183
-
184
- event.preventDefault();
185
- event.stopPropagation();
186
-
187
- if (event.changedTouches.length > 0) {
188
- const touch = event.changedTouches[0];
189
- const coords = this._getCanvasCoordinates(touch.clientX, touch.clientY);
190
-
191
- // Vérifier si le touch end est sur cette carte
192
- if (this.isPointInside(coords.x, coords.y)) {
193
- // Déclencher l'événement onTouchEnd de la carte
194
- if (this.onTouchEnd) {
195
- const localCoords = {
196
- x: coords.x - this.x,
197
- y: coords.y - this.y
198
- };
199
- this.onTouchEnd({
200
- type: 'touchend',
201
- x: localCoords.x,
202
- y: localCoords.y,
203
- clientX: touch.clientX,
204
- clientY: touch.clientY,
205
- originalEvent: event,
206
- target: this
207
- });
208
- }
209
-
210
- // Simuler un clic si le touch start était sur la même position
211
- if (this._lastTouchStart &&
212
- Math.abs(coords.x - this._lastTouchStart.x) < 10 &&
213
- Math.abs(coords.y - this._lastTouchStart.y) < 10) {
214
- this._propagateClickToChildren(coords.x, coords.y, event);
215
- }
216
-
217
- this._lastTouchStart = null;
218
- }
219
- }
220
- }
221
-
222
- // NOUVEAU: Propager le clic aux enfants
223
- _propagateClickToChildren(canvasX, canvasY, originalEvent) {
224
- // Parcourir les enfants du dernier au premier (z-order)
225
- for (let i = this.children.length - 1; i >= 0; i--) {
226
- const child = this.children[i];
227
-
228
- // Vérifier si l'enfant est visible et interactif
229
- const childEnabled = child._enabled !== undefined ? child._enabled : true;
230
- const childInteractive = child._interactive !== undefined ? child._interactive : true;
231
-
232
- if (child.visible && childEnabled && childInteractive &&
233
- child.isPointInside(canvasX, canvasY)) {
234
-
235
- // Déclencher l'événement onClick de l'enfant
236
- if (child.onClick) {
237
- const localCoords = {
238
- x: canvasX - child.x,
239
- y: canvasY - child.y
240
- };
241
- child.onClick({
242
- type: 'click',
243
- x: localCoords.x,
244
- y: localCoords.y,
245
- clientX: originalEvent.clientX,
246
- clientY: originalEvent.clientY,
247
- originalEvent: originalEvent,
248
- target: child
249
- });
250
- }
251
-
252
- // Stopper la propagation après avoir trouvé un enfant cliqué
253
- break;
254
- }
255
- }
46
+ // NOUVEAU: Flag pour forcer les positions absolues
47
+ this._positionsCorrected = false;
256
48
  }
257
49
 
258
50
  /**
@@ -263,53 +55,29 @@ class Card extends Component {
263
55
  add(child) {
264
56
  this.children.push(child);
265
57
 
266
- // CONVERTIR les positions relatives en positions absolues
267
- child.x = this.x + child.x;
268
- child.y = this.y + child.y;
58
+ // SOLUTION: Stocker les coordonnées relatives AVANT conversion
59
+ const relativeX = child.x || 0;
60
+ const relativeY = child.y || 0;
269
61
 
270
62
  // Stocker la position relative originale
271
63
  this.childPositions.set(child, {
272
- x: child.x - this.x,
273
- y: child.y - this.y
64
+ x: relativeX,
65
+ y: relativeY
274
66
  });
275
67
 
276
- // NOUVEAU: Initialiser les événements si c'est la première fois
277
- if (this.children.length === 1 && this._canvas === null) {
278
- this._initEvents();
279
- }
68
+ // CONVERTIR IMMÉDIATEMENT en positions absolues
69
+ child.x = this.x + relativeX;
70
+ child.y = this.y + relativeY;
280
71
 
281
72
  // Si autoLayout est activé, organiser automatiquement
282
73
  if (this.autoLayout) {
283
- this.layout();
74
+ // Utiliser setTimeout pour éviter les conflits avec Vite Hot Reload
75
+ setTimeout(() => this.layout(), 0);
284
76
  }
285
77
 
286
78
  return child;
287
79
  }
288
80
 
289
- /**
290
- * Supprime un enfant
291
- * @param {Component} child - Composant enfant à supprimer
292
- * @returns {boolean} True si l'enfant a été supprimé
293
- */
294
- remove(child) {
295
- const index = this.children.indexOf(child);
296
- if (index > -1) {
297
- this.children.splice(index, 1);
298
- this.childPositions.delete(child);
299
- if (this.autoLayout) this.layout();
300
- return true;
301
- }
302
- return false;
303
- }
304
-
305
- /**
306
- * Supprime tous les enfants
307
- */
308
- clear() {
309
- this.children = [];
310
- this.childPositions.clear();
311
- }
312
-
313
81
  /**
314
82
  * Organise les enfants selon le layout
315
83
  * @private
@@ -334,7 +102,7 @@ class Card extends Component {
334
102
  child.width = this.width - (this.padding * 2);
335
103
  }
336
104
 
337
- // Positionner l'enfant RELATIVEMENT à la Card
105
+ // Positionner l'enfant en COORDONNÉES ABSOLUES
338
106
  child.x = this.x + childX;
339
107
  child.y = this.y + currentY;
340
108
 
@@ -367,7 +135,7 @@ class Card extends Component {
367
135
  child.height = this.height - (this.padding * 2);
368
136
  }
369
137
 
370
- // Positionner l'enfant RELATIVEMENT à la Card
138
+ // Positionner l'enfant en COORDONNÉES ABSOLUES
371
139
  child.x = this.x + currentX;
372
140
  child.y = this.y + childY;
373
141
 
@@ -383,6 +151,68 @@ class Card extends Component {
383
151
  }
384
152
  }
385
153
  }
154
+
155
+ // Marquer que les positions sont corrigées
156
+ this._positionsCorrected = true;
157
+ }
158
+
159
+ /**
160
+ * Dessine la carte et ses enfants
161
+ * @param {CanvasRenderingContext2D} ctx - Contexte de dessin
162
+ */
163
+ draw(ctx) {
164
+ ctx.save();
165
+
166
+ // SOLUTION: Vérifier et corriger les positions AVANT de dessiner
167
+ // Ceci garantit que les enfants sont en coordonnées absolues
168
+ if (this._positionsCorrected) {
169
+ this._ensureAbsolutePositions();
170
+ }
171
+
172
+ // Dessiner l'ombre si elevation > 0
173
+ if (this.elevation > 0) {
174
+ this.drawShadow(ctx);
175
+ }
176
+
177
+ // Dessiner le fond de la carte
178
+ if (this.bgColor !== 'transparent') {
179
+ ctx.fillStyle = this.bgColor;
180
+ if (this.borderRadius > 0) {
181
+ ctx.beginPath();
182
+ this.roundRect(ctx, this.x, this.y, this.width, this.height, this.borderRadius);
183
+ ctx.fill();
184
+ } else {
185
+ ctx.fillRect(this.x, this.y, this.width, this.height);
186
+ }
187
+ }
188
+
189
+ // Dessiner les enfants
190
+ for (let child of this.children) {
191
+ if (child.visible) child.draw(ctx);
192
+ }
193
+
194
+ ctx.restore();
195
+ }
196
+
197
+ /**
198
+ * S'assure que tous les enfants ont des coordonnées absolues
199
+ * @private
200
+ */
201
+ _ensureAbsolutePositions() {
202
+ for (let child of this.children) {
203
+ const relativePos = this.childPositions.get(child);
204
+ if (relativePos) {
205
+ // Recalculer la position absolue basée sur la position de la carte
206
+ const expectedX = this.x + relativePos.x;
207
+ const expectedY = this.y + relativePos.y;
208
+
209
+ // Corriger si nécessaire (important pour Vite Hot Reload)
210
+ if (Math.abs(child.x - expectedX) > 1 || Math.abs(child.y - expectedY) > 1) {
211
+ child.x = expectedX;
212
+ child.y = expectedY;
213
+ }
214
+ }
215
+ }
386
216
  }
387
217
 
388
218
  /**
@@ -401,6 +231,44 @@ class Card extends Component {
401
231
  child.x += deltaX;
402
232
  child.y += deltaY;
403
233
  }
234
+
235
+ // Mettre à jour les positions relatives stockées
236
+ this._updateRelativePositions();
237
+ }
238
+
239
+ /**
240
+ * Met à jour les positions relatives stockées
241
+ * @private
242
+ */
243
+ _updateRelativePositions() {
244
+ for (let [child, pos] of this.childPositions.entries()) {
245
+ // Les positions relatives ne changent pas quand la carte bouge
246
+ // On garde juste la référence
247
+ }
248
+ }
249
+
250
+ /**
251
+ * Supprime un enfant
252
+ * @param {Component} child - Composant enfant à supprimer
253
+ * @returns {boolean} True si l'enfant a été supprimé
254
+ */
255
+ remove(child) {
256
+ const index = this.children.indexOf(child);
257
+ if (index > -1) {
258
+ this.children.splice(index, 1);
259
+ this.childPositions.delete(child);
260
+ if (this.autoLayout) this.layout();
261
+ return true;
262
+ }
263
+ return false;
264
+ }
265
+
266
+ /**
267
+ * Supprime tous les enfants
268
+ */
269
+ clear() {
270
+ this.children = [];
271
+ this.childPositions.clear();
404
272
  }
405
273
 
406
274
  /**
@@ -411,6 +279,7 @@ class Card extends Component {
411
279
  */
412
280
  setChildPosition(child, relativeX, relativeY) {
413
281
  if (this.children.includes(child)) {
282
+ // Définir en coordonnées absolues
414
283
  child.x = this.x + relativeX;
415
284
  child.y = this.y + relativeY;
416
285
  this.childPositions.set(child, { x: relativeX, y: relativeY });
@@ -482,38 +351,6 @@ class Card extends Component {
482
351
  ctx.restore();
483
352
  }
484
353
 
485
- /**
486
- * Dessine la carte et ses enfants
487
- * @param {CanvasRenderingContext2D} ctx - Contexte de dessin
488
- */
489
- draw(ctx) {
490
- ctx.save();
491
-
492
- // Dessiner l'ombre si elevation > 0
493
- if (this.elevation > 0) {
494
- this.drawShadow(ctx);
495
- }
496
-
497
- // Dessiner le fond de la carte
498
- if (this.bgColor !== 'transparent') {
499
- ctx.fillStyle = this.bgColor;
500
- if (this.borderRadius > 0) {
501
- ctx.beginPath();
502
- this.roundRect(ctx, this.x, this.y, this.width, this.height, this.borderRadius);
503
- ctx.fill();
504
- } else {
505
- ctx.fillRect(this.x, this.y, this.width, this.height);
506
- }
507
- }
508
-
509
- // Dessiner les enfants
510
- for (let child of this.children) {
511
- if (child.visible) child.draw(ctx);
512
- }
513
-
514
- ctx.restore();
515
- }
516
-
517
354
  /**
518
355
  * Dessine un rectangle avec coins arrondis
519
356
  * @param {CanvasRenderingContext2D} ctx - Contexte de dessin
@@ -550,63 +387,12 @@ class Card extends Component {
550
387
  * @returns {boolean} True si le point est dans la vue
551
388
  */
552
389
  isPointInside(x, y) {
553
- // Pour les rectangles arrondis, vérification plus précise
554
- if (this.borderRadius > 0) {
555
- return this._isPointInRoundedRect(x, y);
556
- }
557
-
558
- // Pour les rectangles normaux
559
390
  return x >= this.x &&
560
391
  x <= this.x + this.width &&
561
392
  y >= this.y &&
562
393
  y <= this.y + this.height;
563
394
  }
564
395
 
565
- // NOUVEAU: Vérifier si un point est dans un rectangle arrondi
566
- _isPointInRoundedRect(x, y) {
567
- const rectX = this.x;
568
- const rectY = this.y;
569
- const width = this.width;
570
- const height = this.height;
571
- const radius = this.borderRadius;
572
-
573
- // Vérifier la zone centrale (sans les coins arrondis)
574
- if (x >= rectX + radius && x <= rectX + width - radius &&
575
- y >= rectY && y <= rectY + height) {
576
- return true;
577
- }
578
- if (x >= rectX && x <= rectX + width &&
579
- y >= rectY + radius && y <= rectY + height - radius) {
580
- return true;
581
- }
582
-
583
- // Vérifier les quatre coins arrondis
584
- const checkCorner = (cornerX, cornerY) => {
585
- const dx = x - cornerX;
586
- const dy = y - cornerY;
587
- return (dx * dx + dy * dy) <= (radius * radius);
588
- };
589
-
590
- // Coin supérieur gauche
591
- if (x < rectX + radius && y < rectY + radius) {
592
- return checkCorner(rectX + radius, rectY + radius);
593
- }
594
- // Coin supérieur droit
595
- if (x > rectX + width - radius && y < rectY + radius) {
596
- return checkCorner(rectX + width - radius, rectY + radius);
597
- }
598
- // Coin inférieur gauche
599
- if (x < rectX + radius && y > rectY + height - radius) {
600
- return checkCorner(rectX + radius, rectY + height - radius);
601
- }
602
- // Coin inférieur droit
603
- if (x > rectX + width - radius && y > rectY + height - radius) {
604
- return checkCorner(rectX + width - radius, rectY + height - radius);
605
- }
606
-
607
- return false;
608
- }
609
-
610
396
  /**
611
397
  * Définit le niveau d'élévation
612
398
  * @param {number} elevation - Nouveau niveau d'élévation (0-5)
@@ -743,21 +529,6 @@ class Card extends Component {
743
529
  getChildPosition(child) {
744
530
  return this.childPositions.get(child) || null;
745
531
  }
746
-
747
- // NOUVEAU: Méthodes pour activer/désactiver l'interactivité
748
- setEnabled(enabled) {
749
- this._enabled = enabled;
750
- }
751
-
752
- setInteractive(interactive) {
753
- this._interactive = interactive;
754
- }
755
-
756
- // NOUVEAU: Méthode de nettoyage
757
- destroy() {
758
- this._cleanupEvents();
759
- super.destroy();
760
- }
761
532
  }
762
533
 
763
534
  export default Card;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "canvasframework",
3
- "version": "0.5.39",
3
+ "version": "0.5.40",
4
4
  "repository": {
5
5
  "type": "git",
6
6
  "url": "https://github.com/beyons/CanvasFramework.git"