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.
- package/components/Accordion.js +140 -146
- package/package.json +1 -1
package/components/Accordion.js
CHANGED
|
@@ -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.
|
|
25
|
-
this.
|
|
28
|
+
this.animProgress = this.expanded ? 1 : 0;
|
|
29
|
+
this.animating = false;
|
|
26
30
|
|
|
27
|
-
// Pour les ripples Material
|
|
28
31
|
this.ripples = [];
|
|
29
|
-
|
|
32
|
+
|
|
33
|
+
this.rippleColor = options.rippleColor || 'rgba(98,0,238,0.12)';
|
|
30
34
|
|
|
31
|
-
|
|
32
|
-
this.
|
|
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
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
const
|
|
62
|
-
if (width < maxWidth)
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
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
|
-
|
|
69
|
-
|
|
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
|
-
|
|
86
|
-
|
|
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
|
-
|
|
100
|
+
} else {
|
|
101
|
+
requestAnimationFrame(animate);
|
|
91
102
|
}
|
|
92
|
-
|
|
103
|
+
|
|
93
104
|
this.height = this.headerHeight + this.contentHeight * this.animProgress;
|
|
94
|
-
|
|
105
|
+
|
|
95
106
|
};
|
|
96
|
-
doAnimate();
|
|
97
|
-
}
|
|
98
107
|
|
|
99
|
-
|
|
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
|
|
131
|
-
let
|
|
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
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
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
|
-
//
|
|
157
|
-
ctx.fillStyle =
|
|
158
|
-
ctx.fillRect(this.x,
|
|
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
|
-
//
|
|
161
|
-
if
|
|
162
|
+
// Cupertino separator
|
|
163
|
+
if(this.platform === 'cupertino'){
|
|
162
164
|
ctx.strokeStyle = borderColor;
|
|
163
|
-
ctx.
|
|
164
|
-
ctx.
|
|
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
|
-
//
|
|
168
|
-
|
|
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,
|
|
176
|
+
ctx.rect(this.x,this.y,this.width,this.headerHeight);
|
|
176
177
|
ctx.clip();
|
|
177
|
-
|
|
178
|
-
|
|
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
|
|
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
|
-
//
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
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
|
-
|
|
198
|
-
ctx.
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
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
|
-
|
|
214
|
-
ctx.
|
|
215
|
-
ctx.
|
|
216
|
-
|
|
217
|
-
ctx.
|
|
218
|
-
ctx.
|
|
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(-
|
|
221
|
-
ctx.lineTo(0,
|
|
222
|
-
ctx.lineTo(
|
|
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
|
-
//
|
|
227
|
-
if
|
|
225
|
+
// content
|
|
226
|
+
if(this.animProgress>0){
|
|
227
|
+
|
|
228
228
|
ctx.save();
|
|
229
|
+
|
|
229
230
|
ctx.beginPath();
|
|
230
|
-
ctx.rect(
|
|
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.
|
|
234
|
-
ctx.
|
|
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
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
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;
|