Skip to content

Commit 51a9766

Browse files
committed
Merge branch 'release/1.2.1'
2 parents 14e4405 + 6699ec8 commit 51a9766

File tree

9 files changed

+128
-63
lines changed

9 files changed

+128
-63
lines changed

README.md

+4-14
Original file line numberDiff line numberDiff line change
@@ -132,36 +132,26 @@ Types of pages
132132

133133
You can find the config file in the project root, then you can refer to https://tailwindcss.com/docs/configuration to learn how to make your own adjustments.
134134

135-
- Change light and dark mode colors:
136-
137-
Go to `src/theme/01-base/variables.scss` and change them to your preference.
138-
139135
- Set a default theme (First time load)
140136

141-
Go to `src\app\@core\services\theme\theme.service.ts` and change the following line of code:
137+
Go to `src\app\@core\services\theme\theme.config.ts` and change the following line of code
142138

143139
from operating system preference
144140

145141
```ts
146-
private currentTheme$ = new BehaviorSubject<ThemeList>(
147-
this.currentTheme || ThemeList.System
148-
);
142+
export const defaultBaseTheme = ThemeList.System;
149143
```
150144

151145
to light mode
152146

153147
```ts
154-
private currentTheme$ = new BehaviorSubject<ThemeList>(
155-
this.currentTheme || ThemeList.Light
156-
);
148+
export const defaultBaseTheme = ThemeList.Light;
157149
```
158150

159151
or dark mode
160152

161153
```ts
162-
private currentTheme$ = new BehaviorSubject<ThemeList>(
163-
this.currentTheme || ThemeList.Dark
164-
);
154+
export const defaultBaseTheme = ThemeList.Dark;
165155
```
166156

167157
## 🗑️ Remove features

src/app/@core/services/theme/theme.config.ts

+2
Original file line numberDiff line numberDiff line change
@@ -3,3 +3,5 @@ export enum ThemeList {
33
Light = 'light',
44
Dark = 'dark',
55
}
6+
7+
export const defaultBaseTheme = ThemeList.System;
+63-33
Original file line numberDiff line numberDiff line change
@@ -1,64 +1,94 @@
11
import { DOCUMENT } from '@angular/common';
2-
import { Inject, Injectable } from '@angular/core';
2+
import { Inject, Injectable, OnDestroy } from '@angular/core';
33
import { getItem, setItem, StorageItem } from '@app/@core/utils';
4-
import { BehaviorSubject } from 'rxjs';
5-
import { ThemeList } from './theme.config';
4+
import { fromEventPattern, Subject } from 'rxjs';
5+
import { takeUntil } from 'rxjs/operators';
6+
import { defaultBaseTheme, ThemeList } from './theme.config';
67

78
@Injectable({
89
providedIn: 'root',
910
})
10-
export class ThemeService {
11-
private currentTheme$ = new BehaviorSubject<ThemeList>(
12-
this.currentTheme || ThemeList.System,
11+
export class ThemeService implements OnDestroy {
12+
destroy$ = new Subject();
13+
14+
private readonly mediaQuery = window.matchMedia(
15+
'(prefers-color-scheme: dark)',
1316
);
1417

1518
constructor(@Inject(DOCUMENT) private document: Document) {}
1619

17-
get currentTheme(): ThemeList {
20+
get systemTheme(): ThemeList.Light | ThemeList.Dark {
21+
return this.mediaQuery.matches ? ThemeList.Dark : ThemeList.Light;
22+
}
23+
24+
get currentAppTheme(): ThemeList {
1825
return getItem(StorageItem.Theme) as ThemeList;
1926
}
2027

28+
/**
29+
* Makes initial check for system preferences and attach mediaQuery listener
30+
*
31+
*/
2132
init(): void {
22-
if (!this.currentTheme) {
23-
this.listenForMediaQuery();
33+
if (this.currentAppTheme) {
34+
this.setTheme(this.currentAppTheme);
35+
} else if (this.currentAppTheme === ThemeList.System) {
36+
this.setTheme(this.systemTheme);
37+
} else {
38+
this.setTheme(defaultBaseTheme);
2439
}
2540

26-
this.listenForThemeChanges();
41+
this.listenForMediaQueryChanges();
2742
}
2843

29-
changeTheme(theme: ThemeList): void {
30-
this.currentTheme$.next(theme);
31-
}
32-
33-
private listenForMediaQuery(): void {
34-
const colorScheme = window.matchMedia('(prefers-color-scheme: dark)');
35-
36-
colorScheme.addEventListener('change', (e: MediaQueryListEvent) => {
37-
const currentTheme = this.currentTheme;
44+
/**
45+
* Manually changes theme in BehaviorSubject, LocalStorage & HTML element
46+
*
47+
* @param theme new theme
48+
*/
49+
setTheme(theme: ThemeList): void {
50+
this.clearThemes();
51+
setItem(StorageItem.Theme, theme);
3852

39-
if (currentTheme === ThemeList.System) {
40-
const theme = e.matches ? ThemeList.Dark : ThemeList.Light;
41-
this.setTheme(theme);
42-
}
43-
});
44-
}
53+
let bodyClass = theme;
4554

46-
private listenForThemeChanges(): void {
47-
this.currentTheme$.subscribe((theme) => {
48-
this.setTheme(theme);
49-
});
55+
if (theme === ThemeList.System) {
56+
bodyClass = this.systemTheme;
57+
}
58+
this.document.body.classList.add(bodyClass);
5059
}
5160

52-
private setTheme(theme: ThemeList): void {
53-
this.clearThemes();
54-
this.document.body.classList.add(theme);
55-
setItem(StorageItem.Theme, theme);
61+
/**
62+
* Handles system color preference changes
63+
*
64+
*/
65+
private listenForMediaQueryChanges(): void {
66+
fromEventPattern<MediaQueryListEvent>(
67+
this.mediaQuery.addListener.bind(this.mediaQuery),
68+
this.mediaQuery.removeListener.bind(this.mediaQuery),
69+
)
70+
.pipe(takeUntil(this.destroy$))
71+
.subscribe(() => {
72+
// Only applies changes when the current theme is "system"
73+
if (this.currentAppTheme === ThemeList.System) {
74+
this.setTheme(ThemeList.System);
75+
}
76+
});
5677
}
5778

79+
/**
80+
* Clears all themes in ThemeList enum from the HTML element
81+
*
82+
*/
5883
private clearThemes(): void {
5984
for (const theme in ThemeList) {
6085
const key: ThemeList = ThemeList[theme as keyof typeof ThemeList];
6186
this.document.body.classList.remove(key);
6287
}
6388
}
89+
90+
ngOnDestroy(): void {
91+
this.destroy$.complete();
92+
this.destroy$.unsubscribe();
93+
}
6494
}

src/app/@core/utils/local-storage.utils.ts

+2-2
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
export enum StorageItem {
2-
Auth = 'auth',
3-
Theme = 'theme',
2+
Auth = 'App/auth',
3+
Theme = 'App/theme',
44
}
55

66
export const getItem = (itemName: StorageItem): unknown | null => {

src/app/app.component.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -22,9 +22,9 @@ export class AppComponent implements OnInit {
2222
) {}
2323

2424
ngOnInit(): void {
25-
this.isLoggedIn$ = this.authService.isLoggedIn$;
2625
this.seoService.init();
2726
this.themeService.init();
27+
this.isLoggedIn$ = this.authService.isLoggedIn$;
2828
}
2929

3030
onLogout(): void {
+1-11
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
import { ChangeDetectionStrategy, Component, OnInit } from '@angular/core';
22
import { Router } from '@angular/router';
3-
import { ThemeList, ThemeService } from '@app/@core/services/theme';
43
import { Path } from '@app/@core/structs';
54
import { AuthService } from '@app/pages/+auth/_services/auth.service';
65

@@ -12,22 +11,13 @@ import { AuthService } from '@app/pages/+auth/_services/auth.service';
1211
})
1312
export class HeaderComponent implements OnInit {
1413
path = Path;
15-
theme = ThemeList;
1614

17-
constructor(
18-
private router: Router,
19-
private themeService: ThemeService,
20-
private authService: AuthService,
21-
) {}
15+
constructor(private router: Router, private authService: AuthService) {}
2216

2317
ngOnInit(): void {}
2418

2519
onClickSignOut(): void {
2620
this.authService.signOut();
2721
this.router.navigate(['/', Path.SignIn]);
2822
}
29-
30-
onClickToggleTheme(theme: ThemeList): void {
31-
this.themeService.changeTheme(theme);
32-
}
3323
}

src/app/pages/+home/home.page.html

+47-1
Original file line numberDiff line numberDiff line change
@@ -5,10 +5,56 @@ <h1 i18n class="mb-4">👋 Welcome to Angular Boilerplate.</h1>
55

66
<a
77
[routerLink]="['/', path.App]"
8-
class="inline-block h-12 px-6 py-3 font-semibold text-white bg-blue-600 rounded-lg hover:bg-blue-500 focus:ring-4 focus:ring-offset-blue-600 focus:outline-none"
8+
class="inline-block h-12 px-6 py-3 mb-12 font-semibold text-white bg-blue-600 rounded-lg hover:bg-blue-500 focus:ring-4 focus:ring-offset-blue-600 focus:outline-none"
99
type="submit"
1010
>
1111
Go to dashboard
1212
</a>
13+
14+
<h3>Choose a theme</h3>
15+
<div class="flex items-center justify-center space-x-6">
16+
<button
17+
class="inline-block h-12 px-6 py-3 mb-12 font-semibold text-white bg-gray-600 rounded-lg hover:bg-gray-500 focus:ring-4 focus:ring-offset-gray-600 focus:outline-none"
18+
(click)="onClickChangeTheme(theme.System)"
19+
>
20+
System
21+
</button>
22+
<button
23+
class="inline-flex items-center h-12 px-6 py-3 mb-12 font-semibold text-white bg-gray-600 rounded-lg hover:bg-gray-500 focus:ring-4 focus:ring-offset-gray-600 focus:outline-none"
24+
(click)="onClickChangeTheme(theme.Light)"
25+
>
26+
<svg
27+
xmlns="http://www.w3.org/2000/svg"
28+
width="26"
29+
height="26"
30+
class="mr-3"
31+
>
32+
<path
33+
fill="#FFF"
34+
fill-rule="evenodd"
35+
d="M13 21a1 1 0 011 1v3a1 1 0 11-2 0v-3a1 1 0 011-1zm-5.657-2.343a1 1 0 010 1.414l-2.121 2.121a1 1 0 01-1.414-1.414l2.12-2.121a1 1 0 011.415 0zm12.728 0l2.121 2.121a1 1 0 01-1.414 1.414l-2.121-2.12a1 1 0 011.414-1.415zM13 8a5 5 0 110 10 5 5 0 010-10zm12 4a1 1 0 110 2h-3a1 1 0 110-2h3zM4 12a1 1 0 110 2H1a1 1 0 110-2h3zm18.192-8.192a1 1 0 010 1.414l-2.12 2.121a1 1 0 01-1.415-1.414l2.121-2.121a1 1 0 011.414 0zm-16.97 0l2.121 2.12A1 1 0 015.93 7.344L3.808 5.222a1 1 0 011.414-1.414zM13 0a1 1 0 011 1v3a1 1 0 11-2 0V1a1 1 0 011-1z"
36+
/>
37+
</svg>
38+
Light
39+
</button>
40+
<button
41+
class="inline-flex items-center h-12 px-6 py-3 mb-12 font-semibold text-white bg-gray-600 rounded-lg hover:bg-gray-500 focus:ring-4 focus:ring-offset-gray-600 focus:outline-none"
42+
(click)="onClickChangeTheme(theme.Dark)"
43+
>
44+
<svg
45+
xmlns="http://www.w3.org/2000/svg"
46+
width="26"
47+
height="26"
48+
class="mr-3"
49+
>
50+
<path
51+
fill="#FFF"
52+
fill-rule="evenodd"
53+
d="M13 0c.81 0 1.603.074 2.373.216C10.593 1.199 7 5.43 7 10.5 7 16.299 11.701 21 17.5 21c2.996 0 5.7-1.255 7.613-3.268C23.22 22.572 18.51 26 13 26 5.82 26 0 20.18 0 13S5.82 0 13 0z"
54+
/>
55+
</svg>
56+
Dark
57+
</button>
58+
</div>
1359
</div>
1460
</div>

src/app/pages/+home/home.page.ts

+7-1
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import { Component, OnInit } from '@angular/core';
2+
import { ThemeList, ThemeService } from '@app/@core/services/theme';
23
import { Path } from '@core/structs';
34

45
@Component({
@@ -7,8 +8,13 @@ import { Path } from '@core/structs';
78
})
89
export class HomePage implements OnInit {
910
path = Path;
11+
theme = ThemeList;
1012

11-
constructor() {}
13+
constructor(private themeService: ThemeService) {}
1214

1315
ngOnInit(): void {}
16+
17+
onClickChangeTheme(theme: ThemeList): void {
18+
this.themeService.setTheme(theme);
19+
}
1420
}

tailwind.config.js

+1
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ module.exports = {
22
prefix: '',
33
mode: 'jit',
44
purge: {
5+
enabled: process.env.NODE_ENV === 'production',
56
content: ['./src/**/*.{html,scss,ts}'],
67
},
78
darkMode: 'class',

0 commit comments

Comments
 (0)