106 lines
3.5 KiB
JavaScript
106 lines
3.5 KiB
JavaScript
// Filename: frontend/js/components/themeManager.js
|
|
|
|
/**
|
|
* 负责管理应用程序的三态主题(系统、亮色、暗色)。
|
|
* 封装了所有与主题切换相关的 DOM 操作、事件监听和 localStorage 交互。
|
|
*/
|
|
export const themeManager = {
|
|
// 用于存储图标的 SVG HTML
|
|
icons: {},
|
|
|
|
init: function() {
|
|
this.html = document.documentElement;
|
|
this.buttons = document.querySelectorAll('.theme-btn');
|
|
this.cyclerBtn = document.getElementById('theme-cycler-btn');
|
|
this.cyclerIconContainer = document.getElementById('theme-cycler-icon');
|
|
|
|
if (!this.html || this.buttons.length === 0 || !this.cyclerBtn || !this.cyclerIconContainer) {
|
|
console.warn("ThemeManager init failed: one or more required elements not found.");
|
|
return;
|
|
}
|
|
|
|
this.mediaQuery = window.matchMedia('(prefers-color-scheme: dark)');
|
|
|
|
// 初始化时,从三按钮组中提取 SVG 并存储起来
|
|
this.storeIcons();
|
|
|
|
// 绑定宽屏按钮组的点击事件
|
|
this.buttons.forEach(btn => {
|
|
btn.addEventListener('click', () => this.setTheme(btn.dataset.theme));
|
|
});
|
|
|
|
// 绑定移动端循环按钮的点击事件
|
|
this.cyclerBtn.addEventListener('click', () => this.cycleTheme());
|
|
|
|
this.mediaQuery.addEventListener('change', () => this.applyTheme());
|
|
this.applyTheme();
|
|
},
|
|
|
|
// 从现有按钮中提取并存储 SVG 图标
|
|
storeIcons: function() {
|
|
this.buttons.forEach(btn => {
|
|
const theme = btn.dataset.theme;
|
|
const svg = btn.querySelector('svg');
|
|
if (theme && svg) {
|
|
this.icons[theme] = svg.outerHTML;
|
|
}
|
|
});
|
|
},
|
|
|
|
// 循环切换主题的核心逻辑
|
|
cycleTheme: function() {
|
|
const themes = ['system', 'light', 'dark'];
|
|
const currentTheme = this.getTheme();
|
|
const currentIndex = themes.indexOf(currentTheme);
|
|
const nextIndex = (currentIndex + 1) % themes.length; // brilliantly simple cycling logic
|
|
this.setTheme(themes[nextIndex]);
|
|
},
|
|
|
|
applyTheme: function() {
|
|
let theme = this.getTheme();
|
|
if (theme === 'system') {
|
|
theme = this.mediaQuery.matches ? 'dark' : 'light';
|
|
}
|
|
|
|
if (theme === 'dark') {
|
|
this.html.classList.add('dark');
|
|
} else {
|
|
this.html.classList.remove('dark');
|
|
}
|
|
|
|
this.updateButtons();
|
|
this.updateCyclerIcon();
|
|
},
|
|
|
|
setTheme: function(theme) {
|
|
localStorage.setItem('theme', theme);
|
|
this.applyTheme();
|
|
},
|
|
|
|
getTheme: function() {
|
|
return localStorage.getItem('theme') || 'system';
|
|
},
|
|
|
|
updateButtons: function() {
|
|
const currentTheme = this.getTheme();
|
|
this.buttons.forEach(btn => {
|
|
if (btn.dataset.theme === currentTheme) {
|
|
btn.classList.add('bg-white', 'dark:bg-zinc-700');
|
|
} else {
|
|
btn.classList.remove('bg-white', 'dark:bg-zinc-700');
|
|
}
|
|
});
|
|
},
|
|
|
|
// 更新移动端循环按钮的图标
|
|
updateCyclerIcon: function() {
|
|
if (this.cyclerIconContainer) {
|
|
const currentTheme = this.getTheme();
|
|
// 从我们存储的 icons 对象中找到对应的 SVG 并注入
|
|
if (this.icons[currentTheme]) {
|
|
this.cyclerIconContainer.innerHTML = this.icons[currentTheme];
|
|
}
|
|
}
|
|
}
|
|
};
|