canvasframework 0.5.18 → 0.5.20

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 (113) hide show
  1. package/README.md +30 -0
  2. package/components/Accordion.js +265 -0
  3. package/components/AndroidDatePickerDialog.js +406 -0
  4. package/components/AppBar.js +398 -0
  5. package/components/AudioPlayer.js +611 -0
  6. package/components/Avatar.js +202 -0
  7. package/components/Banner.js +342 -0
  8. package/components/BottomNavigationBar.js +433 -0
  9. package/components/BottomSheet.js +234 -0
  10. package/components/Button.js +358 -0
  11. package/components/Camera.js +644 -0
  12. package/components/Card.js +193 -0
  13. package/components/Chart.js +700 -0
  14. package/components/Checkbox.js +166 -0
  15. package/components/Chip.js +212 -0
  16. package/components/CircularProgress.js +327 -0
  17. package/components/ContextMenu.js +116 -0
  18. package/components/DatePicker.js +298 -0
  19. package/components/Dialog.js +337 -0
  20. package/components/Divider.js +125 -0
  21. package/components/Drawer.js +276 -0
  22. package/components/FAB.js +270 -0
  23. package/components/FileUpload.js +315 -0
  24. package/components/FloatedCamera.js +644 -0
  25. package/components/IOSDatePickerWheel.js +430 -0
  26. package/components/ImageCarousel.js +219 -0
  27. package/components/ImageComponent.js +223 -0
  28. package/components/Input.js +831 -0
  29. package/components/InputDatalist.js +723 -0
  30. package/components/InputTags.js +624 -0
  31. package/components/List.js +95 -0
  32. package/components/ListItem.js +269 -0
  33. package/components/Modal.js +364 -0
  34. package/components/MorphingFAB.js +428 -0
  35. package/components/MultiSelectDialog.js +206 -0
  36. package/components/NumberInput.js +271 -0
  37. package/components/PasswordInput.js +462 -0
  38. package/components/ProgressBar.js +88 -0
  39. package/components/QRCodeReader.js +539 -0
  40. package/components/RadioButton.js +151 -0
  41. package/components/SearchInput.js +315 -0
  42. package/components/SegmentedControl.js +357 -0
  43. package/components/Select.js +199 -0
  44. package/components/SelectDialog.js +255 -0
  45. package/components/Slider.js +113 -0
  46. package/components/SliverAppBar.js +139 -0
  47. package/components/Snackbar.js +243 -0
  48. package/components/SpeedDialFAB.js +397 -0
  49. package/components/Stepper.js +281 -0
  50. package/components/SwipeableListItem.js +327 -0
  51. package/components/Switch.js +147 -0
  52. package/components/Table.js +492 -0
  53. package/components/Tabs.js +423 -0
  54. package/components/Text.js +141 -0
  55. package/components/TextField.js +151 -0
  56. package/components/TimePicker.js +934 -0
  57. package/components/Toast.js +236 -0
  58. package/components/TreeView.js +420 -0
  59. package/components/Video.js +397 -0
  60. package/components/View.js +140 -0
  61. package/components/VirtualList.js +120 -0
  62. package/core/CanvasFramework.js +3045 -0
  63. package/core/Component.js +243 -0
  64. package/core/ThemeManager.js +358 -0
  65. package/core/UIBuilder.js +267 -0
  66. package/core/WebGLCanvasAdapter.js +782 -0
  67. package/features/Column.js +43 -0
  68. package/features/Grid.js +47 -0
  69. package/features/LayoutComponent.js +43 -0
  70. package/features/OpenStreetMap.js +310 -0
  71. package/features/Positioned.js +33 -0
  72. package/features/PullToRefresh.js +328 -0
  73. package/features/Row.js +40 -0
  74. package/features/SignaturePad.js +257 -0
  75. package/features/Skeleton.js +193 -0
  76. package/features/Stack.js +21 -0
  77. package/index.js +119 -0
  78. package/manager/AccessibilityManager.js +107 -0
  79. package/manager/ErrorHandler.js +59 -0
  80. package/manager/FeatureFlags.js +60 -0
  81. package/manager/MemoryManager.js +107 -0
  82. package/manager/PerformanceMonitor.js +84 -0
  83. package/manager/SecurityManager.js +54 -0
  84. package/package.json +22 -16
  85. package/utils/AnimationEngine.js +734 -0
  86. package/utils/CryptoManager.js +303 -0
  87. package/utils/DataStore.js +403 -0
  88. package/utils/DevTools.js +1618 -0
  89. package/utils/DevToolsConsole.js +201 -0
  90. package/utils/EventBus.js +407 -0
  91. package/utils/FetchClient.js +74 -0
  92. package/utils/FirebaseAuth.js +653 -0
  93. package/utils/FirebaseCore.js +246 -0
  94. package/utils/FirebaseFirestore.js +581 -0
  95. package/utils/FirebaseFunctions.js +97 -0
  96. package/utils/FirebaseRealtimeDB.js +498 -0
  97. package/utils/FirebaseStorage.js +612 -0
  98. package/utils/FormValidator.js +355 -0
  99. package/utils/GeoLocationService.js +62 -0
  100. package/utils/I18n.js +207 -0
  101. package/utils/IndexedDBManager.js +273 -0
  102. package/utils/InspectionOverlay.js +308 -0
  103. package/utils/NotificationManager.js +60 -0
  104. package/utils/OfflineSyncManager.js +342 -0
  105. package/utils/PayPalPayment.js +678 -0
  106. package/utils/QueryBuilder.js +478 -0
  107. package/utils/SafeArea.js +64 -0
  108. package/utils/SecureStorage.js +289 -0
  109. package/utils/StateManager.js +207 -0
  110. package/utils/StripePayment.js +552 -0
  111. package/utils/WebSocketClient.js +66 -0
  112. package/dist/canvasframework.js +0 -2
  113. package/dist/canvasframework.js.LICENSE.txt +0 -1
@@ -0,0 +1,243 @@
1
+ /**
2
+ * Classe de base pour tous les composants
3
+ * @class
4
+ * @abstract
5
+ * @property {CanvasFramework} framework - Référence au framework parent
6
+ * @property {number} x - Position X
7
+ * @property {number} y - Position Y
8
+ * @property {number} width - Largeur
9
+ * @property {number} height - Hauteur
10
+ * @property {boolean} visible - Visibilité du composant
11
+ * @property {boolean} pressed - État pressé
12
+ * @property {boolean} hovered - État survolé
13
+ * @property {Function} onClick - Callback au clic
14
+ * @property {Function} onPress - Callback à la pression
15
+ */
16
+ class Component {
17
+ /**
18
+ * Crée une instance de Component
19
+ * @param {CanvasFramework} framework - Framework parent
20
+ * @param {Object} [options={}] - Options de configuration
21
+ * @param {number} [options.x=0] - Position X
22
+ * @param {number} [options.y=0] - Position Y
23
+ * @param {number} [options.width=100] - Largeur
24
+ * @param {number} [options.height=50] - Hauteur
25
+ * @param {boolean} [options.visible=true] - Visibilité
26
+ * @param {Function} [options.onClick] - Callback au clic
27
+ * @param {Function} [options.onPress] - Callback à la pression
28
+ */
29
+ constructor(framework, options = {}) {
30
+ this.framework = framework;
31
+ this.x = options.x || 0;
32
+ this.y = options.y || 0;
33
+ this.width = options.width || 100;
34
+ this.height = options.height || 50;
35
+ this.visible = options.visible !== false;
36
+ this.pressed = false;
37
+ this.hovered = false;
38
+ this.onClick = options.onClick;
39
+ this.onPress = options.onPress;
40
+
41
+ // Système dirty simple (optionnel)
42
+ this._dirty = true;
43
+
44
+ // Lifecycle
45
+ this._mounted = false;
46
+
47
+ // Pour détecter les updates
48
+ this._prevProps = { ...options };
49
+
50
+ // Système de listeners
51
+ this._listeners = new Map();
52
+ }
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
+ }
141
+ /* =======================
142
+ LIFECYCLE HOOKS
143
+ ======================= */
144
+
145
+ onMount() {}
146
+ onUnmount() {}
147
+ onUpdate(prevProps) {}
148
+ onResize(width, height) {}
149
+
150
+ /* ======================= */
151
+
152
+ _mount() {
153
+ if (!this._mounted) {
154
+ this._mounted = true;
155
+ this.onMount();
156
+ }
157
+ }
158
+
159
+ _unmount() {
160
+ if (this._mounted) {
161
+ this.onUnmount();
162
+ this._mounted = false;
163
+ }
164
+ }
165
+
166
+ _update(newProps) {
167
+ this.onUpdate(this._prevProps);
168
+ this._prevProps = { ...newProps };
169
+ this.markDirty();
170
+ }
171
+
172
+ _resize(width, height) {
173
+ this.onResize(width, height);
174
+ this.markDirty();
175
+ }
176
+
177
+ setProps(newProps = {}) {
178
+ const changed = Object.keys(newProps).some(
179
+ key => this[key] !== newProps[key]
180
+ );
181
+
182
+ if (!changed) return;
183
+
184
+ Object.assign(this, newProps);
185
+ this._update(newProps);
186
+ }
187
+
188
+ measure(constraints) {
189
+ return {
190
+ width: this.width,
191
+ height: this.height
192
+ };
193
+ }
194
+
195
+ /**
196
+ * Marque le composant pour redessin
197
+ * Appelez cette méthode après avoir modifié une propriété
198
+ */
199
+ markDirty() {
200
+ this._dirty = true;
201
+ if (this.framework && this.framework.markComponentDirty) {
202
+ this.framework.markComponentDirty(this);
203
+ }
204
+ }
205
+
206
+ /**
207
+ * Marque le composant comme propre (appelé automatiquement après draw)
208
+ */
209
+ markClean() {
210
+ this._dirty = false;
211
+ }
212
+
213
+ /**
214
+ * Vérifie si le composant est dirty
215
+ */
216
+ isDirty() {
217
+ return this._dirty;
218
+ }
219
+
220
+ /**
221
+ * Vérifie si un point est dans les limites du composant
222
+ */
223
+ isPointInside(x, y) {
224
+ return x >= this.x && x <= this.x + this.width &&
225
+ y >= this.y && y <= this.y + this.height;
226
+ }
227
+
228
+ /**
229
+ * Méthode de dessin (à implémenter par les sous-classes)
230
+ */
231
+ draw(ctx) {
232
+ // À implémenter par les sous-classes
233
+ }
234
+ }
235
+
236
+
237
+ export default Component;
238
+
239
+
240
+
241
+
242
+
243
+
@@ -0,0 +1,358 @@
1
+ /**
2
+ * Gestionnaire de thèmes indépendant et fiable
3
+ * Gère automatiquement le light/dark mode avec persistance
4
+ */
5
+ class ThemeManager {
6
+ constructor(framework, options = {}) {
7
+ this.framework = framework;
8
+
9
+ // Thèmes par défaut (peuvent être overridés)
10
+ this.themes = {
11
+ light: options.lightTheme || {
12
+ // Couleurs de base
13
+ background: '#FFFFFF',
14
+ surface: '#F5F5F5',
15
+ surfaceVariant: '#E7E0EC',
16
+
17
+ // Texte
18
+ text: '#1C1B1F',
19
+ textSecondary: '#49454F',
20
+ textDisabled: '#79747E',
21
+
22
+ // Primaire
23
+ primary: '#6750A4',
24
+ onPrimary: '#FFFFFF',
25
+ primaryContainer: '#EADDFF',
26
+ onPrimaryContainer: '#21005D',
27
+
28
+ // Secondaire
29
+ secondary: '#625B71',
30
+ onSecondary: '#FFFFFF',
31
+ secondaryContainer: '#E8DEF8',
32
+ onSecondaryContainer: '#1D192B',
33
+
34
+ // Tertiaire
35
+ tertiary: '#7D5260',
36
+ onTertiary: '#FFFFFF',
37
+ tertiaryContainer: '#FFD8E4',
38
+ onTertiaryContainer: '#31111D',
39
+
40
+ // Erreur
41
+ error: '#B3261E',
42
+ onError: '#FFFFFF',
43
+ errorContainer: '#F9DEDC',
44
+ onErrorContainer: '#410E0B',
45
+
46
+ // Bordures et dividers
47
+ border: '#CAC4D0',
48
+ divider: '#E0E0E0',
49
+ outline: '#79747E',
50
+ outlineVariant: '#CAC4D0',
51
+
52
+ // États
53
+ hover: 'rgba(103, 80, 164, 0.08)',
54
+ pressed: 'rgba(103, 80, 164, 0.12)',
55
+ focus: 'rgba(103, 80, 164, 0.12)',
56
+ disabled: 'rgba(28, 27, 31, 0.12)',
57
+
58
+ // Ombres
59
+ shadow: 'rgba(0, 0, 0, 0.2)',
60
+ elevation1: 'rgba(0, 0, 0, 0.05)',
61
+ elevation2: 'rgba(0, 0, 0, 0.08)',
62
+ elevation3: 'rgba(0, 0, 0, 0.12)',
63
+ },
64
+
65
+ dark: options.darkTheme || {
66
+ // Couleurs de base
67
+ background: '#1C1B1F',
68
+ surface: '#2B2930',
69
+ surfaceVariant: '#49454F',
70
+
71
+ // Texte
72
+ text: '#E6E1E5',
73
+ textSecondary: '#CAC4D0',
74
+ textDisabled: '#938F99',
75
+
76
+ // Primaire
77
+ primary: '#D0BCFF',
78
+ onPrimary: '#381E72',
79
+ primaryContainer: '#4F378B',
80
+ onPrimaryContainer: '#EADDFF',
81
+
82
+ // Secondaire
83
+ secondary: '#CCC2DC',
84
+ onSecondary: '#332D41',
85
+ secondaryContainer: '#4A4458',
86
+ onSecondaryContainer: '#E8DEF8',
87
+
88
+ // Tertiaire
89
+ tertiary: '#EFB8C8',
90
+ onTertiary: '#492532',
91
+ tertiaryContainer: '#633B48',
92
+ onTertiaryContainer: '#FFD8E4',
93
+
94
+ // Erreur
95
+ error: '#F2B8B5',
96
+ onError: '#601410',
97
+ errorContainer: '#8C1D18',
98
+ onErrorContainer: '#F9DEDC',
99
+
100
+ // Bordures et dividers
101
+ border: '#938F99',
102
+ divider: '#3D3D3D',
103
+ outline: '#938F99',
104
+ outlineVariant: '#49454F',
105
+
106
+ // États
107
+ hover: 'rgba(208, 188, 255, 0.08)',
108
+ pressed: 'rgba(208, 188, 255, 0.12)',
109
+ focus: 'rgba(208, 188, 255, 0.12)',
110
+ disabled: 'rgba(230, 225, 229, 0.12)',
111
+
112
+ // Ombres
113
+ shadow: 'rgba(0, 0, 0, 0.8)',
114
+ elevation1: 'rgba(0, 0, 0, 0.3)',
115
+ elevation2: 'rgba(0, 0, 0, 0.4)',
116
+ elevation3: 'rgba(0, 0, 0, 0.5)',
117
+ }
118
+ };
119
+
120
+ // État actuel
121
+ this.currentMode = null; // 'light', 'dark', ou null (system)
122
+ this.currentTheme = null;
123
+
124
+ // Callbacks
125
+ this.listeners = [];
126
+
127
+ // Storage key
128
+ this.storageKey = options.storageKey || 'app-theme-mode';
129
+
130
+ // Initialisation
131
+ this.init();
132
+ }
133
+
134
+ /**
135
+ * Initialise le ThemeManager
136
+ */
137
+ init() {
138
+ // 1. Charger la préférence sauvegardée
139
+ const savedMode = this.loadPreference();
140
+
141
+ // 2. Écouter les changements système
142
+ this.setupSystemListener();
143
+
144
+ // 3. Appliquer le thème
145
+ if (savedMode) {
146
+ this.setMode(savedMode, false); // false = ne pas re-sauvegarder
147
+ } else {
148
+ this.setMode('system', false);
149
+ }
150
+ }
151
+
152
+ /**
153
+ * Charge la préférence depuis le localStorage
154
+ */
155
+ loadPreference() {
156
+ try {
157
+ const saved = localStorage.getItem(this.storageKey);
158
+ if (saved && ['light', 'dark', 'system'].includes(saved)) {
159
+ return saved;
160
+ }
161
+ } catch (e) {
162
+ console.warn('Impossible de charger les préférences de thème:', e);
163
+ }
164
+ return null;
165
+ }
166
+
167
+ /**
168
+ * Sauvegarde la préférence dans le localStorage
169
+ */
170
+ savePreference(mode) {
171
+ try {
172
+ localStorage.setItem(this.storageKey, mode);
173
+ } catch (e) {
174
+ console.warn('Impossible de sauvegarder les préférences de thème:', e);
175
+ }
176
+ }
177
+
178
+ /**
179
+ * Configure l'écoute des changements système
180
+ */
181
+ setupSystemListener() {
182
+ const mediaQuery = window.matchMedia('(prefers-color-scheme: dark)');
183
+
184
+ const handleChange = (e) => {
185
+ // Ne réagir que si on est en mode 'system'
186
+ if (this.currentMode === 'system') {
187
+ this.applySystemTheme();
188
+ }
189
+ };
190
+
191
+ // Méthode moderne
192
+ if (mediaQuery.addEventListener) {
193
+ mediaQuery.addEventListener('change', handleChange);
194
+ } else {
195
+ // Fallback pour anciens navigateurs
196
+ mediaQuery.addListener(handleChange);
197
+ }
198
+
199
+ // Sauvegarder pour cleanup
200
+ this.systemMediaQuery = mediaQuery;
201
+ this.systemChangeHandler = handleChange;
202
+ }
203
+
204
+ /**
205
+ * Applique le thème système
206
+ */
207
+ applySystemTheme() {
208
+ const prefersDark = window.matchMedia('(prefers-color-scheme: dark)').matches;
209
+ const theme = prefersDark ? this.themes.dark : this.themes.light;
210
+ this.applyTheme(theme);
211
+ }
212
+
213
+ /**
214
+ * Définit le mode de thème
215
+ * @param {'light'|'dark'|'system'} mode
216
+ * @param {boolean} save - Sauvegarder la préférence
217
+ */
218
+ setMode(mode, save = true) {
219
+ if (!['light', 'dark', 'system'].includes(mode)) {
220
+ console.warn(`Mode invalide: ${mode}. Utilisez 'light', 'dark' ou 'system'.`);
221
+ return;
222
+ }
223
+
224
+ this.currentMode = mode;
225
+
226
+ // Sauvegarder si demandé
227
+ if (save) {
228
+ this.savePreference(mode);
229
+ }
230
+
231
+ // Appliquer le thème correspondant
232
+ if (mode === 'system') {
233
+ this.applySystemTheme();
234
+ } else {
235
+ const theme = this.themes[mode];
236
+ this.applyTheme(theme);
237
+ }
238
+ }
239
+
240
+ /**
241
+ * Applique un thème au framework
242
+ */
243
+ applyTheme(theme) {
244
+ this.currentTheme = theme;
245
+
246
+ // Mettre à jour le framework
247
+ if (this.framework) {
248
+ this.framework.theme = theme;
249
+
250
+ // Forcer le rendu de tous les composants
251
+ if (this.framework.components) {
252
+ this.framework.components.forEach(comp => {
253
+ if (comp.markDirty) {
254
+ comp.markDirty();
255
+ }
256
+ });
257
+ }
258
+ }
259
+
260
+ // Notifier les listeners
261
+ this.notifyListeners(theme);
262
+ }
263
+
264
+ /**
265
+ * Obtient le mode actuel
266
+ */
267
+ getMode() {
268
+ return this.currentMode;
269
+ }
270
+
271
+ /**
272
+ * Obtient le thème actuel
273
+ */
274
+ getTheme() {
275
+ return this.currentTheme;
276
+ }
277
+
278
+ /**
279
+ * Obtient une couleur spécifique
280
+ */
281
+ getColor(colorName) {
282
+ return this.currentTheme?.[colorName] || '#000000';
283
+ }
284
+
285
+ /**
286
+ * Bascule entre light et dark
287
+ */
288
+ toggle() {
289
+ if (this.currentMode === 'system') {
290
+ // Si en mode system, basculer vers le mode opposé
291
+ const isDark = window.matchMedia('(prefers-color-scheme: dark)').matches;
292
+ this.setMode(isDark ? 'light' : 'dark');
293
+ } else {
294
+ // Basculer entre light et dark
295
+ this.setMode(this.currentMode === 'light' ? 'dark' : 'light');
296
+ }
297
+ }
298
+
299
+ /**
300
+ * Ajoute un listener de changement de thème
301
+ */
302
+ addListener(callback) {
303
+ if (typeof callback === 'function') {
304
+ this.listeners.push(callback);
305
+ // Appeler immédiatement avec le thème actuel
306
+ callback(this.currentTheme);
307
+ }
308
+ }
309
+
310
+ /**
311
+ * Retire un listener
312
+ */
313
+ removeListener(callback) {
314
+ const index = this.listeners.indexOf(callback);
315
+ if (index > -1) {
316
+ this.listeners.splice(index, 1);
317
+ }
318
+ }
319
+
320
+ /**
321
+ * Notifie tous les listeners
322
+ */
323
+ notifyListeners(theme) {
324
+ this.listeners.forEach(callback => {
325
+ try {
326
+ callback(theme);
327
+ } catch (e) {
328
+ console.error('Erreur dans un listener de thème:', e);
329
+ }
330
+ });
331
+ }
332
+
333
+ /**
334
+ * Définit un thème personnalisé
335
+ */
336
+ setCustomTheme(name, theme) {
337
+ this.themes[name] = theme;
338
+ }
339
+
340
+ /**
341
+ * Nettoie les ressources
342
+ */
343
+ destroy() {
344
+ // Retirer l'écoute système
345
+ if (this.systemMediaQuery && this.systemChangeHandler) {
346
+ if (this.systemMediaQuery.removeEventListener) {
347
+ this.systemMediaQuery.removeEventListener('change', this.systemChangeHandler);
348
+ } else {
349
+ this.systemMediaQuery.removeListener(this.systemChangeHandler);
350
+ }
351
+ }
352
+
353
+ // Vider les listeners
354
+ this.listeners = [];
355
+ }
356
+ }
357
+
358
+ export default ThemeManager;