canvasframework 0.5.24 → 0.5.26

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 +255 -52
  2. package/package.json +1 -1
@@ -1,46 +1,44 @@
1
1
  import Component from '../core/Component.js';
2
2
 
3
3
  /**
4
- * Carte avec système de layout, ombre et bordures arrondies
4
+ * Container avec système de layout et effet d'élévation
5
5
  * @class
6
6
  * @extends Component
7
7
  * @property {Component[]} children - Enfants
8
8
  * @property {number} padding - Padding interne
9
- * @property {number} gap - Espacement entre enfants
9
+ * @property {number} gap - Espacement constant entre enfants
10
10
  * @property {string} direction - Direction ('column' ou 'row')
11
- * @property {string} align - Alignement ('start', 'center', 'end')
11
+ * @property {string} align - Alignement ('start', 'center', 'end', 'stretch')
12
12
  * @property {string} bgColor - Couleur de fond
13
13
  * @property {number} borderRadius - Rayon des coins
14
- * @property {number} elevation - Élévation (ombre)
15
- * @property {boolean} clipContent - Clip le contenu aux bordures
14
+ * @property {number} elevation - Niveau d'élévation (ombres)
15
+ * @property {boolean} autoHeight - Ajuste automatiquement la hauteur selon le contenu
16
16
  */
17
17
  class Card extends Component {
18
18
  /**
19
19
  * Crée une instance de Card
20
20
  * @param {CanvasFramework} framework - Framework parent
21
21
  * @param {Object} [options={}] - Options de configuration
22
- * @param {number} [options.padding=16] - Padding interne
23
- * @param {number} [options.gap=0] - Espacement entre enfants
22
+ * @param {number} [options.padding=0] - Padding interne
23
+ * @param {number} [options.gap=0] - Espacement constant entre enfants
24
24
  * @param {string} [options.direction='column'] - Direction
25
25
  * @param {string} [options.align='start'] - Alignement
26
- * @param {string} [options.bgColor='#FFFFFF'] - Couleur de fond
27
- * @param {number} [options.borderRadius] - Rayon des coins (auto selon platform)
28
- * @param {number} [options.elevation=2] - Élévation (ombre)
29
- * @param {boolean} [options.clipContent=true] - Clip le contenu
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.autoHeight=false] - Ajuste automatiquement la hauteur
30
30
  */
31
31
  constructor(framework, options = {}) {
32
32
  super(framework, options);
33
33
  this.children = [];
34
- this.padding = options.padding !== undefined ? options.padding : 16;
35
- this.gap = options.gap || 0;
36
- this.direction = options.direction || 'column'; // 'column' ou 'row'
37
- this.align = options.align || 'start'; // 'start', 'center', 'end'
38
- this.bgColor = options.bgColor || '#FFFFFF';
39
- this.borderRadius = options.borderRadius !== undefined
40
- ? options.borderRadius
41
- : (framework.platform === 'material' ? 4 : 12);
42
- this.elevation = options.elevation !== undefined ? options.elevation : 2;
43
- this.clipContent = options.clipContent !== false;
34
+ this.padding = options.padding || 0;
35
+ this.gap = options.gap || 0; // Espacement constant entre chaque enfant
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.autoHeight = options.autoHeight || false;
44
42
  }
45
43
 
46
44
  /**
@@ -55,34 +53,157 @@ class Card extends Component {
55
53
  }
56
54
 
57
55
  /**
58
- * Organise les enfants selon le layout
56
+ * Supprime un enfant
57
+ * @param {Component} child - Composant enfant à supprimer
58
+ * @returns {boolean} True si l'enfant a été supprimé
59
+ */
60
+ remove(child) {
61
+ const index = this.children.indexOf(child);
62
+ if (index > -1) {
63
+ this.children.splice(index, 1);
64
+ this.layout();
65
+ return true;
66
+ }
67
+ return false;
68
+ }
69
+
70
+ /**
71
+ * Supprime tous les enfants
72
+ */
73
+ clear() {
74
+ this.children = [];
75
+ this.layout();
76
+ }
77
+
78
+ /**
79
+ * Organise les enfants selon le layout avec espacement constant
59
80
  * @private
60
81
  */
61
82
  layout() {
62
- let currentX = this.padding;
63
- let currentY = this.padding;
83
+ if (this.children.length === 0) return;
84
+
85
+ let currentX = this.x + this.padding;
86
+ let currentY = this.y + this.padding;
64
87
 
65
- for (let child of this.children) {
66
- if (this.direction === 'column') {
88
+ if (this.direction === 'column') {
89
+ for (let i = 0; i < this.children.length; i++) {
90
+ const child = this.children[i];
91
+
92
+ // Positionner l'enfant
67
93
  child.x = currentX;
68
94
  child.y = currentY;
95
+
96
+ // Gérer l'alignement horizontal
69
97
  if (this.align === 'center') {
70
- child.x = (this.width - child.width) / 2;
98
+ child.x = this.x + (this.width - child.width) / 2;
71
99
  } else if (this.align === 'end') {
72
- child.x = this.width - child.width - this.padding;
100
+ child.x = this.x + this.width - child.width - this.padding;
101
+ } else if (this.align === 'stretch') {
102
+ child.x = this.x + this.padding;
103
+ child.width = this.width - (this.padding * 2);
73
104
  }
105
+
106
+ // Mettre à jour la position Y pour l'enfant suivant
107
+ // Toujours le même espacement (gap) entre chaque enfant
74
108
  currentY += child.height + this.gap;
75
- } else {
109
+ }
110
+
111
+ // Ajuster la hauteur automatiquement si autoHeight est activé
112
+ if (this.autoHeight) {
113
+ const totalChildrenHeight = this.children.reduce((sum, child) => sum + child.height, 0);
114
+ const totalGapHeight = this.gap * Math.max(0, this.children.length - 1);
115
+ const neededHeight = totalChildrenHeight + totalGapHeight + (this.padding * 2);
116
+
117
+ if (neededHeight !== this.height) {
118
+ this.height = neededHeight;
119
+ // Notifier un changement de dimensions
120
+ if (this.onResize) this.onResize(this.width, this.height);
121
+ }
122
+ }
123
+ } else {
124
+ // Direction 'row' (logique similaire pour l'alignement horizontal)
125
+ for (let i = 0; i < this.children.length; i++) {
126
+ const child = this.children[i];
127
+
76
128
  child.x = currentX;
77
129
  child.y = currentY;
130
+
131
+ // Gérer l'alignement vertical
78
132
  if (this.align === 'center') {
79
- child.y = (this.height - child.height) / 2;
133
+ child.y = this.y + (this.height - child.height) / 2;
80
134
  } else if (this.align === 'end') {
81
- child.y = this.height - child.height - this.padding;
135
+ child.y = this.y + this.height - child.height - this.padding;
136
+ } else if (this.align === 'stretch') {
137
+ child.y = this.y + this.padding;
138
+ child.height = this.height - (this.padding * 2);
82
139
  }
140
+
141
+ // Espacement constant entre chaque enfant
83
142
  currentX += child.width + this.gap;
84
143
  }
144
+
145
+ // Ajuster la largeur automatiquement si autoHeight est activé (pour row)
146
+ if (this.autoHeight) {
147
+ const totalChildrenWidth = this.children.reduce((sum, child) => sum + child.width, 0);
148
+ const totalGapWidth = this.gap * Math.max(0, this.children.length - 1);
149
+ const neededWidth = totalChildrenWidth + totalGapWidth + (this.padding * 2);
150
+
151
+ if (neededWidth !== this.width) {
152
+ this.width = neededWidth;
153
+ // Notifier un changement de dimensions
154
+ if (this.onResize) this.onResize(this.width, this.height);
155
+ }
156
+ }
157
+ }
158
+ }
159
+
160
+ /**
161
+ * Génère les paramètres d'ombre selon le niveau d'élévation
162
+ * @param {number} elevation - Niveau d'élévation (0-5)
163
+ * @returns {Object} Configuration de l'ombre
164
+ * @private
165
+ */
166
+ getShadowConfig(elevation) {
167
+ const shadows = [
168
+ { blur: 0, offsetY: 0, color: 'transparent', spread: 0 },
169
+ { blur: 2, offsetY: 1, color: 'rgba(0,0,0,0.12)', spread: 0 },
170
+ { blur: 3, offsetY: 1, color: 'rgba(0,0,0,0.14)', spread: 0 },
171
+ { blur: 4, offsetY: 2, color: 'rgba(0,0,0,0.16)', spread: 0 },
172
+ { blur: 6, offsetY: 3, color: 'rgba(0,0,0,0.18)', spread: 0 },
173
+ { blur: 8, offsetY: 4, color: 'rgba(0,0,0,0.20)', spread: 0 },
174
+ ];
175
+
176
+ return shadows[Math.min(elevation, shadows.length - 1)];
177
+ }
178
+
179
+ /**
180
+ * Dessine l'effet d'ombre selon l'élévation
181
+ * @param {CanvasRenderingContext2D} ctx - Contexte de dessin
182
+ * @private
183
+ */
184
+ drawShadow(ctx) {
185
+ if (this.elevation <= 0) return;
186
+
187
+ const shadow = this.getShadowConfig(this.elevation);
188
+
189
+ ctx.save();
190
+
191
+ ctx.shadowColor = shadow.color;
192
+ ctx.shadowBlur = shadow.blur;
193
+ ctx.shadowOffsetX = 0;
194
+ ctx.shadowOffsetY = shadow.offsetY;
195
+
196
+ if (this.borderRadius > 0) {
197
+ ctx.beginPath();
198
+ this.roundRect(ctx, this.x, this.y + shadow.offsetY, this.width, this.height, this.borderRadius);
199
+ ctx.fillStyle = this.bgColor === 'transparent' ? 'white' : this.bgColor;
200
+ ctx.fill();
201
+ } else {
202
+ ctx.fillStyle = this.bgColor === 'transparent' ? 'white' : this.bgColor;
203
+ ctx.fillRect(this.x, this.y + shadow.offsetY, this.width, this.height);
85
204
  }
205
+
206
+ ctx.restore();
86
207
  }
87
208
 
88
209
  /**
@@ -92,14 +213,12 @@ class Card extends Component {
92
213
  draw(ctx) {
93
214
  ctx.save();
94
215
 
95
- // Ombre (elevation)
216
+ // Dessiner l'ombre si elevation > 0
96
217
  if (this.elevation > 0) {
97
- ctx.shadowColor = 'rgba(0, 0, 0, 0.15)';
98
- ctx.shadowBlur = this.elevation * 3;
99
- ctx.shadowOffsetY = this.elevation;
218
+ this.drawShadow(ctx);
100
219
  }
101
220
 
102
- // Background
221
+ // Dessiner le fond de la carte
103
222
  if (this.bgColor !== 'transparent') {
104
223
  ctx.fillStyle = this.bgColor;
105
224
  if (this.borderRadius > 0) {
@@ -111,30 +230,19 @@ class Card extends Component {
111
230
  }
112
231
  }
113
232
 
114
- // Réinitialiser l'ombre
115
- ctx.shadowColor = 'transparent';
116
- ctx.shadowBlur = 0;
117
- ctx.shadowOffsetY = 0;
118
-
119
- // Clipping (optionnel)
120
- if (this.clipContent && this.borderRadius > 0) {
233
+ // Appliquer le clipping pour le borderRadius (empêche les débordements)
234
+ if (this.borderRadius > 0) {
121
235
  ctx.beginPath();
122
236
  this.roundRect(ctx, this.x, this.y, this.width, this.height, this.borderRadius);
123
237
  ctx.clip();
124
238
  }
125
239
 
126
- // Children - coordonnées relatives à la Card
127
- ctx.save();
128
- ctx.translate(this.x, this.y);
129
-
240
+ // Dessiner les enfants
130
241
  for (let child of this.children) {
131
- if (child.visible) {
132
- child.draw(ctx);
133
- }
242
+ if (child.visible) child.draw(ctx);
134
243
  }
135
244
 
136
245
  ctx.restore();
137
- ctx.restore();
138
246
  }
139
247
 
140
248
  /**
@@ -148,6 +256,11 @@ class Card extends Component {
148
256
  * @private
149
257
  */
150
258
  roundRect(ctx, x, y, width, height, radius) {
259
+ if (radius === 0) {
260
+ ctx.rect(x, y, width, height);
261
+ return;
262
+ }
263
+
151
264
  ctx.beginPath();
152
265
  ctx.moveTo(x + radius, y);
153
266
  ctx.lineTo(x + width - radius, y);
@@ -165,7 +278,7 @@ class Card extends Component {
165
278
  * Vérifie si un point est dans les limites
166
279
  * @param {number} x - Coordonnée X
167
280
  * @param {number} y - Coordonnée Y
168
- * @returns {boolean} True si le point est dans la carte
281
+ * @returns {boolean} True si le point est dans la vue
169
282
  */
170
283
  isPointInside(x, y) {
171
284
  return x >= this.x &&
@@ -173,6 +286,96 @@ class Card extends Component {
173
286
  y >= this.y &&
174
287
  y <= this.y + this.height;
175
288
  }
289
+
290
+ /**
291
+ * Définit le niveau d'élévation
292
+ * @param {number} elevation - Nouveau niveau d'élévation (0-5)
293
+ */
294
+ setElevation(elevation) {
295
+ this.elevation = Math.max(0, Math.min(elevation, 5));
296
+ }
297
+
298
+ /**
299
+ * Augmente le niveau d'élévation
300
+ */
301
+ raise() {
302
+ this.setElevation(this.elevation + 1);
303
+ }
304
+
305
+ /**
306
+ * Réduit le niveau d'élévation
307
+ */
308
+ lower() {
309
+ this.setElevation(this.elevation - 1);
310
+ }
311
+
312
+ /**
313
+ * Met à jour la position de la carte (surcharge pour recalculer le layout)
314
+ * @param {number} x - Nouvelle position X
315
+ * @param {number} y - Nouvelle position Y
316
+ */
317
+ setPosition(x, y) {
318
+ const deltaX = x - this.x;
319
+ const deltaY = y - this.y;
320
+
321
+ super.setPosition(x, y);
322
+
323
+ // Déplacer tous les enfants avec la carte
324
+ for (let child of this.children) {
325
+ child.x += deltaX;
326
+ child.y += deltaY;
327
+ }
328
+ }
329
+
330
+ /**
331
+ * Met à jour les dimensions (surcharge pour recalculer le layout)
332
+ * @param {number} width - Nouvelle largeur
333
+ * @param {number} height - Nouvelle hauteur
334
+ */
335
+ setSize(width, height) {
336
+ super.setSize(width, height);
337
+ this.layout();
338
+ }
339
+
340
+ /**
341
+ * Définit l'espacement entre enfants
342
+ * @param {number} gap - Nouvel espacement
343
+ */
344
+ setGap(gap) {
345
+ this.gap = Math.max(0, gap);
346
+ this.layout();
347
+ }
348
+
349
+ /**
350
+ * Définit le padding
351
+ * @param {number} padding - Nouveau padding
352
+ */
353
+ setPadding(padding) {
354
+ this.padding = Math.max(0, padding);
355
+ this.layout();
356
+ }
357
+
358
+ /**
359
+ * Définit la direction du layout
360
+ * @param {string} direction - 'column' ou 'row'
361
+ */
362
+ setDirection(direction) {
363
+ if (direction === 'column' || direction === 'row') {
364
+ this.direction = direction;
365
+ this.layout();
366
+ }
367
+ }
368
+
369
+ /**
370
+ * Définit l'alignement
371
+ * @param {string} align - 'start', 'center', 'end' ou 'stretch'
372
+ */
373
+ setAlign(align) {
374
+ if (['start', 'center', 'end', 'stretch'].includes(align)) {
375
+ this.align = align;
376
+ this.layout();
377
+ }
378
+ }
176
379
  }
177
380
 
178
381
  export default Card;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "canvasframework",
3
- "version": "0.5.24",
3
+ "version": "0.5.26",
4
4
  "repository": {
5
5
  "type": "git",
6
6
  "url": "https://github.com/beyons/CanvasFramework.git"