@scania-nl/tegel-angular-extensions 0.0.1 → 0.0.4

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.
Files changed (40) hide show
  1. package/esm2022/index.mjs +5 -0
  2. package/esm2022/lib/toast/models/toast-state.enum.mjs +19 -0
  3. package/esm2022/lib/toast/models/toast-type.mjs +5 -0
  4. package/esm2022/lib/toast/models/toast.model.mjs +2 -0
  5. package/esm2022/lib/toast/provide-toast.mjs +19 -0
  6. package/esm2022/lib/toast/toast.component.mjs +34 -0
  7. package/esm2022/lib/toast/toast.config.mjs +11 -0
  8. package/esm2022/lib/toast/toast.service.mjs +206 -0
  9. package/esm2022/lib/utils/bootstrap-global-component.mjs +43 -0
  10. package/esm2022/scania-nl-tegel-angular-extensions.mjs +5 -0
  11. package/{src/index.ts → index.d.ts} +1 -1
  12. package/lib/toast/models/toast-state.enum.d.ts +17 -0
  13. package/lib/toast/models/toast-type.d.ts +8 -0
  14. package/lib/toast/models/toast.model.d.ts +73 -0
  15. package/lib/toast/provide-toast.d.ts +3 -0
  16. package/lib/toast/toast.component.d.ts +24 -0
  17. package/lib/toast/toast.config.d.ts +35 -0
  18. package/lib/toast/toast.service.d.ts +98 -0
  19. package/lib/utils/bootstrap-global-component.d.ts +23 -0
  20. package/package.json +16 -2
  21. package/eslint.config.mjs +0 -48
  22. package/jest.config.ts +0 -21
  23. package/ng-package.json +0 -7
  24. package/project.json +0 -36
  25. package/src/lib/toast/models/toast-state.enum.ts +0 -19
  26. package/src/lib/toast/models/toast-type.ts +0 -9
  27. package/src/lib/toast/models/toast.model.ts +0 -87
  28. package/src/lib/toast/provide-toast.ts +0 -29
  29. package/src/lib/toast/toast.component.html +0 -41
  30. package/src/lib/toast/toast.component.scss +0 -118
  31. package/src/lib/toast/toast.component.spec.ts +0 -28
  32. package/src/lib/toast/toast.component.ts +0 -37
  33. package/src/lib/toast/toast.config.ts +0 -50
  34. package/src/lib/toast/toast.service.ts +0 -275
  35. package/src/lib/utils/bootstrap-global-component.ts +0 -73
  36. package/src/test-setup.ts +0 -6
  37. package/tsconfig.json +0 -28
  38. package/tsconfig.lib.json +0 -17
  39. package/tsconfig.lib.prod.json +0 -7
  40. package/tsconfig.spec.json +0 -16
@@ -1,275 +0,0 @@
1
- import { computed, inject, Injectable, signal } from '@angular/core';
2
- import { takeUntilDestroyed } from '@angular/core/rxjs-interop';
3
- import { delay, filter, mergeMap, of, Subject, tap } from 'rxjs';
4
- import { ToastState } from './models/toast-state.enum';
5
- import { TOAST_TYPES } from './models/toast-type';
6
- import { Toast, ToastOptions } from './models/toast.model';
7
- import { TOAST_CONFIG, ToastConfig } from './toast.config';
8
-
9
- /**
10
- * Service for creating, managing, and removing toast notifications.
11
- * Supports automatic dismissal, manual control, and lifecycle hooks.
12
- */
13
- @Injectable({
14
- providedIn: 'root',
15
- })
16
- export class ToastService {
17
- private readonly config = inject<ToastConfig>(TOAST_CONFIG);
18
-
19
- //*------------------------------------------------------------
20
- //* Section: Internal variables
21
- //*------------------------------------------------------------
22
-
23
- /** Internal ID tracker for unique toast IDs */
24
- private id = 0;
25
-
26
- /** Signal state holding all toasts */
27
- private readonly _toasts = signal<Toast[]>([]);
28
-
29
- /** Public signal of all toasts */
30
- readonly toasts = this._toasts.asReadonly();
31
-
32
- /** Signal of toasts that are not yet closed (open or closing) */
33
- readonly activeToasts = computed(() =>
34
- this._toasts().filter((toast) => toast.state !== ToastState.Closed)
35
- );
36
-
37
- /** Internal stream for auto-closing toasts */
38
- private readonly autoCloseSubject = new Subject<Toast>();
39
-
40
- /** Internal stream for fade-out/removal of toasts */
41
- private readonly closeSubject = new Subject<Toast>();
42
-
43
- constructor() {
44
- // Auto-close after toast.duration
45
- this.autoCloseSubject
46
- .pipe(
47
- takeUntilDestroyed(),
48
- filter((toast) => toast.duration > 0),
49
- mergeMap((toast: Toast) =>
50
- of(toast).pipe(
51
- delay(toast.duration),
52
- filter(() => this.shouldAutoClose(toast.id)),
53
- tap(() => this.close(toast.id))
54
- )
55
- )
56
- )
57
- .subscribe();
58
-
59
- // Remove after toast.closeDuration (fade-out)
60
- this.closeSubject
61
- .pipe(
62
- takeUntilDestroyed(),
63
- mergeMap((toast: Toast) =>
64
- of(toast).pipe(
65
- delay(toast.closeDuration),
66
- filter(() => this.shouldRemove(toast.id)),
67
- tap(() => this.remove(toast.id))
68
- )
69
- )
70
- )
71
- .subscribe();
72
- }
73
-
74
- //*------------------------------------------------------------
75
- //* Section: Public methods
76
- //*------------------------------------------------------------
77
-
78
- /**
79
- * Creates and adds a new toast.
80
- * @param toastOptions Partial toast definition.
81
- * @returns The toast's unique ID.
82
- */
83
- create(toastOptions: Partial<ToastOptions>): number {
84
- const id = this.createId();
85
- const toast: Toast = {
86
- ...this.config,
87
- ...toastOptions,
88
- id,
89
- state: ToastState.Open,
90
- duration: this.normalizeDuration(
91
- toastOptions.duration,
92
- this.config.duration
93
- ),
94
- closeDuration: this.normalizeDuration(
95
- toastOptions.closeDuration,
96
- this.config.closeDuration
97
- ),
98
- };
99
-
100
- this.addToast(toast);
101
- toast.onCreated?.(toast);
102
-
103
- // Schedule auto-close if duration > 0
104
- if (toast.duration > 0) {
105
- this.autoCloseSubject.next(toast);
106
- }
107
-
108
- return id;
109
- }
110
-
111
- /**
112
- * Initiates the fade-out transition for a toast.
113
- * @param id The toast ID.
114
- */
115
- close(id: number): void {
116
- let toast = this.getToast(id);
117
- if (!toast || toast.state !== ToastState.Open) return;
118
-
119
- toast = this.updateToastState(toast.id, ToastState.Closing);
120
- toast?.onClose?.(toast);
121
-
122
- // Schedule removal after close duration
123
- if (toast) this.closeSubject.next(toast);
124
- }
125
-
126
- /**
127
- * Immediately marks a toast as closed and removes it from display.
128
- * @param id The toast ID.
129
- */
130
- remove(id: number): void {
131
- const toast = this.updateToastState(id, ToastState.Closed);
132
- toast?.onRemoved?.(toast);
133
- }
134
-
135
- /**
136
- * Closes and removes all toasts immediately without fade-out.
137
- */
138
- removeAll(): void {
139
- const closedToasts: Toast[] = [];
140
-
141
- this._toasts.update((toasts) =>
142
- toasts.map((toast) => {
143
- if (toast.state !== ToastState.Closed) {
144
- const updated = { ...toast, state: ToastState.Closed };
145
- closedToasts.push(updated);
146
- return updated;
147
- }
148
- return toast;
149
- })
150
- );
151
-
152
- closedToasts.forEach((toast) => toast.onRemoved?.(toast));
153
- }
154
-
155
- /**
156
- * Initiates closing process for all open toasts.
157
- */
158
- closeAll(): void {
159
- const openToasts = this.toasts().filter(
160
- (toast) => toast.state === ToastState.Open
161
- );
162
- openToasts.forEach((toast) => this.close(toast.id));
163
- }
164
-
165
- /**
166
- * Gets a toast by ID.
167
- * @param id The toast ID.
168
- * @returns The toast instance or undefined.
169
- */
170
- getToast(id: number): Toast | undefined {
171
- return this._toasts().find((toast) => toast.id === id);
172
- }
173
-
174
- //*------------------------------------------------------------
175
- //* Section: Internal methods
176
- //*------------------------------------------------------------
177
-
178
- /** Whether the toast is eligible for auto-closing */
179
- private shouldAutoClose(id: number): boolean {
180
- const currentToast = this.getToast(id);
181
- return currentToast?.state === ToastState.Open;
182
- }
183
-
184
- /** Whether the toast is eligible for final removal */
185
- private shouldRemove(id: number): boolean {
186
- const currentToast = this.getToast(id);
187
- return currentToast?.state === ToastState.Closing;
188
- }
189
-
190
- /** Add toast to signal list */
191
- private addToast(toast: Toast): void {
192
- this._toasts.update((prev) => [...prev, toast]);
193
- }
194
-
195
- /**
196
- * Updates the state of a toast.
197
- * @param id Toast ID
198
- * @param state New state
199
- * @returns The updated toast or undefined
200
- */
201
- private updateToastState(id: number, state: ToastState): Toast | undefined {
202
- let updatedToast: Toast | undefined;
203
-
204
- this._toasts.update((prev) =>
205
- prev.map((toast) => {
206
- if (toast.id === id) {
207
- updatedToast = { ...toast, state };
208
- return updatedToast;
209
- }
210
- return toast;
211
- })
212
- );
213
-
214
- return updatedToast;
215
- }
216
-
217
- /**
218
- * Creates a unique id
219
- * @returns New id
220
- */
221
- private createId(): number {
222
- return ++this.id;
223
- }
224
-
225
- //*------------------------------------------------------------
226
- //* Section: Public convenience methods
227
- //*------------------------------------------------------------
228
-
229
- /**
230
- * Creates a success toast.
231
- * @param props Toast props without type.
232
- */
233
- readonly success = (props: Partial<Omit<ToastOptions, 'type'>> = {}) =>
234
- this.create({ ...props, type: 'success' });
235
-
236
- /**
237
- * Creates an error toast.
238
- * @param props Toast props without type.
239
- */
240
- readonly error = (props: Partial<Omit<ToastOptions, 'type'>> = {}) =>
241
- this.create({ ...props, type: 'error' });
242
-
243
- /**
244
- * Creates a warning toast.
245
- * @param props Toast props without type.
246
- */
247
- readonly warning = (props: Partial<Omit<ToastOptions, 'type'>> = {}) =>
248
- this.create({ ...props, type: 'warning' });
249
-
250
- /**
251
- * Creates an informational toast.
252
- * @param props Toast props without type.
253
- */
254
- readonly info = (props: Partial<Omit<ToastOptions, 'type'>> = {}) =>
255
- this.create({ ...props, type: 'information' });
256
-
257
- /**
258
- * Creates a random toast for testing/demo purposes.
259
- * @param props Optional overrides
260
- */
261
- readonly createRandomToast = (props: Partial<ToastOptions> = {}) =>
262
- this.create({
263
- type: TOAST_TYPES[~~(Math.random() * 4)],
264
- title: `Random Toast ${Math.random().toString(36).substring(7)}`,
265
- ...props,
266
- });
267
-
268
- //*------------------------------------------------------------
269
- //* Section: Helper methods
270
- //*------------------------------------------------------------
271
- private normalizeDuration(value: unknown, fallback: number): number {
272
- const num = typeof value === 'number' ? value : fallback;
273
- return !Number.isFinite(num) || num < 0 ? 0 : num;
274
- }
275
- }
@@ -1,73 +0,0 @@
1
- import {
2
- ApplicationRef,
3
- ComponentRef,
4
- EmbeddedViewRef,
5
- EnvironmentInjector,
6
- Type,
7
- createComponent,
8
- inject,
9
- } from '@angular/core';
10
-
11
- /**
12
- * Internal map to track which global components have been bootstrapped.
13
- */
14
- const bootstrappedComponents = new WeakMap<
15
- Type<unknown>,
16
- ComponentRef<unknown>
17
- >();
18
-
19
- interface BootstrapGlobalComponentOptions {
20
- /**
21
- * If true, avoids re-creating the component if it's already mounted.
22
- */
23
- reuseIfExists?: boolean;
24
- }
25
-
26
- /**
27
- * Bootstraps a global Angular component directly into the <body> element.
28
- * Useful for toasts, modals, and other global overlays.
29
- *
30
- * @param component - The component class to bootstrap.
31
- * @param options - Optional settings like preventing duplicates.
32
- * @returns The created ComponentRef.
33
- */
34
- export function bootstrapGlobalComponent<T>(
35
- component: Type<T>,
36
- options?: BootstrapGlobalComponentOptions
37
- ): ComponentRef<T> {
38
- const appRef = inject(ApplicationRef);
39
- const injector = inject(EnvironmentInjector);
40
-
41
- const existing = bootstrappedComponents.get(component);
42
- if (existing && options?.reuseIfExists) {
43
- return existing as ComponentRef<T>;
44
- }
45
-
46
- const cmpRef = createComponent(component, {
47
- environmentInjector: injector,
48
- });
49
-
50
- appRef.attachView(cmpRef.hostView);
51
-
52
- const viewRef = cmpRef.hostView as EmbeddedViewRef<unknown>;
53
- const element = viewRef.rootNodes[0] as HTMLElement;
54
-
55
- document.body.appendChild(element);
56
-
57
- bootstrappedComponents.set(component, cmpRef);
58
-
59
- return cmpRef;
60
- }
61
-
62
- /**
63
- * Destroys a previously bootstrapped global component.
64
- *
65
- * @param component - The component class to remove from DOM.
66
- */
67
- export function destroyGlobalComponent<T>(component: Type<T>): void {
68
- const cmpRef = bootstrappedComponents.get(component);
69
- if (cmpRef) {
70
- cmpRef.destroy();
71
- bootstrappedComponents.delete(component);
72
- }
73
- }
package/src/test-setup.ts DELETED
@@ -1,6 +0,0 @@
1
- import { setupZoneTestEnv } from 'jest-preset-angular/setup-env/zone';
2
-
3
- setupZoneTestEnv({
4
- errorOnUnknownElements: true,
5
- errorOnUnknownProperties: true,
6
- });
package/tsconfig.json DELETED
@@ -1,28 +0,0 @@
1
- {
2
- "compilerOptions": {
3
- "target": "es2022",
4
- "forceConsistentCasingInFileNames": true,
5
- "strict": true,
6
- "noImplicitOverride": true,
7
- "noPropertyAccessFromIndexSignature": true,
8
- "noImplicitReturns": true,
9
- "noFallthroughCasesInSwitch": true
10
- },
11
- "files": [],
12
- "include": [],
13
- "references": [
14
- {
15
- "path": "./tsconfig.lib.json"
16
- },
17
- {
18
- "path": "./tsconfig.spec.json"
19
- }
20
- ],
21
- "extends": "../../tsconfig.base.json",
22
- "angularCompilerOptions": {
23
- "enableI18nLegacyMessageIdFormat": false,
24
- "strictInjectionParameters": true,
25
- "strictInputAccessModifiers": true,
26
- "strictTemplates": true
27
- }
28
- }
package/tsconfig.lib.json DELETED
@@ -1,17 +0,0 @@
1
- {
2
- "extends": "./tsconfig.json",
3
- "compilerOptions": {
4
- "outDir": "../../dist/out-tsc",
5
- "declaration": true,
6
- "declarationMap": true,
7
- "inlineSources": true,
8
- "types": []
9
- },
10
- "exclude": [
11
- "src/**/*.spec.ts",
12
- "src/test-setup.ts",
13
- "jest.config.ts",
14
- "src/**/*.test.ts"
15
- ],
16
- "include": ["src/**/*.ts"]
17
- }
@@ -1,7 +0,0 @@
1
- {
2
- "extends": "./tsconfig.lib.json",
3
- "compilerOptions": {
4
- "declarationMap": false
5
- },
6
- "angularCompilerOptions": {}
7
- }
@@ -1,16 +0,0 @@
1
- {
2
- "extends": "./tsconfig.json",
3
- "compilerOptions": {
4
- "outDir": "../../dist/out-tsc",
5
- "module": "commonjs",
6
- "target": "es2016",
7
- "types": ["jest", "node"]
8
- },
9
- "files": ["src/test-setup.ts"],
10
- "include": [
11
- "jest.config.ts",
12
- "src/**/*.test.ts",
13
- "src/**/*.spec.ts",
14
- "src/**/*.d.ts"
15
- ]
16
- }