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.
- package/core/CanvasFramework.js +1683 -1394
- package/core/ThemeManager.js +358 -0
- package/index.js +1 -17
- package/package.json +1 -1
|
@@ -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.
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
118
|
+
export const VERSION = '0.4.7';
|