Skip to content

Commit 5824a87

Browse files
authored
patch: handling theme selection persistent + docs (#97)
1 parent 1a9441a commit 5824a87

File tree

8 files changed

+70
-17
lines changed

8 files changed

+70
-17
lines changed

docs/assets/theme-menu-shot.png

5.45 KB
Loading

docs/theme-menu.md

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ The `FsThemeMenu` component provides a user-friendly theme switcher for Angular
1010
- **Material 3 theming** support
1111
- **Accessible menu** with clear icons and labels
1212
- **Easy integration** into toolbars or navigation frames
13+
- **Persistent theme selection** using `localStorage` (see `localStorageKey` input)
1314

1415
---
1516

@@ -30,7 +31,7 @@ import { FsThemeMenu } from '@fullstack-devops/ngx-mat-components';
3031
## Usage Example
3132

3233
```html
33-
<fs-theme-menu>
34+
<fs-theme-menu [localStorageKey]="'my-theme-key'">
3435
<i-lucide [img]="PaintBucketIcon"></i-lucide>
3536
</fs-theme-menu>
3637
```
@@ -44,6 +45,9 @@ You can place `<fs-theme-menu>` inside your toolbar or anywhere in your layout.
4445
- `@Input() theme: FsThemeColorSchemes`
4546
Set the current theme (`'auto'`, `'light-mode'`, `'dark-mode'`).
4647

48+
- `@Input() localStorageKey: string`
49+
(Optional) The key used for storing the selected theme in `localStorage`. Defaults to `'fs-selected-theme'`. Use this if you want to scope the theme preference to a specific part of your app or avoid conflicts with other components.
50+
4751
- `@Output() themeChange: EventEmitter<FsThemeColorSchemes>`
4852
Emits when the user selects a new theme.
4953

@@ -69,6 +73,8 @@ export enum FsThemeColorSchemes {
6973
- When `'auto'` is selected, the theme follows the user's system preference.
7074
- The menu uses Material Design and includes icons for each theme option.
7175
- Works only with Angular Material 3.
76+
- The selected theme is persisted in `localStorage` under the key specified by `localStorageKey` (default: `'fs-selected-theme'`). Only explicit selections of `'light-mode'` or `'dark-mode'` are stored; selecting `'auto'` removes the key and follows the system preference.
77+
- On initialization, the component reads the value from `localStorage` (if present) and applies it.
7278
- Prepare the `style.scss` file in your project to include the theme styles.
7379

7480
```scss
@@ -92,6 +98,7 @@ body.dark-mode {
9298
body.light-mode {
9399
color-scheme: light;
94100
}
101+
```
95102

96103
---
97104

@@ -116,7 +123,9 @@ body.light-mode {
116123

117124
## Example Screenshot
118125

119-
![Theme Menu Example](../projects/lib-workspace/src/assets/theme-menu-shot.png)
126+
> **Note:** The colors shown in the theme icon reflect your currently selected theme, providing a visual preview that matches your actual color scheme.
127+
128+
![Theme Menu Example](./assets/theme-menu-shot.png)
120129

121130
---
122131

package.json

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,10 @@
11
{
22
"name": "@fullstack-devops/ngx-mat-components",
33
"version": "0.0.0-PLACEHOLDER",
4+
"description": "An addon library for Angular Material that provides additional components and utilities, including a calendar date picker, theme switcher, and navigation frame.",
5+
"license": "Apache-2.0",
46
"engines": {
5-
"node": ">=20.0.0",
7+
"node": "^20.19.0 || ^22.12.0 || >=24.0.0",
68
"yarn": "^1.22.17",
79
"npm": "Please use Yarn instead of NPM to install dependencies. See: https://yarnpkg.com/lang/en/docs/install/"
810
},

projects/lib-workspace/src/app/app.ts

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
1-
import { Component } from '@angular/core';
1+
import { version } from 'packageJson';
2+
import { Component, HostBinding } from '@angular/core';
23
import { CommonModule } from '@angular/common';
34
import { FormsModule, ReactiveFormsModule } from '@angular/forms';
45
import { MatButtonModule } from '@angular/material/button';
@@ -63,9 +64,11 @@ export class App {
6364
readonly CogIcon = CogIcon;
6465
readonly PaintBucketIcon = PaintBucketIcon;
6566

67+
@HostBinding('attr.app-version') appVersion = version;
68+
6669
navFrameConfig: NavFrameConfig = {
6770
appName: 'Demo App',
68-
appVersion: '0.0.0',
71+
appVersion: version,
6972
// logoSrc: 'https://upload.wikimedia.org/wikipedia/commons/thumb/c/cf/Angular_full_color_logo.svg/1024px-Angular_full_color_logo.svg.png',
7073
};
7174
sizing: NavFrameSizing = {

projects/ngx-mat-components/package.json

Lines changed: 21 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,24 +1,37 @@
11
{
22
"name": "@fullstack-devops/ngx-mat-components",
33
"version": "0.0.0-PLACEHOLDER",
4+
"description": "An addon library for Angular Material that provides additional components and utilities, including a calendar date picker, theme switcher, and navigation frame.",
45
"license": "Apache-2.0",
6+
"keywords": [
7+
"angular",
8+
"material",
9+
"components",
10+
"ui",
11+
"ngx-mat-components",
12+
"fullstack-devops",
13+
"calendar",
14+
"date-picker",
15+
"theme-switcher",
16+
"nav-frame",
17+
"nav-menu"
18+
],
19+
"repository": {
20+
"type": "git",
21+
"url": "https://github.com/fullstack-devops/ngx-mat-components.git"
22+
},
23+
"homepage": "https://github.com/fullstack-devops/ngx-mat-components",
524
"peerDependencies": {
625
"@angular/common": "^19.0.0",
726
"@angular/core": "^19.0.0",
827
"@angular/material": "^19.0.0",
9-
"date-fns": "^2.29.3"
28+
"date-fns": "^4.0.0"
1029
},
1130
"dependencies": {
1231
"tslib": "^2.8.1"
1332
},
14-
"repository": {
15-
"type": "git",
16-
"url": "https://github.com/fullstack-devops/ngx-mat-components.git"
17-
},
18-
"homepage": "https://github.com/fullstack-devops/ngx-mat-components",
19-
"description": "Central component repo for angular applications",
2033
"engines": {
21-
"node": ">= 20.0.0",
34+
"node": "^20.19.0 || ^22.12.0 || >=24.0.0",
2235
"npm": ">= 9.8.0",
2336
"yarn": ">= 1.22.0"
2437
},

projects/ngx-mat-components/src/fs-nav-frame/fs-nav-frame.component.html

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -27,8 +27,12 @@
2727
</button>
2828
</div>
2929
<div class="app-info-wrapper" [class.opened]="!isClosed">
30-
<span class="app-name" *ngIf="!isClosed">{{ navFrameConfig.appName }}</span>
31-
<span class="app-version" *ngIf="!isClosed">{{ navFrameConfig.appVersion }}</span>
30+
@if (!isClosed) {
31+
<span class="app-name">{{ navFrameConfig.appName }}</span>
32+
@if (navFrameConfig.appVersion) {
33+
<span class="app-version">{{ navFrameConfig.appVersion }}</span>
34+
}
35+
}
3236
</div>
3337
</div>
3438

projects/ngx-mat-components/src/fs-theme-menu/fs-theme-menu.ts

Lines changed: 22 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { Component, EventEmitter, Input, Output } from '@angular/core';
1+
import { Component, EventEmitter, Input, OnInit, Output } from '@angular/core';
22
import { MatMenuModule } from '@angular/material/menu';
33
import { FsCheckSvg } from './fs-check-svg';
44
import { FsThemeIcon } from './fs-theme-icon/fs-theme-icon';
@@ -16,17 +16,23 @@ export enum FsThemeColorSchemes {
1616
templateUrl: './fs-theme-menu.html',
1717
styleUrls: ['./fs-theme-menu.scss'],
1818
})
19-
export class FsThemeMenu {
19+
export class FsThemeMenu implements OnInit {
2020
private _theme: FsThemeColorSchemes = FsThemeColorSchemes.Auto;
2121
readonly FsThemeColorSchemes = FsThemeColorSchemes;
2222

23+
@Input() localStorageKey = 'fs-selected-theme';
2324
@Input()
2425
set theme(value: FsThemeColorSchemes) {
2526
this._theme = value;
2627
const body = document.body;
2728
body.classList.remove(FsThemeColorSchemes.Light, FsThemeColorSchemes.Dark);
29+
30+
// Persist only if not auto
2831
if (value && value !== FsThemeColorSchemes.Auto) {
2932
body.classList.add(value);
33+
localStorage.setItem(this.localStorageKey, value);
34+
} else {
35+
localStorage.removeItem(this.localStorageKey);
3036
}
3137
this.themeChange.emit(value);
3238
}
@@ -35,11 +41,25 @@ export class FsThemeMenu {
3541
}
3642
@Output() themeChange = new EventEmitter<FsThemeColorSchemes>();
3743

44+
ngOnInit() {
45+
this.loadThemeFromStorage();
46+
}
47+
3848
isSelected(requested: FsThemeColorSchemes): boolean {
3949
return this.theme === requested;
4050
}
4151

4252
onColorSchemeChange(value: FsThemeColorSchemes): void {
4353
this.theme = value;
4454
}
55+
56+
// Call this in ngOnInit or constructor
57+
loadThemeFromStorage() {
58+
const stored = localStorage.getItem(this.localStorageKey) as FsThemeColorSchemes;
59+
if (stored === FsThemeColorSchemes.Light || stored === FsThemeColorSchemes.Dark) {
60+
this.theme = stored;
61+
} else {
62+
this.theme = FsThemeColorSchemes.Auto;
63+
}
64+
}
4565
}

tsconfig.json

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,8 +11,10 @@
1111
"noPropertyAccessFromIndexSignature": true,
1212
"noImplicitReturns": true,
1313
"noFallthroughCasesInSwitch": true,
14+
"resolveJsonModule": true,
1415
"sourceMap": true,
1516
"paths": {
17+
"packageJson": ["./package.json"],
1618
"ngx-mat-components": ["dist/ngx-mat-components"]
1719
},
1820
"declaration": false,

0 commit comments

Comments
 (0)