ngx-theme-stack 0.0.15 → 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 +103 -36
- package/fesm2022/ngx-theme-stack.mjs +10 -1
- package/fesm2022/ngx-theme-stack.mjs.map +1 -1
- package/package.json +4 -5
- package/schematics/collection.json +1 -1
- package/schematics/ng-add/app-config.d.ts +11 -0
- package/schematics/ng-add/app-config.js +53 -0
- package/schematics/ng-add/app-config.js.map +1 -0
- package/schematics/ng-add/constants.d.ts +18 -0
- package/schematics/ng-add/constants.js +22 -0
- package/schematics/ng-add/constants.js.map +1 -0
- package/schematics/ng-add/index.d.ts +8 -0
- package/schematics/ng-add/index.js +89 -0
- package/schematics/ng-add/index.js.map +1 -1
- package/schematics/ng-add/schema.d.ts +1 -12
- package/schematics/ng-add/schema.json +3 -38
- package/schematics/ng-add/utils.d.ts +20 -0
- package/schematics/ng-add/utils.js +70 -0
- package/schematics/ng-add/utils.js.map +1 -0
- package/types/ngx-theme-stack.d.ts +11 -2
- package/schematics/ng-add/index.cjs +0 -157
- /package/schematics/ng-add/{schema.cjs → schema.js} +0 -0
package/README.md
CHANGED
|
@@ -1,64 +1,131 @@
|
|
|
1
|
-
#
|
|
1
|
+
# ngx-theme-stack 🎨
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
A complete and lightweight solution for managing themes (light, dark, system, and custom) in **Angular** applications. Built with performance, accessibility, and SSR (Server-Side Rendering) support in mind.
|
|
4
4
|
|
|
5
|
-
##
|
|
5
|
+
## 🚀 Features
|
|
6
6
|
|
|
7
|
-
|
|
7
|
+
- ⚡ **Single Command Installation**: Automatic configuration via `ng add`.
|
|
8
|
+
- 🌓 **System Preference Detection**: Automatic synchronization with OS settings (`prefers-color-scheme`).
|
|
9
|
+
- 🔄 **Dynamic Switching**: Multiple ways to toggle themes (toggle, cycle, select).
|
|
10
|
+
- 🛠️ **Highly Customizable**: Support for custom themes, class prefixes, and configurable storage.
|
|
11
|
+
- 🧱 **Modern Architecture**: Powered by Angular Signals for maximum reactivity and performance.
|
|
12
|
+
- 🌍 **SSR Ready**: Safe to use in Server-Side Rendering environments.
|
|
8
13
|
|
|
9
|
-
|
|
10
|
-
ng generate component component-name
|
|
11
|
-
```
|
|
14
|
+
## 📦 Installation
|
|
12
15
|
|
|
13
|
-
|
|
16
|
+
To install the library and configure it automatically in your project, run:
|
|
14
17
|
|
|
15
18
|
```bash
|
|
16
|
-
ng
|
|
19
|
+
ng add ngx-theme-stack
|
|
17
20
|
```
|
|
18
21
|
|
|
19
|
-
|
|
22
|
+
### Installation Modes
|
|
20
23
|
|
|
21
|
-
|
|
24
|
+
When running `ng add`, you will be presented with two configuration options:
|
|
22
25
|
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
+
1. **Quick Mode**:
|
|
27
|
+
- Applies default configuration instantly.
|
|
28
|
+
- Initial theme: `system`.
|
|
29
|
+
- Apply mode: `class` (adds the theme class to the `<html>` element).
|
|
30
|
+
- Available themes: `['light', 'dark', 'system']`.
|
|
26
31
|
|
|
27
|
-
|
|
32
|
+
2. **Custom Mode**:
|
|
33
|
+
- Choose which themes to include (e.g., if you have a `blue` or `high-contrast` theme).
|
|
34
|
+
- Configure the default theme upon app startup.
|
|
35
|
+
- Change the `localStorage` key where the theme choice is saved.
|
|
36
|
+
- Decide how to apply themes: via classes (`class`), attributes (`data-theme`), or both.
|
|
28
37
|
|
|
29
|
-
|
|
38
|
+
## 🏗️ Architecture & Extensibility
|
|
30
39
|
|
|
31
|
-
|
|
40
|
+
The library is designed to be flexible. The **`CoreThemeService`** is the foundation:
|
|
32
41
|
|
|
33
|
-
|
|
42
|
+
- **Solid Base:** Manages state (`Signal`), persistence (`localStorage`), system detection (`matchMedia`), and safe DOM manipulation (SSR compatible).
|
|
43
|
+
- **Extensibility:** You can inject `CoreThemeService` to build your own custom services or components with specific business logic.
|
|
34
44
|
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
```
|
|
45
|
+
### Utility Services (Ready to Use)
|
|
46
|
+
For common use cases, we include three services with predefined logic:
|
|
38
47
|
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
```
|
|
48
|
+
1. **`ThemeToggleService`**: A simple binary switch between `light` and `dark`.
|
|
49
|
+
2. **`ThemeSelectService`**: Exposes the full list of themes and methods to select them.
|
|
50
|
+
3. **`ThemeCycleService`**: A circular function to cycle through all available themes with a single click.
|
|
43
51
|
|
|
44
|
-
|
|
52
|
+
---
|
|
45
53
|
|
|
46
|
-
|
|
54
|
+
## ⚙️ Supported Versions
|
|
47
55
|
|
|
48
|
-
|
|
49
|
-
|
|
56
|
+
| Angular Version | Support |
|
|
57
|
+
| :--- | :--- |
|
|
58
|
+
| **Angular 21** | ✅ Stable |
|
|
59
|
+
| **Angular 20** | ✅ Stable |
|
|
60
|
+
| **Angular 19** | ✅ Stable |
|
|
61
|
+
| **Angular 18** | ✅ Stable |
|
|
62
|
+
|
|
63
|
+
## 🛠️ Basic Usage
|
|
64
|
+
|
|
65
|
+
### CoreThemeService
|
|
66
|
+
|
|
67
|
+
This is the main service managing the theme state.
|
|
68
|
+
|
|
69
|
+
```typescript
|
|
70
|
+
import { inject } from '@angular/core';
|
|
71
|
+
import { CoreThemeService } from 'ngx-theme-stack';
|
|
72
|
+
|
|
73
|
+
@Component({ ... })
|
|
74
|
+
export class MyComponent {
|
|
75
|
+
private themeService = inject(CoreThemeService);
|
|
76
|
+
|
|
77
|
+
// Reactive signals
|
|
78
|
+
isDark = this.themeService.isDark; // boolean (true/false)
|
|
79
|
+
selectedTheme = this.themeService.selectedTheme; // 'light' | 'dark' | 'system' | ...
|
|
80
|
+
|
|
81
|
+
changeTheme(theme: string) {
|
|
82
|
+
this.themeService.setTheme(theme);
|
|
83
|
+
}
|
|
84
|
+
}
|
|
50
85
|
```
|
|
51
86
|
|
|
52
|
-
|
|
87
|
+
### Utility Services Examples
|
|
53
88
|
|
|
54
|
-
|
|
89
|
+
#### ThemeToggleService usage:
|
|
55
90
|
|
|
56
|
-
```
|
|
57
|
-
|
|
91
|
+
```typescript
|
|
92
|
+
import { ThemeToggleService } from 'ngx-theme-stack';
|
|
93
|
+
|
|
94
|
+
@Component({
|
|
95
|
+
selector: 'app-theme-toggle',
|
|
96
|
+
template: `
|
|
97
|
+
<button (click)="toggle.toggle()">
|
|
98
|
+
Switch to {{ toggle.isDark() ? 'Light' : 'Dark' }}
|
|
99
|
+
</button>
|
|
100
|
+
`
|
|
101
|
+
})
|
|
102
|
+
export class ThemeToggleComponent {
|
|
103
|
+
protected toggle = inject(ThemeToggleService);
|
|
104
|
+
}
|
|
58
105
|
```
|
|
59
106
|
|
|
60
|
-
|
|
107
|
+
## 🎨 Styling
|
|
108
|
+
|
|
109
|
+
By default, the library adds the theme name as a class or attribute to the `<html>` element. Use this in your global styles:
|
|
110
|
+
|
|
111
|
+
```css
|
|
112
|
+
/* Using Classes (Default) */
|
|
113
|
+
html.dark {
|
|
114
|
+
background-color: #121212;
|
|
115
|
+
color: white;
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
html.light {
|
|
119
|
+
background-color: #ffffff;
|
|
120
|
+
color: #333;
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
/* Using Attributes */
|
|
124
|
+
[data-theme='blue'] {
|
|
125
|
+
--primary-color: #0000ff;
|
|
126
|
+
}
|
|
127
|
+
```
|
|
61
128
|
|
|
62
|
-
##
|
|
129
|
+
## 📄 License
|
|
63
130
|
|
|
64
|
-
|
|
131
|
+
[MIT](./LICENSE)
|
|
@@ -3,8 +3,17 @@ import * as i0 from '@angular/core';
|
|
|
3
3
|
import { InjectionToken, inject, DestroyRef, DOCUMENT, PLATFORM_ID, signal, computed, effect, Injectable } from '@angular/core';
|
|
4
4
|
|
|
5
5
|
/** Built-in themes. All other values are considered custom themes. */
|
|
6
|
-
const DEFAULT_THEMES = ['
|
|
6
|
+
const DEFAULT_THEMES = ['system', 'light', 'dark'];
|
|
7
7
|
|
|
8
|
+
/**
|
|
9
|
+
* ⚠ ATTENTION: SHARED CONFIGURATION VALUES
|
|
10
|
+
*
|
|
11
|
+
* These values MUST match the schematic defaults in:
|
|
12
|
+
* projects/ngx-theme-stack/schematics/ng-add/index.ts
|
|
13
|
+
*
|
|
14
|
+
* If you change any of these, you MUST also update the schematic's DEFAULTS
|
|
15
|
+
* constant so 'ng add' continues to provide correct hints and clean code.
|
|
16
|
+
*/
|
|
8
17
|
const DEFAULT_NG_CONFIG = {
|
|
9
18
|
theme: 'system',
|
|
10
19
|
storageKey: 'ngx-theme-stack-theme',
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ngx-theme-stack.mjs","sources":["../../../projects/ngx-theme-stack/src/lib/types.ts","../../../projects/ngx-theme-stack/src/lib/services/theme-stack.config.ts","../../../projects/ngx-theme-stack/src/lib/core/core-theme.service.ts","../../../projects/ngx-theme-stack/src/lib/services/theme-cycle.service.ts","../../../projects/ngx-theme-stack/src/lib/services/theme-select.service.ts","../../../projects/ngx-theme-stack/src/lib/services/theme-toggle.service.ts","../../../projects/ngx-theme-stack/src/public-api.ts","../../../projects/ngx-theme-stack/src/ngx-theme-stack.ts"],"sourcesContent":["/** Built-in themes. All other values are considered custom themes. */\nexport const DEFAULT_THEMES = ['light', 'dark', 'system'] as const;\n\n/** String union with autocompletion for defaults + any string support for customization. */\nexport type NgTheme = (typeof DEFAULT_THEMES)[number] | (string & {});\n\nexport type NgSystemTheme = Exclude<NgTheme, 'system'>;\nexport type NgMode = 'attribute' | 'class' | 'both';\n\nexport interface NgConfig {\n theme: NgTheme;\n storageKey: string;\n mode: NgMode;\n themes: NgTheme[];\n}\n","import { InjectionToken } from '@angular/core';\nimport { DEFAULT_THEMES, NgConfig } from '../types';\n\nexport const DEFAULT_NG_CONFIG = {\n theme: 'system',\n storageKey: 'ngx-theme-stack-theme',\n mode: 'class',\n themes: [...DEFAULT_THEMES],\n} satisfies NgConfig;\n\nexport const NGX_THEME_STACK_CONFIG = new InjectionToken<NgConfig>('NGX_THEME_STACK_CONFIG', {\n factory: () => DEFAULT_NG_CONFIG,\n});\n\n/**\n * Helper function to provide Theme Stack configuration.\n */\nexport function provideThemeStack(config: Partial<NgConfig> = {}) {\n return {\n provide: NGX_THEME_STACK_CONFIG,\n useValue: {\n ...DEFAULT_NG_CONFIG,\n ...config,\n } satisfies NgConfig,\n };\n}\n","import { isPlatformBrowser } from '@angular/common';\nimport {\n computed,\n DestroyRef,\n DOCUMENT,\n effect,\n inject,\n Injectable,\n PLATFORM_ID,\n signal,\n} from '@angular/core';\nimport { NGX_THEME_STACK_CONFIG } from '../services/theme-stack.config';\nimport { NgTheme, NgSystemTheme } from '../types';\n\n\n/**\n * Core service for managing the application's color theme.\n *\n * Handles theme persistence, system preference detection, and DOM updates.\n * Supports built-in themes ('dark', 'light', 'system') and custom extensions.\n */\n@Injectable({ providedIn: 'root' })\nexport class CoreThemeService {\n // ── Dependencies ──────────────────────────────────────────────────────────\n\n readonly #config = inject(NGX_THEME_STACK_CONFIG);\n readonly #destroyRef = inject(DestroyRef);\n readonly #document = inject(DOCUMENT);\n readonly #isBrowser = isPlatformBrowser(inject(PLATFORM_ID));\n\n // ── Theme configuration ───────────────────────────────────────────────────\n\n /** List of available themes for Select/Cycle services. Defaults to ['light', 'dark', 'system']. */\n readonly availableThemes = this.#config.themes;\n\n /** Internal Set for O(1) existence checks. */\n readonly #validThemes = new Set<NgTheme>(this.availableThemes);\n\n // ── System preference ─────────────────────────────────────────────────────\n\n /** MediaQueryList for OS color scheme, created once and reused. Null in SSR. */\n readonly #mediaQuery: MediaQueryList | null = this.#isBrowser\n ? (this.#document.defaultView?.matchMedia('(prefers-color-scheme: dark)') ?? null)\n : null;\n\n readonly #systemPreference = signal<NgSystemTheme>(this.resolveSystemPreference());\n\n // ── Theme state ───────────────────────────────────────────────────────────\n\n readonly #selectedTheme = signal<NgTheme>(this.resolveInitialTheme());\n\n /** The theme explicitly selected by the user. May be `'system'`. */\n readonly selectedTheme = this.#selectedTheme.asReadonly();\n\n /** Resolved theme applied to the DOM. Always `'dark'` or `'light'` (or custom) — never `'system'`. */\n readonly userTheme = computed(() => {\n const theme = this.#selectedTheme();\n return theme === 'system' ? this.#systemPreference() : theme;\n });\n\n /** Whether the currently applied theme is dark. */\n readonly isDark = computed(() => this.userTheme() === 'dark');\n\n /** Whether the currently applied theme is light. */\n readonly isLight = computed(() => this.userTheme() === 'light');\n\n // ── Event handler ─────────────────────────────────────────────────────────\n\n readonly #onSystemPreferenceChange = () =>\n this.#systemPreference.set(this.resolveSystemPreference());\n\n // ── Lifecycle ─────────────────────────────────────────────────────────────\n\n constructor() {\n if (this.#isBrowser && this.#selectedTheme() === 'system') {\n this.startSystemThemeListener();\n }\n\n effect(() => this.applyThemeToDOM(this.userTheme()));\n\n this.#destroyRef.onDestroy(() => this.stopSystemThemeListener());\n }\n\n // ── Public API ────────────────────────────────────────────────────────────\n\n /**\n * Changes the active theme.\n *\n * Persists the choice explicitly so that switching e.g. from `'system'` to\n * `'light'` is saved even when the resolved `userTheme` did not change\n * (system preference was already `'light'`).\n *\n * @param theme - The theme to apply: `'dark'`, `'light'`, `'system'`, or a custom theme name.\n * @throws If `theme` is not a valid theme according to library configuration.\n */\n public setTheme(theme: NgTheme): void {\n if (!this.#isBrowser) return;\n\n if (!this.#validThemes.has(theme)) {\n throw new Error(\n `[ngx-theme-stack] Invalid theme: \"${theme}\". Valid values are: ${[...this.#validThemes].join(', ')}.`,\n );\n }\n\n if (theme === 'system') {\n this.#systemPreference.set(this.resolveSystemPreference());\n this.startSystemThemeListener();\n } else {\n this.stopSystemThemeListener();\n }\n\n this.#selectedTheme.set(theme);\n this.saveTheme(theme);\n }\n\n // ── Private ───────────────────────────────────────────────────────────────\n\n private resolveSystemPreference(): NgSystemTheme {\n return this.#mediaQuery?.matches ? 'dark' : 'light';\n }\n\n private resolveInitialTheme(): NgTheme {\n if (!this.#isBrowser) return this.#config.theme;\n return this.readStoredTheme() ?? this.#config.theme;\n }\n\n private startSystemThemeListener(): void {\n if (!this.#mediaQuery) return;\n this.stopSystemThemeListener();\n this.#mediaQuery.addEventListener('change', this.#onSystemPreferenceChange);\n }\n\n private stopSystemThemeListener(): void {\n this.#mediaQuery?.removeEventListener('change', this.#onSystemPreferenceChange);\n }\n\n private applyThemeToDOM(userTheme: NgTheme): void {\n if (!this.#isBrowser) return;\n\n const host = this.#document.documentElement;\n const { mode } = this.#config;\n\n if (mode === 'attribute' || mode === 'both') {\n this.applyThemeAttribute(host, userTheme);\n }\n\n if (mode === 'class' || mode === 'both') {\n this.applyThemeClasses(host, userTheme);\n }\n\n this.applyColorSchemeHint(host, userTheme);\n }\n\n private applyThemeAttribute(host: HTMLElement, theme: NgTheme): void {\n host.setAttribute('data-theme', theme);\n }\n\n private applyThemeClasses(host: HTMLElement, theme: NgTheme): void {\n for (const t of this.availableThemes) {\n host.classList.remove(t);\n }\n\n host.classList.add(theme);\n }\n\n private applyColorSchemeHint(host: HTMLElement, theme: NgTheme): void {\n if (theme === 'dark' || theme === 'light') {\n host.style.setProperty('color-scheme', theme);\n return;\n }\n\n host.style.removeProperty('color-scheme');\n }\n\n private readStoredTheme(): NgTheme | null {\n try {\n const stored = localStorage.getItem(this.#config.storageKey);\n if (stored && this.#validThemes.has(stored as NgTheme)) {\n return stored as NgTheme;\n }\n return null;\n } catch (e) {\n console.warn('[ngx-theme-stack] Could not read theme from localStorage.', e);\n return null;\n }\n }\n\n private saveTheme(theme: NgTheme): void {\n try {\n localStorage.setItem(this.#config.storageKey, theme);\n } catch (e) {\n console.warn('[ngx-theme-stack] Could not save theme to localStorage.', e);\n }\n }\n}\n","import { inject, Injectable } from '@angular/core';\nimport { CoreThemeService } from '../core/core-theme.service';\n\n/**\n * Convenience service for cycling through themes in a fixed order.\n *\n * Default cycle: `'light'` → `'dark'` → `'system'` → `'light'` → ...\n *\n * Use this when you want to offer users a single button that rotates\n * through all available theme options.\n */\n@Injectable({ providedIn: 'root' })\nexport class ThemeCycleService {\n\n readonly #core = inject(CoreThemeService);\n\n /** List of all configured themes for cycling. Defaults to ['light', 'dark', 'system']. */\n readonly #cycle = this.#core.availableThemes;\n\n /** The theme explicitly selected by the user. May be `'system'`. */\n readonly selectedTheme = this.#core.selectedTheme;\n\n /** Resolved theme applied to the DOM. Always concrete — never `'system'`. */\n readonly userTheme = this.#core.userTheme;\n\n /** Whether the currently applied theme is dark. */\n readonly isDark = this.#core.isDark;\n\n /** Whether the currently applied theme is light. */\n readonly isLight = this.#core.isLight;\n\n /**\n * Advances to the next theme in the cycle.\n *\n * Cycle order is determined by the configured `themes` property in `NgConfig`.\n *\n * If the current theme is not found in the cycle (e.g. set externally),\n * the cycle restarts from the first theme.\n */\n cycle(): void {\n const current = this.#core.selectedTheme();\n const index = this.#cycle.indexOf(current);\n const next = this.#cycle[(index + 1) % this.#cycle.length];\n this.#core.setTheme(next);\n }\n}\n","import { inject, Injectable } from '@angular/core';\nimport { CoreThemeService } from '../core/core-theme.service';\nimport { NgTheme } from '../types';\n\n\n/**\n * Convenience service for selecting a theme from a list.\n *\n * Use this when you want to bind a `<select>` or a group of radio/tab\n * buttons to the full set of available themes.\n */\n@Injectable({ providedIn: 'root' })\nexport class ThemeSelectService {\n\n readonly #core = inject(CoreThemeService);\n\n /** List of all configured themes. Defaults to ['light', 'dark', 'system']. */\n readonly availableThemes = this.#core.availableThemes;\n\n /** The theme explicitly selected by the user. May be `'system'`. */\n readonly selectedTheme = this.#core.selectedTheme;\n\n /** Resolved theme applied to the DOM. Always concrete — never `'system'`. */\n readonly userTheme = this.#core.userTheme;\n\n /** Whether the currently applied theme is dark. */\n readonly isDark = this.#core.isDark;\n\n /** Whether the currently applied theme is light. */\n readonly isLight = this.#core.isLight;\n\n /**\n * Applies the given theme.\n *\n * @param theme - The theme to apply: `'dark'`, `'light'`, `'system'`, or custom.\n * @throws If `theme` is not a valid theme according to library configuration.\n */\n select(theme: NgTheme): void {\n this.#core.setTheme(theme);\n }\n}\n","import { inject, Injectable } from '@angular/core';\nimport { CoreThemeService } from '../core/core-theme.service';\n\n/**\n * Convenience service for toggling between `'dark'` and `'light'`.\n *\n * Use this when you only need a simple on/off switch and do not\n * need to manage `'system'` or cycle through themes.\n */\n@Injectable({ providedIn: 'root' })\nexport class ThemeToggleService {\n\n readonly #core = inject(CoreThemeService);\n\n /** Resolved theme applied to the DOM. Always concrete — never `'system'`. */\n readonly userTheme = this.#core.userTheme;\n\n /** Whether the currently applied theme is dark. */\n readonly isDark = this.#core.isDark;\n\n /** Whether the currently applied theme is light. */\n readonly isLight = this.#core.isLight;\n\n /**\n * Toggles between `'dark'` and `'light'`.\n *\n * If the selected theme is explicitly `'dark'`, switches to `'light'`.\n * Otherwise (including `'system'`), switches to `'dark'`.\n */\n toggle(): void {\n const next = this.#core.selectedTheme() === 'dark' ? 'light' : 'dark';\n this.#core.setTheme(next);\n }\n}\n","/*\n * Public API Surface of ngx-theme-stack\n */\n\nexport * from './lib/core/core-theme.service';\nexport * from './lib/services/theme-stack.config';\nexport * from './lib/services/theme-cycle.service';\nexport * from './lib/services/theme-select.service';\nexport * from './lib/services/theme-toggle.service';\nexport * from './lib/types';\n","/**\n * Generated bundle index. Do not edit.\n */\n\nexport * from './public-api';\n"],"names":[],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"ngx-theme-stack.mjs","sources":["../../../projects/ngx-theme-stack/src/lib/types.ts","../../../projects/ngx-theme-stack/src/lib/services/theme-stack.config.ts","../../../projects/ngx-theme-stack/src/lib/core/core-theme.service.ts","../../../projects/ngx-theme-stack/src/lib/services/theme-cycle.service.ts","../../../projects/ngx-theme-stack/src/lib/services/theme-select.service.ts","../../../projects/ngx-theme-stack/src/lib/services/theme-toggle.service.ts","../../../projects/ngx-theme-stack/src/public-api.ts","../../../projects/ngx-theme-stack/src/ngx-theme-stack.ts"],"sourcesContent":["/** Built-in themes. All other values are considered custom themes. */\nexport const DEFAULT_THEMES = ['system', 'light', 'dark'] as const;\n\n/** String union with autocompletion for defaults + any string support for customization. */\nexport type NgTheme = (typeof DEFAULT_THEMES)[number] | (string & {});\n\nexport type NgSystemTheme = Exclude<NgTheme, 'system'>;\nexport type NgMode = 'attribute' | 'class' | 'both';\n\nexport interface NgConfig {\n theme: NgTheme;\n storageKey: string;\n mode: NgMode;\n themes: NgTheme[];\n}\n","import { InjectionToken } from '@angular/core';\nimport { DEFAULT_THEMES, NgConfig } from '../types';\n\n/**\n * ⚠ ATTENTION: SHARED CONFIGURATION VALUES\n *\n * These values MUST match the schematic defaults in:\n * projects/ngx-theme-stack/schematics/ng-add/index.ts\n *\n * If you change any of these, you MUST also update the schematic's DEFAULTS\n * constant so 'ng add' continues to provide correct hints and clean code.\n */\nexport const DEFAULT_NG_CONFIG = {\n theme: 'system',\n storageKey: 'ngx-theme-stack-theme',\n mode: 'class',\n themes: [...DEFAULT_THEMES],\n} satisfies NgConfig;\n\nexport const NGX_THEME_STACK_CONFIG = new InjectionToken<NgConfig>('NGX_THEME_STACK_CONFIG', {\n factory: () => DEFAULT_NG_CONFIG,\n});\n\n/**\n * Helper function to provide Theme Stack configuration.\n */\nexport function provideThemeStack(config: Partial<NgConfig> = {}) {\n return {\n provide: NGX_THEME_STACK_CONFIG,\n useValue: {\n ...DEFAULT_NG_CONFIG,\n ...config,\n } satisfies NgConfig,\n };\n}\n","import { isPlatformBrowser } from '@angular/common';\nimport {\n computed,\n DestroyRef,\n DOCUMENT,\n effect,\n inject,\n Injectable,\n PLATFORM_ID,\n signal,\n} from '@angular/core';\nimport { NGX_THEME_STACK_CONFIG } from '../services/theme-stack.config';\nimport { NgTheme, NgSystemTheme } from '../types';\n\n\n/**\n * Core service for managing the application's color theme.\n *\n * Handles theme persistence, system preference detection, and DOM updates.\n * Supports built-in themes ('dark', 'light', 'system') and custom extensions.\n */\n@Injectable({ providedIn: 'root' })\nexport class CoreThemeService {\n // ── Dependencies ──────────────────────────────────────────────────────────\n\n readonly #config = inject(NGX_THEME_STACK_CONFIG);\n readonly #destroyRef = inject(DestroyRef);\n readonly #document = inject(DOCUMENT);\n readonly #isBrowser = isPlatformBrowser(inject(PLATFORM_ID));\n\n // ── Theme configuration ───────────────────────────────────────────────────\n\n /** List of available themes for Select/Cycle services. Defaults to ['light', 'dark', 'system']. */\n readonly availableThemes = this.#config.themes;\n\n /** Internal Set for O(1) existence checks. */\n readonly #validThemes = new Set<NgTheme>(this.availableThemes);\n\n // ── System preference ─────────────────────────────────────────────────────\n\n /** MediaQueryList for OS color scheme, created once and reused. Null in SSR. */\n readonly #mediaQuery: MediaQueryList | null = this.#isBrowser\n ? (this.#document.defaultView?.matchMedia('(prefers-color-scheme: dark)') ?? null)\n : null;\n\n readonly #systemPreference = signal<NgSystemTheme>(this.resolveSystemPreference());\n\n // ── Theme state ───────────────────────────────────────────────────────────\n\n readonly #selectedTheme = signal<NgTheme>(this.resolveInitialTheme());\n\n /** The theme explicitly selected by the user. May be `'system'`. */\n readonly selectedTheme = this.#selectedTheme.asReadonly();\n\n /** Resolved theme applied to the DOM. Always `'dark'` or `'light'` (or custom) — never `'system'`. */\n readonly userTheme = computed(() => {\n const theme = this.#selectedTheme();\n return theme === 'system' ? this.#systemPreference() : theme;\n });\n\n /** Whether the currently applied theme is dark. */\n readonly isDark = computed(() => this.userTheme() === 'dark');\n\n /** Whether the currently applied theme is light. */\n readonly isLight = computed(() => this.userTheme() === 'light');\n\n // ── Event handler ─────────────────────────────────────────────────────────\n\n readonly #onSystemPreferenceChange = () =>\n this.#systemPreference.set(this.resolveSystemPreference());\n\n // ── Lifecycle ─────────────────────────────────────────────────────────────\n\n constructor() {\n if (this.#isBrowser && this.#selectedTheme() === 'system') {\n this.startSystemThemeListener();\n }\n\n effect(() => this.applyThemeToDOM(this.userTheme()));\n\n this.#destroyRef.onDestroy(() => this.stopSystemThemeListener());\n }\n\n // ── Public API ────────────────────────────────────────────────────────────\n\n /**\n * Changes the active theme.\n *\n * Persists the choice explicitly so that switching e.g. from `'system'` to\n * `'light'` is saved even when the resolved `userTheme` did not change\n * (system preference was already `'light'`).\n *\n * @param theme - The theme to apply: `'dark'`, `'light'`, `'system'`, or a custom theme name.\n * @throws If `theme` is not a valid theme according to library configuration.\n */\n public setTheme(theme: NgTheme): void {\n if (!this.#isBrowser) return;\n\n if (!this.#validThemes.has(theme)) {\n throw new Error(\n `[ngx-theme-stack] Invalid theme: \"${theme}\". Valid values are: ${[...this.#validThemes].join(', ')}.`,\n );\n }\n\n if (theme === 'system') {\n this.#systemPreference.set(this.resolveSystemPreference());\n this.startSystemThemeListener();\n } else {\n this.stopSystemThemeListener();\n }\n\n this.#selectedTheme.set(theme);\n this.saveTheme(theme);\n }\n\n // ── Private ───────────────────────────────────────────────────────────────\n\n private resolveSystemPreference(): NgSystemTheme {\n return this.#mediaQuery?.matches ? 'dark' : 'light';\n }\n\n private resolveInitialTheme(): NgTheme {\n if (!this.#isBrowser) return this.#config.theme;\n return this.readStoredTheme() ?? this.#config.theme;\n }\n\n private startSystemThemeListener(): void {\n if (!this.#mediaQuery) return;\n this.stopSystemThemeListener();\n this.#mediaQuery.addEventListener('change', this.#onSystemPreferenceChange);\n }\n\n private stopSystemThemeListener(): void {\n this.#mediaQuery?.removeEventListener('change', this.#onSystemPreferenceChange);\n }\n\n private applyThemeToDOM(userTheme: NgTheme): void {\n if (!this.#isBrowser) return;\n\n const host = this.#document.documentElement;\n const { mode } = this.#config;\n\n if (mode === 'attribute' || mode === 'both') {\n this.applyThemeAttribute(host, userTheme);\n }\n\n if (mode === 'class' || mode === 'both') {\n this.applyThemeClasses(host, userTheme);\n }\n\n this.applyColorSchemeHint(host, userTheme);\n }\n\n private applyThemeAttribute(host: HTMLElement, theme: NgTheme): void {\n host.setAttribute('data-theme', theme);\n }\n\n private applyThemeClasses(host: HTMLElement, theme: NgTheme): void {\n for (const t of this.availableThemes) {\n host.classList.remove(t);\n }\n\n host.classList.add(theme);\n }\n\n private applyColorSchemeHint(host: HTMLElement, theme: NgTheme): void {\n if (theme === 'dark' || theme === 'light') {\n host.style.setProperty('color-scheme', theme);\n return;\n }\n\n host.style.removeProperty('color-scheme');\n }\n\n private readStoredTheme(): NgTheme | null {\n try {\n const stored = localStorage.getItem(this.#config.storageKey);\n if (stored && this.#validThemes.has(stored as NgTheme)) {\n return stored as NgTheme;\n }\n return null;\n } catch (e) {\n console.warn('[ngx-theme-stack] Could not read theme from localStorage.', e);\n return null;\n }\n }\n\n private saveTheme(theme: NgTheme): void {\n try {\n localStorage.setItem(this.#config.storageKey, theme);\n } catch (e) {\n console.warn('[ngx-theme-stack] Could not save theme to localStorage.', e);\n }\n }\n}\n","import { inject, Injectable } from '@angular/core';\nimport { CoreThemeService } from '../core/core-theme.service';\n\n/**\n * Convenience service for cycling through themes in a fixed order.\n *\n * Default cycle: `'light'` → `'dark'` → `'system'` → `'light'` → ...\n *\n * Use this when you want to offer users a single button that rotates\n * through all available theme options.\n */\n@Injectable({ providedIn: 'root' })\nexport class ThemeCycleService {\n\n readonly #core = inject(CoreThemeService);\n\n /** List of all configured themes for cycling. Defaults to ['light', 'dark', 'system']. */\n readonly #cycle = this.#core.availableThemes;\n\n /** The theme explicitly selected by the user. May be `'system'`. */\n readonly selectedTheme = this.#core.selectedTheme;\n\n /** Resolved theme applied to the DOM. Always concrete — never `'system'`. */\n readonly userTheme = this.#core.userTheme;\n\n /** Whether the currently applied theme is dark. */\n readonly isDark = this.#core.isDark;\n\n /** Whether the currently applied theme is light. */\n readonly isLight = this.#core.isLight;\n\n /**\n * Advances to the next theme in the cycle.\n *\n * Cycle order is determined by the configured `themes` property in `NgConfig`.\n *\n * If the current theme is not found in the cycle (e.g. set externally),\n * the cycle restarts from the first theme.\n */\n cycle(): void {\n const current = this.#core.selectedTheme();\n const index = this.#cycle.indexOf(current);\n const next = this.#cycle[(index + 1) % this.#cycle.length];\n this.#core.setTheme(next);\n }\n}\n","import { inject, Injectable } from '@angular/core';\nimport { CoreThemeService } from '../core/core-theme.service';\nimport { NgTheme } from '../types';\n\n\n/**\n * Convenience service for selecting a theme from a list.\n *\n * Use this when you want to bind a `<select>` or a group of radio/tab\n * buttons to the full set of available themes.\n */\n@Injectable({ providedIn: 'root' })\nexport class ThemeSelectService {\n\n readonly #core = inject(CoreThemeService);\n\n /** List of all configured themes. Defaults to ['light', 'dark', 'system']. */\n readonly availableThemes = this.#core.availableThemes;\n\n /** The theme explicitly selected by the user. May be `'system'`. */\n readonly selectedTheme = this.#core.selectedTheme;\n\n /** Resolved theme applied to the DOM. Always concrete — never `'system'`. */\n readonly userTheme = this.#core.userTheme;\n\n /** Whether the currently applied theme is dark. */\n readonly isDark = this.#core.isDark;\n\n /** Whether the currently applied theme is light. */\n readonly isLight = this.#core.isLight;\n\n /**\n * Applies the given theme.\n *\n * @param theme - The theme to apply: `'dark'`, `'light'`, `'system'`, or custom.\n * @throws If `theme` is not a valid theme according to library configuration.\n */\n select(theme: NgTheme): void {\n this.#core.setTheme(theme);\n }\n}\n","import { inject, Injectable } from '@angular/core';\nimport { CoreThemeService } from '../core/core-theme.service';\n\n/**\n * Convenience service for toggling between `'dark'` and `'light'`.\n *\n * Use this when you only need a simple on/off switch and do not\n * need to manage `'system'` or cycle through themes.\n */\n@Injectable({ providedIn: 'root' })\nexport class ThemeToggleService {\n\n readonly #core = inject(CoreThemeService);\n\n /** Resolved theme applied to the DOM. Always concrete — never `'system'`. */\n readonly userTheme = this.#core.userTheme;\n\n /** Whether the currently applied theme is dark. */\n readonly isDark = this.#core.isDark;\n\n /** Whether the currently applied theme is light. */\n readonly isLight = this.#core.isLight;\n\n /**\n * Toggles between `'dark'` and `'light'`.\n *\n * If the selected theme is explicitly `'dark'`, switches to `'light'`.\n * Otherwise (including `'system'`), switches to `'dark'`.\n */\n toggle(): void {\n const next = this.#core.selectedTheme() === 'dark' ? 'light' : 'dark';\n this.#core.setTheme(next);\n }\n}\n","/*\n * Public API Surface of ngx-theme-stack\n */\n\nexport * from './lib/core/core-theme.service';\nexport * from './lib/services/theme-stack.config';\nexport * from './lib/services/theme-cycle.service';\nexport * from './lib/services/theme-select.service';\nexport * from './lib/services/theme-toggle.service';\nexport * from './lib/types';\n","/**\n * Generated bundle index. Do not edit.\n */\n\nexport * from './public-api';\n"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "ngx-theme-stack",
|
|
3
|
-
"version": "0.0
|
|
3
|
+
"version": "1.0.0",
|
|
4
4
|
"description": "A stack of themes for Angular applications.",
|
|
5
5
|
"schematics": "./schematics/collection.json",
|
|
6
6
|
"ng-add": {
|
|
7
7
|
"save": "devDependencies"
|
|
8
8
|
},
|
|
9
9
|
"peerDependencies": {
|
|
10
|
-
"@angular/common": "^21.
|
|
11
|
-
"@angular/core": "^21.
|
|
10
|
+
"@angular/common": "^18.0.0 || ^19.0.0 || ^20.0.0 || ^21.0.0",
|
|
11
|
+
"@angular/core": "^18.0.0 || ^19.0.0 || ^20.0.0 || ^21.0.0"
|
|
12
12
|
},
|
|
13
13
|
"dependencies": {
|
|
14
14
|
"tslib": "^2.3.0"
|
|
@@ -25,6 +25,5 @@
|
|
|
25
25
|
"types": "./types/ngx-theme-stack.d.ts",
|
|
26
26
|
"default": "./fesm2022/ngx-theme-stack.mjs"
|
|
27
27
|
}
|
|
28
|
-
}
|
|
29
|
-
"type": "module"
|
|
28
|
+
}
|
|
30
29
|
}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { SchematicContext, Tree } from '@angular-devkit/schematics';
|
|
2
|
+
/**
|
|
3
|
+
* Attempts to automatically locate and update the application configuration files.
|
|
4
|
+
* It searches for standard Angular file paths like 'app.config.ts', 'main.ts',
|
|
5
|
+
* and 'app.module.ts', injecting the proper import and provider call.
|
|
6
|
+
*
|
|
7
|
+
* @param tree The virtual file tree of the project.
|
|
8
|
+
* @param context The schematic's execution context.
|
|
9
|
+
* @param provideCall The pre-formatted code string for provideThemeStack().
|
|
10
|
+
*/
|
|
11
|
+
export declare function patchAppConfig(tree: Tree, context: SchematicContext, provideCall: string): void;
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.patchAppConfig = patchAppConfig;
|
|
4
|
+
/**
|
|
5
|
+
* Attempts to automatically locate and update the application configuration files.
|
|
6
|
+
* It searches for standard Angular file paths like 'app.config.ts', 'main.ts',
|
|
7
|
+
* and 'app.module.ts', injecting the proper import and provider call.
|
|
8
|
+
*
|
|
9
|
+
* @param tree The virtual file tree of the project.
|
|
10
|
+
* @param context The schematic's execution context.
|
|
11
|
+
* @param provideCall The pre-formatted code string for provideThemeStack().
|
|
12
|
+
*/
|
|
13
|
+
function patchAppConfig(tree, context, provideCall) {
|
|
14
|
+
const candidates = ['src/app/app.config.ts', 'src/main.ts', 'src/app/app.module.ts'];
|
|
15
|
+
for (const filePath of candidates) {
|
|
16
|
+
if (!tree.exists(filePath))
|
|
17
|
+
continue;
|
|
18
|
+
const content = tree.readText(filePath);
|
|
19
|
+
if (content.includes('provideThemeStack')) {
|
|
20
|
+
context.logger.info(`✔ ngx-theme-stack already configured in ${filePath}`);
|
|
21
|
+
return;
|
|
22
|
+
}
|
|
23
|
+
let updated = content;
|
|
24
|
+
// Add import if missing
|
|
25
|
+
if (!updated.includes("from 'ngx-theme-stack'")) {
|
|
26
|
+
const lastImport = updated.lastIndexOf('import ');
|
|
27
|
+
const eol = updated.indexOf('\n', lastImport);
|
|
28
|
+
updated =
|
|
29
|
+
updated.slice(0, eol + 1) +
|
|
30
|
+
`import { provideThemeStack } from 'ngx-theme-stack';\n` +
|
|
31
|
+
updated.slice(eol + 1);
|
|
32
|
+
}
|
|
33
|
+
// Inject into providers array
|
|
34
|
+
if (updated.includes('providers:')) {
|
|
35
|
+
updated = updated.replace(/providers:\s*\[([\s\S]*?)\]/, (_m, inner) => {
|
|
36
|
+
const trimmed = inner.trimEnd();
|
|
37
|
+
const sep = trimmed.length > 0 && !trimmed.endsWith(',') ? ',\n ' : '\n ';
|
|
38
|
+
return `providers: [${trimmed}${sep}${provideCall}\n ]`;
|
|
39
|
+
});
|
|
40
|
+
}
|
|
41
|
+
else if (updated.includes('bootstrapApplication')) {
|
|
42
|
+
updated = updated.replace(/bootstrapApplication\s*\(\s*([^,)]+)\s*\)/, `bootstrapApplication($1, {\n providers: [\n ${provideCall}\n ]\n})`);
|
|
43
|
+
}
|
|
44
|
+
tree.overwrite(filePath, updated);
|
|
45
|
+
context.logger.info(`✔ Added ${provideCall} to ${filePath}`);
|
|
46
|
+
return;
|
|
47
|
+
}
|
|
48
|
+
context.logger.warn(`⚠ Could not find app.config.ts / main.ts / app.module.ts.\n` +
|
|
49
|
+
` Add manually:\n\n` +
|
|
50
|
+
` import { provideThemeStack } from 'ngx-theme-stack';\n\n` +
|
|
51
|
+
` providers: [ ${provideCall} ]`);
|
|
52
|
+
}
|
|
53
|
+
//# sourceMappingURL=app-config.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"app-config.js","sourceRoot":"","sources":["../../../../projects/ngx-theme-stack/schematics/ng-add/app-config.ts"],"names":[],"mappings":";;AAWA,wCAqDC;AA9DD;;;;;;;;GAQG;AACH,SAAgB,cAAc,CAAC,IAAU,EAAE,OAAyB,EAAE,WAAmB;IACvF,MAAM,UAAU,GAAG,CAAC,uBAAuB,EAAE,aAAa,EAAE,uBAAuB,CAAC,CAAC;IAErF,KAAK,MAAM,QAAQ,IAAI,UAAU,EAAE,CAAC;QAClC,IAAI,CAAC,IAAI,CAAC,MAAM,CAAC,QAAQ,CAAC;YAAE,SAAS;QAErC,MAAM,OAAO,GAAG,IAAI,CAAC,QAAQ,CAAC,QAAQ,CAAC,CAAC;QAExC,IAAI,OAAO,CAAC,QAAQ,CAAC,mBAAmB,CAAC,EAAE,CAAC;YAC1C,OAAO,CAAC,MAAM,CAAC,IAAI,CAAC,2CAA2C,QAAQ,EAAE,CAAC,CAAC;YAC3E,OAAO;QACT,CAAC;QAED,IAAI,OAAO,GAAG,OAAO,CAAC;QAEtB,wBAAwB;QACxB,IAAI,CAAC,OAAO,CAAC,QAAQ,CAAC,wBAAwB,CAAC,EAAE,CAAC;YAChD,MAAM,UAAU,GAAG,OAAO,CAAC,WAAW,CAAC,SAAS,CAAC,CAAC;YAClD,MAAM,GAAG,GAAG,OAAO,CAAC,OAAO,CAAC,IAAI,EAAE,UAAU,CAAC,CAAC;YAC9C,OAAO;gBACL,OAAO,CAAC,KAAK,CAAC,CAAC,EAAE,GAAG,GAAG,CAAC,CAAC;oBACzB,wDAAwD;oBACxD,OAAO,CAAC,KAAK,CAAC,GAAG,GAAG,CAAC,CAAC,CAAC;QAC3B,CAAC;QAED,8BAA8B;QAC9B,IAAI,OAAO,CAAC,QAAQ,CAAC,YAAY,CAAC,EAAE,CAAC;YACnC,OAAO,GAAG,OAAO,CAAC,OAAO,CACvB,6BAA6B,EAC7B,CAAC,EAAU,EAAE,KAAa,EAAE,EAAE;gBAC5B,MAAM,OAAO,GAAG,KAAK,CAAC,OAAO,EAAE,CAAC;gBAChC,MAAM,GAAG,GAAG,OAAO,CAAC,MAAM,GAAG,CAAC,IAAI,CAAC,OAAO,CAAC,QAAQ,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,QAAQ,CAAC;gBAChF,OAAO,eAAe,OAAO,GAAG,GAAG,GAAG,WAAW,OAAO,CAAC;YAC3D,CAAC,CACF,CAAC;QACJ,CAAC;aAAM,IAAI,OAAO,CAAC,QAAQ,CAAC,sBAAsB,CAAC,EAAE,CAAC;YACpD,OAAO,GAAG,OAAO,CAAC,OAAO,CACvB,2CAA2C,EAC3C,mDAAmD,WAAW,WAAW,CAC1E,CAAC;QACJ,CAAC;QAED,IAAI,CAAC,SAAS,CAAC,QAAQ,EAAE,OAAO,CAAC,CAAC;QAClC,OAAO,CAAC,MAAM,CAAC,IAAI,CAAC,WAAW,WAAW,OAAO,QAAQ,EAAE,CAAC,CAAC;QAC7D,OAAO;IACT,CAAC;IAED,OAAO,CAAC,MAAM,CAAC,IAAI,CACjB,6DAA6D;QAC3D,qBAAqB;QACrB,4DAA4D;QAC5D,kBAAkB,WAAW,IAAI,CACpC,CAAC;AACJ,CAAC"}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* ⚠ ATTENTION: SHARED CONFIGURATION VALUES
|
|
3
|
+
*
|
|
4
|
+
* These values MUST match the library defaults in:
|
|
5
|
+
* projects/ngx-theme-stack/src/lib/services/theme-stack.config.ts
|
|
6
|
+
*
|
|
7
|
+
* Schematics run in Node.js (CommonJS) and cannot import from the library (ESM),
|
|
8
|
+
* so these defaults are intentionally duplicated here for:
|
|
9
|
+
* 1. Proposing hints/defaults in interactive prompts.
|
|
10
|
+
* 2. Deciding if a property can be omitted from the generated provideThemeStack() call.
|
|
11
|
+
*/
|
|
12
|
+
export declare const DEFAULT_THEMES: readonly ["system", "light", "dark"];
|
|
13
|
+
export declare const DEFAULTS: {
|
|
14
|
+
readonly theme: "system";
|
|
15
|
+
readonly storageKey: "ngx-theme-stack-theme";
|
|
16
|
+
readonly mode: "class";
|
|
17
|
+
readonly themes: readonly ["system", "light", "dark"];
|
|
18
|
+
};
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.DEFAULTS = exports.DEFAULT_THEMES = void 0;
|
|
4
|
+
/**
|
|
5
|
+
* ⚠ ATTENTION: SHARED CONFIGURATION VALUES
|
|
6
|
+
*
|
|
7
|
+
* These values MUST match the library defaults in:
|
|
8
|
+
* projects/ngx-theme-stack/src/lib/services/theme-stack.config.ts
|
|
9
|
+
*
|
|
10
|
+
* Schematics run in Node.js (CommonJS) and cannot import from the library (ESM),
|
|
11
|
+
* so these defaults are intentionally duplicated here for:
|
|
12
|
+
* 1. Proposing hints/defaults in interactive prompts.
|
|
13
|
+
* 2. Deciding if a property can be omitted from the generated provideThemeStack() call.
|
|
14
|
+
*/
|
|
15
|
+
exports.DEFAULT_THEMES = ['system', 'light', 'dark'];
|
|
16
|
+
exports.DEFAULTS = {
|
|
17
|
+
theme: 'system',
|
|
18
|
+
storageKey: 'ngx-theme-stack-theme',
|
|
19
|
+
mode: 'class',
|
|
20
|
+
themes: [...exports.DEFAULT_THEMES],
|
|
21
|
+
};
|
|
22
|
+
//# sourceMappingURL=constants.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"constants.js","sourceRoot":"","sources":["../../../../projects/ngx-theme-stack/schematics/ng-add/constants.ts"],"names":[],"mappings":";;;AAAA;;;;;;;;;;GAUG;AACU,QAAA,cAAc,GAAG,CAAC,QAAQ,EAAE,OAAO,EAAE,MAAM,CAAU,CAAC;AAEtD,QAAA,QAAQ,GAAG;IACtB,KAAK,EAAE,QAAQ;IACf,UAAU,EAAE,uBAAuB;IACnC,IAAI,EAAE,OAAO;IACb,MAAM,EAAE,CAAC,GAAG,sBAAc,CAAC;CACnB,CAAC"}
|
|
@@ -1,3 +1,11 @@
|
|
|
1
1
|
import { Rule } from '@angular-devkit/schematics';
|
|
2
2
|
import { Schema } from './schema';
|
|
3
|
+
/**
|
|
4
|
+
* Main schematic factory for the 'ng-add' command.
|
|
5
|
+
* Depending on the 'mode' option, it either applies default library values ("quick")
|
|
6
|
+
* or starts an interactive session to collect personalized configuration ("custom").
|
|
7
|
+
*
|
|
8
|
+
* @param options The schema options provided by the Angular CLI.
|
|
9
|
+
* @returns A rule that modifies the project's setup.
|
|
10
|
+
*/
|
|
3
11
|
export declare function ngAdd(options: Schema): Rule;
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
|
3
|
+
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
|
4
|
+
return new (P || (P = Promise))(function (resolve, reject) {
|
|
5
|
+
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
|
6
|
+
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
|
7
|
+
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
|
8
|
+
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
9
|
+
});
|
|
10
|
+
};
|
|
11
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
12
|
+
exports.ngAdd = ngAdd;
|
|
13
|
+
const schematics_1 = require("@angular-devkit/schematics");
|
|
14
|
+
const constants_1 = require("./constants");
|
|
15
|
+
const utils_1 = require("./utils");
|
|
16
|
+
const app_config_1 = require("./app-config");
|
|
17
|
+
/**
|
|
18
|
+
* Interactively prompts the user for custom configuration options using readline.
|
|
19
|
+
* It allows defining additional themes, selecting a default theme from the expanded list,
|
|
20
|
+
* providing a custom localStorage key, and choosing a CSS application mode.
|
|
21
|
+
*
|
|
22
|
+
* @returns A promise resolving to the user's selected configuration options.
|
|
23
|
+
*/
|
|
24
|
+
function collectCustomOptions() {
|
|
25
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
26
|
+
const rl = (0, utils_1.createRl)();
|
|
27
|
+
try {
|
|
28
|
+
process.stdout.write('\n');
|
|
29
|
+
const rawThemes = yield (0, utils_1.ask)(rl, ' Custom themes (comma-separated, Enter to skip): ');
|
|
30
|
+
const customThemes = rawThemes
|
|
31
|
+
? rawThemes
|
|
32
|
+
.split(',')
|
|
33
|
+
.map((t) => t.trim())
|
|
34
|
+
.filter(Boolean)
|
|
35
|
+
: [];
|
|
36
|
+
const allThemes = [...constants_1.DEFAULT_THEMES, ...customThemes];
|
|
37
|
+
const theme = yield (0, utils_1.askList)(rl, 'Default theme:', allThemes, 0);
|
|
38
|
+
const rawKey = yield (0, utils_1.ask)(rl, ` localStorage key [${constants_1.DEFAULTS.storageKey}]: `);
|
|
39
|
+
const storageKey = rawKey || constants_1.DEFAULTS.storageKey;
|
|
40
|
+
const MODES = ['class', 'attribute', 'both'];
|
|
41
|
+
const mode = yield (0, utils_1.askList)(rl, 'How to apply theme on <html>:', MODES, 0);
|
|
42
|
+
process.stdout.write('\n');
|
|
43
|
+
return { theme, storageKey, mode, themes: allThemes };
|
|
44
|
+
}
|
|
45
|
+
finally {
|
|
46
|
+
rl.close();
|
|
47
|
+
}
|
|
48
|
+
});
|
|
49
|
+
}
|
|
50
|
+
/**
|
|
51
|
+
* Main schematic factory for the 'ng-add' command.
|
|
52
|
+
* Depending on the 'mode' option, it either applies default library values ("quick")
|
|
53
|
+
* or starts an interactive session to collect personalized configuration ("custom").
|
|
54
|
+
*
|
|
55
|
+
* @param options The schema options provided by the Angular CLI.
|
|
56
|
+
* @returns A rule that modifies the project's setup.
|
|
57
|
+
*/
|
|
58
|
+
function ngAdd(options) {
|
|
59
|
+
return (_tree, context) => __awaiter(this, void 0, void 0, function* () {
|
|
60
|
+
context.logger.info('');
|
|
61
|
+
context.logger.info('🎨 ngx-theme-stack — setup');
|
|
62
|
+
context.logger.info('');
|
|
63
|
+
let provideCall;
|
|
64
|
+
if (options.mode === 'quick') {
|
|
65
|
+
provideCall = 'provideThemeStack()';
|
|
66
|
+
context.logger.info('⚡ Quick setup — defaults applied by the library (DEFAULT_NG_CONFIG).');
|
|
67
|
+
}
|
|
68
|
+
else {
|
|
69
|
+
context.logger.info('🛠 Custom setup — answer the prompts below:');
|
|
70
|
+
const opts = yield collectCustomOptions();
|
|
71
|
+
const { theme, storageKey, mode, themes } = opts;
|
|
72
|
+
context.logger.info(' Applying your configuration:');
|
|
73
|
+
context.logger.info(` theme : ${theme}`);
|
|
74
|
+
context.logger.info(` themes : [${themes.join(', ')}]`);
|
|
75
|
+
context.logger.info(` storageKey : ${storageKey}`);
|
|
76
|
+
context.logger.info(` mode : ${mode}`);
|
|
77
|
+
provideCall = (0, utils_1.buildProvideCall)(theme, storageKey, mode, themes);
|
|
78
|
+
}
|
|
79
|
+
return (0, schematics_1.chain)([
|
|
80
|
+
(t, ctx) => {
|
|
81
|
+
(0, app_config_1.patchAppConfig)(t, ctx, provideCall);
|
|
82
|
+
ctx.logger.info('');
|
|
83
|
+
ctx.logger.info('✅ Done! Run `ng serve` to see ngx-theme-stack in action.');
|
|
84
|
+
ctx.logger.info('');
|
|
85
|
+
},
|
|
86
|
+
]);
|
|
87
|
+
});
|
|
88
|
+
}
|
|
89
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../../projects/ngx-theme-stack/schematics/ng-add/index.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../../projects/ngx-theme-stack/schematics/ng-add/index.ts"],"names":[],"mappings":"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"}
|
|
@@ -1,17 +1,6 @@
|
|
|
1
|
-
/** Options passed to the ng-add schematic by the Angular CLI. */
|
|
2
1
|
export interface Schema {
|
|
3
2
|
/** Name of the Angular project to configure. */
|
|
4
3
|
project: string;
|
|
5
|
-
/**
|
|
6
|
-
* Installation mode:
|
|
7
|
-
* - 'quick' → apply defaults immediately, no further questions
|
|
8
|
-
* - 'custom' → prompt the user for each individual option
|
|
9
|
-
*/
|
|
4
|
+
/** 'quick' applies defaults silently. 'custom' prompts interactively. */
|
|
10
5
|
mode: 'quick' | 'custom';
|
|
11
|
-
/** Default theme applied on startup. Only used in custom mode. */
|
|
12
|
-
theme: 'system' | 'light' | 'dark' | string;
|
|
13
|
-
/** localStorage key used to persist the theme. Only used in custom mode. */
|
|
14
|
-
storageKey: string;
|
|
15
|
-
/** Strategy used to apply the theme to the <html> element. Only used in custom mode. */
|
|
16
|
-
themeMode: 'class' | 'attribute' | 'both';
|
|
17
6
|
}
|
|
@@ -13,7 +13,7 @@
|
|
|
13
13
|
},
|
|
14
14
|
"mode": {
|
|
15
15
|
"type": "string",
|
|
16
|
-
"description": "Installation mode
|
|
16
|
+
"description": "Installation mode.",
|
|
17
17
|
"default": "quick",
|
|
18
18
|
"enum": ["quick", "custom"],
|
|
19
19
|
"x-prompt": {
|
|
@@ -22,49 +22,14 @@
|
|
|
22
22
|
"items": [
|
|
23
23
|
{
|
|
24
24
|
"value": "quick",
|
|
25
|
-
"label": "Quick –
|
|
25
|
+
"label": "Quick – apply defaults instantly (theme: system, mode: class)"
|
|
26
26
|
},
|
|
27
27
|
{
|
|
28
28
|
"value": "custom",
|
|
29
|
-
"label": "Custom – choose
|
|
29
|
+
"label": "Custom – choose themes, default theme, storage key and apply mode"
|
|
30
30
|
}
|
|
31
31
|
]
|
|
32
32
|
}
|
|
33
|
-
},
|
|
34
|
-
"theme": {
|
|
35
|
-
"type": "string",
|
|
36
|
-
"description": "Default theme to apply on startup.",
|
|
37
|
-
"default": "system",
|
|
38
|
-
"x-prompt": {
|
|
39
|
-
"message": "Which default theme should be applied on startup?",
|
|
40
|
-
"type": "list",
|
|
41
|
-
"items": [
|
|
42
|
-
{ "value": "system", "label": "system – follow OS preference (recommended)" },
|
|
43
|
-
{ "value": "light", "label": "light – always light" },
|
|
44
|
-
{ "value": "dark", "label": "dark – always dark" }
|
|
45
|
-
]
|
|
46
|
-
}
|
|
47
|
-
},
|
|
48
|
-
"storageKey": {
|
|
49
|
-
"type": "string",
|
|
50
|
-
"description": "localStorage key used to persist the selected theme.",
|
|
51
|
-
"default": "ngx-theme-stack-theme",
|
|
52
|
-
"x-prompt": "What localStorage key should be used to persist the theme? (default: ngx-theme-stack-theme)"
|
|
53
|
-
},
|
|
54
|
-
"themeMode": {
|
|
55
|
-
"type": "string",
|
|
56
|
-
"description": "Strategy used to apply the theme to the <html> element.",
|
|
57
|
-
"default": "class",
|
|
58
|
-
"enum": ["class", "attribute", "both"],
|
|
59
|
-
"x-prompt": {
|
|
60
|
-
"message": "How should the theme be applied to the <html> element?",
|
|
61
|
-
"type": "list",
|
|
62
|
-
"items": [
|
|
63
|
-
{ "value": "class", "label": "class – add/remove CSS class (e.g. .dark)" },
|
|
64
|
-
{ "value": "attribute", "label": "attribute – set data-theme attribute" },
|
|
65
|
-
{ "value": "both", "label": "both – apply class AND attribute simultaneously" }
|
|
66
|
-
]
|
|
67
|
-
}
|
|
68
33
|
}
|
|
69
34
|
},
|
|
70
35
|
"required": ["project"]
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import * as readline from 'readline';
|
|
2
|
+
/**
|
|
3
|
+
* Creates a readline interface using the system's standard input and output.
|
|
4
|
+
*/
|
|
5
|
+
export declare function createRl(): readline.Interface;
|
|
6
|
+
/**
|
|
7
|
+
* Prompts the user with a question and returns their trimmed answer.
|
|
8
|
+
*/
|
|
9
|
+
export declare function ask(rl: readline.Interface, question: string): Promise<string>;
|
|
10
|
+
/**
|
|
11
|
+
* Displays a numbered list of items to the user and returns the selected item.
|
|
12
|
+
* If the input is invalid or ignored, the default index item is returned.
|
|
13
|
+
*/
|
|
14
|
+
export declare function askList(rl: readline.Interface, label: string, items: readonly string[], defaultIndex?: number): Promise<string>;
|
|
15
|
+
/**
|
|
16
|
+
* Builds the 'provideThemeStack({...})' string representation.
|
|
17
|
+
* It compares the selected values with library defaults to generate a
|
|
18
|
+
* minimal call string (omitting properties that match defaults).
|
|
19
|
+
*/
|
|
20
|
+
export declare function buildProvideCall(theme: string, storageKey: string, mode: string, themes: string[]): string;
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
|
3
|
+
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
|
4
|
+
return new (P || (P = Promise))(function (resolve, reject) {
|
|
5
|
+
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
|
6
|
+
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
|
7
|
+
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
|
8
|
+
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
9
|
+
});
|
|
10
|
+
};
|
|
11
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
12
|
+
exports.createRl = createRl;
|
|
13
|
+
exports.ask = ask;
|
|
14
|
+
exports.askList = askList;
|
|
15
|
+
exports.buildProvideCall = buildProvideCall;
|
|
16
|
+
const readline = require("readline");
|
|
17
|
+
const constants_1 = require("./constants");
|
|
18
|
+
/**
|
|
19
|
+
* Creates a readline interface using the system's standard input and output.
|
|
20
|
+
*/
|
|
21
|
+
function createRl() {
|
|
22
|
+
return readline.createInterface({ input: process.stdin, output: process.stdout });
|
|
23
|
+
}
|
|
24
|
+
/**
|
|
25
|
+
* Prompts the user with a question and returns their trimmed answer.
|
|
26
|
+
*/
|
|
27
|
+
function ask(rl, question) {
|
|
28
|
+
return new Promise((resolve) => rl.question(question, (a) => resolve(a.trim())));
|
|
29
|
+
}
|
|
30
|
+
/**
|
|
31
|
+
* Displays a numbered list of items to the user and returns the selected item.
|
|
32
|
+
* If the input is invalid or ignored, the default index item is returned.
|
|
33
|
+
*/
|
|
34
|
+
function askList(rl_1, label_1, items_1) {
|
|
35
|
+
return __awaiter(this, arguments, void 0, function* (rl, label, items, defaultIndex = 0) {
|
|
36
|
+
process.stdout.write(`\n ${label}\n`);
|
|
37
|
+
items.forEach((item, i) => process.stdout.write(` ${i + 1}) ${item}\n`));
|
|
38
|
+
const raw = yield ask(rl, ` Choice [${defaultIndex + 1}]: `);
|
|
39
|
+
const n = parseInt(raw, 10);
|
|
40
|
+
return isNaN(n) || n < 1 || n > items.length ? items[defaultIndex] : items[n - 1];
|
|
41
|
+
});
|
|
42
|
+
}
|
|
43
|
+
/**
|
|
44
|
+
* Builds the 'provideThemeStack({...})' string representation.
|
|
45
|
+
* It compares the selected values with library defaults to generate a
|
|
46
|
+
* minimal call string (omitting properties that match defaults).
|
|
47
|
+
*/
|
|
48
|
+
function buildProvideCall(theme, storageKey, mode, themes) {
|
|
49
|
+
const defaultThemes = [...constants_1.DEFAULT_THEMES];
|
|
50
|
+
const isDefaultThemes = themes.length === defaultThemes.length && themes.every((t, i) => t === defaultThemes[i]);
|
|
51
|
+
const isAllDefault = theme === constants_1.DEFAULTS.theme &&
|
|
52
|
+
storageKey === constants_1.DEFAULTS.storageKey &&
|
|
53
|
+
mode === constants_1.DEFAULTS.mode &&
|
|
54
|
+
isDefaultThemes;
|
|
55
|
+
if (isAllDefault)
|
|
56
|
+
return `provideThemeStack()`;
|
|
57
|
+
const parts = [];
|
|
58
|
+
if (theme !== constants_1.DEFAULTS.theme)
|
|
59
|
+
parts.push(`theme: '${theme}'`);
|
|
60
|
+
if (storageKey !== constants_1.DEFAULTS.storageKey)
|
|
61
|
+
parts.push(`storageKey: '${storageKey}'`);
|
|
62
|
+
if (mode !== constants_1.DEFAULTS.mode)
|
|
63
|
+
parts.push(`mode: '${mode}'`);
|
|
64
|
+
if (!isDefaultThemes) {
|
|
65
|
+
const arr = themes.map((t) => `'${t}'`).join(', ');
|
|
66
|
+
parts.push(`themes: [${arr}]`);
|
|
67
|
+
}
|
|
68
|
+
return `provideThemeStack({ ${parts.join(', ')} })`;
|
|
69
|
+
}
|
|
70
|
+
//# sourceMappingURL=utils.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"utils.js","sourceRoot":"","sources":["../../../../projects/ngx-theme-stack/schematics/ng-add/utils.ts"],"names":[],"mappings":"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"}
|
|
@@ -3,7 +3,7 @@ import { InjectionToken } from '@angular/core';
|
|
|
3
3
|
import * as ngx_theme_stack from 'ngx-theme-stack';
|
|
4
4
|
|
|
5
5
|
/** Built-in themes. All other values are considered custom themes. */
|
|
6
|
-
declare const DEFAULT_THEMES: readonly ["
|
|
6
|
+
declare const DEFAULT_THEMES: readonly ["system", "light", "dark"];
|
|
7
7
|
/** String union with autocompletion for defaults + any string support for customization. */
|
|
8
8
|
type NgTheme = (typeof DEFAULT_THEMES)[number] | (string & {});
|
|
9
9
|
type NgSystemTheme = Exclude<NgTheme, 'system'>;
|
|
@@ -59,11 +59,20 @@ declare class CoreThemeService {
|
|
|
59
59
|
static ɵprov: _angular_core.ɵɵInjectableDeclaration<CoreThemeService>;
|
|
60
60
|
}
|
|
61
61
|
|
|
62
|
+
/**
|
|
63
|
+
* ⚠ ATTENTION: SHARED CONFIGURATION VALUES
|
|
64
|
+
*
|
|
65
|
+
* These values MUST match the schematic defaults in:
|
|
66
|
+
* projects/ngx-theme-stack/schematics/ng-add/index.ts
|
|
67
|
+
*
|
|
68
|
+
* If you change any of these, you MUST also update the schematic's DEFAULTS
|
|
69
|
+
* constant so 'ng add' continues to provide correct hints and clean code.
|
|
70
|
+
*/
|
|
62
71
|
declare const DEFAULT_NG_CONFIG: {
|
|
63
72
|
theme: "system";
|
|
64
73
|
storageKey: string;
|
|
65
74
|
mode: "class";
|
|
66
|
-
themes: ("
|
|
75
|
+
themes: ("system" | "light" | "dark")[];
|
|
67
76
|
};
|
|
68
77
|
declare const NGX_THEME_STACK_CONFIG: InjectionToken<NgConfig>;
|
|
69
78
|
/**
|
|
@@ -1,157 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.ngAdd = ngAdd;
|
|
4
|
-
const schematics_1 = require("@angular-devkit/schematics");
|
|
5
|
-
// ---------------------------------------------------------------------------
|
|
6
|
-
// Default configuration (mirrors DEFAULT_NG_CONFIG from the library)
|
|
7
|
-
// ---------------------------------------------------------------------------
|
|
8
|
-
const DEFAULTS = {
|
|
9
|
-
theme: 'system',
|
|
10
|
-
storageKey: 'ngx-theme-stack-theme',
|
|
11
|
-
themeMode: 'class',
|
|
12
|
-
};
|
|
13
|
-
// ---------------------------------------------------------------------------
|
|
14
|
-
// Helper: build the provideThemeStack() call string
|
|
15
|
-
// ---------------------------------------------------------------------------
|
|
16
|
-
function buildProvideCall(theme, storageKey, themeMode) {
|
|
17
|
-
const isDefault = theme === DEFAULTS.theme &&
|
|
18
|
-
storageKey === DEFAULTS.storageKey &&
|
|
19
|
-
themeMode === DEFAULTS.themeMode;
|
|
20
|
-
if (isDefault) {
|
|
21
|
-
// Clean call — no arguments needed, defaults will be used by the library
|
|
22
|
-
return `provideThemeStack()`;
|
|
23
|
-
}
|
|
24
|
-
const parts = [];
|
|
25
|
-
if (theme !== DEFAULTS.theme)
|
|
26
|
-
parts.push(`theme: '${theme}'`);
|
|
27
|
-
if (storageKey !== DEFAULTS.storageKey)
|
|
28
|
-
parts.push(`storageKey: '${storageKey}'`);
|
|
29
|
-
if (themeMode !== DEFAULTS.themeMode)
|
|
30
|
-
parts.push(`mode: '${themeMode}'`);
|
|
31
|
-
return `provideThemeStack({ ${parts.join(', ')} })`;
|
|
32
|
-
}
|
|
33
|
-
// ---------------------------------------------------------------------------
|
|
34
|
-
// Helper: patch app.config.ts (or main.ts for bootstrapApplication)
|
|
35
|
-
// ---------------------------------------------------------------------------
|
|
36
|
-
function patchAppConfig(tree, context, provideCall) {
|
|
37
|
-
// Try app.config.ts first (standalone Angular ≥ 17 default)
|
|
38
|
-
const candidates = [
|
|
39
|
-
'src/app/app.config.ts',
|
|
40
|
-
'src/main.ts',
|
|
41
|
-
'src/app/app.module.ts',
|
|
42
|
-
];
|
|
43
|
-
let patched = false;
|
|
44
|
-
for (const filePath of candidates) {
|
|
45
|
-
if (!tree.exists(filePath))
|
|
46
|
-
continue;
|
|
47
|
-
const content = tree.readText(filePath);
|
|
48
|
-
// Already configured?
|
|
49
|
-
if (content.includes('provideThemeStack')) {
|
|
50
|
-
context.logger.info(`✔ ngx-theme-stack is already configured in ${filePath}`);
|
|
51
|
-
patched = true;
|
|
52
|
-
break;
|
|
53
|
-
}
|
|
54
|
-
// -----------------------------------------------------------------------
|
|
55
|
-
// app.config.ts / standalone bootstrapApplication pattern
|
|
56
|
-
// -----------------------------------------------------------------------
|
|
57
|
-
if (filePath.endsWith('app.config.ts') || filePath.endsWith('main.ts')) {
|
|
58
|
-
// Add import statement
|
|
59
|
-
let updated = content;
|
|
60
|
-
if (!updated.includes("from 'ngx-theme-stack'")) {
|
|
61
|
-
const lastImportIndex = updated.lastIndexOf("import ");
|
|
62
|
-
const endOfLine = updated.indexOf('\n', lastImportIndex);
|
|
63
|
-
const importStatement = `import { provideThemeStack } from 'ngx-theme-stack';`;
|
|
64
|
-
updated =
|
|
65
|
-
updated.slice(0, endOfLine + 1) +
|
|
66
|
-
importStatement + '\n' +
|
|
67
|
-
updated.slice(endOfLine + 1);
|
|
68
|
-
}
|
|
69
|
-
// Inject into providers array
|
|
70
|
-
if (updated.includes('providers:')) {
|
|
71
|
-
// Insert before closing bracket of providers array
|
|
72
|
-
updated = updated.replace(/providers:\s*\[([\s\S]*?)\]/, (_match, inner) => {
|
|
73
|
-
const trimmed = inner.trimEnd();
|
|
74
|
-
const separator = trimmed.length > 0 && !trimmed.endsWith(',') ? ',\n ' : '\n ';
|
|
75
|
-
return `providers: [${trimmed}${separator}${provideCall}\n ]`;
|
|
76
|
-
});
|
|
77
|
-
}
|
|
78
|
-
else if (updated.includes('bootstrapApplication')) {
|
|
79
|
-
// No providers key yet — add it
|
|
80
|
-
updated = updated.replace(/bootstrapApplication\s*\(\s*([^,)]+)\s*\)/, `bootstrapApplication($1, {\n providers: [\n ${provideCall}\n ]\n})`);
|
|
81
|
-
}
|
|
82
|
-
tree.overwrite(filePath, updated);
|
|
83
|
-
context.logger.info(`✔ Added ${provideCall} to ${filePath}`);
|
|
84
|
-
patched = true;
|
|
85
|
-
break;
|
|
86
|
-
}
|
|
87
|
-
// -----------------------------------------------------------------------
|
|
88
|
-
// app.module.ts (legacy NgModule pattern)
|
|
89
|
-
// -----------------------------------------------------------------------
|
|
90
|
-
if (filePath.endsWith('app.module.ts')) {
|
|
91
|
-
let updated = content;
|
|
92
|
-
if (!updated.includes("from 'ngx-theme-stack'")) {
|
|
93
|
-
const lastImportIndex = updated.lastIndexOf("import ");
|
|
94
|
-
const endOfLine = updated.indexOf('\n', lastImportIndex);
|
|
95
|
-
const importStatement = `import { provideThemeStack } from 'ngx-theme-stack';`;
|
|
96
|
-
updated =
|
|
97
|
-
updated.slice(0, endOfLine + 1) +
|
|
98
|
-
importStatement + '\n' +
|
|
99
|
-
updated.slice(endOfLine + 1);
|
|
100
|
-
}
|
|
101
|
-
// Add to NgModule providers
|
|
102
|
-
updated = updated.replace(/providers:\s*\[([\s\S]*?)\]/, (_match, inner) => {
|
|
103
|
-
const trimmed = inner.trimEnd();
|
|
104
|
-
const separator = trimmed.length > 0 && !trimmed.endsWith(',') ? ',\n ' : '\n ';
|
|
105
|
-
return `providers: [${trimmed}${separator}${provideCall}\n ]`;
|
|
106
|
-
});
|
|
107
|
-
tree.overwrite(filePath, updated);
|
|
108
|
-
context.logger.info(`✔ Added ${provideCall} to ${filePath}`);
|
|
109
|
-
patched = true;
|
|
110
|
-
break;
|
|
111
|
-
}
|
|
112
|
-
}
|
|
113
|
-
if (!patched) {
|
|
114
|
-
context.logger.warn(`⚠ Could not locate app.config.ts, main.ts, or app.module.ts.\n` +
|
|
115
|
-
` Please add the following manually to your providers array:\n\n` +
|
|
116
|
-
` import { provideThemeStack } from 'ngx-theme-stack';\n\n` +
|
|
117
|
-
` providers: [ ${provideCall} ]`);
|
|
118
|
-
}
|
|
119
|
-
}
|
|
120
|
-
// ---------------------------------------------------------------------------
|
|
121
|
-
// Schematic factory
|
|
122
|
-
// ---------------------------------------------------------------------------
|
|
123
|
-
function ngAdd(options) {
|
|
124
|
-
return (_tree, context) => {
|
|
125
|
-
var _a, _b, _c;
|
|
126
|
-
context.logger.info('');
|
|
127
|
-
context.logger.info('🎨 ngx-theme-stack — setup');
|
|
128
|
-
context.logger.info('');
|
|
129
|
-
// Resolve final config values
|
|
130
|
-
// In 'quick' mode the x-prompt for theme/storageKey/themeMode are never shown
|
|
131
|
-
// (they keep their schema defaults), so we always have usable values here.
|
|
132
|
-
const theme = options.mode === 'quick' ? DEFAULTS.theme : ((_a = options.theme) !== null && _a !== void 0 ? _a : DEFAULTS.theme);
|
|
133
|
-
const storageKey = options.mode === 'quick' ? DEFAULTS.storageKey : ((_b = options.storageKey) !== null && _b !== void 0 ? _b : DEFAULTS.storageKey);
|
|
134
|
-
const themeMode = options.mode === 'quick' ? DEFAULTS.themeMode : ((_c = options.themeMode) !== null && _c !== void 0 ? _c : DEFAULTS.themeMode);
|
|
135
|
-
if (options.mode === 'quick') {
|
|
136
|
-
context.logger.info('⚡ Quick setup — applying default configuration:');
|
|
137
|
-
}
|
|
138
|
-
else {
|
|
139
|
-
context.logger.info('🛠 Custom setup — applying your configuration:');
|
|
140
|
-
}
|
|
141
|
-
context.logger.info(` theme : ${theme}`);
|
|
142
|
-
context.logger.info(` storageKey : ${storageKey}`);
|
|
143
|
-
context.logger.info(` mode : ${themeMode}`);
|
|
144
|
-
context.logger.info('');
|
|
145
|
-
const provideCall = buildProvideCall(theme, storageKey, themeMode);
|
|
146
|
-
return (0, schematics_1.chain)([
|
|
147
|
-
(t, ctx) => {
|
|
148
|
-
patchAppConfig(t, ctx, provideCall);
|
|
149
|
-
ctx.logger.info('');
|
|
150
|
-
ctx.logger.info('✅ ngx-theme-stack configured successfully!');
|
|
151
|
-
ctx.logger.info(' Run `ng serve` to see your theme stack in action.');
|
|
152
|
-
ctx.logger.info('');
|
|
153
|
-
},
|
|
154
|
-
]);
|
|
155
|
-
};
|
|
156
|
-
}
|
|
157
|
-
//# sourceMappingURL=index.js.map
|
|
File without changes
|