feat(theme): add system preference (#20)
This commit is contained in:
1 parent
bb8d09dda4
commit
c19b62ff85
6 files changed
+247
-51
No files matched your search
@@ -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);
|
||||
}
|
||||
|
||||
Reference in new issue
Block a user