canvasframework 0.5.38 → 0.5.39
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.
- package/components/Card.js +282 -1
- package/package.json +1 -1
package/components/Card.js
CHANGED
|
@@ -42,6 +42,217 @@ class Card extends Component {
|
|
|
42
42
|
|
|
43
43
|
// Stocker les positions relatives des enfants
|
|
44
44
|
this.childPositions = new Map();
|
|
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
|
+
}
|
|
45
256
|
}
|
|
46
257
|
|
|
47
258
|
/**
|
|
@@ -53,7 +264,6 @@ class Card extends Component {
|
|
|
53
264
|
this.children.push(child);
|
|
54
265
|
|
|
55
266
|
// CONVERTIR les positions relatives en positions absolues
|
|
56
|
-
// Les x, y passés sont relatifs à la Card
|
|
57
267
|
child.x = this.x + child.x;
|
|
58
268
|
child.y = this.y + child.y;
|
|
59
269
|
|
|
@@ -63,6 +273,11 @@ class Card extends Component {
|
|
|
63
273
|
y: child.y - this.y
|
|
64
274
|
});
|
|
65
275
|
|
|
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
|
+
}
|
|
280
|
+
|
|
66
281
|
// Si autoLayout est activé, organiser automatiquement
|
|
67
282
|
if (this.autoLayout) {
|
|
68
283
|
this.layout();
|
|
@@ -335,12 +550,63 @@ class Card extends Component {
|
|
|
335
550
|
* @returns {boolean} True si le point est dans la vue
|
|
336
551
|
*/
|
|
337
552
|
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
|
|
338
559
|
return x >= this.x &&
|
|
339
560
|
x <= this.x + this.width &&
|
|
340
561
|
y >= this.y &&
|
|
341
562
|
y <= this.y + this.height;
|
|
342
563
|
}
|
|
343
564
|
|
|
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
|
+
|
|
344
610
|
/**
|
|
345
611
|
* Définit le niveau d'élévation
|
|
346
612
|
* @param {number} elevation - Nouveau niveau d'élévation (0-5)
|
|
@@ -477,6 +743,21 @@ class Card extends Component {
|
|
|
477
743
|
getChildPosition(child) {
|
|
478
744
|
return this.childPositions.get(child) || null;
|
|
479
745
|
}
|
|
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
|
+
}
|
|
480
761
|
}
|
|
481
762
|
|
|
482
763
|
export default Card;
|