141 lines
4.2 KiB
JavaScript
141 lines
4.2 KiB
JavaScript
// File: ./src/js/theme.js
|
|
// Theme management functionality
|
|
(function() {
|
|
'use strict';
|
|
|
|
let currentTheme = 'auto';
|
|
let mediaQuery;
|
|
|
|
// Initialize theme system
|
|
function init() {
|
|
// Get stored theme or default to auto
|
|
currentTheme = localStorage.getItem('theme') || 'auto';
|
|
|
|
// Set up media query listener for auto mode
|
|
mediaQuery = window.matchMedia('(prefers-color-scheme: dark)');
|
|
mediaQuery.addEventListener('change', handleSystemThemeChange);
|
|
|
|
// Apply initial theme
|
|
applyTheme(currentTheme);
|
|
|
|
// Set up theme selector buttons
|
|
setupThemeSelector();
|
|
}
|
|
|
|
// Set up theme selector button event handlers
|
|
function setupThemeSelector() {
|
|
const themeButtons = document.querySelectorAll('.theme-btn');
|
|
|
|
themeButtons.forEach(button => {
|
|
const theme = button.getAttribute('data-theme');
|
|
|
|
// Set initial active state
|
|
button.classList.toggle('active', theme === currentTheme);
|
|
|
|
// Add click handler
|
|
button.addEventListener('click', () => {
|
|
setTheme(theme);
|
|
});
|
|
});
|
|
}
|
|
|
|
// Set theme and update UI
|
|
function setTheme(theme) {
|
|
if (!['light', 'dark', 'auto'].includes(theme)) {
|
|
console.warn('Invalid theme:', theme);
|
|
return;
|
|
}
|
|
|
|
currentTheme = theme;
|
|
|
|
// Store in localStorage
|
|
localStorage.setItem('theme', theme);
|
|
|
|
// Apply theme
|
|
applyTheme(theme);
|
|
|
|
// Update button states
|
|
updateThemeButtons();
|
|
}
|
|
|
|
// Apply theme to document
|
|
function applyTheme(theme) {
|
|
const html = document.documentElement;
|
|
|
|
// Update theme class
|
|
html.className = html.className.replace(/theme-\w+/g, '');
|
|
html.classList.add(`theme-${theme}`);
|
|
|
|
// Handle dark mode class
|
|
if (theme === 'auto') {
|
|
// Use system preference
|
|
const isDark = mediaQuery ? mediaQuery.matches :
|
|
window.matchMedia('(prefers-color-scheme: dark)').matches;
|
|
html.classList.toggle('dark', isDark);
|
|
} else {
|
|
// Use explicit theme
|
|
html.classList.toggle('dark', theme === 'dark');
|
|
}
|
|
}
|
|
|
|
// Handle system theme changes (for auto mode)
|
|
function handleSystemThemeChange(event) {
|
|
if (currentTheme === 'auto') {
|
|
document.documentElement.classList.toggle('dark', event.matches);
|
|
}
|
|
}
|
|
|
|
// Update theme button active states
|
|
function updateThemeButtons() {
|
|
const themeButtons = document.querySelectorAll('.theme-btn');
|
|
|
|
themeButtons.forEach(button => {
|
|
const theme = button.getAttribute('data-theme');
|
|
button.classList.toggle('active', theme === currentTheme);
|
|
});
|
|
}
|
|
|
|
// Get current effective theme (resolves 'auto' to actual theme)
|
|
function getEffectiveTheme() {
|
|
if (currentTheme === 'auto') {
|
|
return mediaQuery && mediaQuery.matches ? 'dark' : 'light';
|
|
}
|
|
return currentTheme;
|
|
}
|
|
|
|
// Toggle between light and dark (skips auto)
|
|
function toggleTheme() {
|
|
const effectiveTheme = getEffectiveTheme();
|
|
setTheme(effectiveTheme === 'dark' ? 'light' : 'dark');
|
|
}
|
|
|
|
// Keyboard shortcut support
|
|
function setupKeyboardShortcuts() {
|
|
document.addEventListener('keydown', (event) => {
|
|
// Ctrl/Cmd + Shift + T to toggle theme
|
|
if ((event.ctrlKey || event.metaKey) && event.shiftKey && event.key === 'T') {
|
|
event.preventDefault();
|
|
toggleTheme();
|
|
}
|
|
});
|
|
}
|
|
|
|
// Export functions for external use
|
|
window.themeManager = {
|
|
setTheme,
|
|
getTheme: () => currentTheme,
|
|
getEffectiveTheme,
|
|
toggleTheme
|
|
};
|
|
|
|
// Initialize when DOM is ready
|
|
if (document.readyState === 'loading') {
|
|
document.addEventListener('DOMContentLoaded', () => {
|
|
init();
|
|
setupKeyboardShortcuts();
|
|
});
|
|
} else {
|
|
init();
|
|
setupKeyboardShortcuts();
|
|
}
|
|
})(); |