canvasframework 0.3.14 → 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/CircularProgress.js +213 -29
- 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 +3 -7
- package/core/Component.js +90 -0
- package/index.js +2 -1
- package/package.json +1 -1
- package/utils/CryptoManager.js +303 -0
package/components/TextField.js
CHANGED
|
@@ -1,331 +1,151 @@
|
|
|
1
|
-
import
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
* @class
|
|
5
|
-
* @extends Component
|
|
6
|
-
* @param {Framework} framework - Instance du framework
|
|
7
|
-
* @param {Object} [options={}] - Options de configuration
|
|
8
|
-
* @param {string} [options.label=''] - Label du champ
|
|
9
|
-
* @param {string} [options.value=''] - Valeur initiale
|
|
10
|
-
* @param {string} [options.placeholder=''] - Placeholder
|
|
11
|
-
* @param {string} [options.helperText=''] - Texte d'aide
|
|
12
|
-
* @param {string} [options.errorText=''] - Texte d'erreur
|
|
13
|
-
* @param {boolean} [options.error=false] - État d'erreur
|
|
14
|
-
* @param {number} [options.fontSize=16] - Taille de police
|
|
15
|
-
* @param {Function} [options.onChange] - Callback lors du changement
|
|
16
|
-
* @param {number} [options.height=80] - Hauteur totale (inclut label + input + helper)
|
|
17
|
-
* @example
|
|
18
|
-
* const textField = new TextField(framework, {
|
|
19
|
-
* label: 'Email',
|
|
20
|
-
* placeholder: 'Entrez votre email',
|
|
21
|
-
* helperText: 'Nous ne partagerons jamais votre email',
|
|
22
|
-
* onChange: (value) => validateEmail(value)
|
|
23
|
-
* });
|
|
24
|
-
*/
|
|
25
|
-
class TextField extends Component {
|
|
26
|
-
/**
|
|
27
|
-
* @constructs TextField
|
|
28
|
-
*/
|
|
1
|
+
import Input from './Input.js';
|
|
2
|
+
|
|
3
|
+
class TextField extends Input {
|
|
29
4
|
constructor(framework, options = {}) {
|
|
30
5
|
super(framework, options);
|
|
31
|
-
|
|
6
|
+
|
|
32
7
|
this.label = options.label || '';
|
|
33
|
-
/** @type {string} */
|
|
34
|
-
this.value = options.value || '';
|
|
35
|
-
/** @type {string} */
|
|
36
|
-
this.placeholder = options.placeholder || '';
|
|
37
|
-
/** @type {string} */
|
|
38
8
|
this.helperText = options.helperText || '';
|
|
39
|
-
/** @type {string} */
|
|
40
9
|
this.errorText = options.errorText || '';
|
|
41
|
-
/** @type {boolean} */
|
|
42
10
|
this.error = options.error || false;
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
this.
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
/** @type {boolean} */
|
|
56
|
-
this.cursorVisible = true;
|
|
57
|
-
|
|
58
|
-
// Hauteur pour inclure label + input + helper
|
|
59
|
-
this.height = options.height || 80;
|
|
60
|
-
|
|
61
|
-
this.onFocus = this.handleFocus.bind(this);
|
|
62
|
-
this.onBlur = this.handleBlur.bind(this);
|
|
63
|
-
|
|
64
|
-
this.setupHiddenInput();
|
|
65
|
-
|
|
66
|
-
// Animation du curseur
|
|
67
|
-
/** @type {number} */
|
|
68
|
-
this.cursorInterval = setInterval(() => {
|
|
69
|
-
if (this.focused) this.cursorVisible = !this.cursorVisible;
|
|
70
|
-
}, 500);
|
|
71
|
-
}
|
|
72
|
-
|
|
73
|
-
/**
|
|
74
|
-
* Configure l'input caché dans le DOM
|
|
75
|
-
* @private
|
|
76
|
-
*/
|
|
77
|
-
setupHiddenInput() {
|
|
78
|
-
let hiddenInput = document.getElementById('hidden-textfield-input');
|
|
79
|
-
if (!hiddenInput) {
|
|
80
|
-
hiddenInput = document.createElement('input');
|
|
81
|
-
hiddenInput.id = 'hidden-textfield-input';
|
|
82
|
-
hiddenInput.type = 'text';
|
|
83
|
-
hiddenInput.style.position = 'fixed';
|
|
84
|
-
hiddenInput.style.opacity = '0';
|
|
85
|
-
hiddenInput.style.pointerEvents = 'none';
|
|
86
|
-
hiddenInput.style.top = '-100px';
|
|
87
|
-
document.body.appendChild(hiddenInput);
|
|
88
|
-
|
|
89
|
-
hiddenInput.addEventListener('input', (e) => {
|
|
90
|
-
if (this.focused) {
|
|
91
|
-
this.value = e.target.value;
|
|
92
|
-
if (this.onChange) this.onChange(this.value);
|
|
93
|
-
this.animateLabel();
|
|
94
|
-
}
|
|
95
|
-
});
|
|
96
|
-
|
|
97
|
-
hiddenInput.addEventListener('blur', () => {
|
|
98
|
-
this.handleBlur();
|
|
99
|
-
});
|
|
100
|
-
}
|
|
101
|
-
/** @type {HTMLInputElement} */
|
|
102
|
-
this.hiddenInput = hiddenInput;
|
|
11
|
+
|
|
12
|
+
// Label position (PLUS HAUT que placeholder)
|
|
13
|
+
this.labelRestY = 14; // label quand vide / pas focus, légèrement au-dessus du placeholder
|
|
14
|
+
this.labelFloatY = 4; // label flottant quand focus / value
|
|
15
|
+
this.labelY = this.value ? this.labelFloatY : this.labelRestY;
|
|
16
|
+
this.labelFontSize = this.value ? 12 : 16;
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
this.labelRestSize = 15;
|
|
20
|
+
this.labelFontSize = this.value
|
|
21
|
+
? this.labelFloatSize
|
|
22
|
+
: this.labelRestSize;
|
|
103
23
|
}
|
|
104
|
-
|
|
105
|
-
/**
|
|
106
|
-
* Anime le label (flottant)
|
|
107
|
-
* @private
|
|
108
|
-
*/
|
|
24
|
+
|
|
109
25
|
animateLabel() {
|
|
110
|
-
const
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
const diffSize = targetSize - this.labelFontSize;
|
|
117
|
-
|
|
118
|
-
if (Math.abs(diffY) < 0.5 && Math.abs(diffSize) < 0.5) {
|
|
119
|
-
this.labelY = targetY;
|
|
120
|
-
this.labelFontSize = targetSize;
|
|
121
|
-
return;
|
|
122
|
-
}
|
|
123
|
-
|
|
124
|
-
this.labelY += diffY * 0.2;
|
|
125
|
-
this.labelFontSize += diffSize * 0.2;
|
|
126
|
-
|
|
127
|
-
requestAnimationFrame(animate);
|
|
128
|
-
};
|
|
129
|
-
|
|
130
|
-
animate();
|
|
26
|
+
const float = this.focused || this.value;
|
|
27
|
+
|
|
28
|
+
this.labelY = float ? this.labelFloatY : this.labelRestY;
|
|
29
|
+
this.labelFontSize = float
|
|
30
|
+
? this.labelFloatSize
|
|
31
|
+
: this.labelRestSize;
|
|
131
32
|
}
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
*/
|
|
136
|
-
handleFocus() {
|
|
137
|
-
this.focused = true;
|
|
138
|
-
this.cursorVisible = true;
|
|
139
|
-
if (this.hiddenInput) {
|
|
140
|
-
this.hiddenInput.value = this.value;
|
|
141
|
-
const adjustedY = this.y + this.framework.scrollOffset;
|
|
142
|
-
this.hiddenInput.style.top = `${adjustedY}px`;
|
|
143
|
-
this.hiddenInput.focus();
|
|
144
|
-
}
|
|
33
|
+
|
|
34
|
+
onFocus() {
|
|
35
|
+
super.onFocus();
|
|
145
36
|
this.animateLabel();
|
|
146
37
|
}
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
*/
|
|
151
|
-
handleBlur() {
|
|
152
|
-
this.focused = false;
|
|
153
|
-
this.cursorVisible = false;
|
|
38
|
+
|
|
39
|
+
onBlur() {
|
|
40
|
+
super.onBlur();
|
|
154
41
|
this.animateLabel();
|
|
155
42
|
}
|
|
156
|
-
|
|
157
|
-
/**
|
|
158
|
-
* Gère le clic sur le champ
|
|
159
|
-
*/
|
|
160
|
-
onClick() {
|
|
161
|
-
this.handleFocus();
|
|
162
|
-
}
|
|
163
|
-
|
|
164
|
-
/**
|
|
165
|
-
* Dessine le champ de texte
|
|
166
|
-
* @param {CanvasRenderingContext2D} ctx - Contexte de dessin
|
|
167
|
-
*/
|
|
43
|
+
|
|
168
44
|
draw(ctx) {
|
|
169
45
|
ctx.save();
|
|
170
|
-
|
|
171
|
-
const inputY = this.y +
|
|
172
|
-
const inputHeight =
|
|
173
|
-
|
|
46
|
+
|
|
47
|
+
const inputY = this.y + 28;
|
|
48
|
+
const inputHeight = 42;
|
|
49
|
+
|
|
50
|
+
/* ================= MATERIAL ================= */
|
|
174
51
|
if (this.platform === 'material') {
|
|
175
|
-
//
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
52
|
+
// Label
|
|
53
|
+
ctx.fillStyle = this.error
|
|
54
|
+
? '#B00020'
|
|
55
|
+
: this.focused
|
|
56
|
+
? '#6200EE'
|
|
57
|
+
: '#757575';
|
|
58
|
+
|
|
181
59
|
ctx.font = `${this.labelFontSize}px Roboto, sans-serif`;
|
|
182
|
-
ctx.
|
|
183
|
-
ctx.
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
60
|
+
ctx.textBaseline = 'top';
|
|
61
|
+
ctx.fillText(this.label, this.x, this.y + this.labelY);
|
|
62
|
+
|
|
63
|
+
// Ligne
|
|
64
|
+
ctx.strokeStyle = this.error
|
|
65
|
+
? '#B00020'
|
|
66
|
+
: this.focused
|
|
67
|
+
? '#6200EE'
|
|
68
|
+
: '#CCCCCC';
|
|
69
|
+
|
|
190
70
|
ctx.lineWidth = this.focused ? 2 : 1;
|
|
191
71
|
ctx.beginPath();
|
|
192
72
|
ctx.moveTo(this.x, inputY + inputHeight);
|
|
193
73
|
ctx.lineTo(this.x + this.width, inputY + inputHeight);
|
|
194
74
|
ctx.stroke();
|
|
195
|
-
|
|
196
|
-
//
|
|
197
|
-
const
|
|
198
|
-
ctx.fillStyle =
|
|
75
|
+
|
|
76
|
+
// Texte / placeholder
|
|
77
|
+
const showPlaceholder = !this.value && !this.focused;
|
|
78
|
+
ctx.fillStyle = showPlaceholder ? '#9E9E9E' : '#000';
|
|
199
79
|
ctx.font = `${this.fontSize}px Roboto, sans-serif`;
|
|
200
80
|
ctx.textBaseline = 'middle';
|
|
201
|
-
|
|
202
|
-
|
|
81
|
+
|
|
82
|
+
ctx.fillText(
|
|
83
|
+
showPlaceholder ? this.placeholder : this.value,
|
|
84
|
+
this.x,
|
|
85
|
+
inputY + inputHeight / 2
|
|
86
|
+
);
|
|
87
|
+
|
|
203
88
|
// Curseur
|
|
204
89
|
if (this.focused && this.cursorVisible) {
|
|
205
|
-
const
|
|
90
|
+
const w = ctx.measureText(this.value).width;
|
|
206
91
|
ctx.fillStyle = '#6200EE';
|
|
207
|
-
ctx.fillRect(this.x +
|
|
208
|
-
}
|
|
209
|
-
|
|
210
|
-
// Helper text ou error text
|
|
211
|
-
const helperColor = this.error ? '#B00020' : '#757575';
|
|
212
|
-
const helperMessage = this.error ? this.errorText : this.helperText;
|
|
213
|
-
|
|
214
|
-
if (helperMessage) {
|
|
215
|
-
ctx.fillStyle = helperColor;
|
|
216
|
-
ctx.font = '12px Roboto, sans-serif';
|
|
217
|
-
ctx.fillText(helperMessage, this.x, inputY + inputHeight + 20);
|
|
218
|
-
}
|
|
219
|
-
|
|
220
|
-
} else {
|
|
221
|
-
// Cupertino style (label au-dessus)
|
|
222
|
-
|
|
223
|
-
if (this.label) {
|
|
224
|
-
ctx.fillStyle = '#000000';
|
|
225
|
-
ctx.font = 'bold 14px -apple-system, sans-serif';
|
|
226
|
-
ctx.textAlign = 'left';
|
|
227
|
-
ctx.textBaseline = 'top';
|
|
228
|
-
ctx.fillText(this.label, this.x, this.y);
|
|
92
|
+
ctx.fillRect(this.x + w + 2, inputY + 10, 2, inputHeight - 20);
|
|
229
93
|
}
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
/* ================= CUPERTINO ================= */
|
|
97
|
+
else {
|
|
98
|
+
// Label (toujours visible)
|
|
99
|
+
ctx.fillStyle = '#6D6D72';
|
|
100
|
+
ctx.font = '12px -apple-system, sans-serif';
|
|
101
|
+
ctx.textBaseline = 'top';
|
|
102
|
+
ctx.fillText(this.label, this.x, this.y);
|
|
103
|
+
|
|
104
|
+
// Box
|
|
105
|
+
ctx.strokeStyle = this.error
|
|
106
|
+
? '#FF3B30'
|
|
107
|
+
: this.focused
|
|
108
|
+
? '#007AFF'
|
|
109
|
+
: '#C7C7CC';
|
|
110
|
+
|
|
234
111
|
ctx.lineWidth = 1;
|
|
235
112
|
ctx.beginPath();
|
|
236
|
-
this.roundRect(
|
|
113
|
+
this.roundRect(
|
|
114
|
+
ctx,
|
|
115
|
+
this.x,
|
|
116
|
+
inputY,
|
|
117
|
+
this.width,
|
|
118
|
+
inputHeight,
|
|
119
|
+
10
|
|
120
|
+
);
|
|
237
121
|
ctx.stroke();
|
|
238
|
-
|
|
239
|
-
//
|
|
240
|
-
|
|
241
|
-
ctx.fillStyle = this.value ? '#000000' : '#999999';
|
|
122
|
+
|
|
123
|
+
// Texte / placeholder
|
|
124
|
+
ctx.fillStyle = this.value ? '#000' : '#8E8E93';
|
|
242
125
|
ctx.font = `${this.fontSize}px -apple-system, sans-serif`;
|
|
243
|
-
ctx.textAlign = 'left';
|
|
244
126
|
ctx.textBaseline = 'middle';
|
|
245
|
-
|
|
246
|
-
|
|
127
|
+
|
|
128
|
+
ctx.fillText(
|
|
129
|
+
this.value || this.placeholder,
|
|
130
|
+
this.x + 12,
|
|
131
|
+
inputY + inputHeight / 2
|
|
132
|
+
);
|
|
133
|
+
|
|
247
134
|
// Curseur
|
|
248
135
|
if (this.focused && this.cursorVisible) {
|
|
249
|
-
const
|
|
136
|
+
const w = ctx.measureText(this.value).width;
|
|
250
137
|
ctx.fillStyle = '#007AFF';
|
|
251
|
-
ctx.fillRect(
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
ctx.font = '12px -apple-system, sans-serif';
|
|
258
|
-
ctx.fillText(this.errorText, this.x, inputY + inputHeight + 8);
|
|
259
|
-
} else if (this.helperText) {
|
|
260
|
-
ctx.fillStyle = '#8E8E93';
|
|
261
|
-
ctx.font = '12px -apple-system, sans-serif';
|
|
262
|
-
ctx.fillText(this.helperText, this.x, inputY + inputHeight + 8);
|
|
138
|
+
ctx.fillRect(
|
|
139
|
+
this.x + 12 + w + 2,
|
|
140
|
+
inputY + 10,
|
|
141
|
+
2,
|
|
142
|
+
inputHeight - 20
|
|
143
|
+
);
|
|
263
144
|
}
|
|
264
145
|
}
|
|
265
|
-
|
|
146
|
+
|
|
266
147
|
ctx.restore();
|
|
267
148
|
}
|
|
268
|
-
|
|
269
|
-
/**
|
|
270
|
-
* Dessine un rectangle avec des coins arrondis
|
|
271
|
-
* @param {CanvasRenderingContext2D} ctx - Contexte de dessin
|
|
272
|
-
* @param {number} x - Position X
|
|
273
|
-
* @param {number} y - Position Y
|
|
274
|
-
* @param {number} width - Largeur
|
|
275
|
-
* @param {number} height - Hauteur
|
|
276
|
-
* @param {number} radius - Rayon des coins
|
|
277
|
-
* @private
|
|
278
|
-
*/
|
|
279
|
-
roundRect(ctx, x, y, width, height, radius) {
|
|
280
|
-
ctx.beginPath();
|
|
281
|
-
ctx.moveTo(x + radius, y);
|
|
282
|
-
ctx.lineTo(x + width - radius, y);
|
|
283
|
-
ctx.quadraticCurveTo(x + width, y, x + width, y + radius);
|
|
284
|
-
ctx.lineTo(x + width, y + height - radius);
|
|
285
|
-
ctx.quadraticCurveTo(x + width, y + height, x + width - radius, y + height);
|
|
286
|
-
ctx.lineTo(x + radius, y + height);
|
|
287
|
-
ctx.quadraticCurveTo(x, y + height, x, y + height - radius);
|
|
288
|
-
ctx.lineTo(x, y + radius);
|
|
289
|
-
ctx.quadraticCurveTo(x, y, x + radius, y);
|
|
290
|
-
ctx.closePath();
|
|
291
|
-
}
|
|
292
|
-
|
|
293
|
-
/**
|
|
294
|
-
* Vérifie si un point est à l'intérieur du composant
|
|
295
|
-
* @param {number} x - Position X
|
|
296
|
-
* @param {number} y - Position Y
|
|
297
|
-
* @returns {boolean} True si le point est à l'intérieur
|
|
298
|
-
*/
|
|
299
|
-
isPointInside(x, y) {
|
|
300
|
-
return x >= this.x && x <= this.x + this.width &&
|
|
301
|
-
y >= this.y && y <= this.y + this.height;
|
|
302
|
-
}
|
|
303
|
-
|
|
304
|
-
/**
|
|
305
|
-
* Définit une erreur sur le champ
|
|
306
|
-
* @param {string} errorText - Texte d'erreur à afficher
|
|
307
|
-
*/
|
|
308
|
-
setError(errorText) {
|
|
309
|
-
this.error = true;
|
|
310
|
-
this.errorText = errorText;
|
|
311
|
-
}
|
|
312
|
-
|
|
313
|
-
/**
|
|
314
|
-
* Efface l'erreur du champ
|
|
315
|
-
*/
|
|
316
|
-
clearError() {
|
|
317
|
-
this.error = false;
|
|
318
|
-
this.errorText = '';
|
|
319
|
-
}
|
|
320
|
-
|
|
321
|
-
/**
|
|
322
|
-
* Nettoie les ressources (arrête l'animation du curseur)
|
|
323
|
-
*/
|
|
324
|
-
destroy() {
|
|
325
|
-
if (this.cursorInterval) {
|
|
326
|
-
clearInterval(this.cursorInterval);
|
|
327
|
-
}
|
|
328
|
-
}
|
|
329
149
|
}
|
|
330
150
|
|
|
331
|
-
export default TextField;
|
|
151
|
+
export default TextField;
|
package/core/CanvasFramework.js
CHANGED
|
@@ -66,6 +66,7 @@ import FetchClient from '../utils/FetchClient.js';
|
|
|
66
66
|
import GeoLocationService from '../utils/GeoLocationService.js';
|
|
67
67
|
import WebSocketClient from '../utils/WebSocketClient.js';
|
|
68
68
|
import AnimationEngine from '../utils/AnimationEngine.js';
|
|
69
|
+
import CryptoManager from '../utils/CryptoManager.js';
|
|
69
70
|
|
|
70
71
|
// Features
|
|
71
72
|
import PullToRefresh from '../features/PullToRefresh.js';
|
|
@@ -825,6 +826,7 @@ class CanvasFramework {
|
|
|
825
826
|
comp instanceof Dialog ||
|
|
826
827
|
comp instanceof Modal ||
|
|
827
828
|
comp instanceof FAB ||
|
|
829
|
+
comp instanceof Toast ||
|
|
828
830
|
comp instanceof BottomSheet ||
|
|
829
831
|
comp instanceof ContextMenu ||
|
|
830
832
|
comp instanceof OpenStreetMap ||
|
|
@@ -1255,6 +1257,7 @@ class CanvasFramework {
|
|
|
1255
1257
|
comp instanceof Dialog ||
|
|
1256
1258
|
comp instanceof Modal ||
|
|
1257
1259
|
comp instanceof FAB ||
|
|
1260
|
+
comp instanceof Toast ||
|
|
1258
1261
|
comp instanceof BottomSheet ||
|
|
1259
1262
|
comp instanceof ContextMenu ||
|
|
1260
1263
|
comp instanceof OpenStreetMap ||
|
|
@@ -1274,10 +1277,3 @@ class CanvasFramework {
|
|
|
1274
1277
|
}
|
|
1275
1278
|
|
|
1276
1279
|
export default CanvasFramework;
|
|
1277
|
-
|
|
1278
|
-
|
|
1279
|
-
|
|
1280
|
-
|
|
1281
|
-
|
|
1282
|
-
|
|
1283
|
-
|
package/core/Component.js
CHANGED
|
@@ -46,8 +46,98 @@ class Component {
|
|
|
46
46
|
|
|
47
47
|
// Pour détecter les updates
|
|
48
48
|
this._prevProps = { ...options };
|
|
49
|
+
|
|
50
|
+
// Système de listeners
|
|
51
|
+
this._listeners = new Map();
|
|
49
52
|
}
|
|
50
53
|
|
|
54
|
+
/**
|
|
55
|
+
* Ajoute un listener pour un événement
|
|
56
|
+
* @param {string} event - Nom de l'événement
|
|
57
|
+
* @param {Function} handler - Fonction callback
|
|
58
|
+
* @returns {Component} - Pour le chaînage
|
|
59
|
+
*/
|
|
60
|
+
on(event, handler) {
|
|
61
|
+
if (!this._listeners.has(event)) {
|
|
62
|
+
this._listeners.set(event, []);
|
|
63
|
+
}
|
|
64
|
+
this._listeners.get(event).push(handler);
|
|
65
|
+
return this;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
/**
|
|
69
|
+
* Retire un listener
|
|
70
|
+
* @param {string} event - Nom de l'événement
|
|
71
|
+
* @param {Function} handler - Fonction à retirer
|
|
72
|
+
* @returns {Component}
|
|
73
|
+
*/
|
|
74
|
+
off(event, handler) {
|
|
75
|
+
if (!this._listeners.has(event)) return this;
|
|
76
|
+
|
|
77
|
+
const handlers = this._listeners.get(event);
|
|
78
|
+
const index = handlers.indexOf(handler);
|
|
79
|
+
if (index > -1) {
|
|
80
|
+
handlers.splice(index, 1);
|
|
81
|
+
}
|
|
82
|
+
return this;
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
/**
|
|
86
|
+
* Ajoute un listener qui s'exécute une seule fois
|
|
87
|
+
* @param {string} event - Nom de l'événement
|
|
88
|
+
* @param {Function} handler - Fonction callback
|
|
89
|
+
* @returns {Component}
|
|
90
|
+
*/
|
|
91
|
+
once(event, handler) {
|
|
92
|
+
const wrapper = (...args) => {
|
|
93
|
+
handler(...args);
|
|
94
|
+
this.off(event, wrapper);
|
|
95
|
+
};
|
|
96
|
+
return this.on(event, wrapper);
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
/**
|
|
100
|
+
* Émet un événement
|
|
101
|
+
* @param {string} event - Nom de l'événement
|
|
102
|
+
* @param {...any} args - Arguments à passer aux handlers
|
|
103
|
+
* @returns {Component}
|
|
104
|
+
*/
|
|
105
|
+
emit(event, ...args) {
|
|
106
|
+
if (!this._listeners.has(event)) return this;
|
|
107
|
+
|
|
108
|
+
const handlers = this._listeners.get(event);
|
|
109
|
+
for (let handler of handlers) {
|
|
110
|
+
try {
|
|
111
|
+
handler(...args);
|
|
112
|
+
} catch (error) {
|
|
113
|
+
console.error(`Error in ${event} handler:`, error);
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
return this;
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
/**
|
|
120
|
+
* Retire tous les listeners d'un événement (ou tous)
|
|
121
|
+
* @param {string} [event] - Nom de l'événement (optionnel)
|
|
122
|
+
* @returns {Component}
|
|
123
|
+
*/
|
|
124
|
+
removeAllListeners(event) {
|
|
125
|
+
if (event) {
|
|
126
|
+
this._listeners.delete(event);
|
|
127
|
+
} else {
|
|
128
|
+
this._listeners.clear();
|
|
129
|
+
}
|
|
130
|
+
return this;
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
/**
|
|
134
|
+
* Retourne le nombre de listeners pour un événement
|
|
135
|
+
* @param {string} event - Nom de l'événement
|
|
136
|
+
* @returns {number}
|
|
137
|
+
*/
|
|
138
|
+
listenerCount(event) {
|
|
139
|
+
return this._listeners.has(event) ? this._listeners.get(event).length : 0;
|
|
140
|
+
}
|
|
51
141
|
/* =======================
|
|
52
142
|
LIFECYCLE HOOKS
|
|
53
143
|
======================= */
|
package/index.js
CHANGED
|
@@ -74,6 +74,7 @@ export { default as FetchClient } from './utils/FetchClient.js';
|
|
|
74
74
|
export { default as GeoLocationService } from './utils/GeoLocationService.js';
|
|
75
75
|
export { default as WebSocketClient } from './utils/WebSocketClient.js';
|
|
76
76
|
export { default as AnimationEngine } from './utils/AnimationEngine.js';
|
|
77
|
+
export { default as CryptoManager } from './utils/CryptoManager.js';
|
|
77
78
|
|
|
78
79
|
// Features
|
|
79
80
|
export { default as PullToRefresh } from './features/PullToRefresh.js';
|
|
@@ -97,7 +98,7 @@ export { default as FeatureFlags } from './manager/FeatureFlags.js';
|
|
|
97
98
|
|
|
98
99
|
// Version du framework
|
|
99
100
|
|
|
100
|
-
export const VERSION = '0.3.
|
|
101
|
+
export const VERSION = '0.3.16';
|
|
101
102
|
|
|
102
103
|
|
|
103
104
|
|