ngx-easy-theme-switcher 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +208 -0
- package/fesm2022/ngx-easy-theme-switcher.mjs +195 -0
- package/fesm2022/ngx-easy-theme-switcher.mjs.map +1 -0
- package/index.d.ts +5 -0
- package/lib/theme-toggle/theme-toggle.component.d.ts +27 -0
- package/lib/theme.service.d.ts +33 -0
- package/package.json +51 -0
- package/public-api.d.ts +4 -0
package/README.md
ADDED
|
@@ -0,0 +1,208 @@
|
|
|
1
|
+
# NGX Easy Theme Switcher
|
|
2
|
+
|
|
3
|
+
Angular library for easy theme switching (light/dark) with customizable toggle icons.
|
|
4
|
+
|
|
5
|
+
## Features
|
|
6
|
+
|
|
7
|
+
- **Service-based** — inject `ThemeService` anywhere to programmatically get/set/toggle themes
|
|
8
|
+
- **Persistent** — theme preference is saved in `localStorage`
|
|
9
|
+
- **Customizable icon** — default FontAwesome, with support for any icon font or custom SVG
|
|
10
|
+
- **Standalone** — compatible with Angular standalone components
|
|
11
|
+
- **Minimal** — just a service and a toggle component
|
|
12
|
+
|
|
13
|
+
## Installation
|
|
14
|
+
|
|
15
|
+
```bash
|
|
16
|
+
npm install ngx-easy-theme-switcher
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
FontAwesome is used by default. For icons to display, install your preferred icon library:
|
|
20
|
+
|
|
21
|
+
```bash
|
|
22
|
+
# For default FontAwesome icons
|
|
23
|
+
npm install @fortawesome/fontawesome-free
|
|
24
|
+
|
|
25
|
+
# Or for Material Icons
|
|
26
|
+
npm install material-symbols
|
|
27
|
+
|
|
28
|
+
# Or for Bootstrap Icons
|
|
29
|
+
npm install bootstrap-icons
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
## Quick Start
|
|
33
|
+
|
|
34
|
+
### 1. Create theme files
|
|
35
|
+
|
|
36
|
+
Create two SCSS files in `src/themes/`:
|
|
37
|
+
|
|
38
|
+
**`src/themes/_light.scss`**:
|
|
39
|
+
```scss
|
|
40
|
+
[data-theme="light"] {
|
|
41
|
+
--bg: #ffffff;
|
|
42
|
+
--text: #1a1a2e;
|
|
43
|
+
--primary: #4361ee;
|
|
44
|
+
}
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
**`src/themes/_dark.scss`**:
|
|
48
|
+
```scss
|
|
49
|
+
[data-theme="dark"] {
|
|
50
|
+
--bg: #0f172a;
|
|
51
|
+
--text: #e2e8f0;
|
|
52
|
+
--primary: #818cf8;
|
|
53
|
+
}
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
### 2. Import themes in `styles.scss`
|
|
57
|
+
|
|
58
|
+
```scss
|
|
59
|
+
@use 'themes/light';
|
|
60
|
+
@use 'themes/dark';
|
|
61
|
+
|
|
62
|
+
body {
|
|
63
|
+
background-color: var(--bg);
|
|
64
|
+
color: var(--text);
|
|
65
|
+
transition: background-color 0.3s, color 0.3s;
|
|
66
|
+
}
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
### 3. Configure the service
|
|
70
|
+
|
|
71
|
+
In `app.config.ts`:
|
|
72
|
+
|
|
73
|
+
```typescript
|
|
74
|
+
import { ApplicationConfig } from '@angular/core';
|
|
75
|
+
import { THEME_CONFIG } from 'ngx-easy-theme-switcher';
|
|
76
|
+
|
|
77
|
+
export const appConfig: ApplicationConfig = {
|
|
78
|
+
providers: [
|
|
79
|
+
{
|
|
80
|
+
provide: THEME_CONFIG,
|
|
81
|
+
useValue: {
|
|
82
|
+
themes: ['light', 'dark'], // your theme names
|
|
83
|
+
defaultTheme: 'light', // initial theme
|
|
84
|
+
},
|
|
85
|
+
},
|
|
86
|
+
],
|
|
87
|
+
};
|
|
88
|
+
```
|
|
89
|
+
|
|
90
|
+
### 4. Add the toggle button
|
|
91
|
+
|
|
92
|
+
In any component template:
|
|
93
|
+
|
|
94
|
+
```html
|
|
95
|
+
<ets-theme-toggle />
|
|
96
|
+
```
|
|
97
|
+
|
|
98
|
+
## Customization
|
|
99
|
+
|
|
100
|
+
### Icon Fonts
|
|
101
|
+
|
|
102
|
+
The toggle component supports various icon fonts via the `iconFont` input:
|
|
103
|
+
|
|
104
|
+
```html
|
|
105
|
+
<!-- Default (FontAwesome Solid) -->
|
|
106
|
+
<ets-theme-toggle />
|
|
107
|
+
|
|
108
|
+
<!-- Material Symbols -->
|
|
109
|
+
<ets-theme-toggle
|
|
110
|
+
iconFont="material-symbols-outlined"
|
|
111
|
+
lightIcon="dark_mode"
|
|
112
|
+
darkIcon="light_mode"
|
|
113
|
+
/>
|
|
114
|
+
|
|
115
|
+
<!-- Bootstrap Icons -->
|
|
116
|
+
<ets-theme-toggle
|
|
117
|
+
iconFont="bi"
|
|
118
|
+
lightIcon="bi-sun-fill"
|
|
119
|
+
darkIcon="bi-moon-fill"
|
|
120
|
+
/>
|
|
121
|
+
```
|
|
122
|
+
|
|
123
|
+
Supported icon fonts: `fas`, `far`, `fal`, `fab`, `material-icons`, `material-icons-outlined`, `material-symbols-outlined`, `bi`, `pi`, `ion-icon`.
|
|
124
|
+
|
|
125
|
+
### Custom SVG
|
|
126
|
+
|
|
127
|
+
Pass any SVG markup as the `svgIcon` input:
|
|
128
|
+
|
|
129
|
+
```html
|
|
130
|
+
<ets-theme-toggle [svgIcon]="mySvg" />
|
|
131
|
+
```
|
|
132
|
+
|
|
133
|
+
```typescript
|
|
134
|
+
mySvg = `<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" width="24" height="24">
|
|
135
|
+
<path d="M12 3c-4.97 0-9 4.03-9 9s4.03 9 9 9..."/>
|
|
136
|
+
</svg>`;
|
|
137
|
+
```
|
|
138
|
+
|
|
139
|
+
### Custom Icon Names
|
|
140
|
+
|
|
141
|
+
Override the icon class names for each theme:
|
|
142
|
+
|
|
143
|
+
```html
|
|
144
|
+
<ets-theme-toggle
|
|
145
|
+
lightIcon="fa-sun"
|
|
146
|
+
darkIcon="fa-moon"
|
|
147
|
+
/>
|
|
148
|
+
```
|
|
149
|
+
|
|
150
|
+
### Programmatic Control
|
|
151
|
+
|
|
152
|
+
Inject `ThemeService` anywhere:
|
|
153
|
+
|
|
154
|
+
```typescript
|
|
155
|
+
import { ThemeService } from 'ngx-easy-theme-switcher';
|
|
156
|
+
|
|
157
|
+
constructor(private themeService: ThemeService) {}
|
|
158
|
+
|
|
159
|
+
// Toggle theme
|
|
160
|
+
this.themeService.toggleTheme();
|
|
161
|
+
|
|
162
|
+
// Set specific theme
|
|
163
|
+
this.themeService.setTheme('dark');
|
|
164
|
+
|
|
165
|
+
// Get current theme
|
|
166
|
+
const current = this.themeService.currentTheme;
|
|
167
|
+
|
|
168
|
+
// Observe theme changes
|
|
169
|
+
this.themeService.currentTheme$.subscribe(theme => {
|
|
170
|
+
console.log('Theme changed to:', theme);
|
|
171
|
+
});
|
|
172
|
+
```
|
|
173
|
+
|
|
174
|
+
## API
|
|
175
|
+
|
|
176
|
+
### `ThemeService`
|
|
177
|
+
|
|
178
|
+
| Method/Property | Description |
|
|
179
|
+
|-----------------------|----------------------------------------------|
|
|
180
|
+
| `currentTheme` | Get the current theme name |
|
|
181
|
+
| `currentTheme$` | Observable that emits on theme change |
|
|
182
|
+
| `themes` | The configured theme names |
|
|
183
|
+
| `toggleTheme()` | Switch between the two themes |
|
|
184
|
+
| `setTheme(name)` | Set a specific theme |
|
|
185
|
+
|
|
186
|
+
### `ThemeConfig` (InjectionToken)
|
|
187
|
+
|
|
188
|
+
| Property | Default | Description |
|
|
189
|
+
|----------------|---------------------|---------------------------------------|
|
|
190
|
+
| `themes` | `['light', 'dark']` | Pair of theme names |
|
|
191
|
+
| `attribute` | `'data-theme'` | HTML attribute set on `<html>` |
|
|
192
|
+
| `storageKey` | `'ets-pref-theme'` | localStorage key |
|
|
193
|
+
| `defaultTheme` | `'light'` | Initial theme before user choice |
|
|
194
|
+
|
|
195
|
+
### `ThemeToggleComponent` Inputs
|
|
196
|
+
|
|
197
|
+
| Input | Default | Description |
|
|
198
|
+
|-------------|--------------|------------------------------------------|
|
|
199
|
+
| `iconFont` | `'fas'` | Icon font abbreviation |
|
|
200
|
+
| `svgIcon` | — | Custom SVG markup (overrides iconFont) |
|
|
201
|
+
| `lightIcon` | `'fa-sun'` | Icon name/class for the first theme |
|
|
202
|
+
| `darkIcon` | `'fa-moon'` | Icon name/class for the second theme |
|
|
203
|
+
|
|
204
|
+
## License
|
|
205
|
+
|
|
206
|
+
Copyright (c) 2024 **Gennady Artamonov**
|
|
207
|
+
|
|
208
|
+
All use is free. Redistribution or resale of the library itself as a paid product is prohibited. See [LICENSE.md](./LICENSE.md) for full terms.
|
|
@@ -0,0 +1,195 @@
|
|
|
1
|
+
import * as i0 from '@angular/core';
|
|
2
|
+
import { InjectionToken, Optional, Inject, Injectable, Input, ChangeDetectionStrategy, Component } from '@angular/core';
|
|
3
|
+
import { BehaviorSubject, Subject, takeUntil } from 'rxjs';
|
|
4
|
+
import { NgClass } from '@angular/common';
|
|
5
|
+
|
|
6
|
+
const THEME_CONFIG = new InjectionToken('THEME_CONFIG');
|
|
7
|
+
const DEFAULT_CONFIG = {
|
|
8
|
+
themes: ['light', 'dark'],
|
|
9
|
+
attribute: 'data-theme',
|
|
10
|
+
storageKey: 'ets-pref-theme',
|
|
11
|
+
defaultTheme: 'light',
|
|
12
|
+
};
|
|
13
|
+
class ThemeService {
|
|
14
|
+
config;
|
|
15
|
+
currentThemeSubject;
|
|
16
|
+
currentTheme$;
|
|
17
|
+
constructor(config) {
|
|
18
|
+
this.config = { ...DEFAULT_CONFIG, ...config };
|
|
19
|
+
this.currentThemeSubject = new BehaviorSubject(this.getStoredTheme());
|
|
20
|
+
this.currentTheme$ = this.currentThemeSubject.asObservable();
|
|
21
|
+
this.applyTheme(this.currentThemeSubject.value);
|
|
22
|
+
}
|
|
23
|
+
/** Get the current theme name */
|
|
24
|
+
get currentTheme() {
|
|
25
|
+
return this.currentThemeSubject.value;
|
|
26
|
+
}
|
|
27
|
+
/** Get all available theme names */
|
|
28
|
+
get themes() {
|
|
29
|
+
return this.config.themes;
|
|
30
|
+
}
|
|
31
|
+
/** Toggle between the two configured themes */
|
|
32
|
+
toggleTheme() {
|
|
33
|
+
const [themeA, themeB] = this.config.themes;
|
|
34
|
+
const next = this.currentTheme === themeA ? themeB : themeA;
|
|
35
|
+
this.setTheme(next);
|
|
36
|
+
}
|
|
37
|
+
/** Set a specific theme by name */
|
|
38
|
+
setTheme(theme) {
|
|
39
|
+
if (!this.config.themes.includes(theme)) {
|
|
40
|
+
console.warn(`[EasyThemeSwitcher] Theme "${theme}" is not in configured themes:`, this.config.themes);
|
|
41
|
+
return;
|
|
42
|
+
}
|
|
43
|
+
this.currentThemeSubject.next(theme);
|
|
44
|
+
this.applyTheme(theme);
|
|
45
|
+
this.storeTheme(theme);
|
|
46
|
+
}
|
|
47
|
+
applyTheme(theme) {
|
|
48
|
+
const attr = this.config.attribute;
|
|
49
|
+
document.documentElement.setAttribute(attr, theme);
|
|
50
|
+
}
|
|
51
|
+
getStoredTheme() {
|
|
52
|
+
try {
|
|
53
|
+
const stored = localStorage.getItem(this.config.storageKey);
|
|
54
|
+
if (stored && this.config.themes.includes(stored)) {
|
|
55
|
+
return stored;
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
catch {
|
|
59
|
+
// localStorage not available
|
|
60
|
+
}
|
|
61
|
+
return this.config.defaultTheme;
|
|
62
|
+
}
|
|
63
|
+
storeTheme(theme) {
|
|
64
|
+
try {
|
|
65
|
+
localStorage.setItem(this.config.storageKey, theme);
|
|
66
|
+
}
|
|
67
|
+
catch {
|
|
68
|
+
// localStorage not available
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.25", ngImport: i0, type: ThemeService, deps: [{ token: THEME_CONFIG, optional: true }], target: i0.ɵɵFactoryTarget.Injectable });
|
|
72
|
+
static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "19.2.25", ngImport: i0, type: ThemeService, providedIn: 'root' });
|
|
73
|
+
}
|
|
74
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.25", ngImport: i0, type: ThemeService, decorators: [{
|
|
75
|
+
type: Injectable,
|
|
76
|
+
args: [{
|
|
77
|
+
providedIn: 'root',
|
|
78
|
+
}]
|
|
79
|
+
}], ctorParameters: () => [{ type: undefined, decorators: [{
|
|
80
|
+
type: Optional
|
|
81
|
+
}, {
|
|
82
|
+
type: Inject,
|
|
83
|
+
args: [THEME_CONFIG]
|
|
84
|
+
}] }] });
|
|
85
|
+
|
|
86
|
+
class ThemeToggleComponent {
|
|
87
|
+
themeService;
|
|
88
|
+
cdr;
|
|
89
|
+
/** Icon font abbreviation (e.g. 'fas', 'material-icons', 'bi') */
|
|
90
|
+
iconFont;
|
|
91
|
+
/** Custom SVG markup (overrides iconFont if provided) */
|
|
92
|
+
svgIcon;
|
|
93
|
+
/** Icon name for the first theme (default light) */
|
|
94
|
+
lightIcon;
|
|
95
|
+
/** Icon name for the second theme (default dark) */
|
|
96
|
+
darkIcon;
|
|
97
|
+
oppositeTheme = '';
|
|
98
|
+
iconClasses = {};
|
|
99
|
+
displayIconText = '';
|
|
100
|
+
destroy$ = new Subject();
|
|
101
|
+
constructor(themeService, cdr) {
|
|
102
|
+
this.themeService = themeService;
|
|
103
|
+
this.cdr = cdr;
|
|
104
|
+
}
|
|
105
|
+
ngOnInit() {
|
|
106
|
+
this.updateState(this.themeService.currentTheme);
|
|
107
|
+
this.themeService.currentTheme$
|
|
108
|
+
.pipe(takeUntil(this.destroy$))
|
|
109
|
+
.subscribe((theme) => {
|
|
110
|
+
this.updateState(theme);
|
|
111
|
+
this.cdr.markForCheck();
|
|
112
|
+
});
|
|
113
|
+
}
|
|
114
|
+
ngOnDestroy() {
|
|
115
|
+
this.destroy$.next();
|
|
116
|
+
this.destroy$.complete();
|
|
117
|
+
}
|
|
118
|
+
toggle() {
|
|
119
|
+
this.themeService.toggleTheme();
|
|
120
|
+
}
|
|
121
|
+
updateState(currentTheme) {
|
|
122
|
+
const [lightTheme, darkTheme] = this.themeService.themes;
|
|
123
|
+
const isLight = currentTheme === lightTheme;
|
|
124
|
+
this.oppositeTheme = currentTheme === lightTheme ? darkTheme : lightTheme;
|
|
125
|
+
const iconName = isLight
|
|
126
|
+
? (this.darkIcon ?? 'fa-moon')
|
|
127
|
+
: (this.lightIcon ?? 'fa-sun');
|
|
128
|
+
const font = this.iconFont ?? 'fas';
|
|
129
|
+
if (font.startsWith('material')) {
|
|
130
|
+
this.iconClasses = { [font]: true };
|
|
131
|
+
this.displayIconText = iconName;
|
|
132
|
+
}
|
|
133
|
+
else {
|
|
134
|
+
this.iconClasses = { [font]: true, [iconName]: true };
|
|
135
|
+
this.displayIconText = '';
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.25", ngImport: i0, type: ThemeToggleComponent, deps: [{ token: ThemeService }, { token: i0.ChangeDetectorRef }], target: i0.ɵɵFactoryTarget.Component });
|
|
139
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "19.2.25", type: ThemeToggleComponent, isStandalone: true, selector: "ets-theme-toggle", inputs: { iconFont: "iconFont", svgIcon: "svgIcon", lightIcon: "lightIcon", darkIcon: "darkIcon" }, ngImport: i0, template: `
|
|
140
|
+
<button
|
|
141
|
+
class="ets-toggle-btn"
|
|
142
|
+
(click)="toggle()"
|
|
143
|
+
[attr.aria-label]="'Switch to ' + oppositeTheme + ' mode'"
|
|
144
|
+
type="button"
|
|
145
|
+
>
|
|
146
|
+
@if (svgIcon) {
|
|
147
|
+
<span class="ets-icon ets-icon--svg" [innerHTML]="svgIcon"></span>
|
|
148
|
+
} @else {
|
|
149
|
+
<span
|
|
150
|
+
class="ets-icon"
|
|
151
|
+
[ngClass]="iconClasses"
|
|
152
|
+
>{{ displayIconText }}</span>
|
|
153
|
+
}
|
|
154
|
+
</button>
|
|
155
|
+
`, isInline: true, styles: [".ets-toggle-btn{display:inline-flex;align-items:center;justify-content:center;background:none;border:none;cursor:pointer;padding:8px;border-radius:50%;transition:background-color .3s,color .3s;color:inherit;font-size:1.25rem;line-height:1}.ets-toggle-btn:hover{background-color:#80808026}.ets-icon{display:inline-flex;align-items:center;justify-content:center}.ets-icon--svg{line-height:0}.ets-icon--svg ::ng-deep svg{width:1.25rem;height:1.25rem;fill:currentColor}\n"], dependencies: [{ kind: "directive", type: NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
|
|
156
|
+
}
|
|
157
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.25", ngImport: i0, type: ThemeToggleComponent, decorators: [{
|
|
158
|
+
type: Component,
|
|
159
|
+
args: [{ selector: 'ets-theme-toggle', standalone: true, imports: [NgClass], template: `
|
|
160
|
+
<button
|
|
161
|
+
class="ets-toggle-btn"
|
|
162
|
+
(click)="toggle()"
|
|
163
|
+
[attr.aria-label]="'Switch to ' + oppositeTheme + ' mode'"
|
|
164
|
+
type="button"
|
|
165
|
+
>
|
|
166
|
+
@if (svgIcon) {
|
|
167
|
+
<span class="ets-icon ets-icon--svg" [innerHTML]="svgIcon"></span>
|
|
168
|
+
} @else {
|
|
169
|
+
<span
|
|
170
|
+
class="ets-icon"
|
|
171
|
+
[ngClass]="iconClasses"
|
|
172
|
+
>{{ displayIconText }}</span>
|
|
173
|
+
}
|
|
174
|
+
</button>
|
|
175
|
+
`, changeDetection: ChangeDetectionStrategy.OnPush, styles: [".ets-toggle-btn{display:inline-flex;align-items:center;justify-content:center;background:none;border:none;cursor:pointer;padding:8px;border-radius:50%;transition:background-color .3s,color .3s;color:inherit;font-size:1.25rem;line-height:1}.ets-toggle-btn:hover{background-color:#80808026}.ets-icon{display:inline-flex;align-items:center;justify-content:center}.ets-icon--svg{line-height:0}.ets-icon--svg ::ng-deep svg{width:1.25rem;height:1.25rem;fill:currentColor}\n"] }]
|
|
176
|
+
}], ctorParameters: () => [{ type: ThemeService }, { type: i0.ChangeDetectorRef }], propDecorators: { iconFont: [{
|
|
177
|
+
type: Input
|
|
178
|
+
}], svgIcon: [{
|
|
179
|
+
type: Input
|
|
180
|
+
}], lightIcon: [{
|
|
181
|
+
type: Input
|
|
182
|
+
}], darkIcon: [{
|
|
183
|
+
type: Input
|
|
184
|
+
}] } });
|
|
185
|
+
|
|
186
|
+
/*
|
|
187
|
+
* Public API Surface of ngx-easy-theme-switcher
|
|
188
|
+
*/
|
|
189
|
+
|
|
190
|
+
/**
|
|
191
|
+
* Generated bundle index. Do not edit.
|
|
192
|
+
*/
|
|
193
|
+
|
|
194
|
+
export { THEME_CONFIG, ThemeService, ThemeToggleComponent };
|
|
195
|
+
//# sourceMappingURL=ngx-easy-theme-switcher.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ngx-easy-theme-switcher.mjs","sources":["../../../projects/ngx-easy-theme-switcher/src/lib/theme.service.ts","../../../projects/ngx-easy-theme-switcher/src/lib/theme-toggle/theme-toggle.component.ts","../../../projects/ngx-easy-theme-switcher/src/public-api.ts","../../../projects/ngx-easy-theme-switcher/src/ngx-easy-theme-switcher.ts"],"sourcesContent":["import { Injectable, Inject, Optional, InjectionToken } from '@angular/core';\nimport { BehaviorSubject, Observable } from 'rxjs';\n\nexport interface ThemeConfig {\n /** Names of the two themes, e.g. ['light', 'dark'] */\n themes?: [string, string];\n /** CSS attribute/class to set on the document element */\n attribute?: string;\n /** Storage key for persisting theme preference */\n storageKey?: string;\n /** Default theme (index 0 or 1 from themes array) */\n defaultTheme?: string;\n}\n\nexport const THEME_CONFIG = new InjectionToken<ThemeConfig>('THEME_CONFIG');\n\nconst DEFAULT_CONFIG: ThemeConfig = {\n themes: ['light', 'dark'],\n attribute: 'data-theme',\n storageKey: 'ets-pref-theme',\n defaultTheme: 'light',\n};\n\n@Injectable({\n providedIn: 'root',\n})\nexport class ThemeService {\n private config: ThemeConfig;\n private currentThemeSubject: BehaviorSubject<string>;\n public currentTheme$: Observable<string>;\n\n constructor(@Optional() @Inject(THEME_CONFIG) config?: ThemeConfig) {\n this.config = { ...DEFAULT_CONFIG, ...config };\n this.currentThemeSubject = new BehaviorSubject<string>(\n this.getStoredTheme()\n );\n this.currentTheme$ = this.currentThemeSubject.asObservable();\n this.applyTheme(this.currentThemeSubject.value);\n }\n\n /** Get the current theme name */\n get currentTheme(): string {\n return this.currentThemeSubject.value;\n }\n\n /** Get all available theme names */\n get themes(): [string, string] {\n return this.config.themes!;\n }\n\n /** Toggle between the two configured themes */\n toggleTheme(): void {\n const [themeA, themeB] = this.config.themes!;\n const next = this.currentTheme === themeA ? themeB : themeA;\n this.setTheme(next);\n }\n\n /** Set a specific theme by name */\n setTheme(theme: string): void {\n if (!this.config.themes!.includes(theme)) {\n console.warn(\n `[EasyThemeSwitcher] Theme \"${theme}\" is not in configured themes:`,\n this.config.themes\n );\n return;\n }\n this.currentThemeSubject.next(theme);\n this.applyTheme(theme);\n this.storeTheme(theme);\n }\n\n private applyTheme(theme: string): void {\n const attr = this.config.attribute!;\n document.documentElement.setAttribute(attr, theme);\n }\n\n private getStoredTheme(): string {\n try {\n const stored = localStorage.getItem(this.config.storageKey!);\n if (stored && this.config.themes!.includes(stored)) {\n return stored;\n }\n } catch {\n // localStorage not available\n }\n return this.config.defaultTheme!;\n }\n\n private storeTheme(theme: string): void {\n try {\n localStorage.setItem(this.config.storageKey!, theme);\n } catch {\n // localStorage not available\n }\n }\n}\n","import { Component, Input, ChangeDetectionStrategy, ChangeDetectorRef, OnInit, OnDestroy } from '@angular/core';\nimport { NgClass } from '@angular/common';\nimport { Subject, takeUntil } from 'rxjs';\nimport { ThemeService } from '../theme.service';\n\nexport type IconFont =\n | 'fas' // FontAwesome Solid\n | 'far' // FontAwesome Regular\n | 'fal' // FontAwesome Light\n | 'fab' // FontAwesome Brands\n | 'material-icons'\n | 'material-icons-outlined'\n | 'material-symbols-outlined'\n | 'bi' // Bootstrap Icons\n | 'pi' // PrimeIcons\n | 'ion-icon'; // Ionicons\n\n@Component({\n selector: 'ets-theme-toggle',\n standalone: true,\n imports: [NgClass],\n template: `\n <button\n class=\"ets-toggle-btn\"\n (click)=\"toggle()\"\n [attr.aria-label]=\"'Switch to ' + oppositeTheme + ' mode'\"\n type=\"button\"\n >\n @if (svgIcon) {\n <span class=\"ets-icon ets-icon--svg\" [innerHTML]=\"svgIcon\"></span>\n } @else {\n <span\n class=\"ets-icon\"\n [ngClass]=\"iconClasses\"\n >{{ displayIconText }}</span>\n }\n </button>\n `,\n styles: `\n .ets-toggle-btn {\n display: inline-flex;\n align-items: center;\n justify-content: center;\n background: none;\n border: none;\n cursor: pointer;\n padding: 8px;\n border-radius: 50%;\n transition: background-color 0.3s, color 0.3s;\n color: inherit;\n font-size: 1.25rem;\n line-height: 1;\n }\n .ets-toggle-btn:hover {\n background-color: rgba(128, 128, 128, 0.15);\n }\n .ets-icon {\n display: inline-flex;\n align-items: center;\n justify-content: center;\n }\n .ets-icon--svg {\n line-height: 0;\n }\n .ets-icon--svg ::ng-deep svg {\n width: 1.25rem;\n height: 1.25rem;\n fill: currentColor;\n }\n `,\n changeDetection: ChangeDetectionStrategy.OnPush,\n})\nexport class ThemeToggleComponent implements OnInit, OnDestroy {\n /** Icon font abbreviation (e.g. 'fas', 'material-icons', 'bi') */\n @Input() iconFont?: IconFont;\n\n /** Custom SVG markup (overrides iconFont if provided) */\n @Input() svgIcon?: string;\n\n /** Icon name for the first theme (default light) */\n @Input() lightIcon?: string;\n\n /** Icon name for the second theme (default dark) */\n @Input() darkIcon?: string;\n\n protected oppositeTheme = '';\n protected iconClasses: Record<string, boolean> = {};\n protected displayIconText = '';\n\n private destroy$ = new Subject<void>();\n\n constructor(\n private themeService: ThemeService,\n private cdr: ChangeDetectorRef,\n ) {}\n\n ngOnInit(): void {\n this.updateState(this.themeService.currentTheme);\n this.themeService.currentTheme$\n .pipe(takeUntil(this.destroy$))\n .subscribe((theme) => {\n this.updateState(theme);\n this.cdr.markForCheck();\n });\n }\n\n ngOnDestroy(): void {\n this.destroy$.next();\n this.destroy$.complete();\n }\n\n toggle(): void {\n this.themeService.toggleTheme();\n }\n\n private updateState(currentTheme: string): void {\n const [lightTheme, darkTheme] = this.themeService.themes;\n const isLight = currentTheme === lightTheme;\n\n this.oppositeTheme = currentTheme === lightTheme ? darkTheme : lightTheme;\n\n const iconName = isLight\n ? (this.darkIcon ?? 'fa-moon')\n : (this.lightIcon ?? 'fa-sun');\n\n const font = this.iconFont ?? 'fas';\n\n if (font.startsWith('material')) {\n this.iconClasses = { [font]: true };\n this.displayIconText = iconName;\n } else {\n this.iconClasses = { [font]: true, [iconName]: true };\n this.displayIconText = '';\n }\n }\n}\n","/*\r\n * Public API Surface of ngx-easy-theme-switcher\r\n */\r\n\r\nexport { ThemeService, THEME_CONFIG } from './lib/theme.service';\r\nexport type { ThemeConfig } from './lib/theme.service';\r\nexport { ThemeToggleComponent } from './lib/theme-toggle/theme-toggle.component';\r\nexport type { IconFont } from './lib/theme-toggle/theme-toggle.component';\r\n","/**\n * Generated bundle index. Do not edit.\n */\n\nexport * from './public-api';\n"],"names":["i1.ThemeService"],"mappings":"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"}
|
package/index.d.ts
ADDED
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import { ChangeDetectorRef, OnInit, OnDestroy } from '@angular/core';
|
|
2
|
+
import { ThemeService } from '../theme.service';
|
|
3
|
+
import * as i0 from "@angular/core";
|
|
4
|
+
export type IconFont = 'fas' | 'far' | 'fal' | 'fab' | 'material-icons' | 'material-icons-outlined' | 'material-symbols-outlined' | 'bi' | 'pi' | 'ion-icon';
|
|
5
|
+
export declare class ThemeToggleComponent implements OnInit, OnDestroy {
|
|
6
|
+
private themeService;
|
|
7
|
+
private cdr;
|
|
8
|
+
/** Icon font abbreviation (e.g. 'fas', 'material-icons', 'bi') */
|
|
9
|
+
iconFont?: IconFont;
|
|
10
|
+
/** Custom SVG markup (overrides iconFont if provided) */
|
|
11
|
+
svgIcon?: string;
|
|
12
|
+
/** Icon name for the first theme (default light) */
|
|
13
|
+
lightIcon?: string;
|
|
14
|
+
/** Icon name for the second theme (default dark) */
|
|
15
|
+
darkIcon?: string;
|
|
16
|
+
protected oppositeTheme: string;
|
|
17
|
+
protected iconClasses: Record<string, boolean>;
|
|
18
|
+
protected displayIconText: string;
|
|
19
|
+
private destroy$;
|
|
20
|
+
constructor(themeService: ThemeService, cdr: ChangeDetectorRef);
|
|
21
|
+
ngOnInit(): void;
|
|
22
|
+
ngOnDestroy(): void;
|
|
23
|
+
toggle(): void;
|
|
24
|
+
private updateState;
|
|
25
|
+
static ɵfac: i0.ɵɵFactoryDeclaration<ThemeToggleComponent, never>;
|
|
26
|
+
static ɵcmp: i0.ɵɵComponentDeclaration<ThemeToggleComponent, "ets-theme-toggle", never, { "iconFont": { "alias": "iconFont"; "required": false; }; "svgIcon": { "alias": "svgIcon"; "required": false; }; "lightIcon": { "alias": "lightIcon"; "required": false; }; "darkIcon": { "alias": "darkIcon"; "required": false; }; }, {}, never, never, true, never>;
|
|
27
|
+
}
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import { InjectionToken } from '@angular/core';
|
|
2
|
+
import { Observable } from 'rxjs';
|
|
3
|
+
import * as i0 from "@angular/core";
|
|
4
|
+
export interface ThemeConfig {
|
|
5
|
+
/** Names of the two themes, e.g. ['light', 'dark'] */
|
|
6
|
+
themes?: [string, string];
|
|
7
|
+
/** CSS attribute/class to set on the document element */
|
|
8
|
+
attribute?: string;
|
|
9
|
+
/** Storage key for persisting theme preference */
|
|
10
|
+
storageKey?: string;
|
|
11
|
+
/** Default theme (index 0 or 1 from themes array) */
|
|
12
|
+
defaultTheme?: string;
|
|
13
|
+
}
|
|
14
|
+
export declare const THEME_CONFIG: InjectionToken<ThemeConfig>;
|
|
15
|
+
export declare class ThemeService {
|
|
16
|
+
private config;
|
|
17
|
+
private currentThemeSubject;
|
|
18
|
+
currentTheme$: Observable<string>;
|
|
19
|
+
constructor(config?: ThemeConfig);
|
|
20
|
+
/** Get the current theme name */
|
|
21
|
+
get currentTheme(): string;
|
|
22
|
+
/** Get all available theme names */
|
|
23
|
+
get themes(): [string, string];
|
|
24
|
+
/** Toggle between the two configured themes */
|
|
25
|
+
toggleTheme(): void;
|
|
26
|
+
/** Set a specific theme by name */
|
|
27
|
+
setTheme(theme: string): void;
|
|
28
|
+
private applyTheme;
|
|
29
|
+
private getStoredTheme;
|
|
30
|
+
private storeTheme;
|
|
31
|
+
static ɵfac: i0.ɵɵFactoryDeclaration<ThemeService, [{ optional: true; }]>;
|
|
32
|
+
static ɵprov: i0.ɵɵInjectableDeclaration<ThemeService>;
|
|
33
|
+
}
|
package/package.json
ADDED
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "ngx-easy-theme-switcher",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"description": "A lightweight Angular service and component for seamless dark/light theme switching. Supports FontAwesome, Material Icons, Bootstrap Icons, custom SVG icons, and popular icon fonts out of the box. Persistent theme selection with localStorage.",
|
|
5
|
+
"keywords": [
|
|
6
|
+
"angular",
|
|
7
|
+
"theme",
|
|
8
|
+
"dark theme",
|
|
9
|
+
"utils",
|
|
10
|
+
"service",
|
|
11
|
+
"ng",
|
|
12
|
+
"typescript",
|
|
13
|
+
"theme-switcher",
|
|
14
|
+
"dark-mode",
|
|
15
|
+
"light-mode",
|
|
16
|
+
"themes",
|
|
17
|
+
"angular-library",
|
|
18
|
+
"dark",
|
|
19
|
+
"light",
|
|
20
|
+
"toggle",
|
|
21
|
+
"icon",
|
|
22
|
+
"fontawesome",
|
|
23
|
+
"material-icons",
|
|
24
|
+
"bootstrap-icons"
|
|
25
|
+
],
|
|
26
|
+
"author": "Gennady Artamonov",
|
|
27
|
+
"license": "SEE LICENSE IN LICENSE.md",
|
|
28
|
+
"repository": {
|
|
29
|
+
"type": "git",
|
|
30
|
+
"url": "https://github.com/cyclon-b/ngx-easy-theme-switcher.git"
|
|
31
|
+
},
|
|
32
|
+
"peerDependencies": {
|
|
33
|
+
"@angular/common": "^19.2.0",
|
|
34
|
+
"@angular/core": "^19.2.0"
|
|
35
|
+
},
|
|
36
|
+
"dependencies": {
|
|
37
|
+
"tslib": "^2.3.0"
|
|
38
|
+
},
|
|
39
|
+
"sideEffects": false,
|
|
40
|
+
"module": "fesm2022/ngx-easy-theme-switcher.mjs",
|
|
41
|
+
"typings": "index.d.ts",
|
|
42
|
+
"exports": {
|
|
43
|
+
"./package.json": {
|
|
44
|
+
"default": "./package.json"
|
|
45
|
+
},
|
|
46
|
+
".": {
|
|
47
|
+
"types": "./index.d.ts",
|
|
48
|
+
"default": "./fesm2022/ngx-easy-theme-switcher.mjs"
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
}
|
package/public-api.d.ts
ADDED
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
export { ThemeService, THEME_CONFIG } from './lib/theme.service';
|
|
2
|
+
export type { ThemeConfig } from './lib/theme.service';
|
|
3
|
+
export { ThemeToggleComponent } from './lib/theme-toggle/theme-toggle.component';
|
|
4
|
+
export type { IconFont } from './lib/theme-toggle/theme-toggle.component';
|