@shohojdhara/atomix 0.3.1 → 0.3.2

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 (90) hide show
  1. package/CHANGELOG.md +0 -1
  2. package/README.md +3 -5
  3. package/dist/atomix.css +458 -552
  4. package/dist/atomix.min.css +3 -3
  5. package/dist/index.d.ts +2435 -358
  6. package/dist/index.esm.js +5758 -1901
  7. package/dist/index.esm.js.map +1 -1
  8. package/dist/index.js +5768 -1933
  9. package/dist/index.js.map +1 -1
  10. package/dist/index.min.js +1 -1
  11. package/dist/index.min.js.map +1 -1
  12. package/package.json +1 -11
  13. package/src/lib/composables/useAtomixGlass.ts +46 -46
  14. package/src/lib/index.ts +1 -4
  15. package/src/lib/theme/config/index.ts +21 -0
  16. package/src/lib/theme/config/loader.ts +276 -0
  17. package/src/lib/theme/config/types.ts +98 -0
  18. package/src/lib/theme/config/validator.ts +326 -0
  19. package/src/lib/theme/constants.ts +183 -0
  20. package/src/lib/theme/core/ThemeCache.ts +283 -0
  21. package/src/lib/theme/core/ThemeEngine.test.ts +146 -0
  22. package/src/lib/theme/core/ThemeEngine.ts +657 -0
  23. package/src/lib/theme/core/ThemeRegistry.ts +284 -0
  24. package/src/lib/theme/core/ThemeValidator.ts +530 -0
  25. package/src/lib/theme/core/index.ts +24 -0
  26. package/src/lib/theme/createTheme.ts +81 -70
  27. package/src/lib/theme/devtools/CLI.ts +279 -0
  28. package/src/lib/theme/devtools/Inspector.tsx +594 -0
  29. package/src/lib/theme/devtools/Preview.tsx +392 -0
  30. package/src/lib/theme/devtools/index.ts +21 -0
  31. package/src/lib/theme/errors.test.ts +207 -0
  32. package/src/lib/theme/errors.ts +233 -0
  33. package/src/lib/theme/generateCSSVariables.ts +93 -9
  34. package/src/lib/theme/generators/CSSGenerator.ts +311 -0
  35. package/src/lib/theme/generators/ConfigGenerator.ts +287 -0
  36. package/src/lib/theme/generators/TypeGenerator.ts +228 -0
  37. package/src/lib/theme/generators/index.ts +21 -0
  38. package/src/lib/theme/i18n/index.ts +9 -0
  39. package/src/lib/theme/i18n/rtl.ts +325 -0
  40. package/src/lib/theme/index.ts +155 -11
  41. package/src/lib/theme/monitoring/ThemeAnalytics.ts +409 -0
  42. package/src/lib/theme/monitoring/index.ts +17 -0
  43. package/src/lib/theme/overrides/ComponentOverrides.ts +243 -0
  44. package/src/lib/theme/overrides/index.ts +15 -0
  45. package/src/lib/theme/runtime/ThemeErrorBoundary.tsx +233 -0
  46. package/src/lib/theme/runtime/ThemeManager.test.ts +176 -0
  47. package/src/lib/theme/runtime/ThemeManager.ts +442 -0
  48. package/src/lib/theme/runtime/ThemeProvider.tsx +318 -0
  49. package/src/lib/theme/runtime/index.ts +17 -0
  50. package/src/lib/theme/runtime/useTheme.ts +52 -0
  51. package/src/lib/theme/studio/ThemeStudio.tsx +312 -0
  52. package/src/lib/theme/studio/index.ts +8 -0
  53. package/src/lib/theme/types.ts +3 -1
  54. package/src/lib/theme/utils.ts +23 -22
  55. package/src/lib/theme/whitelabel/WhiteLabelManager.ts +364 -0
  56. package/src/lib/theme/whitelabel/index.ts +13 -0
  57. package/src/styles/01-settings/_settings.badge.scss +1 -1
  58. package/src/styles/01-settings/_settings.callout.scss +1 -1
  59. package/src/styles/01-settings/_settings.card.scss +1 -1
  60. package/src/styles/01-settings/_settings.input.scss +1 -1
  61. package/src/styles/01-settings/_settings.navbar.scss +1 -1
  62. package/src/styles/01-settings/_settings.upload.scss +1 -1
  63. package/src/styles/06-components/_components.chart.scss +2 -2
  64. package/src/styles/99-utilities/_utilities.border.scss +27 -58
  65. package/src/styles/99-utilities/_utilities.gradient.scss +12 -0
  66. package/src/styles/99-utilities/_utilities.position.scss +8 -15
  67. package/src/styles/99-utilities/_utilities.scss +2 -0
  68. package/src/styles/99-utilities/_utilities.spacing.scss +76 -121
  69. package/src/styles/99-utilities/_utilities.text.scss +30 -49
  70. package/dist/themes/applemix.css +0 -15615
  71. package/dist/themes/applemix.min.css +0 -70
  72. package/dist/themes/boomdevs.css +0 -15193
  73. package/dist/themes/boomdevs.min.css +0 -403
  74. package/dist/themes/esrar.css +0 -17399
  75. package/dist/themes/esrar.min.css +0 -187
  76. package/dist/themes/flashtrade.css +0 -16613
  77. package/dist/themes/flashtrade.min.css +0 -190
  78. package/dist/themes/mashroom.css +0 -30104
  79. package/dist/themes/mashroom.min.css +0 -401
  80. package/dist/themes/shaj-default.css +0 -16228
  81. package/dist/themes/shaj-default.min.css +0 -498
  82. package/src/lib/theme/ThemeManager.integration.test.ts +0 -124
  83. package/src/lib/theme/ThemeManager.stories.tsx +0 -472
  84. package/src/lib/theme/ThemeManager.test.ts +0 -190
  85. package/src/lib/theme/ThemeManager.ts +0 -645
  86. package/src/lib/theme/ThemeProvider.tsx +0 -377
  87. package/src/lib/theme/createTheme.test.ts +0 -475
  88. package/src/lib/theme/useTheme.test.tsx +0 -67
  89. package/src/lib/theme/useTheme.ts +0 -64
  90. package/src/lib/theme/utils.test.ts +0 -140
@@ -1,645 +0,0 @@
1
- /**
2
- * Theme Manager
3
- *
4
- * Core theme management class for the Atomix Design System.
5
- * Handles theme loading, switching, persistence, and events.
6
- * Supports both CSS-based themes and JavaScript-based themes.
7
- */
8
-
9
- import type {
10
- ThemeManagerConfig,
11
- ThemeMetadata,
12
- ThemeChangeEvent,
13
- ThemeLoadOptions,
14
- ThemeEventListeners,
15
- ThemeChangeCallback,
16
- ThemeLoadCallback,
17
- ThemeErrorCallback,
18
- StorageAdapter,
19
- Theme,
20
- } from './types';
21
-
22
- import {
23
- isBrowser,
24
- isServer,
25
- loadThemeCSS,
26
- removeThemeCSS,
27
- removeAllThemeCSS,
28
- applyThemeAttributes,
29
- getCurrentThemeFromDOM,
30
- isThemeLoaded as checkThemeLoaded,
31
- validateThemeMetadata,
32
- isValidThemeName,
33
- createLocalStorageAdapter,
34
- } from './utils';
35
-
36
- import { isJSTheme } from './themeUtils';
37
- import { generateCSSVariables, injectCSS, removeInjectedCSS } from './generateCSSVariables';
38
-
39
- /**
40
- * Default configuration values
41
- */
42
- const DEFAULT_CONFIG: Partial<ThemeManagerConfig> = {
43
- basePath: '/themes',
44
- cdnPath: null,
45
- lazy: true,
46
- storageKey: 'atomix-theme',
47
- dataAttribute: 'data-theme',
48
- enablePersistence: true,
49
- useMinified: false,
50
- preload: [],
51
- };
52
-
53
- // ID for injected JS theme styles
54
- const JS_THEME_STYLE_ID = 'atomix-js-theme-styles';
55
-
56
- /**
57
- * ThemeManager class
58
- *
59
- * Manages theme loading, switching, and persistence for Atomix Design System.
60
- *
61
- * @example
62
- * ```typescript
63
- * const themeManager = new ThemeManager({
64
- * themes: themesConfig.metadata,
65
- * defaultTheme: 'shaj-default',
66
- * });
67
- *
68
- * await themeManager.setTheme('flashtrade');
69
- * ```
70
- */
71
- export class ThemeManager {
72
- private config: Required<Omit<ThemeManagerConfig, 'onThemeChange' | 'onError' | 'cdnPath' | 'defaultTheme'>> & {
73
- defaultTheme?: string | Theme;
74
- cdnPath: string | null;
75
- onThemeChange?: (theme: string | Theme) => void;
76
- onError?: (error: Error, themeName: string) => void;
77
- };
78
-
79
- private currentTheme: string | null = null;
80
- private activeTheme: Theme | null = null;
81
- private loadedThemes: Set<string> = new Set();
82
- private loadingThemes: Map<string, Promise<void>> = new Map();
83
- private eventListeners: ThemeEventListeners = {
84
- themeChange: [],
85
- themeLoad: [],
86
- themeError: [],
87
- };
88
- private storageAdapter: StorageAdapter;
89
- private initialized: boolean = false;
90
-
91
- /**
92
- * Create a new ThemeManager instance
93
- *
94
- * @param config - Theme manager configuration
95
- */
96
- constructor(config: ThemeManagerConfig) {
97
- // Validate required config
98
- const hasThemes = config.themes && Object.keys(config.themes).length > 0;
99
- const hasDefaultThemeObject = config.defaultTheme && typeof config.defaultTheme !== 'string';
100
-
101
- if (!hasThemes && !hasDefaultThemeObject) {
102
- // For backward compatibility: require themes if no JS theme object is provided
103
- throw new Error('ThemeManager: themes configuration is required');
104
- }
105
-
106
- // Merge with defaults
107
- this.config = {
108
- ...DEFAULT_CONFIG,
109
- ...config,
110
- themes: config.themes || {},
111
- defaultTheme: config.defaultTheme || (config.themes && Object.keys(config.themes)[0]),
112
- } as any;
113
-
114
- // Default theme required if provided
115
- if (!this.config.defaultTheme) {
116
- console.warn('ThemeManager: No default theme provided.');
117
- }
118
-
119
- // Validate default theme exists (if string)
120
- if (typeof this.config.defaultTheme === 'string') {
121
- const defName = this.config.defaultTheme;
122
- if (!this.config.themes[defName]) {
123
- throw new Error(`ThemeManager: default theme "${defName}" not found in themes configuration`);
124
- }
125
- }
126
-
127
- // Initialize storage adapter
128
- this.storageAdapter = createLocalStorageAdapter();
129
-
130
- // Initialize theme manager
131
- this.initialize();
132
- }
133
-
134
- /**
135
- * Initialize the theme manager
136
- */
137
- private initialize(): void {
138
- if (this.initialized || isServer()) {
139
- return;
140
- }
141
-
142
- // Try to load theme from storage
143
- let initialTheme = this.config.defaultTheme;
144
-
145
- if (this.config.enablePersistence && this.storageAdapter.isAvailable()) {
146
- const storedTheme = this.storageAdapter.getItem(this.config.storageKey);
147
- if (storedTheme) {
148
- // If stored theme is a name in config.themes, use it
149
- if (typeof storedTheme === 'string' && this.config.themes[storedTheme]) {
150
- initialTheme = storedTheme;
151
- } else if (typeof storedTheme === 'string') {
152
- // Check if stored theme name matches a JS theme (defaultTheme as Theme object)
153
- // This handles persistence of JS themes that aren't in config.themes
154
- if (isJSTheme(this.config.defaultTheme)) {
155
- const defaultThemeName = this.config.defaultTheme.name || 'custom-theme';
156
- if (storedTheme === defaultThemeName) {
157
- initialTheme = this.config.defaultTheme;
158
- }
159
- }
160
- }
161
- }
162
- }
163
-
164
- // Check if theme is already set in DOM (CSS themes)
165
- const domTheme = getCurrentThemeFromDOM(this.config.dataAttribute);
166
- if (domTheme && this.config.themes[domTheme]) {
167
- initialTheme = domTheme;
168
- }
169
-
170
- // Set initial theme synchronously
171
- if (initialTheme) {
172
- if (isJSTheme(initialTheme)) {
173
- // JS Theme
174
- this.activeTheme = initialTheme;
175
- this.currentTheme = initialTheme.name || 'custom-theme';
176
- try {
177
- const css = generateCSSVariables(initialTheme);
178
- injectCSS(css, JS_THEME_STYLE_ID);
179
- applyThemeAttributes(this.currentTheme, this.config.dataAttribute);
180
- } catch (e) {
181
- console.warn('Failed to apply initial JS theme:', e);
182
- }
183
- } else {
184
- // CSS Theme string
185
- this.currentTheme = initialTheme as string;
186
- applyThemeAttributes(this.currentTheme, this.config.dataAttribute);
187
-
188
- // Trigger load async
189
- this.preloadTheme(this.currentTheme).catch(err => {
190
- console.warn('Failed to preload initial theme:', err);
191
- });
192
- }
193
- }
194
-
195
- // Preload themes if configured
196
- if (this.config.preload && this.config.preload.length > 0) {
197
- this.config.preload.forEach(themeName => {
198
- if (this.config.themes[themeName]) {
199
- this.preloadTheme(themeName).catch(error => {
200
- console.warn(`Failed to preload theme "${themeName}":`, error);
201
- });
202
- }
203
- });
204
- }
205
-
206
- this.initialized = true;
207
- }
208
-
209
- /**
210
- * Get the current theme name
211
- *
212
- * @returns Current theme name
213
- */
214
- public getTheme(): string {
215
- return this.currentTheme || (typeof this.config.defaultTheme === 'string' ? this.config.defaultTheme : 'unknown');
216
- }
217
-
218
- /**
219
- * Get the current active theme object (for JS themes)
220
- */
221
- public getActiveTheme(): Theme | null {
222
- return this.activeTheme;
223
- }
224
-
225
- /**
226
- * Get all available themes
227
- *
228
- * @returns Array of theme metadata
229
- */
230
- public getAvailableThemes(): ThemeMetadata[] {
231
- return Object.entries(this.config.themes).map(([key, metadata]) => ({
232
- ...metadata,
233
- class: key,
234
- }));
235
- }
236
-
237
- /**
238
- * Get metadata for a specific theme
239
- *
240
- * @param themeName - Name of the theme
241
- * @returns Theme metadata or null if not found
242
- */
243
- public getThemeMetadata(themeName: string): ThemeMetadata | null {
244
- const metadata = this.config.themes[themeName];
245
- return metadata ? { ...metadata, class: themeName } : null;
246
- }
247
-
248
- /**
249
- * Check if a theme is currently loaded
250
- *
251
- * @param themeName - Name of the theme to check
252
- * @returns True if theme is loaded
253
- */
254
- public isThemeLoaded(themeName: string): boolean {
255
- if (isServer()) {
256
- return false;
257
- }
258
- return this.loadedThemes.has(themeName) || checkThemeLoaded(themeName);
259
- }
260
-
261
- /**
262
- * Validate a theme name
263
- *
264
- * @param themeName - Theme name to validate
265
- * @returns True if theme exists and is valid
266
- */
267
- public validateTheme(themeName: string): boolean {
268
- if (!isValidThemeName(themeName)) {
269
- return false;
270
- }
271
-
272
- const metadata = this.config.themes[themeName];
273
- if (!metadata) {
274
- return false;
275
- }
276
-
277
- const validation = validateThemeMetadata(metadata);
278
- return validation.valid;
279
- }
280
-
281
- /**
282
- * Preload a theme without applying it
283
- *
284
- * @param themeName - Name of the theme to preload
285
- * @returns Promise that resolves when theme is loaded
286
- */
287
- public async preloadTheme(themeName: string): Promise<void> {
288
- if (isServer()) {
289
- return Promise.resolve();
290
- }
291
-
292
- // Validate theme name format to prevent path injection
293
- if (!isValidThemeName(themeName)) {
294
- const error = new Error(`Invalid theme name: "${themeName}". Theme names must be lowercase alphanumeric with hyphens.`);
295
- this.emitError(error, themeName);
296
- throw error;
297
- }
298
-
299
- // Check if theme exists
300
- if (!this.config.themes[themeName]) {
301
- const error = new Error(`Theme "${themeName}" not found`);
302
- this.emitError(error, themeName);
303
- throw error;
304
- }
305
-
306
- // Check if already loaded
307
- if (this.isThemeLoaded(themeName)) {
308
- return Promise.resolve();
309
- }
310
-
311
- // Check if already loading
312
- if (this.loadingThemes.has(themeName)) {
313
- return this.loadingThemes.get(themeName)!;
314
- }
315
-
316
- // Load theme CSS
317
- const loadPromise = loadThemeCSS(
318
- themeName,
319
- this.config.basePath,
320
- this.config.useMinified,
321
- this.config.cdnPath
322
- )
323
- .then(() => {
324
- this.loadedThemes.add(themeName);
325
- this.loadingThemes.delete(themeName);
326
- this.emitLoad(themeName);
327
- })
328
- .catch(error => {
329
- this.loadingThemes.delete(themeName);
330
- this.emitError(error, themeName);
331
- throw error;
332
- });
333
-
334
- this.loadingThemes.set(themeName, loadPromise);
335
- return loadPromise;
336
- }
337
-
338
- /**
339
- * Set the current theme
340
- *
341
- * @param themeOrName - Name of the theme or Theme object to set
342
- * @param options - Load options
343
- * @returns Promise that resolves when theme is applied
344
- */
345
- public async setTheme(
346
- themeOrName: string | Theme,
347
- options: ThemeLoadOptions = {}
348
- ): Promise<void> {
349
- if (isServer()) {
350
- return Promise.resolve();
351
- }
352
-
353
- const isJS = isJSTheme(themeOrName);
354
- let themeName: string;
355
- let themeObject: Theme | null = null;
356
-
357
- if (isJS) {
358
- themeObject = themeOrName as Theme;
359
- themeName = themeObject.name || 'custom-theme';
360
- } else {
361
- themeName = themeOrName as string;
362
- // Validate theme name format
363
- if (!isValidThemeName(themeName)) {
364
- const error = new Error(`Invalid theme name: "${themeName}". Theme names must be lowercase alphanumeric with hyphens.`);
365
- this.emitError(error, themeName);
366
- throw error;
367
- }
368
- // Validate theme exists in config
369
- if (!this.config.themes[themeName]) {
370
- const error = new Error(`Theme "${themeName}" not found`);
371
- this.emitError(error, themeName);
372
- throw error;
373
- }
374
- }
375
-
376
- const previousTheme = this.currentTheme;
377
- const isCurrentlyJS = this.activeTheme !== null;
378
-
379
- // Check if already current theme (and not forced)
380
- // Only return early if:
381
- // 1. Names match
382
- // 2. Not forced
383
- // 3. Both are the same type (both CSS or both JS)
384
- // - If switching from JS to CSS or CSS to JS with same name, we need to proceed
385
- if (themeName === this.currentTheme && !options.force) {
386
- // If both are CSS themes, return early
387
- if (!isJS && !isCurrentlyJS) {
388
- return Promise.resolve();
389
- }
390
- // If both are JS themes with the same name, return early
391
- // (Note: We can't easily compare JS theme objects, but if name matches and both are JS, assume same)
392
- if (isJS && isCurrentlyJS) {
393
- return Promise.resolve();
394
- }
395
- // If switching between JS and CSS with same name, continue to switch implementations
396
- }
397
-
398
- try {
399
- if (isJS && themeObject) {
400
- // Handle JS Theme
401
-
402
- // 1. Generate CSS Variables
403
- const css = generateCSSVariables(themeObject);
404
-
405
- // 2. Inject CSS
406
- injectCSS(css, JS_THEME_STYLE_ID);
407
-
408
- // 3. Remove previous theme attribute?
409
- // We might want to keep data-theme attribute if it helps selectors,
410
- // but if the theme name matches, good.
411
- applyThemeAttributes(themeName, this.config.dataAttribute);
412
-
413
- const wasJS = !!this.activeTheme;
414
- // 4. Set active theme object
415
- this.activeTheme = themeObject;
416
-
417
- // 5. If we had a previous CSS theme loaded (and it wasn't a JS theme)
418
- if (previousTheme && !wasJS && options.removePrevious) {
419
- // If previously we had a CSS theme, we should remove it.
420
- // We can check if previousTheme was in availableThemes.
421
- if (this.config.themes[previousTheme]) {
422
- removeThemeCSS(previousTheme);
423
- this.loadedThemes.delete(previousTheme);
424
- }
425
- }
426
-
427
- } else {
428
- // Handle CSS Theme
429
-
430
- // 1. Clear any active JS theme
431
- if (this.activeTheme) {
432
- removeInjectedCSS(JS_THEME_STYLE_ID);
433
- this.activeTheme = null;
434
- }
435
-
436
- // 2. Load CSS if needed
437
- if (!this.isThemeLoaded(themeName) || options.force) {
438
- await this.preloadTheme(themeName);
439
- }
440
-
441
- // 3. Remove previous theme CSS
442
- if (options.removePrevious && previousTheme && previousTheme !== themeName) {
443
- removeThemeCSS(previousTheme);
444
- this.loadedThemes.delete(previousTheme);
445
- }
446
-
447
- // 4. Apply attributes
448
- applyThemeAttributes(themeName, this.config.dataAttribute);
449
- }
450
-
451
- // Update current theme name
452
- this.currentTheme = themeName;
453
-
454
- // Persist (only if string name and in config?)
455
- // If it's a JS theme, we can't persist the object, but if it has a name, we might persist that
456
- // and expect the app to restore it (e.g. by re-creating it).
457
- if (this.config.enablePersistence && this.storageAdapter.isAvailable()) {
458
- // Only persist if it's a known theme string or we accept persisting names of JS custom themes
459
- // For now, let's persist whatever string we have.
460
- this.storageAdapter.setItem(this.config.storageKey, themeName);
461
- }
462
-
463
- // Emit change event
464
- this.emitThemeChange(previousTheme, themeName, this.activeTheme);
465
-
466
- // Callback
467
- if (this.config.onThemeChange) {
468
- this.config.onThemeChange(isJS ? (themeObject || themeName) : themeName);
469
- }
470
-
471
- } catch (error) {
472
- const err = error instanceof Error ? error : new Error(String(error));
473
- this.emitError(err, themeName);
474
-
475
- if (this.config.onError) {
476
- this.config.onError(err, themeName);
477
- }
478
-
479
- // Fallback
480
- if (options.fallbackOnError && this.config.defaultTheme) {
481
- // Extract theme names consistently to avoid comparing string to Theme object
482
- const targetName = themeName; // themeName is already a string at this point
483
- const defName = isJSTheme(this.config.defaultTheme)
484
- ? this.config.defaultTheme.name
485
- : this.config.defaultTheme;
486
-
487
- // Only fallback if target theme is different from default theme
488
- if (targetName !== defName) {
489
- const def = this.config.defaultTheme;
490
- if (def && typeof def !== 'string') {
491
- // recursively call set theme with default object
492
- return this.setTheme(def, { ...options, fallbackOnError: false });
493
- } else if (typeof def === 'string') {
494
- return this.setTheme(def, { ...options, fallbackOnError: false });
495
- }
496
- }
497
- }
498
-
499
- throw err;
500
- }
501
- }
502
-
503
- /**
504
- * Enable theme persistence
505
- *
506
- * @param storageKey - Optional custom storage key
507
- */
508
- public enablePersistence(storageKey?: string): void {
509
- this.config.enablePersistence = true;
510
- if (storageKey) {
511
- this.config.storageKey = storageKey;
512
- }
513
-
514
- // Save current theme
515
- if (this.currentTheme && this.storageAdapter.isAvailable()) {
516
- this.storageAdapter.setItem(this.config.storageKey, this.currentTheme);
517
- }
518
- }
519
-
520
- /**
521
- * Disable theme persistence
522
- */
523
- public disablePersistence(): void {
524
- this.config.enablePersistence = false;
525
-
526
- // Clear stored theme
527
- if (this.storageAdapter.isAvailable()) {
528
- this.storageAdapter.removeItem(this.config.storageKey);
529
- }
530
- }
531
-
532
- /**
533
- * Clear all loaded themes
534
- */
535
- public clearThemes(): void {
536
- if (isServer()) {
537
- return;
538
- }
539
-
540
- removeAllThemeCSS();
541
- removeInjectedCSS(JS_THEME_STYLE_ID);
542
- this.loadedThemes.clear();
543
- this.loadingThemes.clear();
544
- this.activeTheme = null;
545
- }
546
-
547
- /**
548
- * Add event listener
549
- *
550
- * @param event - Event name
551
- * @param callback - Callback function
552
- */
553
- public on(event: 'themeChange', callback: ThemeChangeCallback): void;
554
- public on(event: 'themeLoad', callback: ThemeLoadCallback): void;
555
- public on(event: 'themeError', callback: ThemeErrorCallback): void;
556
- public on(event: string, callback: any): void {
557
- if (event === 'themeChange') {
558
- this.eventListeners.themeChange.push(callback);
559
- } else if (event === 'themeLoad') {
560
- this.eventListeners.themeLoad.push(callback);
561
- } else if (event === 'themeError') {
562
- this.eventListeners.themeError.push(callback);
563
- }
564
- }
565
-
566
- /**
567
- * Remove event listener
568
- *
569
- * @param event - Event name
570
- * @param callback - Callback function to remove
571
- */
572
- public off(event: 'themeChange', callback: ThemeChangeCallback): void;
573
- public off(event: 'themeLoad', callback: ThemeLoadCallback): void;
574
- public off(event: 'themeError', callback: ThemeErrorCallback): void;
575
- public off(event: string, callback: any): void {
576
- if (event === 'themeChange') {
577
- this.eventListeners.themeChange = this.eventListeners.themeChange.filter(cb => cb !== callback);
578
- } else if (event === 'themeLoad') {
579
- this.eventListeners.themeLoad = this.eventListeners.themeLoad.filter(cb => cb !== callback);
580
- } else if (event === 'themeError') {
581
- this.eventListeners.themeError = this.eventListeners.themeError.filter(cb => cb !== callback);
582
- }
583
- }
584
-
585
- /**
586
- * Emit theme change event
587
- */
588
- private emitThemeChange(previousTheme: string | null, currentTheme: string, themeObject?: Theme | null): void {
589
- const event: ThemeChangeEvent = {
590
- previousTheme,
591
- currentTheme,
592
- themeObject,
593
- timestamp: Date.now(),
594
- source: 'user',
595
- };
596
-
597
- this.eventListeners.themeChange.forEach(callback => {
598
- try {
599
- callback(event);
600
- } catch (error) {
601
- console.error('Error in themeChange listener:', error);
602
- }
603
- });
604
- }
605
-
606
- /**
607
- * Emit theme load event
608
- */
609
- private emitLoad(themeName: string): void {
610
- this.eventListeners.themeLoad.forEach(callback => {
611
- try {
612
- callback(themeName);
613
- } catch (error) {
614
- console.error('Error in themeLoad listener:', error);
615
- }
616
- });
617
- }
618
-
619
- /**
620
- * Emit theme error event
621
- */
622
- private emitError(error: Error, themeName: string): void {
623
- this.eventListeners.themeError.forEach(callback => {
624
- try {
625
- callback(error, themeName);
626
- } catch (err) {
627
- console.error('Error in themeError listener:', err);
628
- }
629
- });
630
- }
631
-
632
- /**
633
- * Destroy the theme manager and clean up
634
- */
635
- public destroy(): void {
636
- this.clearThemes();
637
- this.eventListeners.themeChange = [];
638
- this.eventListeners.themeLoad = [];
639
- this.eventListeners.themeError = [];
640
- this.initialized = false;
641
- this.activeTheme = null;
642
- }
643
- }
644
-
645
- export default ThemeManager;