adminator-admin-dashboard 2.7.0 → 2.7.1

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,313 @@
1
+ /**
2
+ * Theme Management Utilities
3
+ * Handles light/dark mode switching with Chart.js integration
4
+ */
5
+
6
+ import type { Theme, ThemeConfig, ThemeColors, ThemeChangeEvent } from '../../../types';
7
+
8
+ declare global {
9
+ interface Window {
10
+ Chart?: any; // Chart.js global object
11
+ }
12
+ }
13
+
14
+ interface VectorMapColors {
15
+ backgroundColor: string;
16
+ borderColor: string;
17
+ regionColor: string;
18
+ markerFill: string;
19
+ markerStroke: string;
20
+ hoverColor: string;
21
+ selectedColor: string;
22
+ scaleStart: string;
23
+ scaleEnd: string;
24
+ scaleLight: string;
25
+ scaleDark: string;
26
+ }
27
+
28
+ interface SparklineColors {
29
+ success: string;
30
+ purple: string;
31
+ info: string;
32
+ danger: string;
33
+ light: string;
34
+ }
35
+
36
+ interface ChartThemeColors {
37
+ textColor: string;
38
+ mutedColor: string;
39
+ borderColor: string;
40
+ gridColor: string;
41
+ tooltipBg: string;
42
+ }
43
+
44
+ const THEME_KEY = 'adminator-theme';
45
+
46
+ /**
47
+ * Theme Management Class
48
+ */
49
+ class ThemeManager {
50
+ private currentTheme: Theme = 'light';
51
+ private config: ThemeConfig;
52
+
53
+ constructor(config?: Partial<ThemeConfig>) {
54
+ this.config = {
55
+ theme: 'light',
56
+ autoDetect: true,
57
+ persistChoice: true,
58
+ ...config,
59
+ };
60
+ }
61
+
62
+ /**
63
+ * Apply theme to the application
64
+ */
65
+ apply(theme: Theme): void {
66
+ const previousTheme = this.currentTheme;
67
+ this.currentTheme = theme;
68
+
69
+ // Set theme attribute on document element
70
+ document.documentElement.setAttribute('data-theme', theme);
71
+
72
+ // Update Chart.js defaults if Chart is available
73
+ this.updateChartDefaults(theme);
74
+
75
+ // Persist theme choice if enabled
76
+ if (this.config.persistChoice) {
77
+ this.persistTheme(theme);
78
+ }
79
+
80
+ // Dispatch theme change event
81
+ this.dispatchThemeChange(theme, previousTheme);
82
+ }
83
+
84
+ /**
85
+ * Toggle between light and dark themes
86
+ */
87
+ toggle(): void {
88
+ const nextTheme: Theme = this.currentTheme === 'dark' ? 'light' : 'dark';
89
+ this.apply(nextTheme);
90
+ }
91
+
92
+ /**
93
+ * Get current theme
94
+ */
95
+ current(): Theme {
96
+ return this.currentTheme;
97
+ }
98
+
99
+ /**
100
+ * Initialize theme system
101
+ */
102
+ init(): void {
103
+ let initialTheme: Theme = 'light';
104
+
105
+ // Try to load persisted theme
106
+ if (this.config.persistChoice) {
107
+ const persistedTheme = this.getPersistedTheme();
108
+ if (persistedTheme) {
109
+ initialTheme = persistedTheme;
110
+ } else if (this.config.autoDetect) {
111
+ // Detect OS preference on first visit
112
+ initialTheme = this.detectOSPreference();
113
+ }
114
+ }
115
+
116
+ this.apply(initialTheme);
117
+ }
118
+
119
+ /**
120
+ * Get CSS custom property value
121
+ */
122
+ getCSSVar(varName: string): string {
123
+ return getComputedStyle(document.documentElement)
124
+ .getPropertyValue(varName)
125
+ .trim();
126
+ }
127
+
128
+ /**
129
+ * Get vector map theme colors
130
+ */
131
+ getVectorMapColors(): VectorMapColors {
132
+ return {
133
+ backgroundColor: this.getCSSVar('--vmap-bg-color'),
134
+ borderColor: this.getCSSVar('--vmap-border-color'),
135
+ regionColor: this.getCSSVar('--vmap-region-color'),
136
+ markerFill: this.getCSSVar('--vmap-marker-fill'),
137
+ markerStroke: this.getCSSVar('--vmap-marker-stroke'),
138
+ hoverColor: this.getCSSVar('--vmap-hover-color'),
139
+ selectedColor: this.getCSSVar('--vmap-selected-color'),
140
+ scaleStart: this.getCSSVar('--vmap-scale-start'),
141
+ scaleEnd: this.getCSSVar('--vmap-scale-end'),
142
+ scaleLight: this.getCSSVar('--vmap-scale-light'),
143
+ scaleDark: this.getCSSVar('--vmap-scale-dark'),
144
+ };
145
+ }
146
+
147
+ /**
148
+ * Get sparkline theme colors
149
+ */
150
+ getSparklineColors(): SparklineColors {
151
+ return {
152
+ success: this.getCSSVar('--sparkline-success'),
153
+ purple: this.getCSSVar('--sparkline-purple'),
154
+ info: this.getCSSVar('--sparkline-info'),
155
+ danger: this.getCSSVar('--sparkline-danger'),
156
+ light: this.getCSSVar('--sparkline-light'),
157
+ };
158
+ }
159
+
160
+ /**
161
+ * Get chart theme colors
162
+ */
163
+ getChartColors(): ChartThemeColors {
164
+ const isDark = this.currentTheme === 'dark';
165
+ return {
166
+ textColor: isDark ? '#FFFFFF' : '#212529',
167
+ mutedColor: isDark ? '#D1D5DB' : '#6C757D',
168
+ borderColor: isDark ? '#374151' : '#E2E5E8',
169
+ gridColor: isDark ? 'rgba(209, 213, 219, 0.15)' : 'rgba(0, 0, 0, 0.05)',
170
+ tooltipBg: isDark ? '#1F2937' : 'rgba(255, 255, 255, 0.95)',
171
+ };
172
+ }
173
+
174
+ /**
175
+ * Update configuration
176
+ */
177
+ updateConfig(config: Partial<ThemeConfig>): void {
178
+ this.config = { ...this.config, ...config };
179
+ }
180
+
181
+ /**
182
+ * Get current configuration
183
+ */
184
+ getConfig(): ThemeConfig {
185
+ return { ...this.config };
186
+ }
187
+
188
+ /**
189
+ * Private method: Update Chart.js defaults
190
+ */
191
+ private updateChartDefaults(theme: Theme): void {
192
+ if (!window.Chart || !window.Chart.defaults) {
193
+ return;
194
+ }
195
+
196
+ const isDark = theme === 'dark';
197
+ const colors = this.getChartColors();
198
+
199
+ try {
200
+ // Set global defaults
201
+ window.Chart.defaults.color = colors.textColor;
202
+ window.Chart.defaults.borderColor = colors.borderColor;
203
+ window.Chart.defaults.backgroundColor = colors.tooltipBg;
204
+
205
+ // Set plugin defaults
206
+ if (window.Chart.defaults.plugins?.legend?.labels) {
207
+ window.Chart.defaults.plugins.legend.labels.color = colors.textColor;
208
+ }
209
+
210
+ if (window.Chart.defaults.plugins?.tooltip) {
211
+ window.Chart.defaults.plugins.tooltip.backgroundColor = colors.tooltipBg;
212
+ window.Chart.defaults.plugins.tooltip.titleColor = colors.textColor;
213
+ window.Chart.defaults.plugins.tooltip.bodyColor = colors.textColor;
214
+ window.Chart.defaults.plugins.tooltip.borderColor = colors.borderColor;
215
+ }
216
+
217
+ // Set scale defaults
218
+ const scaleDefaults = window.Chart.defaults.scales;
219
+ if (scaleDefaults) {
220
+ Object.keys(scaleDefaults).forEach(scaleType => {
221
+ const scale = scaleDefaults[scaleType];
222
+ if (scale?.ticks) {
223
+ scale.ticks.color = colors.mutedColor;
224
+ }
225
+ if (scale?.grid) {
226
+ scale.grid.color = colors.gridColor;
227
+ }
228
+ if (scale?.pointLabels) {
229
+ scale.pointLabels.color = colors.mutedColor;
230
+ }
231
+ if (scale?.angleLines) {
232
+ scale.angleLines.color = colors.gridColor;
233
+ }
234
+ });
235
+ }
236
+ } catch (error) {
237
+ console.warn('Error updating Chart.js defaults:', error);
238
+ }
239
+ }
240
+
241
+ /**
242
+ * Private method: Persist theme to localStorage
243
+ */
244
+ private persistTheme(theme: Theme): void {
245
+ try {
246
+ localStorage.setItem(THEME_KEY, theme);
247
+ } catch (error) {
248
+ console.warn('Unable to persist theme:', error);
249
+ }
250
+ }
251
+
252
+ /**
253
+ * Private method: Get persisted theme from localStorage
254
+ */
255
+ private getPersistedTheme(): Theme | null {
256
+ try {
257
+ const theme = localStorage.getItem(THEME_KEY) as Theme;
258
+ return ['light', 'dark'].includes(theme) ? theme : null;
259
+ } catch (error) {
260
+ console.warn('Unable to get persisted theme:', error);
261
+ return null;
262
+ }
263
+ }
264
+
265
+ /**
266
+ * Private method: Detect OS color scheme preference
267
+ */
268
+ private detectOSPreference(): Theme {
269
+ if (window.matchMedia && window.matchMedia('(prefers-color-scheme: dark)').matches) {
270
+ return 'dark';
271
+ }
272
+ return 'light';
273
+ }
274
+
275
+ /**
276
+ * Private method: Dispatch theme change event
277
+ */
278
+ private dispatchThemeChange(theme: Theme, previousTheme: Theme): void {
279
+ const event: ThemeChangeEvent = new CustomEvent('adminator:themeChanged', {
280
+ detail: { theme, previousTheme },
281
+ }) as ThemeChangeEvent;
282
+
283
+ window.dispatchEvent(event);
284
+ }
285
+ }
286
+
287
+ // Create singleton instance
288
+ const themeManager = new ThemeManager();
289
+
290
+ // Export legacy object interface for compatibility
291
+ export const Theme = {
292
+ apply: (theme: Theme) => themeManager.apply(theme),
293
+ toggle: () => themeManager.toggle(),
294
+ current: () => themeManager.current(),
295
+ init: () => themeManager.init(),
296
+ getCSSVar: (varName: string) => themeManager.getCSSVar(varName),
297
+ getVectorMapColors: () => themeManager.getVectorMapColors(),
298
+ getSparklineColors: () => themeManager.getSparklineColors(),
299
+ getChartColors: () => themeManager.getChartColors(),
300
+ };
301
+
302
+ // Export both the manager instance and legacy interface
303
+ export { themeManager as ThemeManager };
304
+ export default Theme;
305
+
306
+ // Export types for external use
307
+ export type {
308
+ Theme as ThemeType,
309
+ ThemeConfig,
310
+ VectorMapColors,
311
+ SparklineColors,
312
+ ChartThemeColors,
313
+ };