/
theme-switcher.ts
84 lines (69 loc) · 2.06 KB
/
theme-switcher.ts
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
import { TypedEvent } from './typed-event';
export class ThemeSwitcher {
readonly prefixTheme = 'theme-';
readonly suffixLight = '-light';
readonly suffixDark = '-dark';
mutationObserver: MutationObserver;
themeChanged = new TypedEvent<string>();
private isThemeClass(className: string) {
return (
className.startsWith(this.prefixTheme) &&
(className.endsWith(this.suffixDark) ||
className.endsWith(this.suffixLight))
);
}
public setTheme(themeName: string) {
const oldThemes: string[] = [];
document.body.classList.forEach((className) => {
if (this.isThemeClass(className)) {
oldThemes.push(className);
}
});
document.body.classList.remove(...oldThemes);
document.body.classList.add(themeName);
}
public toggleMode() {
const oldThemes: string[] = [];
document.body.classList.forEach((className) => {
if (this.isThemeClass(className)) {
oldThemes.push(className);
}
});
oldThemes.forEach((themeName) => {
document.body.classList.replace(
themeName,
this.getOppositeMode(themeName)
);
});
}
private getOppositeMode(themeName: string) {
if (themeName.endsWith(this.suffixDark)) {
return themeName.replace(/-dark$/g, this.suffixLight);
}
if (themeName.endsWith(this.suffixLight)) {
return themeName.replace(/-light$/g, this.suffixDark);
}
}
private handleMutations(mutations: MutationRecord[]) {
return mutations.forEach((mutation) => {
const { target } = mutation;
(target as HTMLElement).classList.forEach((className) => {
if (
this.isThemeClass(className) &&
!mutation.oldValue?.includes(className)
) {
this.themeChanged.emit(className);
}
});
});
}
public constructor() {
this.mutationObserver = new MutationObserver((mutations) => {
this.handleMutations(mutations);
});
this.mutationObserver.observe(document.body, {
attributeFilter: ['class'],
attributeOldValue: true,
});
}
}