canvasframework 0.4.6 → 0.4.8

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.
@@ -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;
package/index.js CHANGED
@@ -93,7 +93,6 @@ export { default as FirebaseRealtimeDB } from './utils/FirebaseRealtimeDB.js';
93
93
  export { default as PayPalPayment } from './utils/PayPalPayment.js';
94
94
  export { default as StripePayment } from './utils/StripePayment.js';
95
95
 
96
-
97
96
  // Features
98
97
  export { default as PullToRefresh } from './features/PullToRefresh.js';
99
98
  export { default as Skeleton } from './features/Skeleton.js';
@@ -116,19 +115,4 @@ export { default as FeatureFlags } from './manager/FeatureFlags.js';
116
115
 
117
116
  // Version du framework
118
117
 
119
- export const VERSION = '0.4.4';
120
-
121
-
122
-
123
-
124
-
125
-
126
-
127
-
128
-
129
-
130
-
131
-
132
-
133
-
134
-
118
+ export const VERSION = '0.4.7';
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "canvasframework",
3
- "version": "0.4.6",
3
+ "version": "0.4.8",
4
4
  "description": "Canvas-based cross-platform UI framework (Material & Cupertino)",
5
5
  "type": "module",
6
6
  "main": "./index.js",