canvasframework 0.5.16 → 0.5.18

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.
Files changed (112) hide show
  1. package/dist/canvasframework.js +2 -0
  2. package/dist/canvasframework.js.LICENSE.txt +1 -0
  3. package/package.json +18 -17
  4. package/components/Accordion.js +0 -265
  5. package/components/AndroidDatePickerDialog.js +0 -406
  6. package/components/AppBar.js +0 -398
  7. package/components/AudioPlayer.js +0 -611
  8. package/components/Avatar.js +0 -202
  9. package/components/Banner.js +0 -342
  10. package/components/BottomNavigationBar.js +0 -433
  11. package/components/BottomSheet.js +0 -234
  12. package/components/Button.js +0 -360
  13. package/components/Camera.js +0 -644
  14. package/components/Card.js +0 -193
  15. package/components/Chart.js +0 -700
  16. package/components/Checkbox.js +0 -166
  17. package/components/Chip.js +0 -212
  18. package/components/CircularProgress.js +0 -327
  19. package/components/ContextMenu.js +0 -116
  20. package/components/DatePicker.js +0 -298
  21. package/components/Dialog.js +0 -337
  22. package/components/Divider.js +0 -125
  23. package/components/Drawer.js +0 -276
  24. package/components/FAB.js +0 -270
  25. package/components/FileUpload.js +0 -315
  26. package/components/FloatedCamera.js +0 -644
  27. package/components/IOSDatePickerWheel.js +0 -430
  28. package/components/ImageCarousel.js +0 -219
  29. package/components/ImageComponent.js +0 -223
  30. package/components/Input.js +0 -831
  31. package/components/InputDatalist.js +0 -723
  32. package/components/InputTags.js +0 -624
  33. package/components/List.js +0 -95
  34. package/components/ListItem.js +0 -269
  35. package/components/Modal.js +0 -364
  36. package/components/MorphingFAB.js +0 -428
  37. package/components/MultiSelectDialog.js +0 -206
  38. package/components/NumberInput.js +0 -271
  39. package/components/PasswordInput.js +0 -462
  40. package/components/ProgressBar.js +0 -88
  41. package/components/QRCodeReader.js +0 -539
  42. package/components/RadioButton.js +0 -151
  43. package/components/SearchInput.js +0 -315
  44. package/components/SegmentedControl.js +0 -357
  45. package/components/Select.js +0 -199
  46. package/components/SelectDialog.js +0 -255
  47. package/components/Slider.js +0 -113
  48. package/components/SliverAppBar.js +0 -139
  49. package/components/Snackbar.js +0 -243
  50. package/components/SpeedDialFAB.js +0 -397
  51. package/components/Stepper.js +0 -281
  52. package/components/SwipeableListItem.js +0 -327
  53. package/components/Switch.js +0 -147
  54. package/components/Table.js +0 -492
  55. package/components/Tabs.js +0 -423
  56. package/components/Text.js +0 -141
  57. package/components/TextField.js +0 -151
  58. package/components/TimePicker.js +0 -934
  59. package/components/Toast.js +0 -236
  60. package/components/TreeView.js +0 -420
  61. package/components/Video.js +0 -397
  62. package/components/View.js +0 -140
  63. package/components/VirtualList.js +0 -120
  64. package/core/CanvasFramework.js +0 -3034
  65. package/core/Component.js +0 -243
  66. package/core/ThemeManager.js +0 -358
  67. package/core/UIBuilder.js +0 -267
  68. package/core/WebGLCanvasAdapter.js +0 -782
  69. package/features/Column.js +0 -43
  70. package/features/Grid.js +0 -47
  71. package/features/LayoutComponent.js +0 -43
  72. package/features/OpenStreetMap.js +0 -310
  73. package/features/Positioned.js +0 -33
  74. package/features/PullToRefresh.js +0 -328
  75. package/features/Row.js +0 -40
  76. package/features/SignaturePad.js +0 -257
  77. package/features/Skeleton.js +0 -193
  78. package/features/Stack.js +0 -21
  79. package/index.js +0 -119
  80. package/manager/AccessibilityManager.js +0 -107
  81. package/manager/ErrorHandler.js +0 -59
  82. package/manager/FeatureFlags.js +0 -60
  83. package/manager/MemoryManager.js +0 -107
  84. package/manager/PerformanceMonitor.js +0 -84
  85. package/manager/SecurityManager.js +0 -54
  86. package/utils/AnimationEngine.js +0 -734
  87. package/utils/CryptoManager.js +0 -303
  88. package/utils/DataStore.js +0 -403
  89. package/utils/DevTools.js +0 -1618
  90. package/utils/DevToolsConsole.js +0 -201
  91. package/utils/EventBus.js +0 -407
  92. package/utils/FetchClient.js +0 -74
  93. package/utils/FirebaseAuth.js +0 -653
  94. package/utils/FirebaseCore.js +0 -246
  95. package/utils/FirebaseFirestore.js +0 -581
  96. package/utils/FirebaseFunctions.js +0 -97
  97. package/utils/FirebaseRealtimeDB.js +0 -498
  98. package/utils/FirebaseStorage.js +0 -612
  99. package/utils/FormValidator.js +0 -355
  100. package/utils/GeoLocationService.js +0 -62
  101. package/utils/I18n.js +0 -207
  102. package/utils/IndexedDBManager.js +0 -273
  103. package/utils/InspectionOverlay.js +0 -308
  104. package/utils/NotificationManager.js +0 -60
  105. package/utils/OfflineSyncManager.js +0 -342
  106. package/utils/PayPalPayment.js +0 -678
  107. package/utils/QueryBuilder.js +0 -478
  108. package/utils/SafeArea.js +0 -64
  109. package/utils/SecureStorage.js +0 -289
  110. package/utils/StateManager.js +0 -207
  111. package/utils/StripePayment.js +0 -552
  112. package/utils/WebSocketClient.js +0 -66
@@ -1,355 +0,0 @@
1
- /**
2
- * Validateur de formulaires avec règles personnalisables
3
- * @class
4
- * @property {Object} rules - Règles de validation
5
- * @property {Object} messages - Messages d'erreur personnalisés
6
- * @property {Object} errors - Erreurs de validation
7
- */
8
- class FormValidator {
9
- /**
10
- * Crée une instance de FormValidator
11
- * @param {Object} [rules={}] - Règles de validation
12
- * @param {Object} [customMessages={}] - Messages personnalisés
13
- */
14
- constructor(rules = {}, customMessages = {}) {
15
- this.rules = rules;
16
- this.customMessages = customMessages;
17
- this.errors = {};
18
-
19
- // Messages par défaut
20
- this.defaultMessages = {
21
- required: 'This field is required',
22
- email: 'Please enter a valid email address',
23
- min: 'Value must be at least {min}',
24
- max: 'Value must not exceed {max}',
25
- minLength: 'Must be at least {minLength} characters',
26
- maxLength: 'Must not exceed {maxLength} characters',
27
- pattern: 'Invalid format',
28
- url: 'Please enter a valid URL',
29
- numeric: 'Must be a number',
30
- integer: 'Must be an integer',
31
- phone: 'Please enter a valid phone number',
32
- match: 'Fields do not match',
33
- custom: 'Invalid value'
34
- };
35
- }
36
-
37
- /**
38
- * Valide toutes les données
39
- * @param {Object} data - Données à valider
40
- * @returns {boolean} True si valide
41
- */
42
- validate(data) {
43
- this.errors = {};
44
- let isValid = true;
45
-
46
- for (let field in this.rules) {
47
- const fieldRules = this.rules[field];
48
- const value = data[field];
49
-
50
- const fieldErrors = this.validateField(field, value, data);
51
-
52
- if (fieldErrors.length > 0) {
53
- this.errors[field] = fieldErrors;
54
- isValid = false;
55
- }
56
- }
57
-
58
- return isValid;
59
- }
60
-
61
- /**
62
- * Valide un champ spécifique
63
- * @param {string} field - Nom du champ
64
- * @param {*} value - Valeur à valider
65
- * @param {Object} [allData={}] - Toutes les données (pour match, etc.)
66
- * @returns {Array} Liste des erreurs
67
- */
68
- validateField(field, value, allData = {}) {
69
- const fieldRules = this.rules[field];
70
- const errors = [];
71
-
72
- if (!fieldRules) return errors;
73
-
74
- // Required
75
- if (fieldRules.required && this.isEmpty(value)) {
76
- errors.push(this.getMessage(field, 'required'));
77
- return errors; // Arrêter si requis et vide
78
- }
79
-
80
- // Si vide et non requis, ne pas valider le reste
81
- if (this.isEmpty(value) && !fieldRules.required) {
82
- return errors;
83
- }
84
-
85
- // Email
86
- if (fieldRules.email && !this.isEmail(value)) {
87
- errors.push(this.getMessage(field, 'email'));
88
- }
89
-
90
- // Min
91
- if (fieldRules.min !== undefined && Number(value) < fieldRules.min) {
92
- errors.push(this.getMessage(field, 'min', { min: fieldRules.min }));
93
- }
94
-
95
- // Max
96
- if (fieldRules.max !== undefined && Number(value) > fieldRules.max) {
97
- errors.push(this.getMessage(field, 'max', { max: fieldRules.max }));
98
- }
99
-
100
- // MinLength
101
- if (fieldRules.minLength && String(value).length < fieldRules.minLength) {
102
- errors.push(this.getMessage(field, 'minLength', { minLength: fieldRules.minLength }));
103
- }
104
-
105
- // MaxLength
106
- if (fieldRules.maxLength && String(value).length > fieldRules.maxLength) {
107
- errors.push(this.getMessage(field, 'maxLength', { maxLength: fieldRules.maxLength }));
108
- }
109
-
110
- // Pattern
111
- if (fieldRules.pattern && !fieldRules.pattern.test(value)) {
112
- errors.push(this.getMessage(field, 'pattern'));
113
- }
114
-
115
- // URL
116
- if (fieldRules.url && !this.isURL(value)) {
117
- errors.push(this.getMessage(field, 'url'));
118
- }
119
-
120
- // Numeric
121
- if (fieldRules.numeric && !this.isNumeric(value)) {
122
- errors.push(this.getMessage(field, 'numeric'));
123
- }
124
-
125
- // Integer
126
- if (fieldRules.integer && !this.isInteger(value)) {
127
- errors.push(this.getMessage(field, 'integer'));
128
- }
129
-
130
- // Phone
131
- if (fieldRules.phone && !this.isPhone(value)) {
132
- errors.push(this.getMessage(field, 'phone'));
133
- }
134
-
135
- // Match (compare avec un autre champ)
136
- if (fieldRules.match && value !== allData[fieldRules.match]) {
137
- errors.push(this.getMessage(field, 'match'));
138
- }
139
-
140
- // Custom validator
141
- if (fieldRules.custom && typeof fieldRules.custom === 'function') {
142
- const customResult = fieldRules.custom(value, allData);
143
- if (customResult !== true) {
144
- errors.push(typeof customResult === 'string' ? customResult : this.getMessage(field, 'custom'));
145
- }
146
- }
147
-
148
- return errors;
149
- }
150
-
151
- /**
152
- * Obtient un message d'erreur
153
- * @param {string} field - Nom du champ
154
- * @param {string} rule - Nom de la règle
155
- * @param {Object} [params={}] - Paramètres à injecter
156
- * @returns {string} Message d'erreur
157
- * @private
158
- */
159
- getMessage(field, rule, params = {}) {
160
- let message = this.customMessages[`${field}.${rule}`] ||
161
- this.customMessages[rule] ||
162
- this.defaultMessages[rule];
163
-
164
- // Remplacer les placeholders
165
- for (let key in params) {
166
- message = message.replace(`{${key}}`, params[key]);
167
- }
168
-
169
- return message;
170
- }
171
-
172
- /**
173
- * Vérifie si une valeur est vide
174
- * @param {*} value - Valeur à vérifier
175
- * @returns {boolean} True si vide
176
- * @private
177
- */
178
- isEmpty(value) {
179
- return value === null ||
180
- value === undefined ||
181
- value === '' ||
182
- (Array.isArray(value) && value.length === 0);
183
- }
184
-
185
- /**
186
- * Vérifie si c'est un email valide
187
- * @param {string} value - Email
188
- * @returns {boolean} True si valide
189
- * @private
190
- */
191
- isEmail(value) {
192
- const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
193
- return emailRegex.test(value);
194
- }
195
-
196
- /**
197
- * Vérifie si c'est une URL valide
198
- * @param {string} value - URL
199
- * @returns {boolean} True si valide
200
- * @private
201
- */
202
- isURL(value) {
203
- try {
204
- new URL(value);
205
- return true;
206
- } catch {
207
- return false;
208
- }
209
- }
210
-
211
- /**
212
- * Vérifie si c'est un nombre
213
- * @param {*} value - Valeur
214
- * @returns {boolean} True si numérique
215
- * @private
216
- */
217
- isNumeric(value) {
218
- return !isNaN(parseFloat(value)) && isFinite(value);
219
- }
220
-
221
- /**
222
- * Vérifie si c'est un entier
223
- * @param {*} value - Valeur
224
- * @returns {boolean} True si entier
225
- * @private
226
- */
227
- isInteger(value) {
228
- return Number.isInteger(Number(value));
229
- }
230
-
231
- /**
232
- * Vérifie si c'est un numéro de téléphone
233
- * @param {string} value - Téléphone
234
- * @returns {boolean} True si valide
235
- * @private
236
- */
237
- isPhone(value) {
238
- const phoneRegex = /^[\d\s\-\+\(\)]+$/;
239
- return phoneRegex.test(value) && value.replace(/\D/g, '').length >= 10;
240
- }
241
-
242
- /**
243
- * Obtient toutes les erreurs
244
- * @returns {Object} Erreurs
245
- */
246
- getErrors() {
247
- return this.errors;
248
- }
249
-
250
- /**
251
- * Obtient les erreurs d'un champ
252
- * @param {string} field - Nom du champ
253
- * @returns {Array} Erreurs du champ
254
- */
255
- getFieldErrors(field) {
256
- return this.errors[field] || [];
257
- }
258
-
259
- /**
260
- * Vérifie si un champ a des erreurs
261
- * @param {string} field - Nom du champ
262
- * @returns {boolean} True si le champ a des erreurs
263
- */
264
- hasError(field) {
265
- return this.errors[field] && this.errors[field].length > 0;
266
- }
267
-
268
- /**
269
- * Réinitialise les erreurs
270
- */
271
- reset() {
272
- this.errors = {};
273
- }
274
-
275
- /**
276
- * Ajoute une règle de validation
277
- * @param {string} field - Nom du champ
278
- * @param {Object} rules - Règles
279
- */
280
- addRule(field, rules) {
281
- this.rules[field] = { ...this.rules[field], ...rules };
282
- }
283
-
284
- /**
285
- * Supprime une règle
286
- * @param {string} field - Nom du champ
287
- */
288
- removeRule(field) {
289
- delete this.rules[field];
290
- }
291
-
292
- /**
293
- * Valide en temps réel (debounced)
294
- * @param {string} field - Nom du champ
295
- * @param {*} value - Valeur
296
- * @param {Object} [allData={}] - Toutes les données
297
- * @param {number} [delay=300] - Délai en ms
298
- * @returns {Promise} Promise qui se résout avec les erreurs
299
- */
300
- validateAsync(field, value, allData = {}, delay = 300) {
301
- if (this.validateTimer) {
302
- clearTimeout(this.validateTimer);
303
- }
304
-
305
- return new Promise((resolve) => {
306
- this.validateTimer = setTimeout(() => {
307
- const errors = this.validateField(field, value, allData);
308
-
309
- if (errors.length > 0) {
310
- this.errors[field] = errors;
311
- } else {
312
- delete this.errors[field];
313
- }
314
-
315
- resolve(errors);
316
- }, delay);
317
- });
318
- }
319
- }
320
-
321
- // Règles prédéfinies utiles
322
- FormValidator.presets = {
323
- // Login form
324
- login: {
325
- email: { required: true, email: true },
326
- password: { required: true, minLength: 8 }
327
- },
328
-
329
- // Registration form
330
- registration: {
331
- username: { required: true, minLength: 3, maxLength: 20, pattern: /^[a-zA-Z0-9_]+$/ },
332
- email: { required: true, email: true },
333
- password: { required: true, minLength: 8, pattern: /^(?=.*[a-z])(?=.*[A-Z])(?=.*\d)/ },
334
- confirmPassword: { required: true, match: 'password' },
335
- terms: { required: true }
336
- },
337
-
338
- // Contact form
339
- contact: {
340
- name: { required: true, minLength: 2 },
341
- email: { required: true, email: true },
342
- phone: { phone: true },
343
- message: { required: true, minLength: 10, maxLength: 500 }
344
- },
345
-
346
- // Payment form
347
- payment: {
348
- cardNumber: { required: true, pattern: /^\d{16}$/ },
349
- cardName: { required: true, minLength: 2 },
350
- expiryDate: { required: true, pattern: /^\d{2}\/\d{2}$/ },
351
- cvv: { required: true, pattern: /^\d{3,4}$/ }
352
- }
353
- };
354
-
355
- export default FormValidator;
@@ -1,62 +0,0 @@
1
- /**
2
- * Service de géolocalisation (point ou suivi continu)
3
- * @class
4
- * @property {number|null} watchId - ID du watch geolocation
5
- */
6
- class GeoLocationService {
7
- constructor() {
8
- this.watchId = null;
9
- }
10
-
11
- /**
12
- * Obtient la position actuelle
13
- * @param {PositionOptions} [options] - Options de geolocation
14
- * @returns {Promise<{latitude:number, longitude:number, accuracy:number}>}
15
- */
16
- getCurrentPosition(options = {}) {
17
- return new Promise((resolve, reject) => {
18
- if (!navigator.geolocation) return reject(new Error("Geolocation not supported"));
19
-
20
- navigator.geolocation.getCurrentPosition(
21
- (pos) => resolve({
22
- latitude: pos.coords.latitude,
23
- longitude: pos.coords.longitude,
24
- accuracy: pos.coords.accuracy
25
- }),
26
- reject,
27
- options
28
- );
29
- });
30
- }
31
-
32
- /**
33
- * Suivi de position en continu
34
- * @param {Function} callback - Callback appelé à chaque mise à jour
35
- * @param {PositionOptions} [options] - Options de geolocation
36
- */
37
- watchPosition(callback, options = {}) {
38
- if (!navigator.geolocation) return;
39
-
40
- this.watchId = navigator.geolocation.watchPosition(
41
- (pos) => callback({
42
- latitude: pos.coords.latitude,
43
- longitude: pos.coords.longitude,
44
- accuracy: pos.coords.accuracy
45
- }),
46
- (err) => console.error("Geolocation error:", err),
47
- options
48
- );
49
- }
50
-
51
- /**
52
- * Arrête le suivi continu
53
- */
54
- clearWatch() {
55
- if (this.watchId !== null) {
56
- navigator.geolocation.clearWatch(this.watchId);
57
- this.watchId = null;
58
- }
59
- }
60
- }
61
-
62
- export default GeoLocationService;
package/utils/I18n.js DELETED
@@ -1,207 +0,0 @@
1
- /**
2
- * Système d'internationalisation avec support de la pluralisation et du formatage
3
- * @class
4
- * @example
5
- * const i18n = new I18n('fr');
6
- * i18n.addTranslations('fr', {
7
- * greeting: 'Bonjour {{name}}',
8
- * items: { one: '{{count}} item', other: '{{count}} items' }
9
- * });
10
- * console.log(i18n.t('greeting', { name: 'John' }));
11
- * console.log(i18n.plural('items', 5, { count: 5 }));
12
- */
13
- class I18n {
14
- /**
15
- * @constructs I18n
16
- * @param {string} [defaultLocale='en'] - Langue par défaut
17
- */
18
- constructor(defaultLocale = 'en') {
19
- /** @type {string} */
20
- this.locale = defaultLocale;
21
- /** @type {string} */
22
- this.fallbackLocale = 'en';
23
- /** @type {Object} */
24
- this.translations = {};
25
- /** @type {Function[]} */
26
- this.listeners = [];
27
- }
28
-
29
- /**
30
- * Définir la langue courante
31
- * @param {string} locale - Code de la langue (ex: 'fr', 'en')
32
- * @returns {boolean} True si la langue a été définie
33
- */
34
- setLocale(locale) {
35
- if (this.translations[locale]) {
36
- this.locale = locale;
37
- this.notifyListeners();
38
- return true;
39
- }
40
- console.warn(`Locale ${locale} not found`);
41
- return false;
42
- }
43
-
44
- /**
45
- * Obtenir la langue courante
46
- * @returns {string} Code de la langue courante
47
- */
48
- getLocale() {
49
- return this.locale;
50
- }
51
-
52
- /**
53
- * Ajouter des traductions pour une langue
54
- * @param {string} locale - Code de la langue
55
- * @param {Object} translations - Objet de traductions
56
- */
57
- addTranslations(locale, translations) {
58
- if (!this.translations[locale]) {
59
- this.translations[locale] = {};
60
- }
61
-
62
- // Merge avec les traductions existantes
63
- this.translations[locale] = {
64
- ...this.translations[locale],
65
- ...translations
66
- };
67
- }
68
-
69
- /**
70
- * Traduire une clé
71
- * @param {string} key - Clé de traduction
72
- * @param {Object} [params={}] - Paramètres à interpoler
73
- * @param {string} [locale=null] - Langue spécifique (null pour la langue courante)
74
- * @returns {string} Texte traduit
75
- */
76
- t(key, params = {}, locale = null) {
77
- const currentLocale = locale || this.locale;
78
-
79
- // Chercher la traduction
80
- let translation = this.getNestedValue(this.translations[currentLocale], key);
81
-
82
- // Fallback sur la langue par défaut
83
- if (!translation && currentLocale !== this.fallbackLocale) {
84
- translation = this.getNestedValue(this.translations[this.fallbackLocale], key);
85
- }
86
-
87
- // Si toujours pas trouvé, retourner la clé
88
- if (!translation) {
89
- console.warn(`Translation not found for key: ${key}`);
90
- return key;
91
- }
92
-
93
- // Remplacer les paramètres
94
- return this.interpolate(translation, params);
95
- }
96
-
97
- /**
98
- * Obtenir une valeur imbriquée dans un objet (ex: "user.profile.name")
99
- * @param {Object} obj - Objet source
100
- * @param {string} path - Chemin de la propriété
101
- * @returns {*} Valeur trouvée ou undefined
102
- * @private
103
- */
104
- getNestedValue(obj, path) {
105
- return path.split('.').reduce((current, key) => {
106
- return current ? current[key] : undefined;
107
- }, obj);
108
- }
109
-
110
- /**
111
- * Interpoler les paramètres dans une chaîne de traduction
112
- * @param {string} template - Template avec paramètres
113
- * @param {Object} params - Paramètres à remplacer
114
- * @returns {string} Chaîne interpolée
115
- * @private
116
- */
117
- interpolate(template, params) {
118
- return template.replace(/\{\{(\w+)\}\}/g, (match, key) => {
119
- return params.hasOwnProperty(key) ? params[key] : match;
120
- });
121
- }
122
-
123
- /**
124
- * Gérer la pluralisation
125
- * @param {string} key - Clé de traduction (base)
126
- * @param {number} count - Nombre pour déterminer la pluralité
127
- * @param {Object} [params={}] - Paramètres additionnels
128
- * @returns {string} Texte traduit avec pluralisation
129
- */
130
- plural(key, count, params = {}) {
131
- const pluralKey = count === 1 ? `${key}.one` : `${key}.other`;
132
- return this.t(pluralKey, { ...params, count });
133
- }
134
-
135
- /**
136
- * Formater une date selon la locale
137
- * @param {Date} date - Date à formater
138
- * @param {string} [format='long'] - Format de date ('short', 'long', 'full')
139
- * @returns {string} Date formatée
140
- */
141
- formatDate(date, format = 'long') {
142
- const options = {
143
- short: { year: 'numeric', month: '2-digit', day: '2-digit' },
144
- long: { year: 'numeric', month: 'long', day: 'numeric' },
145
- full: { weekday: 'long', year: 'numeric', month: 'long', day: 'numeric' }
146
- };
147
-
148
- return new Intl.DateTimeFormat(this.locale, options[format] || options.long)
149
- .format(date);
150
- }
151
-
152
- /**
153
- * Formater un nombre selon la locale
154
- * @param {number} number - Nombre à formater
155
- * @param {Object} [options={}] - Options de formatage Intl.NumberFormat
156
- * @returns {string} Nombre formaté
157
- */
158
- formatNumber(number, options = {}) {
159
- return new Intl.NumberFormat(this.locale, options).format(number);
160
- }
161
-
162
- /**
163
- * Formater une devise selon la locale
164
- * @param {number} amount - Montant à formater
165
- * @param {string} [currency='USD'] - Code de la devise
166
- * @returns {string} Montant formaté avec devise
167
- */
168
- formatCurrency(amount, currency = 'USD') {
169
- return new Intl.NumberFormat(this.locale, {
170
- style: 'currency',
171
- currency: currency
172
- }).format(amount);
173
- }
174
-
175
- /**
176
- * S'abonner aux changements de langue
177
- * @param {Function} callback - Fonction appelée quand la langue change
178
- * @returns {Function} Fonction de désabonnement
179
- */
180
- subscribe(callback) {
181
- this.listeners.push(callback);
182
- return () => {
183
- const index = this.listeners.indexOf(callback);
184
- if (index > -1) {
185
- this.listeners.splice(index, 1);
186
- }
187
- };
188
- }
189
-
190
- /**
191
- * Notifier les observateurs du changement de langue
192
- * @private
193
- */
194
- notifyListeners() {
195
- this.listeners.forEach(callback => callback(this.locale));
196
- }
197
-
198
- /**
199
- * Obtenir les langues disponibles
200
- * @returns {string[]} Liste des codes de langues disponibles
201
- */
202
- getAvailableLocales() {
203
- return Object.keys(this.translations);
204
- }
205
- }
206
-
207
- export default I18n;