feat: add theme mode support with AppThemeMode enum and AppThemeState service

- Introduced AppThemeMode enum to define theme options: System, Light, Dark.
- Updated AppSettingsDocument to include ThemeMode property.
- Created AppThemeState service to manage current theme mode and handle changes.
- Integrated theme mode handling in CouchbaseLiteAppSettingsService for persistence.
- Added JavaScript for theme management in the frontend, supporting system preference detection.
- Enhanced CSS with theme variables for consistent styling across light and dark modes.
- Updated Playwright tests to ensure sidebar functionality and responsiveness.
This commit is contained in:
MaddoScientisto 2026-04-20 22:58:25 +02:00
commit 158906fa28
19 changed files with 889 additions and 82 deletions

58
wwwroot/theme.js Normal file
View file

@ -0,0 +1,58 @@
window.workTrackerTheme = (() => {
const storageKey = "worktracker.themeMode";
const mediaQuery = window.matchMedia
? window.matchMedia("(prefers-color-scheme: dark)")
: null;
function normalize(mode) {
const normalized = (mode || "system").toString().toLowerCase();
return normalized === "light" || normalized === "dark" || normalized === "system"
? normalized
: "system";
}
function resolve(mode) {
if (mode === "system") {
return mediaQuery && mediaQuery.matches ? "dark" : "light";
}
return mode;
}
function apply(mode) {
const normalized = normalize(mode);
const effectiveMode = resolve(normalized);
document.documentElement.setAttribute("data-bs-theme", effectiveMode);
document.documentElement.setAttribute("data-theme-mode", normalized);
document.documentElement.style.colorScheme = effectiveMode;
localStorage.setItem(storageKey, normalized);
}
function handleSystemThemeChange() {
const currentMode = normalize(localStorage.getItem(storageKey) || document.documentElement.getAttribute("data-theme-mode"));
if (currentMode === "system") {
apply(currentMode);
}
}
if (mediaQuery) {
if (typeof mediaQuery.addEventListener === "function") {
mediaQuery.addEventListener("change", handleSystemThemeChange);
}
else if (typeof mediaQuery.addListener === "function") {
mediaQuery.addListener(handleSystemThemeChange);
}
}
return {
init() {
apply(localStorage.getItem(storageKey) || document.documentElement.getAttribute("data-theme-mode"));
},
setTheme(mode) {
apply(mode);
}
};
})();
window.workTrackerTheme.init();