canvasframework 0.5.42 → 0.5.45

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.
@@ -1,35 +1,42 @@
1
1
  import Component from '../core/Component.js';
2
2
 
3
- /**
4
- * Accordion (section extensible) avec styles Material & Cupertino + Ripple centré Android
5
- * @class
6
- * @extends Component
7
- */
8
3
  class Accordion extends Component {
4
+
9
5
  constructor(framework, options = {}) {
10
6
  super(framework, options);
7
+
11
8
  this.title = options.title || '';
12
9
  this.content = options.content || '';
13
10
  this.icon = options.icon || null;
14
11
  this.expanded = options.expanded || false;
12
+
15
13
  this.platform = framework.platform;
14
+
15
+ this.borderColor = options.borderColor || '#E5E5EA'
16
+
17
+ if(this.platform === 'material'){
18
+ this.headerBg = options.headerBg || '#F8F9FF'; // Material 3 tonal surface
19
+ this.textColor = options.textColor || '#1C1B1F';
20
+ }else{
21
+ this.headerBg = options.headerBg || '#FFFFFF';
22
+ this.textColor = options.textColor || '#000000';
23
+ }
24
+
16
25
  this.headerHeight = 56;
17
26
  this.contentPadding = 16;
18
- this.bgColor = options.bgColor || '#FFFFFF';
19
- this.borderColor = options.borderColor || '#E0E0E0';
20
- this.onToggle = options.onToggle;
21
- this.animating = false;
22
- this.animProgress = this.expanded ? 1 : 0;
23
27
 
24
- this.calculateContentHeight();
25
- this.height = this.headerHeight + (this.expanded ? this.contentHeight : 0);
28
+ this.animProgress = this.expanded ? 1 : 0;
29
+ this.animating = false;
26
30
 
27
- // Pour les ripples Material
28
31
  this.ripples = [];
29
- this.rippleColor = 'rgba(1,0,0,0.2)';
32
+
33
+ this.rippleColor = options.rippleColor || 'rgba(98,0,238,0.12)';
30
34
 
31
- // Clic
32
- this.onClick = () => {
35
+ this.calculateContentHeight();
36
+ this.height = this.headerHeight + (this.expanded ? this.contentHeight : 0);
37
+
38
+
39
+ this.onClick = () => {
33
40
  if (this.animating) return;
34
41
 
35
42
  // Ripple centré Material
@@ -39,64 +46,68 @@ class Accordion extends Component {
39
46
 
40
47
  this.toggle();
41
48
  };
49
+
42
50
  }
43
51
 
44
52
  calculateContentHeight() {
45
53
  const ctx = this.framework.ctx;
46
54
  ctx.save();
47
55
  ctx.font = '14px -apple-system, sans-serif';
56
+
48
57
  const maxWidth = this.width - this.contentPadding * 2;
49
- const lines = this.wrapText(ctx, this.content, maxWidth);
50
- ctx.restore();
51
- const lineHeight = 20;
52
- this.contentHeight = lines.length * lineHeight + this.contentPadding * 2;
53
- }
54
58
 
55
- wrapText(ctx, text, maxWidth) {
56
- const words = text.split(' ');
57
- const lines = [];
58
- let currentLine = words[0] || '';
59
- for (let i = 1; i < words.length; i++) {
60
- const word = words[i];
61
- const width = ctx.measureText(currentLine + ' ' + word).width;
62
- if (width < maxWidth) currentLine += ' ' + word;
63
- else {
64
- lines.push(currentLine);
65
- currentLine = word;
59
+ const words = this.content.split(' ');
60
+ let lines = [];
61
+ let line = '';
62
+
63
+ words.forEach(word => {
64
+
65
+ const test = line + word + ' ';
66
+ if (ctx.measureText(test).width < maxWidth) {
67
+ line = test;
68
+ } else {
69
+ lines.push(line);
70
+ line = word + ' ';
66
71
  }
67
- }
68
- lines.push(currentLine);
69
- return lines;
72
+
73
+ });
74
+
75
+ lines.push(line);
76
+ this.lines = lines;
77
+
78
+ ctx.restore();
79
+
80
+ this.contentHeight = lines.length * 20 + this.contentPadding * 2;
70
81
  }
71
82
 
72
83
  toggle() {
84
+
73
85
  if (this.animating) return;
86
+
74
87
  this.expanded = !this.expanded;
75
- if (this.onToggle) this.onToggle(this.expanded);
76
- this.animate();
77
- }
78
88
 
79
- animate() {
80
- if (this.animating) return;
81
- this.animating = true;
82
89
  const target = this.expanded ? 1 : 0;
83
- const step = 0.1;
84
90
 
85
- const doAnimate = () => {
86
- if (Math.abs(this.animProgress - target) < 0.01) {
91
+ this.animating = true;
92
+
93
+ const animate = () => {
94
+
95
+ this.animProgress += (target - this.animProgress) * 0.2;
96
+
97
+ if (Math.abs(target - this.animProgress) < 0.01) {
87
98
  this.animProgress = target;
88
- this.height = this.headerHeight + this.contentHeight * this.animProgress;
89
99
  this.animating = false;
90
- return;
100
+ } else {
101
+ requestAnimationFrame(animate);
91
102
  }
92
- this.animProgress += this.animProgress < target ? step : -step;
103
+
93
104
  this.height = this.headerHeight + this.contentHeight * this.animProgress;
94
- requestAnimationFrame(doAnimate);
105
+
95
106
  };
96
- doAnimate();
97
- }
98
107
 
99
- addRipple() {
108
+ animate();
109
+ }
110
+ addRipple() {
100
111
  const ripple = {
101
112
  x: this.width / 2,
102
113
  y: this.headerHeight / 2,
@@ -124,142 +135,125 @@ class Accordion extends Component {
124
135
  animate();
125
136
  }
126
137
 
127
- draw(ctx) {
138
+ draw(ctx){
139
+
128
140
  ctx.save();
129
141
 
130
- let headerBg = '#FFFFFF';
131
- let headerTextColor = '#000000';
142
+ let headerBg;
143
+ let textColor;
132
144
  let borderColor = this.borderColor;
133
- let shadowBlur = 0;
134
- let chevronWidth = 2;
135
-
136
- if (this.platform === 'material') {
137
- headerBg = '#F5F5F5';
138
- headerTextColor = '#212121';
139
- shadowBlur = 4;
140
- chevronWidth = 3;
141
- } else if (this.platform === 'cupertino') {
142
- headerBg = '#FFFFFF';
143
- headerTextColor = '#000000';
144
- borderColor = '#C7C7CC';
145
- chevronWidth = 1.5;
146
- }
147
145
 
148
- // Ombre Material
149
- if (shadowBlur > 0) {
150
- ctx.shadowColor = 'rgba(0,0,0,0.2)';
151
- ctx.shadowBlur = shadowBlur;
152
- ctx.shadowOffsetX = 0;
153
- ctx.shadowOffsetY = 2;
146
+ if(this.platform === 'material'){
147
+ headerBg = this.headerBg; // Material 3 tonal surface
148
+ textColor = this.textColor ;
149
+ }else{
150
+ headerBg = this.headerBg;
151
+ textColor = this.textColor ;
154
152
  }
155
153
 
156
- // Background
157
- ctx.fillStyle = this.bgColor;
158
- ctx.fillRect(this.x, this.y, this.width, this.height);
154
+ // background
155
+ ctx.fillStyle = '#FFFFFF';
156
+ ctx.fillRect(this.x,this.y,this.width,this.height);
157
+
158
+ // header
159
+ ctx.fillStyle = headerBg;
160
+ ctx.fillRect(this.x,this.y,this.width,this.headerHeight);
159
161
 
160
- // Bordure Cupertino
161
- if (this.platform === 'cupertino') {
162
+ // Cupertino separator
163
+ if(this.platform === 'cupertino'){
162
164
  ctx.strokeStyle = borderColor;
163
- ctx.lineWidth = 1;
164
- ctx.strokeRect(this.x, this.y, this.width, this.height);
165
+ ctx.beginPath();
166
+ ctx.moveTo(this.x,this.y+this.headerHeight);
167
+ ctx.lineTo(this.x+this.width,this.y+this.headerHeight);
168
+ ctx.stroke();
165
169
  }
166
170
 
167
- // Header
168
- ctx.fillStyle = headerBg;
169
- ctx.fillRect(this.x, this.y, this.width, this.headerHeight);
170
-
171
- // Ripple centré Material
172
- if (this.platform === 'material' && this.ripples.length) {
171
+ // Ripple Material
172
+ if(this.platform === 'material'){
173
173
  ctx.save();
174
+
174
175
  ctx.beginPath();
175
- ctx.rect(this.x, this.y, this.width, this.headerHeight);
176
+ ctx.rect(this.x,this.y,this.width,this.headerHeight);
176
177
  ctx.clip();
177
- for (let ripple of this.ripples) {
178
- ctx.globalAlpha = ripple.opacity;
178
+
179
+ this.ripples.forEach(r=>{
180
+
181
+ ctx.globalAlpha = r.opacity;
179
182
  ctx.fillStyle = this.rippleColor;
180
183
  ctx.beginPath();
181
- ctx.arc(this.x + ripple.x, this.y + ripple.y, ripple.radius, 0, Math.PI * 2);
184
+ ctx.arc(this.x+r.x,this.y+r.y,r.radius,0,Math.PI*2);
182
185
  ctx.fill();
183
- }
186
+
187
+ });
188
+
184
189
  ctx.restore();
185
190
  ctx.globalAlpha = 1;
186
191
  }
187
192
 
188
- // Icône
189
- if (this.icon) {
190
- ctx.font = '20px sans-serif';
191
- ctx.fillStyle = '#666666';
192
- ctx.textAlign = 'left';
193
- ctx.textBaseline = 'middle';
194
- ctx.fillText(this.icon, this.x + 16, this.y + this.headerHeight / 2);
195
- }
193
+ // Title
194
+ ctx.fillStyle = textColor;
195
+ ctx.font = this.platform==='material'
196
+ ? '500 16px Roboto'
197
+ : '600 16px -apple-system';
196
198
 
197
- // Titre
198
- ctx.fillStyle = headerTextColor;
199
- ctx.font =
200
- this.platform === 'material'
201
- ? 'bold 16px Roboto, sans-serif'
202
- : 'bold 16px -apple-system, sans-serif';
203
- ctx.textAlign = 'left';
204
- ctx.textBaseline = 'middle';
205
- const titleX = this.icon ? this.x + 56 : this.x + 16;
206
- ctx.fillText(this.title, titleX, this.y + this.headerHeight / 2);
199
+ ctx.textAlign='left';
200
+ ctx.textBaseline='middle';
201
+
202
+ ctx.fillText(
203
+ this.title,
204
+ this.x+16,
205
+ this.y+this.headerHeight/2
206
+ );
207
207
 
208
208
  // Chevron
209
- const chevronX = this.x + this.width - 30;
210
- const chevronY = this.y + this.headerHeight / 2;
211
- const chevronRotation = this.animProgress * Math.PI;
212
209
  ctx.save();
213
- ctx.translate(chevronX, chevronY);
214
- ctx.rotate(chevronRotation);
215
- ctx.strokeStyle = '#666666';
216
- ctx.lineWidth = chevronWidth;
217
- ctx.lineCap = 'round';
218
- ctx.lineJoin = 'round';
210
+
211
+ ctx.translate(this.x+this.width-24,this.y+this.headerHeight/2);
212
+ ctx.rotate(this.animProgress*Math.PI);
213
+
214
+ ctx.strokeStyle='#666';
215
+ ctx.lineWidth = this.platform==='material'?2:1.3;
216
+
219
217
  ctx.beginPath();
220
- ctx.moveTo(-6, -3);
221
- ctx.lineTo(0, 3);
222
- ctx.lineTo(6, -3);
218
+ ctx.moveTo(-5,-3);
219
+ ctx.lineTo(0,3);
220
+ ctx.lineTo(5,-3);
223
221
  ctx.stroke();
222
+
224
223
  ctx.restore();
225
224
 
226
- // Contenu
227
- if (this.animProgress > 0) {
225
+ // content
226
+ if(this.animProgress>0){
227
+
228
228
  ctx.save();
229
+
229
230
  ctx.beginPath();
230
- ctx.rect(this.x, this.y + this.headerHeight, this.width, this.contentHeight * this.animProgress);
231
+ ctx.rect(
232
+ this.x,
233
+ this.y+this.headerHeight,
234
+ this.width,
235
+ this.contentHeight*this.animProgress
236
+ );
231
237
  ctx.clip();
232
238
 
233
- ctx.strokeStyle = borderColor;
234
- ctx.lineWidth = 1;
235
- ctx.beginPath();
236
- ctx.moveTo(this.x, this.y + this.headerHeight);
237
- ctx.lineTo(this.x + this.width, this.y + this.headerHeight);
238
- ctx.stroke();
239
+ ctx.fillStyle='#666';
240
+ ctx.font='14px -apple-system';
239
241
 
240
- ctx.fillStyle = '#666666';
241
- ctx.font = '14px -apple-system, sans-serif';
242
- ctx.textAlign = 'left';
243
- ctx.textBaseline = 'top';
244
- const contentX = this.x + this.contentPadding;
245
- const contentY = this.y + this.headerHeight + this.contentPadding;
246
- const maxWidth = this.width - this.contentPadding * 2;
247
- const lines = this.wrapText(ctx, this.content, maxWidth);
248
- const lineHeight = 20;
249
- lines.forEach((line, index) => {
250
- ctx.fillText(line, contentX, contentY + index * lineHeight);
242
+ let y=this.y+this.headerHeight+this.contentPadding;
243
+
244
+ this.lines.forEach(line=>{
245
+ ctx.fillText(line,this.x+this.contentPadding,y);
246
+ y+=20;
251
247
  });
252
248
 
253
249
  ctx.restore();
250
+
254
251
  }
255
252
 
256
253
  ctx.restore();
257
- }
258
254
 
259
- isPointInside(x, y) {
260
- return x >= this.x && x <= this.x + this.width &&
261
- y >= this.y && y <= this.y + this.headerHeight;
262
255
  }
256
+
263
257
  }
264
258
 
265
259
  export default Accordion;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "canvasframework",
3
- "version": "0.5.42",
3
+ "version": "0.5.45",
4
4
  "repository": {
5
5
  "type": "git",
6
6
  "url": "https://github.com/beyons/CanvasFramework.git"