canvasframework 0.5.40 → 0.5.41

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.
@@ -0,0 +1,232 @@
1
+ import Component from '../core/Component.js';
2
+
3
+ /**
4
+ * Container avec système de layout et élévation
5
+ * @class
6
+ * @extends Component
7
+ */
8
+ class Cards extends Component {
9
+ constructor(framework, options = {}, children = []) {
10
+ super(framework, options);
11
+ this.children = [];
12
+ this.padding = options.padding || 0;
13
+ this.gap = options.gap || 0;
14
+ this.direction = options.direction || 'column';
15
+ this.align = options.align || 'start';
16
+ this.bgColor = options.bgColor || 'transparent';
17
+ this.borderRadius = options.borderRadius || 0;
18
+ this.elevation = options.elevation || 0;
19
+ this.shadowColor = options.shadowColor || 'rgba(0,0,0,0.15)';
20
+ this._applyElevationStyles();
21
+
22
+ // Mode de positionnement
23
+ this.positionMode = options.positionMode || 'flow';
24
+
25
+ // Stocker les positions relatives des enfants
26
+ this.childRelativePositions = new Map();
27
+
28
+ // Ajouter les enfants passés dans le constructeur
29
+ if (children && children.length > 0) {
30
+ this.addChildren(children);
31
+ }
32
+ }
33
+
34
+ _applyElevationStyles() {
35
+ const elevationStyles = {
36
+ 0: { blur: 0, offsetY: 0, spread: 0, opacity: 0 },
37
+ 1: { blur: 2, offsetY: 1, spread: 0, opacity: 0.1 },
38
+ 2: { blur: 4, offsetY: 2, spread: 1, opacity: 0.15 },
39
+ 3: { blur: 8, offsetY: 4, spread: 2, opacity: 0.2 },
40
+ 4: { blur: 16, offsetY: 8, spread: 3, opacity: 0.25 },
41
+ 5: { blur: 24, offsetY: 12, spread: 4, opacity: 0.3 }
42
+ };
43
+
44
+ const style = elevationStyles[Math.min(this.elevation, 5)] || elevationStyles[0];
45
+
46
+ this.shadowBlur = style.blur;
47
+ this.shadowOffsetY = style.offsetY;
48
+ this.shadowSpread = style.spread;
49
+ this.shadowOpacity = style.opacity;
50
+
51
+ this._updateShadowColor();
52
+ }
53
+
54
+ _updateShadowColor() {
55
+ const rgbMatch = this.shadowColor.match(/rgba?\((\d+),\s*(\d+),\s*(\d+)/);
56
+ if (rgbMatch) {
57
+ const r = rgbMatch[1];
58
+ const g = rgbMatch[2];
59
+ const b = rgbMatch[3];
60
+ this._computedShadowColor = `rgba(${r}, ${g}, ${b}, ${this.shadowOpacity})`;
61
+ } else {
62
+ this._computedShadowColor = this.shadowColor;
63
+ }
64
+ }
65
+
66
+ /**
67
+ * Ajoute plusieurs enfants
68
+ * @param {Component[]} children - Tableau d'enfants
69
+ */
70
+ addChildren(children) {
71
+ for (const child of children) {
72
+ this.add(child);
73
+ }
74
+ }
75
+
76
+ /**
77
+ * Ajoute un enfant
78
+ * @param {Component} child - Composant enfant
79
+ * @returns {Component} L'enfant ajouté
80
+ */
81
+ add(child) {
82
+ this.children.push(child);
83
+
84
+ // Si l'enfant a des coordonnées x,y, on les interprète comme relatives au container
85
+ if (child.x !== undefined && child.y !== undefined) {
86
+ // Stocker les positions RELATIVES par rapport au container
87
+ this.childRelativePositions.set(child, {
88
+ relativeX: child.x, // x relatif au container
89
+ relativeY: child.y // y relatif au container
90
+ });
91
+
92
+ // Calculer la position absolue immédiatement
93
+ this.updateChildPosition(child);
94
+ }
95
+
96
+ return child;
97
+ }
98
+
99
+ /**
100
+ * Met à jour la position d'un enfant en fonction de la position du container
101
+ * @param {Component} child - L'enfant à positionner
102
+ * @private
103
+ */
104
+ updateChildPosition(child) {
105
+ const relativePos = this.childRelativePositions.get(child);
106
+
107
+ if (relativePos) {
108
+ // Position ABSOLUE = position container + position relative + padding
109
+ child.x = this.x + this.padding + relativePos.relativeX;
110
+ child.y = this.y + this.padding + relativePos.relativeY;
111
+ }
112
+ }
113
+
114
+ /**
115
+ * Met à jour la position de tous les enfants quand le container bouge
116
+ * @override
117
+ */
118
+ setPosition(x, y) {
119
+ super.setPosition(x, y);
120
+ this.updateAllChildrenPositions();
121
+ }
122
+
123
+ /**
124
+ * Met à jour toutes les positions des enfants
125
+ * @private
126
+ */
127
+ updateAllChildrenPositions() {
128
+ for (const child of this.children) {
129
+ this.updateChildPosition(child);
130
+ }
131
+ }
132
+
133
+ /**
134
+ * Change la position relative d'un enfant dans le container
135
+ * @param {Component} child - L'enfant à repositionner
136
+ * @param {number} relativeX - Nouvelle position X relative
137
+ * @param {number} relativeY - Nouvelle position Y relative
138
+ */
139
+ setChildPosition(child, relativeX, relativeY) {
140
+ this.childRelativePositions.set(child, {
141
+ relativeX: relativeX,
142
+ relativeY: relativeY
143
+ });
144
+ this.updateChildPosition(child);
145
+ }
146
+
147
+ draw(ctx) {
148
+ ctx.save();
149
+
150
+ // Dessiner l'ombre
151
+ if (this.elevation > 0 && this.bgColor !== 'transparent') {
152
+ this.drawShadow(ctx);
153
+ }
154
+
155
+ // Dessiner le fond
156
+ if (this.bgColor !== 'transparent') {
157
+ ctx.fillStyle = this.bgColor;
158
+ if (this.borderRadius > 0) {
159
+ ctx.beginPath();
160
+ this.roundRect(ctx, this.x, this.y, this.width, this.height, this.borderRadius);
161
+ ctx.fill();
162
+ } else {
163
+ ctx.fillRect(this.x, this.y, this.width, this.height);
164
+ }
165
+ }
166
+
167
+ // Dessiner les enfants
168
+ for (let child of this.children) {
169
+ if (child.visible) child.draw(ctx);
170
+ }
171
+
172
+ ctx.restore();
173
+ }
174
+
175
+ drawShadow(ctx) {
176
+ ctx.save();
177
+
178
+ ctx.shadowColor = this._computedShadowColor;
179
+ ctx.shadowBlur = this.shadowBlur;
180
+ ctx.shadowOffsetX = 0;
181
+ ctx.shadowOffsetY = this.shadowOffsetY;
182
+
183
+ ctx.fillStyle = this.bgColor;
184
+
185
+ if (this.borderRadius > 0) {
186
+ ctx.beginPath();
187
+ const spread = this.shadowSpread;
188
+ this.roundRect(
189
+ ctx,
190
+ this.x - spread/2,
191
+ this.y - spread/2,
192
+ this.width + spread,
193
+ this.height + spread,
194
+ this.borderRadius + spread/2
195
+ );
196
+ ctx.fill();
197
+ } else {
198
+ const spread = this.shadowSpread;
199
+ ctx.fillRect(
200
+ this.x - spread/2,
201
+ this.y - spread/2,
202
+ this.width + spread,
203
+ this.height + spread
204
+ );
205
+ }
206
+
207
+ ctx.restore();
208
+ }
209
+
210
+ roundRect(ctx, x, y, width, height, radius) {
211
+ ctx.beginPath();
212
+ ctx.moveTo(x + radius, y);
213
+ ctx.lineTo(x + width - radius, y);
214
+ ctx.quadraticCurveTo(x + width, y, x + width, y + radius);
215
+ ctx.lineTo(x + width, y + height - radius);
216
+ ctx.quadraticCurveTo(x + width, y + height, x + width - radius, y + height);
217
+ ctx.lineTo(x + radius, y + height);
218
+ ctx.quadraticCurveTo(x, y + height, x, y + height - radius);
219
+ ctx.lineTo(x, y + radius);
220
+ ctx.quadraticCurveTo(x, y, x + radius, y);
221
+ ctx.closePath();
222
+ }
223
+
224
+ isPointInside(x, y) {
225
+ return x >= this.x &&
226
+ x <= this.x + this.width &&
227
+ y >= this.y &&
228
+ y <= this.y + this.height;
229
+ }
230
+ }
231
+
232
+ export default Cards;
@@ -4,7 +4,8 @@ import Input from '../components/Input.js';
4
4
  import Slider from '../components/Slider.js';
5
5
  import Text from '../components/Text.js';
6
6
  import View from '../components/View.js';
7
- import Card from '../components/Card.js';
7
+ //import Card from '../components/Card.js';
8
+ import Cards from '../components/Cards.js';
8
9
  import FAB from '../components/FAB.js';
9
10
  import SpeedDialFAB from '../components/SpeedDialFAB.js';
10
11
  import MorphingFAB from '../components/MorphingFAB.js';
@@ -2233,7 +2234,7 @@ class CanvasFramework {
2233
2234
  if (comp.visible) {
2234
2235
  const adjustedY = isFixedComponent(comp) ? y : y - this.scrollOffset;
2235
2236
 
2236
- if (comp instanceof Card && comp.clickableChildren && comp.children && comp.children.length > 0) {
2237
+ /* if (comp instanceof Card && comp.clickableChildren && comp.children && comp.children.length > 0) {
2237
2238
  if (comp.isPointInside(x, adjustedY)) {
2238
2239
  const cardAdjustedY = adjustedY - comp.y - comp.padding;
2239
2240
  const cardAdjustedX = x - comp.x - comp.padding;
@@ -2300,7 +2301,7 @@ class CanvasFramework {
2300
2301
  }
2301
2302
  }
2302
2303
  }
2303
- }
2304
+ }*/
2304
2305
 
2305
2306
  if (comp.isPointInside(x, adjustedY)) {
2306
2307
  switch (eventType) {
package/core/UIBuilder.js CHANGED
@@ -5,7 +5,7 @@ import Input from '../components/Input.js';
5
5
  import Slider from '../components/Slider.js';
6
6
  import Text from '../components/Text.js';
7
7
  import View from '../components/View.js';
8
- import Card from '../components/Card.js';
8
+ import Cards from '../components/Cards.js';
9
9
  import FAB from '../components/FAB.js';
10
10
  import SpeedDialFAB from '../components/SpeedDialFAB.js';
11
11
  import MorphingFAB from '../components/MorphingFAB.js';
@@ -82,7 +82,7 @@ const Components = {
82
82
  Slider,
83
83
  Text,
84
84
  View,
85
- Card,
85
+ Cards,
86
86
  FAB,
87
87
  SpeedDialFAB,
88
88
  MorphingFAB,
package/index.js CHANGED
@@ -12,7 +12,7 @@ export { default as Input } from './components/Input.js';
12
12
  export { default as Slider } from './components/Slider.js';
13
13
  export { default as Text } from './components/Text.js';
14
14
  export { default as View } from './components/View.js';
15
- export { default as Card } from './components/Card.js';
15
+ export { default as Cards } from './components/Cards.js';
16
16
  export { default as FAB } from './components/FAB.js';
17
17
  export { default as SpeedDialFAB } from './components/SpeedDialFAB.js';
18
18
  export { default as MorphingFAB } from './components/MorphingFAB.js';
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "canvasframework",
3
- "version": "0.5.40",
3
+ "version": "0.5.41",
4
4
  "repository": {
5
5
  "type": "git",
6
6
  "url": "https://github.com/beyons/CanvasFramework.git"
@@ -1,534 +0,0 @@
1
- import Component from '../core/Component.js';
2
-
3
- /**
4
- * Container avec système de layout et effet d'élévation
5
- * @class
6
- * @extends Component
7
- * @property {Component[]} children - Enfants
8
- * @property {number} padding - Padding interne
9
- * @property {number} gap - Espacement constant entre enfants
10
- * @property {string} direction - Direction ('column' ou 'row')
11
- * @property {string} align - Alignement ('start', 'center', 'end', 'stretch')
12
- * @property {string} bgColor - Couleur de fond
13
- * @property {number} borderRadius - Rayon des coins
14
- * @property {number} elevation - Niveau d'élévation (ombres)
15
- * @property {boolean} autoLayout - Active le layout automatique
16
- */
17
- class Card extends Component {
18
- /**
19
- * Crée une instance de Card
20
- * @param {CanvasFramework} framework - Framework parent
21
- * @param {Object} [options={}] - Options de configuration
22
- * @param {number} [options.padding=0] - Padding interne
23
- * @param {number} [options.gap=0] - Espacement constant entre enfants
24
- * @param {string} [options.direction='column'] - Direction
25
- * @param {string} [options.align='start'] - Alignement
26
- * @param {string} [options.bgColor='transparent'] - Couleur de fond
27
- * @param {number} [options.borderRadius=0] - Rayon des coins
28
- * @param {number} [options.elevation=0] - Niveau d'élévation (0-5)
29
- * @param {boolean} [options.autoLayout=true] - Active le layout automatique
30
- */
31
- constructor(framework, options = {}) {
32
- super(framework, options);
33
- this.children = [];
34
- this.padding = options.padding || 0;
35
- this.gap = options.gap || 0;
36
- this.direction = options.direction || 'column';
37
- this.align = options.align || 'start';
38
- this.bgColor = options.bgColor || 'transparent';
39
- this.borderRadius = options.borderRadius || 0;
40
- this.elevation = options.elevation || 0;
41
- this.autoLayout = options.autoLayout !== undefined ? options.autoLayout : true;
42
-
43
- // Stocker les positions relatives des enfants
44
- this.childPositions = new Map();
45
-
46
- // NOUVEAU: Flag pour forcer les positions absolues
47
- this._positionsCorrected = false;
48
- }
49
-
50
- /**
51
- * Ajoute un enfant (position relative convertie en absolue)
52
- * @param {Component} child - Composant enfant
53
- * @returns {Component} L'enfant ajouté
54
- */
55
- add(child) {
56
- this.children.push(child);
57
-
58
- // SOLUTION: Stocker les coordonnées relatives AVANT conversion
59
- const relativeX = child.x || 0;
60
- const relativeY = child.y || 0;
61
-
62
- // Stocker la position relative originale
63
- this.childPositions.set(child, {
64
- x: relativeX,
65
- y: relativeY
66
- });
67
-
68
- // CONVERTIR IMMÉDIATEMENT en positions absolues
69
- child.x = this.x + relativeX;
70
- child.y = this.y + relativeY;
71
-
72
- // Si autoLayout est activé, organiser automatiquement
73
- if (this.autoLayout) {
74
- // Utiliser setTimeout pour éviter les conflits avec Vite Hot Reload
75
- setTimeout(() => this.layout(), 0);
76
- }
77
-
78
- return child;
79
- }
80
-
81
- /**
82
- * Organise les enfants selon le layout
83
- * @private
84
- */
85
- layout() {
86
- if (this.children.length === 0 || !this.autoLayout) return;
87
-
88
- if (this.direction === 'column') {
89
- let currentY = this.padding;
90
-
91
- for (let i = 0; i < this.children.length; i++) {
92
- const child = this.children[i];
93
-
94
- // Calculer la position X selon l'alignement
95
- let childX = this.padding;
96
- if (this.align === 'center') {
97
- childX = (this.width - child.width) / 2;
98
- } else if (this.align === 'end') {
99
- childX = this.width - child.width - this.padding;
100
- } else if (this.align === 'stretch') {
101
- childX = this.padding;
102
- child.width = this.width - (this.padding * 2);
103
- }
104
-
105
- // Positionner l'enfant en COORDONNÉES ABSOLUES
106
- child.x = this.x + childX;
107
- child.y = this.y + currentY;
108
-
109
- // Stocker la position relative
110
- this.childPositions.set(child, { x: childX, y: currentY });
111
-
112
- // Mettre à jour la position Y pour l'enfant suivant
113
- currentY += child.height;
114
-
115
- // Ajouter le gap seulement si ce n'est pas le dernier enfant
116
- if (i < this.children.length - 1) {
117
- currentY += this.gap;
118
- }
119
- }
120
- } else {
121
- // Direction 'row'
122
- let currentX = this.padding;
123
-
124
- for (let i = 0; i < this.children.length; i++) {
125
- const child = this.children[i];
126
-
127
- // Calculer la position Y selon l'alignement
128
- let childY = this.padding;
129
- if (this.align === 'center') {
130
- childY = (this.height - child.height) / 2;
131
- } else if (this.align === 'end') {
132
- childY = this.height - child.height - this.padding;
133
- } else if (this.align === 'stretch') {
134
- childY = this.padding;
135
- child.height = this.height - (this.padding * 2);
136
- }
137
-
138
- // Positionner l'enfant en COORDONNÉES ABSOLUES
139
- child.x = this.x + currentX;
140
- child.y = this.y + childY;
141
-
142
- // Stocker la position relative
143
- this.childPositions.set(child, { x: currentX, y: childY });
144
-
145
- // Mettre à jour la position X pour l'enfant suivant
146
- currentX += child.width;
147
-
148
- // Ajouter le gap seulement si ce n'est pas le dernier enfant
149
- if (i < this.children.length - 1) {
150
- currentX += this.gap;
151
- }
152
- }
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
- }
216
- }
217
-
218
- /**
219
- * Met à jour la position de la carte et ajuste les enfants
220
- * @param {number} x - Nouvelle position X
221
- * @param {number} y - Nouvelle position Y
222
- */
223
- setPosition(x, y) {
224
- const deltaX = x - this.x;
225
- const deltaY = y - this.y;
226
-
227
- super.setPosition(x, y);
228
-
229
- // Déplacer tous les enfants avec la carte
230
- for (let child of this.children) {
231
- child.x += deltaX;
232
- child.y += deltaY;
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();
272
- }
273
-
274
- /**
275
- * Définit la position d'un enfant dans le système de coordonnées de la Card
276
- * @param {Component} child - L'enfant à positionner
277
- * @param {number} relativeX - Position X relative à la Card
278
- * @param {number} relativeY - Position Y relative à la Card
279
- */
280
- setChildPosition(child, relativeX, relativeY) {
281
- if (this.children.includes(child)) {
282
- // Définir en coordonnées absolues
283
- child.x = this.x + relativeX;
284
- child.y = this.y + relativeY;
285
- this.childPositions.set(child, { x: relativeX, y: relativeY });
286
- }
287
- }
288
-
289
- /**
290
- * Active/désactive le layout automatique
291
- * @param {boolean} enabled - True pour activer le layout automatique
292
- */
293
- setAutoLayout(enabled) {
294
- this.autoLayout = enabled;
295
- if (enabled) this.layout();
296
- }
297
-
298
- /**
299
- * Force un recalcul du layout
300
- */
301
- updateLayout() {
302
- this.layout();
303
- }
304
-
305
- /**
306
- * Génère les paramètres d'ombre selon le niveau d'élévation
307
- * @param {number} elevation - Niveau d'élévation (0-5)
308
- * @returns {Object} Configuration de l'ombre
309
- * @private
310
- */
311
- getShadowConfig(elevation) {
312
- const shadows = [
313
- { blur: 0, offsetY: 0, color: 'transparent', spread: 0 },
314
- { blur: 2, offsetY: 1, color: 'rgba(0,0,0,0.12)', spread: 0 },
315
- { blur: 3, offsetY: 1, color: 'rgba(0,0,0,0.14)', spread: 0 },
316
- { blur: 4, offsetY: 2, color: 'rgba(0,0,0,0.16)', spread: 0 },
317
- { blur: 6, offsetY: 3, color: 'rgba(0,0,0,0.18)', spread: 0 },
318
- { blur: 8, offsetY: 4, color: 'rgba(0,0,0,0.20)', spread: 0 },
319
- ];
320
-
321
- return shadows[Math.min(elevation, shadows.length - 1)];
322
- }
323
-
324
- /**
325
- * Dessine l'effet d'ombre selon l'élévation
326
- * @param {CanvasRenderingContext2D} ctx - Contexte de dessin
327
- * @private
328
- */
329
- drawShadow(ctx) {
330
- if (this.elevation <= 0) return;
331
-
332
- const shadow = this.getShadowConfig(this.elevation);
333
-
334
- ctx.save();
335
-
336
- ctx.shadowColor = shadow.color;
337
- ctx.shadowBlur = shadow.blur;
338
- ctx.shadowOffsetX = 0;
339
- ctx.shadowOffsetY = shadow.offsetY;
340
-
341
- if (this.borderRadius > 0) {
342
- ctx.beginPath();
343
- this.roundRect(ctx, this.x, this.y + shadow.offsetY, this.width, this.height, this.borderRadius);
344
- ctx.fillStyle = this.bgColor === 'transparent' ? 'white' : this.bgColor;
345
- ctx.fill();
346
- } else {
347
- ctx.fillStyle = this.bgColor === 'transparent' ? 'white' : this.bgColor;
348
- ctx.fillRect(this.x, this.y + shadow.offsetY, this.width, this.height);
349
- }
350
-
351
- ctx.restore();
352
- }
353
-
354
- /**
355
- * Dessine un rectangle avec coins arrondis
356
- * @param {CanvasRenderingContext2D} ctx - Contexte de dessin
357
- * @param {number} x - Position X
358
- * @param {number} y - Position Y
359
- * @param {number} width - Largeur
360
- * @param {number} height - Hauteur
361
- * @param {number} radius - Rayon des coins
362
- * @private
363
- */
364
- roundRect(ctx, x, y, width, height, radius) {
365
- if (radius === 0) {
366
- ctx.rect(x, y, width, height);
367
- return;
368
- }
369
-
370
- ctx.beginPath();
371
- ctx.moveTo(x + radius, y);
372
- ctx.lineTo(x + width - radius, y);
373
- ctx.quadraticCurveTo(x + width, y, x + width, y + radius);
374
- ctx.lineTo(x + width, y + height - radius);
375
- ctx.quadraticCurveTo(x + width, y + height, x + width - radius, y + height);
376
- ctx.lineTo(x + radius, y + height);
377
- ctx.quadraticCurveTo(x, y + height, x, y + height - radius);
378
- ctx.lineTo(x, y + radius);
379
- ctx.quadraticCurveTo(x, y, x + radius, y);
380
- ctx.closePath();
381
- }
382
-
383
- /**
384
- * Vérifie si un point est dans les limites
385
- * @param {number} x - Coordonnée X
386
- * @param {number} y - Coordonnée Y
387
- * @returns {boolean} True si le point est dans la vue
388
- */
389
- isPointInside(x, y) {
390
- return x >= this.x &&
391
- x <= this.x + this.width &&
392
- y >= this.y &&
393
- y <= this.y + this.height;
394
- }
395
-
396
- /**
397
- * Définit le niveau d'élévation
398
- * @param {number} elevation - Nouveau niveau d'élévation (0-5)
399
- */
400
- setElevation(elevation) {
401
- this.elevation = Math.max(0, Math.min(elevation, 5));
402
- }
403
-
404
- /**
405
- * Augmente le niveau d'élévation
406
- */
407
- raise() {
408
- this.setElevation(this.elevation + 1);
409
- }
410
-
411
- /**
412
- * Réduit le niveau d'élévation
413
- */
414
- lower() {
415
- this.setElevation(this.elevation - 1);
416
- }
417
-
418
- /**
419
- * Définit l'espacement entre enfants
420
- * @param {number} gap - Nouvel espacement
421
- */
422
- setGap(gap) {
423
- this.gap = Math.max(0, gap);
424
- if (this.autoLayout) this.layout();
425
- }
426
-
427
- /**
428
- * Définit le padding
429
- * @param {number} padding - Nouveau padding
430
- */
431
- setPadding(padding) {
432
- this.padding = Math.max(0, padding);
433
- if (this.autoLayout) this.layout();
434
- }
435
-
436
- /**
437
- * Définit la direction du layout
438
- * @param {string} direction - 'column' ou 'row'
439
- */
440
- setDirection(direction) {
441
- if (direction === 'column' || direction === 'row') {
442
- this.direction = direction;
443
- if (this.autoLayout) this.layout();
444
- }
445
- }
446
-
447
- /**
448
- * Définit l'alignement
449
- * @param {string} align - 'start', 'center', 'end' ou 'stretch'
450
- */
451
- setAlign(align) {
452
- if (['start', 'center', 'end', 'stretch'].includes(align)) {
453
- this.align = align;
454
- if (this.autoLayout) this.layout();
455
- }
456
- }
457
-
458
- /**
459
- * Calcule la hauteur totale nécessaire pour contenir tous les enfants
460
- * @returns {number} Hauteur totale nécessaire
461
- */
462
- getTotalHeight() {
463
- if (this.children.length === 0) return 0;
464
-
465
- if (this.direction === 'column') {
466
- const totalChildrenHeight = this.children.reduce((sum, child) => sum + child.height, 0);
467
- const totalGapHeight = this.gap * Math.max(0, this.children.length - 1);
468
- return totalChildrenHeight + totalGapHeight + (this.padding * 2);
469
- }
470
- return this.height;
471
- }
472
-
473
- /**
474
- * Calcule la largeur totale nécessaire pour contenir tous les enfants
475
- * @returns {number} Largeur totale nécessaire
476
- */
477
- getTotalWidth() {
478
- if (this.children.length === 0) return 0;
479
-
480
- if (this.direction === 'row') {
481
- const totalChildrenWidth = this.children.reduce((sum, child) => sum + child.width, 0);
482
- const totalGapWidth = this.gap * Math.max(0, this.children.length - 1);
483
- return totalChildrenWidth + totalGapWidth + (this.padding * 2);
484
- }
485
- return this.width;
486
- }
487
-
488
- /**
489
- * Ajuste automatiquement la hauteur de la carte pour contenir tous les enfants
490
- */
491
- fitHeight() {
492
- if (this.direction === 'column') {
493
- this.height = this.getTotalHeight();
494
- }
495
- }
496
-
497
- /**
498
- * Ajuste automatiquement la largeur de la carte pour contenir tous les enfants
499
- */
500
- fitWidth() {
501
- if (this.direction === 'row') {
502
- this.width = this.getTotalWidth();
503
- }
504
- }
505
-
506
- /**
507
- * Ajuste automatiquement les dimensions de la carte pour contenir tous les enfants
508
- */
509
- fitSize() {
510
- this.fitWidth();
511
- this.fitHeight();
512
- }
513
-
514
- /**
515
- * Met à jour les dimensions et relayout si autoLayout est activé
516
- * @param {number} width - Nouvelle largeur
517
- * @param {number} height - Nouvelle hauteur
518
- */
519
- setSize(width, height) {
520
- super.setSize(width, height);
521
- if (this.autoLayout) this.layout();
522
- }
523
-
524
- /**
525
- * Obtient la position relative d'un enfant
526
- * @param {Component} child - L'enfant
527
- * @returns {Object|null} Position relative {x, y} ou null si non trouvé
528
- */
529
- getChildPosition(child) {
530
- return this.childPositions.get(child) || null;
531
- }
532
- }
533
-
534
- export default Card;