canvasframework 0.3.15 → 0.3.16
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 +135 -122
- package/components/BottomSheet.js +104 -244
- package/components/Checkbox.js +135 -149
- package/components/DatePicker.js +7 -0
- package/components/Dialog.js +252 -282
- package/components/IOSDatePickerWheel.js +257 -95
- package/components/SegmentedControl.js +240 -85
- package/components/TextField.js +109 -289
- package/core/CanvasFramework.js +2 -7
- package/index.js +1 -1
- package/package.json +1 -1
package/components/Checkbox.js
CHANGED
|
@@ -1,180 +1,166 @@
|
|
|
1
1
|
import Component from '../core/Component.js';
|
|
2
|
+
|
|
2
3
|
/**
|
|
3
|
-
*
|
|
4
|
-
* @class
|
|
5
|
-
* @extends Component
|
|
6
|
-
* @property {boolean} checked - État cochée
|
|
7
|
-
* @property {string} label - Texte du label
|
|
8
|
-
* @property {string} platform - Plateforme
|
|
9
|
-
* @property {number} boxWidth - Largeur de la case
|
|
10
|
-
* @property {number} boxHeight - Hauteur de la case
|
|
11
|
-
* @property {Function} onChange - Callback au changement
|
|
4
|
+
* Checkbox Material & Cupertino (iOS-like)
|
|
12
5
|
*/
|
|
13
6
|
class Checkbox extends Component {
|
|
14
|
-
/**
|
|
15
|
-
* Crée une instance de Checkbox
|
|
16
|
-
* @param {CanvasFramework} framework - Framework parent
|
|
17
|
-
* @param {Object} [options={}] - Options de configuration
|
|
18
|
-
* @param {boolean} [options.checked=false] - État initial
|
|
19
|
-
* @param {string} [options.label=''] - Texte du label
|
|
20
|
-
* @param {Function} [options.onChange] - Callback au changement
|
|
21
|
-
*/
|
|
22
7
|
constructor(framework, options = {}) {
|
|
23
8
|
super(framework, options);
|
|
24
|
-
|
|
9
|
+
|
|
10
|
+
this.checked = !!options.checked;
|
|
25
11
|
this.label = options.label || '';
|
|
26
12
|
this.platform = framework.platform;
|
|
27
|
-
this.boxWidth = 24; // Taille de la case
|
|
28
|
-
this.boxHeight = 24; // Taille de la case
|
|
29
13
|
this.onChange = options.onChange;
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
this.
|
|
33
|
-
|
|
34
|
-
this.
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
14
|
+
|
|
15
|
+
this.boxSize = 22;
|
|
16
|
+
this.padding = 10;
|
|
17
|
+
|
|
18
|
+
this.textWidth = this.label
|
|
19
|
+
? this.getTextWidth(this.label)
|
|
20
|
+
: 0;
|
|
21
|
+
|
|
22
|
+
// Largeur totale
|
|
23
|
+
this.width =
|
|
24
|
+
this.platform === 'material'
|
|
25
|
+
? this.boxSize + this.padding + this.textWidth
|
|
26
|
+
: this.textWidth + 28; // place pour checkmark iOS
|
|
27
|
+
|
|
28
|
+
this.height = 28;
|
|
29
|
+
|
|
30
|
+
this.onClick = () => {
|
|
31
|
+
this.checked = !this.checked;
|
|
32
|
+
this.onChange?.(this.checked);
|
|
33
|
+
};
|
|
38
34
|
}
|
|
39
|
-
|
|
40
|
-
/**
|
|
41
|
-
* Calcule la largeur du texte
|
|
42
|
-
* @param {string} text - Texte à mesurer
|
|
43
|
-
* @returns {number} Largeur du texte
|
|
44
|
-
* @private
|
|
45
|
-
*/
|
|
35
|
+
|
|
46
36
|
getTextWidth(text) {
|
|
47
|
-
// Utiliser le contexte temporaire pour mesurer le texte
|
|
48
37
|
const ctx = this.framework.ctx;
|
|
49
38
|
ctx.save();
|
|
50
|
-
ctx.font = '16px -apple-system, sans-serif';
|
|
51
|
-
const
|
|
39
|
+
ctx.font = '16px -apple-system, system-ui, sans-serif';
|
|
40
|
+
const w = ctx.measureText(text).width;
|
|
52
41
|
ctx.restore();
|
|
53
|
-
return
|
|
54
|
-
}
|
|
55
|
-
|
|
56
|
-
/**
|
|
57
|
-
* Gère le clic sur la checkbox
|
|
58
|
-
* @private
|
|
59
|
-
*/
|
|
60
|
-
handleClick() {
|
|
61
|
-
this.checked = !this.checked;
|
|
62
|
-
if (this.onChange) this.onChange(this.checked);
|
|
42
|
+
return w;
|
|
63
43
|
}
|
|
64
44
|
|
|
65
|
-
/**
|
|
66
|
-
* Dessine la checkbox
|
|
67
|
-
* @param {CanvasRenderingContext2D} ctx - Contexte de dessin
|
|
68
|
-
*/
|
|
69
45
|
draw(ctx) {
|
|
70
46
|
ctx.save();
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
const
|
|
75
|
-
|
|
76
|
-
const boxCenterY = boxY + this.boxHeight / 2;
|
|
77
|
-
|
|
47
|
+
ctx.font = '16px -apple-system, system-ui, sans-serif';
|
|
48
|
+
ctx.textBaseline = 'middle';
|
|
49
|
+
|
|
50
|
+
const centerY = this.y + this.height / 2;
|
|
51
|
+
|
|
78
52
|
if (this.platform === 'material') {
|
|
79
|
-
|
|
80
|
-
if (this.checked) {
|
|
81
|
-
// Case cochée
|
|
82
|
-
ctx.fillStyle = '#6200EE';
|
|
83
|
-
ctx.beginPath();
|
|
84
|
-
this.roundRect(ctx, boxX, boxY, this.boxWidth, this.boxHeight, 2);
|
|
85
|
-
ctx.fill();
|
|
86
|
-
|
|
87
|
-
// Coche
|
|
88
|
-
ctx.strokeStyle = '#FFFFFF';
|
|
89
|
-
ctx.lineWidth = 2;
|
|
90
|
-
ctx.beginPath();
|
|
91
|
-
ctx.moveTo(boxX + 6, boxY + 12);
|
|
92
|
-
ctx.lineTo(boxX + 10, boxY + 16);
|
|
93
|
-
ctx.lineTo(boxX + 18, boxY + 8);
|
|
94
|
-
ctx.stroke();
|
|
95
|
-
} else {
|
|
96
|
-
// Case non cochée
|
|
97
|
-
ctx.strokeStyle = '#666666';
|
|
98
|
-
ctx.lineWidth = 2;
|
|
99
|
-
ctx.beginPath();
|
|
100
|
-
this.roundRect(ctx, boxX, boxY, this.boxWidth, this.boxHeight, 2);
|
|
101
|
-
ctx.stroke();
|
|
102
|
-
}
|
|
53
|
+
this.drawMaterial(ctx, centerY);
|
|
103
54
|
} else {
|
|
104
|
-
|
|
105
|
-
if (this.checked) {
|
|
106
|
-
// Case cochée (iOS utilise plutôt un cercle)
|
|
107
|
-
ctx.fillStyle = '#007AFF';
|
|
108
|
-
ctx.beginPath();
|
|
109
|
-
ctx.arc(boxCenterX, boxCenterY, this.boxWidth/2, 0, Math.PI * 2);
|
|
110
|
-
ctx.fill();
|
|
111
|
-
|
|
112
|
-
// Coche
|
|
113
|
-
ctx.strokeStyle = '#FFFFFF';
|
|
114
|
-
ctx.lineWidth = 2;
|
|
115
|
-
ctx.beginPath();
|
|
116
|
-
ctx.moveTo(boxX + 6, boxCenterY);
|
|
117
|
-
ctx.lineTo(boxX + 10, boxCenterY + 4);
|
|
118
|
-
ctx.lineTo(boxX + 18, boxCenterY - 4);
|
|
119
|
-
ctx.stroke();
|
|
120
|
-
} else {
|
|
121
|
-
// Case non cochée
|
|
122
|
-
ctx.strokeStyle = '#C7C7CC';
|
|
123
|
-
ctx.lineWidth = 2;
|
|
124
|
-
ctx.beginPath();
|
|
125
|
-
ctx.arc(boxCenterX, boxCenterY, this.boxWidth/2, 0, Math.PI * 2);
|
|
126
|
-
ctx.stroke();
|
|
127
|
-
}
|
|
55
|
+
this.drawCupertino(ctx, centerY);
|
|
128
56
|
}
|
|
129
|
-
|
|
130
|
-
// Label
|
|
131
|
-
if (this.label) {
|
|
132
|
-
ctx.fillStyle = '#000000';
|
|
133
|
-
ctx.font = '16px -apple-system, sans-serif';
|
|
134
|
-
ctx.textAlign = 'left';
|
|
135
|
-
ctx.textBaseline = 'middle';
|
|
136
|
-
ctx.fillText(this.label, boxX + this.boxWidth + 8, boxCenterY);
|
|
137
|
-
}
|
|
138
|
-
|
|
57
|
+
|
|
139
58
|
ctx.restore();
|
|
140
59
|
}
|
|
141
60
|
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
61
|
+
/* ---------------- MATERIAL ---------------- */
|
|
62
|
+
|
|
63
|
+
drawMaterial(ctx, centerY) {
|
|
64
|
+
const x = this.x;
|
|
65
|
+
const y = centerY - this.boxSize / 2;
|
|
66
|
+
|
|
67
|
+
// Box
|
|
68
|
+
ctx.lineWidth = 2;
|
|
69
|
+
ctx.strokeStyle = this.checked ? '#6200EE' : '#757575';
|
|
70
|
+
ctx.fillStyle = this.checked ? '#6200EE' : 'transparent';
|
|
71
|
+
|
|
72
|
+
this.roundRect(ctx, x, y, this.boxSize, this.boxSize, 3);
|
|
73
|
+
if (this.checked) ctx.fill();
|
|
74
|
+
ctx.stroke();
|
|
75
|
+
|
|
76
|
+
// Check
|
|
77
|
+
if (this.checked) {
|
|
78
|
+
ctx.strokeStyle = '#FFF';
|
|
79
|
+
ctx.lineWidth = 2.4;
|
|
80
|
+
ctx.beginPath();
|
|
81
|
+
ctx.moveTo(x + 5, y + 12);
|
|
82
|
+
ctx.lineTo(x + 9, y + 16);
|
|
83
|
+
ctx.lineTo(x + 17, y + 7);
|
|
84
|
+
ctx.stroke();
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
// Label
|
|
88
|
+
ctx.fillStyle = '#000';
|
|
89
|
+
ctx.fillText(
|
|
90
|
+
this.label,
|
|
91
|
+
x + this.boxSize + this.padding,
|
|
92
|
+
centerY
|
|
93
|
+
);
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
/* ---------------- CUPERTINO ---------------- */
|
|
97
|
+
|
|
98
|
+
/* ---------------- CUPERTINO ---------------- */
|
|
99
|
+
|
|
100
|
+
drawCupertino(ctx, centerY) {
|
|
101
|
+
const radius = 10;
|
|
102
|
+
const circleX = this.x + radius;
|
|
103
|
+
const circleY = centerY;
|
|
104
|
+
|
|
105
|
+
// Cercle
|
|
106
|
+
if (this.checked) {
|
|
107
|
+
ctx.fillStyle = '#007AFF'; // Apple blue
|
|
153
108
|
ctx.beginPath();
|
|
154
|
-
ctx.
|
|
155
|
-
ctx.
|
|
156
|
-
|
|
157
|
-
ctx.
|
|
158
|
-
ctx.
|
|
159
|
-
ctx.
|
|
160
|
-
ctx.
|
|
161
|
-
ctx.
|
|
162
|
-
|
|
109
|
+
ctx.arc(circleX, circleY, radius, 0, Math.PI * 2);
|
|
110
|
+
ctx.fill();
|
|
111
|
+
} else {
|
|
112
|
+
ctx.strokeStyle = '#C7C7CC'; // iOS gray
|
|
113
|
+
ctx.lineWidth = 2;
|
|
114
|
+
ctx.beginPath();
|
|
115
|
+
ctx.arc(circleX, circleY, radius, 0, Math.PI * 2);
|
|
116
|
+
ctx.stroke();
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
// Checkmark
|
|
120
|
+
if (this.checked) {
|
|
121
|
+
ctx.strokeStyle = '#FFFFFF';
|
|
122
|
+
ctx.lineWidth = 2.2;
|
|
123
|
+
ctx.lineCap = 'round';
|
|
124
|
+
ctx.lineJoin = 'round';
|
|
125
|
+
|
|
126
|
+
ctx.beginPath();
|
|
127
|
+
ctx.moveTo(circleX - 4, circleY);
|
|
128
|
+
ctx.lineTo(circleX - 1, circleY + 3);
|
|
129
|
+
ctx.lineTo(circleX + 5, circleY - 4);
|
|
130
|
+
ctx.stroke();
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
// Label
|
|
134
|
+
ctx.fillStyle = '#000';
|
|
135
|
+
ctx.fillText(
|
|
136
|
+
this.label,
|
|
137
|
+
this.x + radius * 2 + this.padding,
|
|
138
|
+
centerY
|
|
139
|
+
);
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
roundRect(ctx, x, y, w, h, r) {
|
|
143
|
+
ctx.beginPath();
|
|
144
|
+
ctx.moveTo(x + r, y);
|
|
145
|
+
ctx.lineTo(x + w - r, y);
|
|
146
|
+
ctx.quadraticCurveTo(x + w, y, x + w, y + r);
|
|
147
|
+
ctx.lineTo(x + w, y + h - r);
|
|
148
|
+
ctx.quadraticCurveTo(x + w, y + h, x + w - r, y + h);
|
|
149
|
+
ctx.lineTo(x + r, y + h);
|
|
150
|
+
ctx.quadraticCurveTo(x, y + h, x, y + h - r);
|
|
151
|
+
ctx.lineTo(x, y + r);
|
|
152
|
+
ctx.quadraticCurveTo(x, y, x + r, y);
|
|
163
153
|
ctx.closePath();
|
|
164
154
|
}
|
|
165
|
-
|
|
166
|
-
/**
|
|
167
|
-
* Vérifie si un point est dans les limites
|
|
168
|
-
* @param {number} x - Coordonnée X
|
|
169
|
-
* @param {number} y - Coordonnée Y
|
|
170
|
-
* @returns {boolean} True si le point est dans la checkbox
|
|
171
|
-
*/
|
|
155
|
+
|
|
172
156
|
isPointInside(x, y) {
|
|
173
|
-
return
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
157
|
+
return (
|
|
158
|
+
x >= this.x &&
|
|
159
|
+
x <= this.x + this.width &&
|
|
160
|
+
y >= this.y &&
|
|
161
|
+
y <= this.y + this.height
|
|
162
|
+
);
|
|
177
163
|
}
|
|
178
164
|
}
|
|
179
165
|
|
|
180
|
-
export default Checkbox;
|
|
166
|
+
export default Checkbox;
|
package/components/DatePicker.js
CHANGED
|
@@ -1,4 +1,9 @@
|
|
|
1
1
|
import Component from '../core/Component.js';
|
|
2
|
+
import AndroidDatePickerDialog from '../components/AndroidDatePickerDialog.js';
|
|
3
|
+
import Modal from '../components/Modal.js';
|
|
4
|
+
import IOSDatePickerWheel from '../components/IOSDatePickerWheel.js';
|
|
5
|
+
import Button from '../components/Button.js';
|
|
6
|
+
|
|
2
7
|
/**
|
|
3
8
|
* Sélecteur de date (wrapper)
|
|
4
9
|
* @class
|
|
@@ -61,6 +66,8 @@ class DatePicker extends Component {
|
|
|
61
66
|
* Ouvre le sélecteur iOS
|
|
62
67
|
* @private
|
|
63
68
|
*/
|
|
69
|
+
|
|
70
|
+
|
|
64
71
|
openIOSPicker() {
|
|
65
72
|
// Créer un modal avec le picker iOS
|
|
66
73
|
const modal = new Modal(this.framework, {
|