ngx-theme-stack 1.0.0 → 2.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.
@@ -2,17 +2,53 @@ import * as _angular_core from '@angular/core';
2
2
  import { InjectionToken } from '@angular/core';
3
3
  import * as ngx_theme_stack from 'ngx-theme-stack';
4
4
 
5
- /** Built-in themes. All other values are considered custom themes. */
5
+ /**
6
+ * Runtime list of built-in themes.
7
+ *
8
+ * Lives here (and not in config/index.ts) because it defines a type:
9
+ * config/index.ts already imports from types.ts, so placing DEFAULT_THEMES
10
+ * here avoids any circular dependency.
11
+ *
12
+ * ⚠ KEEP IN SYNC with the duplicate in:
13
+ * projects/ngx-theme-stack/schematics/ng-add/constants.ts → DEFAULT_THEMES
14
+ *
15
+ * Schematics compile to CommonJS and cannot import from this ESM file,
16
+ * so the values are intentionally duplicated. Change both at the same time.
17
+ */
6
18
  declare const DEFAULT_THEMES: readonly ["system", "light", "dark"];
7
- /** String union with autocompletion for defaults + any string support for customization. */
8
- type NgTheme = (typeof DEFAULT_THEMES)[number] | (string & {});
9
- type NgSystemTheme = Exclude<NgTheme, 'system'>;
19
+ /** Literal union of built-in themes: `'system' | 'light' | 'dark'`. */
20
+ type DefaultNgTheme = (typeof DEFAULT_THEMES)[number];
21
+ /**
22
+ * Theme type.
23
+ *
24
+ * - **Without** `T`: open union — accepts any `string` with IDE autocomplete
25
+ * hints for the built-in themes (`'system' | 'light' | 'dark'`).
26
+ * - **With** `T`: closed union — exactly `DefaultNgTheme | T`, enabling
27
+ * full type-safety for custom theme sets.
28
+ *
29
+ * @example
30
+ * NgTheme // 'system' | 'light' | 'dark' | (string & {})
31
+ * NgTheme<'sepia'> // 'system' | 'light' | 'dark' | 'sepia'
32
+ */
33
+ type NgTheme<T extends string = string & {}> = DefaultNgTheme | T;
34
+ /**
35
+ * Resolved theme — always `'light'` or `'dark'`, never `'system'`.
36
+ * Represents the value that comes from `matchMedia`, not user selection.
37
+ */
38
+ type NgSystemTheme = Exclude<DefaultNgTheme, 'system'>;
10
39
  type NgMode = 'attribute' | 'class' | 'both';
11
- interface NgConfig {
12
- theme: NgTheme;
40
+ /**
41
+ * Library configuration.
42
+ *
43
+ * @typeParam T - Custom theme literals. Defaults to open `string`, preserving
44
+ * backwards compatibility. Pass specific literals (e.g. `'sepia' | 'ocean'`)
45
+ * via {@link provideThemeStack} to get a closed, type-safe theme union.
46
+ */
47
+ interface NgConfig<T extends string = string & {}> {
48
+ defaultTheme: NgTheme<T>;
13
49
  storageKey: string;
14
50
  mode: NgMode;
15
- themes: NgTheme[];
51
+ themes: NgTheme<T>[];
16
52
  }
17
53
 
18
54
  /**
@@ -23,22 +59,42 @@ interface NgConfig {
23
59
  */
24
60
  declare class CoreThemeService {
25
61
  #private;
26
- /** List of available themes for Select/Cycle services. Defaults to ['light', 'dark', 'system']. */
27
- readonly availableThemes: NgTheme[];
62
+ /** List of available themes for Select/Cycle services. Defaults to ['system', 'light', 'dark']. */
63
+ readonly availableThemes: string[];
28
64
  /** The theme explicitly selected by the user. May be `'system'`. */
29
65
  readonly selectedTheme: _angular_core.Signal<NgTheme>;
30
66
  /** Resolved theme applied to the DOM. Always `'dark'` or `'light'` (or custom) — never `'system'`. */
31
- readonly userTheme: _angular_core.Signal<NgSystemTheme>;
67
+ readonly resolvedTheme: _angular_core.Signal<(string & {}) | NgSystemTheme>;
32
68
  /** Whether the currently applied theme is dark. */
33
69
  readonly isDark: _angular_core.Signal<boolean>;
34
70
  /** Whether the currently applied theme is light. */
35
71
  readonly isLight: _angular_core.Signal<boolean>;
72
+ /** Whether the currently applied theme is system. */
73
+ readonly isSystem: _angular_core.Signal<boolean>;
74
+ /**
75
+ * Whether the service has completed client-side initialization.
76
+ *
77
+ * `false` during SSR and on the very first render pass. Becomes `true`
78
+ * immediately after the first browser render, once the real persisted theme
79
+ * has been read from `localStorage`.
80
+ *
81
+ * Use this to guard any template logic that depends on `selectedTheme` or
82
+ * `resolvedTheme` to avoid an SSR hydration-mismatch flash: the server
83
+ * renders the default (`'system'`) while the browser may have a different
84
+ * value stored.
85
+ *
86
+ * @example
87
+ * ```html
88
+ * {{ themeService.isHydrated() ? selectedTheme() : '—' }}
89
+ * ```
90
+ */
91
+ readonly isHydrated: _angular_core.WritableSignal<boolean>;
36
92
  constructor();
37
93
  /**
38
94
  * Changes the active theme.
39
95
  *
40
96
  * Persists the choice explicitly so that switching e.g. from `'system'` to
41
- * `'light'` is saved even when the resolved `userTheme` did not change
97
+ * `'light'` is saved even when the resolved theme did not change
42
98
  * (system preference was already `'light'`).
43
99
  *
44
100
  * @param theme - The theme to apply: `'dark'`, `'light'`, `'system'`, or a custom theme name.
@@ -53,6 +109,7 @@ declare class CoreThemeService {
53
109
  private applyThemeAttribute;
54
110
  private applyThemeClasses;
55
111
  private applyColorSchemeHint;
112
+ private captureAntiFlashClass;
56
113
  private readStoredTheme;
57
114
  private saveTheme;
58
115
  static ɵfac: _angular_core.ɵɵFactoryDeclaration<CoreThemeService, never>;
@@ -62,36 +119,69 @@ declare class CoreThemeService {
62
119
  /**
63
120
  * ⚠ ATTENTION: SHARED CONFIGURATION VALUES
64
121
  *
65
- * These values MUST match the schematic defaults in:
66
- * projects/ngx-theme-stack/schematics/ng-add/index.ts
122
+ * These defaults MUST match the schematic defaults in:
123
+ * projects/ngx-theme-stack/schematics/ng-add/constants.ts → DEFAULTS
67
124
  *
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.
125
+ * Schematics compile to CommonJS and cannot import from this ESM file,
126
+ * so the values are intentionally duplicated. Change both at the same time.
127
+ *
128
+ * If you change defaults here, also update:
129
+ * schematics/ng-add/constants.ts → DEFAULTS + DEFAULT_THEMES
70
130
  */
71
131
  declare const DEFAULT_NG_CONFIG: {
72
- theme: "system";
132
+ defaultTheme: "system";
73
133
  storageKey: string;
74
134
  mode: "class";
75
135
  themes: ("system" | "light" | "dark")[];
76
136
  };
77
- declare const NGX_THEME_STACK_CONFIG: InjectionToken<NgConfig>;
137
+ declare const NGX_THEME_STACK_CONFIG: InjectionToken<NgConfig<string>>;
78
138
  /**
79
- * Helper function to provide Theme Stack configuration.
139
+ * Provides Theme Stack configuration to Angular's DI system.
140
+ *
141
+ * Custom `themes` are **merged** with the built-in defaults
142
+ * (`'light'`, `'dark'`, `'system'`), so you never lose the base themes.
143
+ *
144
+ * The type parameter `T` is **inferred automatically** from the `themes` array
145
+ * when passed as a `const` — no need to specify it manually.
146
+ *
147
+ * @typeParam T - Custom theme string literals, inferred from the `themes` option.
148
+ *
149
+ * @param config - Optional partial configuration. Omitted fields fall back to
150
+ * {@link DEFAULT_NG_CONFIG}.
151
+ *
152
+ * @throws {@link NgxThemeStackError}
153
+ * - If any entry in `themes` is an empty or whitespace-only string.
154
+ * - If `defaultTheme` is not present in the resolved (merged) themes array.
155
+ * - If `storageKey` is an empty or whitespace-only string.
156
+ *
157
+ * @example
158
+ * // Default — uses built-in themes and sensible defaults
159
+ * provideThemeStack()
160
+ *
161
+ * @example
162
+ * // Closed union: TypeScript infers 'sepia' | 'ocean' from the array
163
+ * provideThemeStack({
164
+ * themes: ['sepia', 'ocean'] as const,
165
+ * defaultTheme: 'sepia', // ✅ in resolved themes
166
+ * // defaultTheme: 'nope', // ❌ throws NgxThemeStackError at runtime
167
+ * })
168
+ *
169
+ * @example
170
+ * // Custom storage key and mode
171
+ * provideThemeStack({
172
+ * storageKey: 'my-app-theme',
173
+ * mode: 'class',
174
+ * })
80
175
  */
81
- declare function provideThemeStack(config?: Partial<NgConfig>): {
82
- provide: InjectionToken<NgConfig>;
83
- useValue: {
84
- theme: ngx_theme_stack.NgTheme;
85
- storageKey: string;
86
- mode: ngx_theme_stack.NgMode;
87
- themes: ngx_theme_stack.NgTheme[];
88
- };
176
+ declare function provideThemeStack<const T extends string = DefaultNgTheme>(config?: Partial<NgConfig<T>>): {
177
+ provide: InjectionToken<NgConfig<string>>;
178
+ useValue: NgConfig<string>;
89
179
  };
90
180
 
91
181
  /**
92
182
  * Convenience service for cycling through themes in a fixed order.
93
183
  *
94
- * Default cycle: `'light'` → `'dark'` → `'system'` → `'light'` → ...
184
+ * Default cycle: `'system'` → `'light'` → `'dark'` → `'system'` → ...
95
185
  *
96
186
  * Use this when you want to offer users a single button that rotates
97
187
  * through all available theme options.
@@ -101,11 +191,18 @@ declare class ThemeCycleService {
101
191
  /** The theme explicitly selected by the user. May be `'system'`. */
102
192
  readonly selectedTheme: _angular_core.Signal<ngx_theme_stack.NgTheme>;
103
193
  /** Resolved theme applied to the DOM. Always concrete — never `'system'`. */
104
- readonly userTheme: _angular_core.Signal<ngx_theme_stack.NgSystemTheme>;
194
+ readonly resolvedTheme: _angular_core.Signal<(string & {}) | ngx_theme_stack.NgSystemTheme>;
105
195
  /** Whether the currently applied theme is dark. */
106
196
  readonly isDark: _angular_core.Signal<boolean>;
107
197
  /** Whether the currently applied theme is light. */
108
198
  readonly isLight: _angular_core.Signal<boolean>;
199
+ /** Whether the currently applied theme is system. */
200
+ readonly isSystem: _angular_core.Signal<boolean>;
201
+ /**
202
+ * Whether the service has completed client-side initialization.
203
+ * `false` during SSR. Becomes `true` after the first browser render.
204
+ */
205
+ readonly isHydrated: _angular_core.Signal<boolean>;
109
206
  /**
110
207
  * Advances to the next theme in the cycle.
111
208
  *
@@ -128,15 +225,22 @@ declare class ThemeCycleService {
128
225
  declare class ThemeSelectService {
129
226
  #private;
130
227
  /** List of all configured themes. Defaults to ['light', 'dark', 'system']. */
131
- readonly availableThemes: NgTheme[];
228
+ readonly availableThemes: string[];
132
229
  /** The theme explicitly selected by the user. May be `'system'`. */
133
230
  readonly selectedTheme: _angular_core.Signal<NgTheme>;
134
231
  /** Resolved theme applied to the DOM. Always concrete — never `'system'`. */
135
- readonly userTheme: _angular_core.Signal<ngx_theme_stack.NgSystemTheme>;
232
+ readonly resolvedTheme: _angular_core.Signal<(string & {}) | ngx_theme_stack.NgSystemTheme>;
136
233
  /** Whether the currently applied theme is dark. */
137
234
  readonly isDark: _angular_core.Signal<boolean>;
138
235
  /** Whether the currently applied theme is light. */
139
236
  readonly isLight: _angular_core.Signal<boolean>;
237
+ /** Whether the currently applied theme is system. */
238
+ readonly isSystem: _angular_core.Signal<boolean>;
239
+ /**
240
+ * Whether the service has completed client-side initialization.
241
+ * `false` during SSR. Becomes `true` after the first browser render.
242
+ */
243
+ readonly isHydrated: _angular_core.Signal<boolean>;
140
244
  /**
141
245
  * Applies the given theme.
142
246
  *
@@ -157,11 +261,21 @@ declare class ThemeSelectService {
157
261
  declare class ThemeToggleService {
158
262
  #private;
159
263
  /** Resolved theme applied to the DOM. Always concrete — never `'system'`. */
160
- readonly userTheme: _angular_core.Signal<ngx_theme_stack.NgSystemTheme>;
264
+ readonly resolvedTheme: _angular_core.Signal<(string & {}) | ngx_theme_stack.NgSystemTheme>;
265
+ readonly selectedTheme: _angular_core.Signal<ngx_theme_stack.NgTheme>;
161
266
  /** Whether the currently applied theme is dark. */
162
267
  readonly isDark: _angular_core.Signal<boolean>;
163
268
  /** Whether the currently applied theme is light. */
164
269
  readonly isLight: _angular_core.Signal<boolean>;
270
+ /** Whether the currently applied theme is system. */
271
+ readonly isSystem: _angular_core.Signal<boolean>;
272
+ /**
273
+ * Whether the service has completed client-side initialization.
274
+ * `false` during SSR. Becomes `true` after the first browser render.
275
+ * Guard any template logic that shows `selectedTheme` or `resolvedTheme`
276
+ * behind this signal to avoid a hydration-mismatch flash.
277
+ */
278
+ readonly isHydrated: _angular_core.Signal<boolean>;
165
279
  /**
166
280
  * Toggles between `'dark'` and `'light'`.
167
281
  *
@@ -173,5 +287,26 @@ declare class ThemeToggleService {
173
287
  static ɵprov: _angular_core.ɵɵInjectableDeclaration<ThemeToggleService>;
174
288
  }
175
289
 
176
- export { CoreThemeService, DEFAULT_NG_CONFIG, DEFAULT_THEMES, NGX_THEME_STACK_CONFIG, ThemeCycleService, ThemeSelectService, ThemeToggleService, provideThemeStack };
177
- export type { NgConfig, NgMode, NgSystemTheme, NgTheme };
290
+ /**
291
+ * Base error class for `ngx-theme-stack`.
292
+ *
293
+ * Thrown when the library configuration is invalid.
294
+ * Consumers can use `instanceof NgxThemeStackError` to catch only
295
+ * errors originating from this library.
296
+ *
297
+ * @example
298
+ * try {
299
+ * bootstrapApplication(AppComponent, appConfig);
300
+ * } catch (e) {
301
+ * if (e instanceof NgxThemeStackError) {
302
+ * console.error('Bad ngx-theme-stack config:', e.message);
303
+ * }
304
+ * }
305
+ */
306
+ declare class NgxThemeStackError extends Error {
307
+ readonly name = "NgxThemeStackError";
308
+ constructor(message: string);
309
+ }
310
+
311
+ export { CoreThemeService, DEFAULT_NG_CONFIG, DEFAULT_THEMES, NGX_THEME_STACK_CONFIG, NgxThemeStackError, ThemeCycleService, ThemeSelectService, ThemeToggleService, provideThemeStack };
312
+ export type { DefaultNgTheme, NgConfig, NgMode, NgSystemTheme, NgTheme };