| |
| |
| |
| |
|
|
| class ThemeManager { |
| constructor() { |
| this.storageKey = 'crypto_monitor_theme'; |
| this.currentTheme = 'light'; |
| this.listeners = []; |
| } |
|
|
| |
| |
| |
| init() { |
| |
| this.currentTheme = this.getSavedTheme() || this.getSystemPreference(); |
|
|
| |
| this.applyTheme(this.currentTheme, false); |
|
|
| |
| this.setupToggleButton(); |
|
|
| |
| this.listenToSystemChanges(); |
|
|
| console.log(`[ThemeManager] Initialized with theme: ${this.currentTheme}`); |
| } |
|
|
| |
| |
| |
| getSavedTheme() { |
| try { |
| return localStorage.getItem(this.storageKey); |
| } catch (error) { |
| console.warn('[ThemeManager] localStorage not available:', error); |
| return null; |
| } |
| } |
|
|
| |
| |
| |
| saveTheme(theme) { |
| try { |
| localStorage.setItem(this.storageKey, theme); |
| } catch (error) { |
| console.warn('[ThemeManager] Could not save theme:', error); |
| } |
| } |
|
|
| |
| |
| |
| getSystemPreference() { |
| if (window.matchMedia && window.matchMedia('(prefers-color-scheme: dark)').matches) { |
| return 'dark'; |
| } |
| return 'light'; |
| } |
|
|
| |
| |
| |
| applyTheme(theme, save = true) { |
| const body = document.body; |
|
|
| |
| body.classList.remove('theme-light', 'theme-dark'); |
|
|
| |
| body.classList.add(`theme-${theme}`); |
|
|
| |
| this.currentTheme = theme; |
|
|
| |
| if (save) { |
| this.saveTheme(theme); |
| } |
|
|
| |
| this.updateToggleButton(theme); |
|
|
| |
| this.notifyListeners(theme); |
|
|
| |
| this.announceThemeChange(theme); |
|
|
| console.log(`[ThemeManager] Applied theme: ${theme}`); |
| } |
|
|
| |
| |
| |
| toggleTheme() { |
| const newTheme = this.currentTheme === 'light' ? 'dark' : 'light'; |
| this.applyTheme(newTheme); |
| } |
|
|
| |
| |
| |
| setTheme(theme) { |
| if (theme !== 'light' && theme !== 'dark') { |
| console.warn(`[ThemeManager] Invalid theme: ${theme}`); |
| return; |
| } |
| this.applyTheme(theme); |
| } |
|
|
| |
| |
| |
| getTheme() { |
| return this.currentTheme; |
| } |
|
|
| |
| |
| |
| setupToggleButton() { |
| const toggleBtn = document.getElementById('theme-toggle'); |
| if (toggleBtn) { |
| toggleBtn.addEventListener('click', () => { |
| this.toggleTheme(); |
| }); |
|
|
| |
| toggleBtn.addEventListener('keydown', (e) => { |
| if (e.key === 'Enter' || e.key === ' ') { |
| e.preventDefault(); |
| this.toggleTheme(); |
| } |
| }); |
|
|
| |
| this.updateToggleButton(this.currentTheme); |
| } |
| } |
|
|
| |
| |
| |
| updateToggleButton(theme) { |
| const toggleBtn = document.getElementById('theme-toggle'); |
| const toggleIcon = document.getElementById('theme-toggle-icon'); |
|
|
| if (toggleBtn && toggleIcon) { |
| if (theme === 'dark') { |
| toggleIcon.textContent = '☀️'; |
| toggleBtn.setAttribute('aria-label', 'Switch to light mode'); |
| toggleBtn.setAttribute('title', 'Light Mode'); |
| } else { |
| toggleIcon.textContent = '🌙'; |
| toggleBtn.setAttribute('aria-label', 'Switch to dark mode'); |
| toggleBtn.setAttribute('title', 'Dark Mode'); |
| } |
| } |
| } |
|
|
| |
| |
| |
| listenToSystemChanges() { |
| if (window.matchMedia) { |
| const darkModeQuery = window.matchMedia('(prefers-color-scheme: dark)'); |
|
|
| |
| if (darkModeQuery.addEventListener) { |
| darkModeQuery.addEventListener('change', (e) => { |
| |
| if (!this.getSavedTheme()) { |
| const newTheme = e.matches ? 'dark' : 'light'; |
| this.applyTheme(newTheme, false); |
| } |
| }); |
| } |
| |
| else if (darkModeQuery.addListener) { |
| darkModeQuery.addListener((e) => { |
| if (!this.getSavedTheme()) { |
| const newTheme = e.matches ? 'dark' : 'light'; |
| this.applyTheme(newTheme, false); |
| } |
| }); |
| } |
| } |
| } |
|
|
| |
| |
| |
| onChange(callback) { |
| this.listeners.push(callback); |
| return () => { |
| const index = this.listeners.indexOf(callback); |
| if (index > -1) { |
| this.listeners.splice(index, 1); |
| } |
| }; |
| } |
|
|
| |
| |
| |
| notifyListeners(theme) { |
| this.listeners.forEach(callback => { |
| try { |
| callback(theme); |
| } catch (error) { |
| console.error('[ThemeManager] Error in listener:', error); |
| } |
| }); |
| } |
|
|
| |
| |
| |
| announceThemeChange(theme) { |
| const liveRegion = document.getElementById('sr-live-region'); |
| if (liveRegion) { |
| liveRegion.textContent = `Theme changed to ${theme} mode`; |
| } |
| } |
|
|
| |
| |
| |
| resetToSystem() { |
| try { |
| localStorage.removeItem(this.storageKey); |
| } catch (error) { |
| console.warn('[ThemeManager] Could not remove saved theme:', error); |
| } |
|
|
| const systemTheme = this.getSystemPreference(); |
| this.applyTheme(systemTheme, false); |
| } |
| } |
|
|
| |
| window.themeManager = new ThemeManager(); |
|
|
| |
| document.addEventListener('DOMContentLoaded', () => { |
| window.themeManager.init(); |
| }); |
|
|
| console.log('[ThemeManager] Module loaded'); |
|
|