canvasframework 0.5.41 → 0.5.44
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 +175 -169
- package/components/AppBar.js +107 -249
- package/components/BottomNavigationBar.js +143 -378
- package/components/FileUpload.js +225 -248
- package/package.json +1 -1
package/components/Accordion.js
CHANGED
|
@@ -1,265 +1,271 @@
|
|
|
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
|
-
|
|
30
|
-
|
|
31
|
-
// Clic
|
|
32
|
-
this.onClick = () => {
|
|
33
|
-
if (this.animating) return;
|
|
32
|
+
|
|
33
|
+
this.rippleColor = options.rippleColor || 'rgba(98,0,238,0.12)';
|
|
34
34
|
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
this.addRipple();
|
|
38
|
-
}
|
|
35
|
+
this.calculateContentHeight();
|
|
36
|
+
this.height = this.headerHeight + (this.expanded ? this.contentHeight : 0);
|
|
39
37
|
|
|
40
|
-
this.toggle();
|
|
41
|
-
};
|
|
42
38
|
}
|
|
43
39
|
|
|
44
40
|
calculateContentHeight() {
|
|
45
41
|
const ctx = this.framework.ctx;
|
|
46
42
|
ctx.save();
|
|
47
43
|
ctx.font = '14px -apple-system, sans-serif';
|
|
44
|
+
|
|
48
45
|
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
46
|
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
const
|
|
62
|
-
if (width < maxWidth)
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
47
|
+
const words = this.content.split(' ');
|
|
48
|
+
let lines = [];
|
|
49
|
+
let line = '';
|
|
50
|
+
|
|
51
|
+
words.forEach(word => {
|
|
52
|
+
|
|
53
|
+
const test = line + word + ' ';
|
|
54
|
+
if (ctx.measureText(test).width < maxWidth) {
|
|
55
|
+
line = test;
|
|
56
|
+
} else {
|
|
57
|
+
lines.push(line);
|
|
58
|
+
line = word + ' ';
|
|
66
59
|
}
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
60
|
+
|
|
61
|
+
});
|
|
62
|
+
|
|
63
|
+
lines.push(line);
|
|
64
|
+
this.lines = lines;
|
|
65
|
+
|
|
66
|
+
ctx.restore();
|
|
67
|
+
|
|
68
|
+
this.contentHeight = lines.length * 20 + this.contentPadding * 2;
|
|
70
69
|
}
|
|
71
70
|
|
|
72
71
|
toggle() {
|
|
72
|
+
|
|
73
73
|
if (this.animating) return;
|
|
74
|
+
|
|
74
75
|
this.expanded = !this.expanded;
|
|
75
|
-
if (this.onToggle) this.onToggle(this.expanded);
|
|
76
|
-
this.animate();
|
|
77
|
-
}
|
|
78
76
|
|
|
79
|
-
animate() {
|
|
80
|
-
if (this.animating) return;
|
|
81
|
-
this.animating = true;
|
|
82
77
|
const target = this.expanded ? 1 : 0;
|
|
83
|
-
const step = 0.1;
|
|
84
78
|
|
|
85
|
-
|
|
86
|
-
|
|
79
|
+
this.animating = true;
|
|
80
|
+
|
|
81
|
+
const animate = () => {
|
|
82
|
+
|
|
83
|
+
this.animProgress += (target - this.animProgress) * 0.2;
|
|
84
|
+
|
|
85
|
+
if (Math.abs(target - this.animProgress) < 0.01) {
|
|
87
86
|
this.animProgress = target;
|
|
88
|
-
this.height = this.headerHeight + this.contentHeight * this.animProgress;
|
|
89
87
|
this.animating = false;
|
|
90
|
-
|
|
88
|
+
} else {
|
|
89
|
+
requestAnimationFrame(animate);
|
|
91
90
|
}
|
|
92
|
-
|
|
91
|
+
|
|
93
92
|
this.height = this.headerHeight + this.contentHeight * this.animProgress;
|
|
94
|
-
|
|
93
|
+
|
|
95
94
|
};
|
|
96
|
-
|
|
95
|
+
|
|
96
|
+
animate();
|
|
97
97
|
}
|
|
98
98
|
|
|
99
|
-
addRipple() {
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
99
|
+
addRipple(localX, localY) {
|
|
100
|
+
|
|
101
|
+
if (this.platform !== 'material') return;
|
|
102
|
+
|
|
103
|
+
this.ripples.push({
|
|
104
|
+
x: localX,
|
|
105
|
+
y: localY,
|
|
103
106
|
radius: 0,
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
};
|
|
107
|
-
this.ripples.push(ripple);
|
|
108
|
-
this.animateRipples();
|
|
109
|
-
}
|
|
107
|
+
opacity: 0.25,
|
|
108
|
+
maxRadius: Math.max(this.width, this.headerHeight)
|
|
109
|
+
});
|
|
110
110
|
|
|
111
|
-
animateRipples() {
|
|
112
111
|
const animate = () => {
|
|
112
|
+
|
|
113
113
|
let active = false;
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
114
|
+
|
|
115
|
+
this.ripples.forEach(r => {
|
|
116
|
+
|
|
117
|
+
if (r.radius < r.maxRadius) {
|
|
118
|
+
|
|
119
|
+
r.radius += r.maxRadius / 12;
|
|
120
|
+
r.opacity *= 0.9;
|
|
118
121
|
active = true;
|
|
122
|
+
|
|
119
123
|
}
|
|
120
|
-
|
|
121
|
-
|
|
124
|
+
|
|
125
|
+
});
|
|
126
|
+
|
|
127
|
+
this.ripples = this.ripples.filter(r => r.opacity > 0.02);
|
|
128
|
+
|
|
122
129
|
if (active) requestAnimationFrame(animate);
|
|
130
|
+
|
|
123
131
|
};
|
|
132
|
+
|
|
124
133
|
animate();
|
|
125
134
|
}
|
|
126
135
|
|
|
127
|
-
|
|
136
|
+
handleClick(x,y){
|
|
137
|
+
|
|
138
|
+
const localX = x - this.x;
|
|
139
|
+
const localY = y - this.y;
|
|
140
|
+
|
|
141
|
+
if(localY <= this.headerHeight){
|
|
142
|
+
|
|
143
|
+
this.addRipple(localX, localY);
|
|
144
|
+
this.toggle();
|
|
145
|
+
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
draw(ctx){
|
|
151
|
+
|
|
128
152
|
ctx.save();
|
|
129
153
|
|
|
130
|
-
let headerBg
|
|
131
|
-
let
|
|
154
|
+
let headerBg;
|
|
155
|
+
let textColor;
|
|
132
156
|
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
157
|
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
158
|
+
if(this.platform === 'material'){
|
|
159
|
+
headerBg = this.headerBg; // Material 3 tonal surface
|
|
160
|
+
textColor = this.textColor ;
|
|
161
|
+
}else{
|
|
162
|
+
headerBg = this.headerBg;
|
|
163
|
+
textColor = this.textColor ;
|
|
154
164
|
}
|
|
155
165
|
|
|
156
|
-
//
|
|
157
|
-
ctx.fillStyle =
|
|
158
|
-
ctx.fillRect(this.x,
|
|
166
|
+
// background
|
|
167
|
+
ctx.fillStyle = '#FFFFFF';
|
|
168
|
+
ctx.fillRect(this.x,this.y,this.width,this.height);
|
|
169
|
+
|
|
170
|
+
// header
|
|
171
|
+
ctx.fillStyle = headerBg;
|
|
172
|
+
ctx.fillRect(this.x,this.y,this.width,this.headerHeight);
|
|
159
173
|
|
|
160
|
-
//
|
|
161
|
-
if
|
|
174
|
+
// Cupertino separator
|
|
175
|
+
if(this.platform === 'cupertino'){
|
|
162
176
|
ctx.strokeStyle = borderColor;
|
|
163
|
-
ctx.
|
|
164
|
-
ctx.
|
|
177
|
+
ctx.beginPath();
|
|
178
|
+
ctx.moveTo(this.x,this.y+this.headerHeight);
|
|
179
|
+
ctx.lineTo(this.x+this.width,this.y+this.headerHeight);
|
|
180
|
+
ctx.stroke();
|
|
165
181
|
}
|
|
166
182
|
|
|
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) {
|
|
183
|
+
// Ripple Material
|
|
184
|
+
if(this.platform === 'material'){
|
|
173
185
|
ctx.save();
|
|
186
|
+
|
|
174
187
|
ctx.beginPath();
|
|
175
|
-
ctx.rect(this.x,
|
|
188
|
+
ctx.rect(this.x,this.y,this.width,this.headerHeight);
|
|
176
189
|
ctx.clip();
|
|
177
|
-
|
|
178
|
-
|
|
190
|
+
|
|
191
|
+
this.ripples.forEach(r=>{
|
|
192
|
+
|
|
193
|
+
ctx.globalAlpha = r.opacity;
|
|
179
194
|
ctx.fillStyle = this.rippleColor;
|
|
180
195
|
ctx.beginPath();
|
|
181
|
-
ctx.arc(this.x
|
|
196
|
+
ctx.arc(this.x+r.x,this.y+r.y,r.radius,0,Math.PI*2);
|
|
182
197
|
ctx.fill();
|
|
183
|
-
|
|
198
|
+
|
|
199
|
+
});
|
|
200
|
+
|
|
184
201
|
ctx.restore();
|
|
185
202
|
ctx.globalAlpha = 1;
|
|
186
203
|
}
|
|
187
204
|
|
|
188
|
-
//
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
205
|
+
// Title
|
|
206
|
+
ctx.fillStyle = textColor;
|
|
207
|
+
ctx.font = this.platform==='material'
|
|
208
|
+
? '500 16px Roboto'
|
|
209
|
+
: '600 16px -apple-system';
|
|
210
|
+
|
|
211
|
+
ctx.textAlign='left';
|
|
212
|
+
ctx.textBaseline='middle';
|
|
196
213
|
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
this.
|
|
201
|
-
|
|
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);
|
|
214
|
+
ctx.fillText(
|
|
215
|
+
this.title,
|
|
216
|
+
this.x+16,
|
|
217
|
+
this.y+this.headerHeight/2
|
|
218
|
+
);
|
|
207
219
|
|
|
208
220
|
// 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
221
|
ctx.save();
|
|
213
|
-
|
|
214
|
-
ctx.
|
|
215
|
-
ctx.
|
|
216
|
-
|
|
217
|
-
ctx.
|
|
218
|
-
ctx.
|
|
222
|
+
|
|
223
|
+
ctx.translate(this.x+this.width-24,this.y+this.headerHeight/2);
|
|
224
|
+
ctx.rotate(this.animProgress*Math.PI);
|
|
225
|
+
|
|
226
|
+
ctx.strokeStyle='#666';
|
|
227
|
+
ctx.lineWidth = this.platform==='material'?2:1.3;
|
|
228
|
+
|
|
219
229
|
ctx.beginPath();
|
|
220
|
-
ctx.moveTo(-
|
|
221
|
-
ctx.lineTo(0,
|
|
222
|
-
ctx.lineTo(
|
|
230
|
+
ctx.moveTo(-5,-3);
|
|
231
|
+
ctx.lineTo(0,3);
|
|
232
|
+
ctx.lineTo(5,-3);
|
|
223
233
|
ctx.stroke();
|
|
234
|
+
|
|
224
235
|
ctx.restore();
|
|
225
236
|
|
|
226
|
-
//
|
|
227
|
-
if
|
|
237
|
+
// content
|
|
238
|
+
if(this.animProgress>0){
|
|
239
|
+
|
|
228
240
|
ctx.save();
|
|
241
|
+
|
|
229
242
|
ctx.beginPath();
|
|
230
|
-
ctx.rect(
|
|
243
|
+
ctx.rect(
|
|
244
|
+
this.x,
|
|
245
|
+
this.y+this.headerHeight,
|
|
246
|
+
this.width,
|
|
247
|
+
this.contentHeight*this.animProgress
|
|
248
|
+
);
|
|
231
249
|
ctx.clip();
|
|
232
250
|
|
|
233
|
-
ctx.
|
|
234
|
-
ctx.
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
ctx.lineTo(this.x + this.width, this.y + this.headerHeight);
|
|
238
|
-
ctx.stroke();
|
|
251
|
+
ctx.fillStyle='#666';
|
|
252
|
+
ctx.font='14px -apple-system';
|
|
253
|
+
|
|
254
|
+
let y=this.y+this.headerHeight+this.contentPadding;
|
|
239
255
|
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
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);
|
|
256
|
+
this.lines.forEach(line=>{
|
|
257
|
+
ctx.fillText(line,this.x+this.contentPadding,y);
|
|
258
|
+
y+=20;
|
|
251
259
|
});
|
|
252
260
|
|
|
253
261
|
ctx.restore();
|
|
262
|
+
|
|
254
263
|
}
|
|
255
264
|
|
|
256
265
|
ctx.restore();
|
|
257
|
-
}
|
|
258
266
|
|
|
259
|
-
isPointInside(x, y) {
|
|
260
|
-
return x >= this.x && x <= this.x + this.width &&
|
|
261
|
-
y >= this.y && y <= this.y + this.headerHeight;
|
|
262
267
|
}
|
|
268
|
+
|
|
263
269
|
}
|
|
264
270
|
|
|
265
271
|
export default Accordion;
|