feat(theme): add system preference (#20)

This commit is contained in:
nessar committed 2026-08-28 14:45:16 +02:00
1 parent bb8d09dda4
commit c19b62ff85
6 files changed
+247 -51

No files matched your search

+39 -8
View File
@@ -1,20 +1,51 @@
import { Injectable, signal } from '@angular/core';
import { computed, Injectable, signal } from '@angular/core';
export type ThemePreference = 'system' | 'light' | 'dark';
export type EffectiveTheme = 'light' | 'dark';
@Injectable({ providedIn: 'root' })
export class ThemeService {
private lightModeMedia = window.matchMedia('(prefers-color-scheme: light)');
isLightMode = signal(this.lightModeMedia.matches);
private readonly lightModeMedia = window.matchMedia('(prefers-color-scheme: light)');
private readonly systemIsLight = signal(this.lightModeMedia.matches);
private readonly selectedPreference = signal<ThemePreference>('system');
readonly themePreference = this.selectedPreference.asReadonly();
readonly effectiveTheme = computed<EffectiveTheme>(() => {
const preference = this.selectedPreference();
return preference === 'system' ? (this.systemIsLight() ? 'light' : 'dark') : preference;
});
readonly isLightMode = computed(() => this.effectiveTheme() === 'light');
readonly nextThemePreference = computed(() => this.nextPreference(this.selectedPreference()));
constructor() {
this.toggleTheme(this.isLightMode());
this.applyEffectiveTheme();
this.lightModeMedia.addEventListener('change', (e) => {
this.isLightMode.set(e.matches);
this.systemIsLight.set(e.matches);
if (this.selectedPreference() === 'system') {
this.applyEffectiveTheme();
}
});
}
toggleTheme(forceLight?: boolean) {
const isLight = forceLight ?? !this.isLightMode();
this.isLightMode.set(isLight);
setThemePreference(preference: ThemePreference): void {
this.selectedPreference.set(preference);
this.applyEffectiveTheme();
}
toggleTheme(forceLight?: boolean): void {
if (forceLight !== undefined) {
this.setThemePreference(forceLight ? 'light' : 'dark');
return;
}
this.setThemePreference(this.nextThemePreference());
}
private nextPreference(preference: ThemePreference): ThemePreference {
return preference === 'system' ? 'light' : preference === 'light' ? 'dark' : 'system';
}
private applyEffectiveTheme(): void {
const isLight = this.effectiveTheme() === 'light';
document.body.classList.toggle('light-mode', isLight);
document.body.classList.toggle('dark-mode', !isLight);
}