ng-hub-ui-toast 22.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 ADDED
@@ -0,0 +1,201 @@
1
+ # ng-hub-ui-toast
2
+
3
+ [![NPM Version](https://img.shields.io/npm/v/ng-hub-ui-toast.svg)](https://www.npmjs.com/package/ng-hub-ui-toast)
4
+ [![Angular](https://img.shields.io/badge/Angular-22-red.svg)](https://angular.dev)
5
+ [![License](https://img.shields.io/npm/l/ng-hub-ui-toast.svg)](LICENSE)
6
+
7
+ > Angular 22 standalone toast notification service built on Signals — part of the [ng-hub-ui](https://hubui.dev/) ecosystem.
8
+
9
+ ## Features
10
+
11
+ - **Signal-driven stack** — active toasts live in a `signal<HubToastData[]>`; works with `OnPush` and zoneless apps.
12
+ - **Lazy container mounting** — `ToastContainerComponent` is appended to `document.body` only on the first call; nothing runs at startup.
13
+ - **`HubToastRef`** — every call returns a ref with `onShown`, `onHidden`, `onTap` observables and `manualClose()` / `resetTimeout()`.
14
+ - **Per-call overrides** — set defaults globally with `provideToast()` and override any option individually.
15
+ - **Six positions** — `toast-top-right`, `toast-top-left`, `toast-top-center`, `toast-bottom-right`, `toast-bottom-left`, `toast-bottom-center`.
16
+ - **Progress bar & close button** — built-in configurable dismiss controls.
17
+ - **CSS variable theming** — every colour, radius, shadow, and dimension is a `--hub-toast-*` token.
18
+ - **Built-in semantic types** — `success`, `error`, `warning`, `info` each inherit the matching DS `--hub-sys-color-*` accent family.
19
+
20
+ ## Installation
21
+
22
+ ```bash
23
+ npm install ng-hub-ui-toast
24
+ ```
25
+
26
+ ## Quick start
27
+
28
+ ```typescript
29
+ // app.config.ts
30
+ import { provideToast } from 'ng-hub-ui-toast';
31
+
32
+ export const appConfig: ApplicationConfig = {
33
+ providers: [
34
+ provideToast({ progressBar: true, timeOut: 4000 })
35
+ ]
36
+ };
37
+ ```
38
+
39
+ ```typescript
40
+ // any component or service
41
+ import { ToastService } from 'ng-hub-ui-toast';
42
+
43
+ @Component({ ... })
44
+ export class SaveComponent {
45
+ private toast = inject(ToastService);
46
+
47
+ save() {
48
+ this.toast.success('Record saved.', 'Success');
49
+ }
50
+ }
51
+ ```
52
+
53
+ ## API
54
+
55
+ ### `provideToast(config?)`
56
+
57
+ Call once in `ApplicationConfig.providers`. All options are optional.
58
+
59
+ | Option | Type | Default | Description |
60
+ |---|---|---|---|
61
+ | `timeOut` | `number` | `5000` | Auto-dismiss delay (ms). `0` = persistent. |
62
+ | `extendedTimeOut` | `number` | `2500` | Extra ms added while the user hovers. |
63
+ | `closeButton` | `boolean` | `true` | Show a × close button. |
64
+ | `progressBar` | `boolean` | `false` | Show a countdown progress bar. |
65
+ | `tapToDismiss` | `boolean` | `true` | Dismiss on click. |
66
+ | `disableTimeOut` | `boolean \| 'timeOut' \| 'extendedTimeOut'` | `false` | Disable the auto-dismiss timer. |
67
+ | `newestOnTop` | `boolean` | `true` | Stack newest toasts at the top. |
68
+ | `positionClass` | `HubToastPosition` | `'toast-top-right'` | Container position on screen. |
69
+ | `maxOpened` | `number` | `0` | Max simultaneous toasts (`0` = unlimited). |
70
+ | `autoDismiss` | `boolean` | `false` | Auto-remove oldest when `maxOpened` is reached. |
71
+ | `preventDuplicates` | `boolean` | `false` | Drop new toasts with a matching visible message. |
72
+
73
+ ### `ToastService`
74
+
75
+ | Method | Signature | Description |
76
+ |---|---|---|
77
+ | `success` | `(message, title?, config?) → HubToastRef` | Show a success toast. |
78
+ | `error` | `(message, title?, config?) → HubToastRef` | Show an error toast. |
79
+ | `warning` | `(message, title?, config?) → HubToastRef` | Show a warning toast. |
80
+ | `info` | `(message, title?, config?) → HubToastRef` | Show an info toast. |
81
+ | `show` | `(message, title?, config?, type?) → HubToastRef` | Show a toast with any type (including custom strings). |
82
+ | `remove` | `(toastId: number) → void` | Remove one toast by id. |
83
+ | `clear` | `() → void` | Remove all active toasts. |
84
+ | `toasts` | `Signal<HubToastData[]>` | Read-only signal of the current stack. |
85
+
86
+ ### `HubToastRef`
87
+
88
+ ```typescript
89
+ interface HubToastRef {
90
+ readonly toastId: number;
91
+ readonly onShown: Observable<void>; // fires once when toast enters DOM
92
+ readonly onHidden: Observable<void>; // fires once when toast leaves DOM
93
+ readonly onTap: Observable<void>; // fires each time user clicks toast body
94
+ manualClose(): void; // removes immediately
95
+ resetTimeout(): void; // restarts the auto-dismiss timer
96
+ }
97
+ ```
98
+
99
+ ### Lifecycle example
100
+
101
+ ```typescript
102
+ const ref = this.toast.success('Upload complete', 'Done', { timeOut: 0 });
103
+
104
+ ref.onTap.subscribe(() => this.router.navigate(['/uploads']));
105
+ ref.onHidden.subscribe(() => console.log('toast gone'));
106
+
107
+ // close it programmatically later
108
+ someButton.addEventListener('click', () => ref.manualClose());
109
+ ```
110
+
111
+ ## Positions
112
+
113
+ | Class | Location |
114
+ |---|---|
115
+ | `toast-top-right` | Top-right corner (default) |
116
+ | `toast-top-left` | Top-left corner |
117
+ | `toast-top-center` | Top-center |
118
+ | `toast-bottom-right` | Bottom-right corner |
119
+ | `toast-bottom-left` | Bottom-left corner |
120
+ | `toast-bottom-center` | Bottom-center |
121
+
122
+ ```typescript
123
+ // per-call override
124
+ this.toast.info('Message', '', { positionClass: 'toast-bottom-center' });
125
+ ```
126
+
127
+ ## CSS Variables
128
+
129
+ Override any token from your stylesheet — no component re-configuration needed.
130
+
131
+ ### Toast element
132
+
133
+ | Variable | Default | Description |
134
+ |---|---|---|
135
+ | `--hub-toast-bg` | `var(--hub-sys-surface-page, #fff)` | Background colour. |
136
+ | `--hub-toast-color` | `var(--hub-sys-text-primary, #212529)` | Text colour. |
137
+ | `--hub-toast-border` | `var(--hub-sys-border-color-default, #dee2e6)` | Border colour. |
138
+ | `--hub-toast-accent` | `var(--hub-sys-border-color-default, #dee2e6)` | Left accent border colour. |
139
+ | `--hub-toast-accent-width` | `0.25rem` | Left accent border thickness. |
140
+ | `--hub-toast-min-width` | `18rem` | Minimum width. |
141
+ | `--hub-toast-max-width` | `26rem` | Maximum width. |
142
+ | `--hub-toast-padding-x` | `var(--hub-ref-space-3, 1rem)` | Horizontal padding. |
143
+ | `--hub-toast-padding-y` | `var(--hub-ref-space-3, 1rem)` | Vertical padding. |
144
+ | `--hub-toast-border-radius` | `var(--hub-ref-radius-md, 0.375rem)` | Border radius. |
145
+ | `--hub-toast-border-width` | `var(--hub-ref-border-width, 1px)` | Border width. |
146
+ | `--hub-toast-shadow` | `var(--hub-sys-shadow-md, 0 0.25rem 0.75rem rgba(0,0,0,.1))` | Box shadow. |
147
+ | `--hub-toast-gap` | `var(--hub-ref-space-1, 0.25rem)` | Gap between title and message. |
148
+ | `--hub-toast-font-size` | `var(--hub-ref-font-size-base, 1rem)` | Message font size. |
149
+ | `--hub-toast-title-font-size` | `var(--hub-ref-font-size-base, 1rem)` | Title font size. |
150
+ | `--hub-toast-title-font-weight` | `600` | Title font weight. |
151
+ | `--hub-toast-progress-height` | `0.25rem` | Progress bar height. |
152
+ | `--hub-toast-progress-bg` | `color-mix(in srgb, var(--hub-toast-accent) 30%, transparent)` | Progress bar colour. |
153
+ | `--hub-toast-close-opacity` | `0.5` | Close button opacity. |
154
+ | `--hub-toast-close-opacity-hover` | `1` | Close button hover opacity. |
155
+
156
+ ### Container
157
+
158
+ | Variable | Default | Description |
159
+ |---|---|---|
160
+ | `--hub-toast-container-gap` | `var(--hub-ref-space-2, 0.5rem)` | Gap between stacked toasts. |
161
+ | `--hub-toast-container-offset` | `var(--hub-ref-space-3, 1rem)` | Distance from screen edges. |
162
+ | `--hub-toast-container-z-index` | `1050` | Stack order. |
163
+
164
+ ### Theming example
165
+
166
+ ```css
167
+ /* global dark toast for all types */
168
+ :root {
169
+ --hub-toast-bg: #1e1e1e;
170
+ --hub-toast-color: #f5f5f5;
171
+ --hub-toast-border-radius: 0.5rem;
172
+ --hub-toast-container-offset: 1.5rem;
173
+ }
174
+ ```
175
+
176
+ ## Custom toast types
177
+
178
+ Pass any string as the `type` argument to `show()`. Set `--hub-toast-accent` on the calling element to drive the automatic colour derivation:
179
+
180
+ ```typescript
181
+ this.toast.show('Sync queued.', 'Offline', { timeOut: 0 }, 'offline');
182
+ ```
183
+
184
+ ```css
185
+ /* set the accent for your custom type */
186
+ hub-toast[data-type='offline'] {
187
+ --hub-toast-accent: #6c757d;
188
+ }
189
+ ```
190
+
191
+ ## Peer dependencies
192
+
193
+ | Package | Version |
194
+ |---|---|
195
+ | `@angular/core` | `>=22.0.0` |
196
+ | `@angular/common` | `>=22.0.0` |
197
+ | `@angular/animations` | `>=22.0.0` |
198
+
199
+ ## License
200
+
201
+ MIT © [Carlos Morcillo](https://www.carlosmorcillo.com)
@@ -0,0 +1,450 @@
1
+ import * as i0 from '@angular/core';
2
+ import { InjectionToken, inject, Injectable, ApplicationRef, signal, createComponent, input, output, computed, effect, ChangeDetectionStrategy, Component } from '@angular/core';
3
+ import { Subject } from 'rxjs';
4
+ import { trigger, state, style, transition, animate, keyframes } from '@angular/animations';
5
+
6
+ /** Default configuration applied to every toast unless overridden. */
7
+ const HUB_TOAST_DEFAULT_CONFIG = {
8
+ timeOut: 5000,
9
+ extendedTimeOut: 2500,
10
+ closeButton: true,
11
+ progressBar: false,
12
+ tapToDismiss: true,
13
+ disableTimeOut: false,
14
+ newestOnTop: true,
15
+ positionClass: 'toast-top-right',
16
+ maxOpened: 0,
17
+ autoDismiss: false,
18
+ preventDuplicates: false
19
+ };
20
+ /** Injection token for the global toast configuration. */
21
+ const HUB_TOAST_CONFIG = new InjectionToken('HUB_TOAST_CONFIG');
22
+ /**
23
+ * Registers the toast library providers.
24
+ * Call inside `ApplicationConfig.providers` or a route's `providers` array.
25
+ *
26
+ * @param config - Partial global defaults merged over {@link HUB_TOAST_DEFAULT_CONFIG}.
27
+ *
28
+ * @example
29
+ * ```typescript
30
+ * export const appConfig: ApplicationConfig = {
31
+ * providers: [provideToast({ timeOut: 3000, progressBar: true })]
32
+ * };
33
+ * ```
34
+ */
35
+ function provideToast(config = {}) {
36
+ return [{ provide: HUB_TOAST_CONFIG, useValue: config }];
37
+ }
38
+ /**
39
+ * Resolves per-toast config by merging global defaults, the provider override,
40
+ * and any per-call overrides. Injected by `ToastService`.
41
+ */
42
+ class ToastConfigService {
43
+ _override = inject(HUB_TOAST_CONFIG, { optional: true }) ?? {};
44
+ /** Returns the merged global config (default ← provider override). */
45
+ get defaults() {
46
+ return { ...HUB_TOAST_DEFAULT_CONFIG, ...this._override };
47
+ }
48
+ /**
49
+ * Merges global defaults with per-call overrides into a final config.
50
+ *
51
+ * @param perCall - Per-call partial overrides.
52
+ * @returns Fully resolved config for one toast.
53
+ */
54
+ resolve(perCall = {}) {
55
+ return { ...this.defaults, ...perCall };
56
+ }
57
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.1", ngImport: i0, type: ToastConfigService, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
58
+ static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "22.0.1", ngImport: i0, type: ToastConfigService, providedIn: 'root' });
59
+ }
60
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.1", ngImport: i0, type: ToastConfigService, decorators: [{
61
+ type: Injectable,
62
+ args: [{ providedIn: 'root' }]
63
+ }] });
64
+
65
+ /** Monotonically increasing id counter. */
66
+ let nextId = 0;
67
+ /**
68
+ * Core service for displaying toast notifications.
69
+ * Manages the active toast stack as a signal and lazily mounts the
70
+ * container overlay on the first toast call.
71
+ *
72
+ * @example
73
+ * ```typescript
74
+ * constructor(private toastr: ToastService) {}
75
+ *
76
+ * save() {
77
+ * this.toastr.success('Record saved', 'Success');
78
+ * }
79
+ * ```
80
+ */
81
+ class ToastService {
82
+ _config = inject(ToastConfigService);
83
+ _appRef = inject(ApplicationRef);
84
+ /** Read-only signal of all currently active toasts. */
85
+ toasts = signal([], /* @ts-ignore */
86
+ ...(ngDevMode ? [{ debugName: "toasts" }] : /* istanbul ignore next */ []));
87
+ _containerMounted = false;
88
+ /** Reference to the lazily created container, kept for explicit CD triggers. */
89
+ _containerRef = null;
90
+ // ─── Public shorthand methods ───────────────────────────────────────────
91
+ /**
92
+ * Shows a success toast.
93
+ * @param message - Notification body.
94
+ * @param title - Optional heading.
95
+ * @param config - Per-call config overrides.
96
+ */
97
+ success(message, title = '', config = {}) {
98
+ return this.show(message, title, config, 'success');
99
+ }
100
+ /**
101
+ * Shows an error toast.
102
+ * @param message - Notification body.
103
+ * @param title - Optional heading.
104
+ * @param config - Per-call config overrides.
105
+ */
106
+ error(message, title = '', config = {}) {
107
+ return this.show(message, title, config, 'error');
108
+ }
109
+ /**
110
+ * Shows a warning toast.
111
+ * @param message - Notification body.
112
+ * @param title - Optional heading.
113
+ * @param config - Per-call config overrides.
114
+ */
115
+ warning(message, title = '', config = {}) {
116
+ return this.show(message, title, config, 'warning');
117
+ }
118
+ /**
119
+ * Shows an informational toast.
120
+ * @param message - Notification body.
121
+ * @param title - Optional heading.
122
+ * @param config - Per-call config overrides.
123
+ */
124
+ info(message, title = '', config = {}) {
125
+ return this.show(message, title, config, 'info');
126
+ }
127
+ /**
128
+ * Shows a toast with a custom or built-in type.
129
+ * The type string is applied as `data-type` on the toast host element
130
+ * and drives the SCSS `@each` accent loop.
131
+ *
132
+ * @param message - Notification body.
133
+ * @param title - Optional heading.
134
+ * @param config - Per-call config overrides.
135
+ * @param type - Semantic type or any custom string.
136
+ */
137
+ show(message, title = '', config = {}, type = 'info') {
138
+ const resolved = this._config.resolve(config);
139
+ if (resolved.preventDuplicates) {
140
+ const duplicate = this.toasts().some((t) => t.message === message && t.type === type);
141
+ if (duplicate) {
142
+ return this._refForExisting(message, type);
143
+ }
144
+ }
145
+ if (resolved.maxOpened > 0 && this.toasts().length >= resolved.maxOpened) {
146
+ if (resolved.autoDismiss) {
147
+ const oldest = resolved.newestOnTop ? this.toasts()[this.toasts().length - 1] : this.toasts()[0];
148
+ this._removeById(oldest.toastId);
149
+ }
150
+ else {
151
+ return this._buildRef({ toastId: -1 });
152
+ }
153
+ }
154
+ const data = {
155
+ toastId: ++nextId,
156
+ type,
157
+ message,
158
+ title,
159
+ config: resolved,
160
+ onShown$: new Subject(),
161
+ onHidden$: new Subject(),
162
+ onTap$: new Subject()
163
+ };
164
+ if (resolved.newestOnTop) {
165
+ this.toasts.update((list) => [data, ...list]);
166
+ }
167
+ else {
168
+ this.toasts.update((list) => [...list, data]);
169
+ }
170
+ this._ensureContainerMounted();
171
+ this._syncContainer();
172
+ return this._buildRef(data);
173
+ }
174
+ /**
175
+ * Removes a specific toast by id.
176
+ * @param toastId - The id returned by the show method.
177
+ */
178
+ remove(toastId) {
179
+ this._removeById(toastId);
180
+ }
181
+ /** Removes all active toasts immediately. */
182
+ clear() {
183
+ this.toasts().forEach((t) => {
184
+ t.onHidden$.next();
185
+ t.onHidden$.complete();
186
+ });
187
+ this.toasts.set([]);
188
+ this._syncContainer();
189
+ }
190
+ // ─── Internal helpers ────────────────────────────────────────────────────
191
+ _removeById(toastId) {
192
+ const toast = this.toasts().find((t) => t.toastId === toastId);
193
+ if (toast) {
194
+ toast.onHidden$.next();
195
+ toast.onHidden$.complete();
196
+ this.toasts.update((list) => list.filter((t) => t.toastId !== toastId));
197
+ this._syncContainer();
198
+ }
199
+ }
200
+ _refForExisting(message, type) {
201
+ const existing = this.toasts().find((t) => t.message === message && t.type === type);
202
+ return existing ? this._buildRef(existing) : this._buildRef({ toastId: -1 });
203
+ }
204
+ _buildRef(data) {
205
+ const svc = this;
206
+ return {
207
+ toastId: data.toastId,
208
+ onShown: data.onShown$?.asObservable() ?? new Subject().asObservable(),
209
+ onHidden: data.onHidden$?.asObservable() ?? new Subject().asObservable(),
210
+ onTap: data.onTap$?.asObservable() ?? new Subject().asObservable(),
211
+ manualClose() {
212
+ svc.remove(data.toastId);
213
+ },
214
+ resetTimeout() {
215
+ const toast = svc.toasts().find((t) => t.toastId === data.toastId);
216
+ if (toast) {
217
+ toast.onShown$.next();
218
+ }
219
+ }
220
+ };
221
+ }
222
+ /**
223
+ * Lazily mounts the `ToastContainerComponent` via Angular's `createComponent`.
224
+ * Called on the first toast — subsequent calls are no-ops.
225
+ */
226
+ _ensureContainerMounted() {
227
+ if (this._containerMounted) {
228
+ return;
229
+ }
230
+ this._containerMounted = true;
231
+ Promise.resolve().then(function () { return toastContainer_component; }).then(({ ToastContainerComponent }) => {
232
+ const ref = createComponent(ToastContainerComponent, {
233
+ environmentInjector: this._appRef.injector
234
+ });
235
+ this._containerRef = ref;
236
+ this._appRef.attachView(ref.hostView);
237
+ document.body.appendChild(ref.location.nativeElement);
238
+ // Initial render: signal may already hold toasts queued before the import resolved.
239
+ ref.changeDetectorRef.detectChanges();
240
+ });
241
+ }
242
+ /**
243
+ * Explicitly runs change detection on the container.
244
+ *
245
+ * Views created via `createComponent` + `attachView` are not reachable by
246
+ * Angular's signal-based "mark ancestors dirty" traversal, so they do not
247
+ * update automatically when a signal changes. Calling `detectChanges()`
248
+ * directly on the container's `ChangeDetectorRef` is the reliable alternative.
249
+ */
250
+ _syncContainer() {
251
+ this._containerRef?.changeDetectorRef.detectChanges();
252
+ }
253
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.1", ngImport: i0, type: ToastService, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
254
+ static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "22.0.1", ngImport: i0, type: ToastService, providedIn: 'root' });
255
+ }
256
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.1", ngImport: i0, type: ToastService, decorators: [{
257
+ type: Injectable,
258
+ args: [{ providedIn: 'root' }]
259
+ }] });
260
+
261
+ /**
262
+ * Slide-in/out animation for individual toasts.
263
+ * Enter: slides in from the inline-end edge with a fade.
264
+ * Leave: fades out with a slight upward shift.
265
+ */
266
+ const toastAnimation = trigger('toastState', [
267
+ state('in', style({ opacity: 1, transform: 'translateX(0)' })),
268
+ transition(':enter', [
269
+ animate('200ms ease-out', keyframes([
270
+ style({ opacity: 0, transform: 'translateX(100%)', offset: 0 }),
271
+ style({ opacity: 1, transform: 'translateX(0)', offset: 1 })
272
+ ]))
273
+ ]),
274
+ transition(':leave', [
275
+ animate('150ms ease-in', keyframes([
276
+ style({ opacity: 1, transform: 'translateY(0)', offset: 0 }),
277
+ style({ opacity: 0, transform: 'translateY(-0.5rem)', offset: 1 })
278
+ ]))
279
+ ])
280
+ ]);
281
+
282
+ /** Built-in type names that have exact DS token coverage via `@each`. */
283
+ const BUILT_IN_TYPES = new Set(['success', 'error', 'warning', 'info']);
284
+ /**
285
+ * Renders a single toast notification.
286
+ *
287
+ * Driven by a {@link HubToastData} input. Manages its own auto-dismiss timer
288
+ * via `signal` + `effect` and emits `(closed)` with the toast id when done.
289
+ *
290
+ * The `data-type` host attribute drives the `@each` SCSS accent loop;
291
+ * `--hub-toast-accent` is set inline only for custom types.
292
+ */
293
+ class ToastComponent {
294
+ /** Toast data provided by `ToastContainerComponent`. */
295
+ data = input.required(/* @ts-ignore */
296
+ ...(ngDevMode ? [{ debugName: "data" }] : /* istanbul ignore next */ []));
297
+ /** Emits the toast id when this toast should be dismissed. */
298
+ closed = output();
299
+ /** Remaining progress as a percentage (100 → 0). Used by the progress bar. */
300
+ progress = signal(100, /* @ts-ignore */
301
+ ...(ngDevMode ? [{ debugName: "progress" }] : /* istanbul ignore next */ []));
302
+ /**
303
+ * Inline accent token. Null for built-in types (covered by `@each`);
304
+ * `var(--hub-sys-color-<type>)` for custom types so `color-mix` derives
305
+ * the other tokens automatically.
306
+ */
307
+ accentToken = computed(() => {
308
+ const type = this.data().type;
309
+ return BUILT_IN_TYPES.has(type) ? null : `var(--hub-sys-color-${type})`;
310
+ }, /* @ts-ignore */
311
+ ...(ngDevMode ? [{ debugName: "accentToken" }] : /* istanbul ignore next */ []));
312
+ _timerId = null;
313
+ _intervalId = null;
314
+ constructor() {
315
+ effect(() => {
316
+ const cfg = this.data().config;
317
+ this._clearTimers();
318
+ if (cfg.disableTimeOut === true || cfg.disableTimeOut === 'timeOut') {
319
+ return;
320
+ }
321
+ if (cfg.timeOut > 0) {
322
+ this._startTimer(cfg.timeOut);
323
+ }
324
+ });
325
+ effect(() => {
326
+ this.data().onShown$.next();
327
+ });
328
+ }
329
+ /** Called by `(mouseenter)` binding in the template. */
330
+ onMouseEnter() {
331
+ const cfg = this.data().config;
332
+ if (cfg.disableTimeOut !== 'extendedTimeOut') {
333
+ this._clearTimers();
334
+ }
335
+ }
336
+ /** Called by `(mouseleave)` binding in the template. */
337
+ onMouseLeave() {
338
+ const cfg = this.data().config;
339
+ if (cfg.extendedTimeOut > 0 && cfg.disableTimeOut !== 'extendedTimeOut') {
340
+ this._startTimer(cfg.extendedTimeOut);
341
+ }
342
+ }
343
+ /** Called by the host `(click)` binding. */
344
+ onTap() {
345
+ const cfg = this.data().config;
346
+ this.data().onTap$.next();
347
+ if (cfg.tapToDismiss) {
348
+ this._dismiss();
349
+ }
350
+ }
351
+ /** Called by the close button in the template. */
352
+ onClose(event) {
353
+ event.stopPropagation();
354
+ this._dismiss();
355
+ }
356
+ ngOnDestroy() {
357
+ this._clearTimers();
358
+ }
359
+ _startTimer(duration) {
360
+ const cfg = this.data().config;
361
+ const start = Date.now();
362
+ if (cfg.progressBar) {
363
+ this._intervalId = setInterval(() => {
364
+ const elapsed = Date.now() - start;
365
+ this.progress.set(Math.max(0, 100 - (elapsed / duration) * 100));
366
+ }, 50);
367
+ }
368
+ this._timerId = setTimeout(() => {
369
+ this._dismiss();
370
+ }, duration);
371
+ }
372
+ _dismiss() {
373
+ this._clearTimers();
374
+ this.closed.emit(this.data().toastId);
375
+ }
376
+ _clearTimers() {
377
+ if (this._timerId !== null) {
378
+ clearTimeout(this._timerId);
379
+ this._timerId = null;
380
+ }
381
+ if (this._intervalId !== null) {
382
+ clearInterval(this._intervalId);
383
+ this._intervalId = null;
384
+ }
385
+ this.progress.set(100);
386
+ }
387
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.1", ngImport: i0, type: ToastComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
388
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.0.1", type: ToastComponent, isStandalone: true, selector: "hub-toast", inputs: { data: { classPropertyName: "data", publicName: "data", isSignal: true, isRequired: true, transformFunction: null } }, outputs: { closed: "closed" }, host: { listeners: { "click": "onTap()" }, properties: { "@toastState": "\"in\"", "attr.data-type": "data().type", "style.--hub-toast-accent": "accentToken()" }, classAttribute: "hub-toast" }, ngImport: i0, template: "<div\n\tclass=\"hub-toast__body\"\n\t(mouseenter)=\"onMouseEnter()\"\n\t(mouseleave)=\"onMouseLeave()\"\n>\n\t@if (data().title) {\n\t\t<div class=\"hub-toast__title\">{{ data().title }}</div>\n\t}\n\t<div class=\"hub-toast__message\">{{ data().message }}</div>\n</div>\n\n@if (data().config.closeButton) {\n\t<button\n\t\ttype=\"button\"\n\t\tclass=\"hub-toast__close\"\n\t\taria-label=\"Close\"\n\t\t(click)=\"onClose($event)\"\n\t>\n\t\t<span aria-hidden=\"true\">&times;</span>\n\t</button>\n}\n\n@if (data().config.progressBar) {\n\t<div\n\t\tclass=\"hub-toast__progress\"\n\t\trole=\"progressbar\"\n\t\t[attr.aria-valuenow]=\"progress()\"\n\t\taria-valuemin=\"0\"\n\t\taria-valuemax=\"100\"\n\t>\n\t\t<div class=\"hub-toast__progress-bar\" [style.width.%]=\"progress()\"></div>\n\t</div>\n}\n", styles: [":where(:host){--hub-toast-min-width: 18rem;--hub-toast-max-width: 26rem;--hub-toast-padding-x: var(--hub-ref-space-3, 1rem);--hub-toast-padding-y: var(--hub-ref-space-3, 1rem);--hub-toast-border-radius: var(--hub-ref-radius-md, .375rem);--hub-toast-border-width: var(--hub-ref-border-width, 1px);--hub-toast-shadow: var(--hub-sys-shadow-md, 0 .25rem .75rem rgba(0, 0, 0, .1));--hub-toast-gap: var(--hub-ref-space-1, .25rem);--hub-toast-accent-width: .25rem;--hub-toast-font-size: var(--hub-ref-font-size-base, 1rem);--hub-toast-title-font-size: var(--hub-ref-font-size-base, 1rem);--hub-toast-title-font-weight: 600;--hub-toast-bg: var(--hub-sys-surface-page, #fff);--hub-toast-color: var(--hub-sys-text-primary, #212529);--hub-toast-border: var(--hub-sys-border-color-default, #dee2e6);--hub-toast-accent: var(--hub-sys-border-color-default, #dee2e6);--hub-toast-progress-bg: color-mix(in srgb, var(--hub-toast-accent) 30%, transparent);--hub-toast-progress-height: .25rem;--hub-toast-close-opacity: .5;--hub-toast-close-opacity-hover: 1}:where(:host[data-type]){--hub-toast-bg: color-mix(in srgb, var(--hub-toast-accent) 14%, var(--hub-sys-surface-page, #fff));--hub-toast-color: color-mix(in srgb, var(--hub-toast-accent) 72%, var(--hub-sys-text-primary, #212529));--hub-toast-border: color-mix(in srgb, var(--hub-toast-accent) 30%, transparent)}:where(:host[data-type=success]){--hub-toast-bg: var(--hub-sys-color-success-subtle);--hub-toast-color: var(--hub-sys-color-success-emphasis);--hub-toast-border: var(--hub-sys-color-success-border-subtle);--hub-toast-accent: var(--hub-sys-color-success)}:where(:host[data-type=warning]){--hub-toast-bg: var(--hub-sys-color-warning-subtle);--hub-toast-color: var(--hub-sys-color-warning-emphasis);--hub-toast-border: var(--hub-sys-color-warning-border-subtle);--hub-toast-accent: var(--hub-sys-color-warning)}:where(:host[data-type=info]){--hub-toast-bg: var(--hub-sys-color-info-subtle);--hub-toast-color: var(--hub-sys-color-info-emphasis);--hub-toast-border: var(--hub-sys-color-info-border-subtle);--hub-toast-accent: var(--hub-sys-color-info)}:where(:host[data-type=error]){--hub-toast-bg: var(--hub-sys-color-danger-subtle);--hub-toast-color: var(--hub-sys-color-danger-emphasis);--hub-toast-border: var(--hub-sys-color-danger-border-subtle);--hub-toast-accent: var(--hub-sys-color-danger)}:host{position:relative;display:flex;align-items:flex-start;min-width:var(--hub-toast-min-width);max-width:var(--hub-toast-max-width);padding:var(--hub-toast-padding-y) var(--hub-toast-padding-x);border-radius:var(--hub-toast-border-radius);border:var(--hub-toast-border-width) solid var(--hub-toast-border);border-inline-start-width:var(--hub-toast-accent-width);border-inline-start-color:var(--hub-toast-accent);background:var(--hub-toast-bg);color:var(--hub-toast-color);box-shadow:var(--hub-toast-shadow);cursor:default;overflow:hidden}.hub-toast__body{flex:1;display:flex;flex-direction:column;gap:var(--hub-toast-gap)}.hub-toast__title{font-size:var(--hub-toast-title-font-size);font-weight:var(--hub-toast-title-font-weight);line-height:1.25}.hub-toast__message{font-size:var(--hub-toast-font-size);line-height:1.5}.hub-toast__close{flex:0 0 auto;padding:0;margin-inline-start:var(--hub-toast-padding-x);background:none;border:none;cursor:pointer;font-size:1.25rem;line-height:1;color:inherit;opacity:var(--hub-toast-close-opacity);transition:opacity .15s ease}.hub-toast__close:hover{opacity:var(--hub-toast-close-opacity-hover)}.hub-toast__progress{position:absolute;inset-inline:0;bottom:0;height:var(--hub-toast-progress-height);background:transparent}.hub-toast__progress-bar{height:100%;background:var(--hub-toast-progress-bg);transition:width 50ms linear}\n"], animations: [toastAnimation], changeDetection: i0.ChangeDetectionStrategy.OnPush });
389
+ }
390
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.1", ngImport: i0, type: ToastComponent, decorators: [{
391
+ type: Component,
392
+ args: [{ selector: 'hub-toast', changeDetection: ChangeDetectionStrategy.OnPush, animations: [toastAnimation], host: {
393
+ class: 'hub-toast',
394
+ '[@toastState]': '"in"',
395
+ '[attr.data-type]': 'data().type',
396
+ '[style.--hub-toast-accent]': 'accentToken()',
397
+ '(click)': 'onTap()'
398
+ }, template: "<div\n\tclass=\"hub-toast__body\"\n\t(mouseenter)=\"onMouseEnter()\"\n\t(mouseleave)=\"onMouseLeave()\"\n>\n\t@if (data().title) {\n\t\t<div class=\"hub-toast__title\">{{ data().title }}</div>\n\t}\n\t<div class=\"hub-toast__message\">{{ data().message }}</div>\n</div>\n\n@if (data().config.closeButton) {\n\t<button\n\t\ttype=\"button\"\n\t\tclass=\"hub-toast__close\"\n\t\taria-label=\"Close\"\n\t\t(click)=\"onClose($event)\"\n\t>\n\t\t<span aria-hidden=\"true\">&times;</span>\n\t</button>\n}\n\n@if (data().config.progressBar) {\n\t<div\n\t\tclass=\"hub-toast__progress\"\n\t\trole=\"progressbar\"\n\t\t[attr.aria-valuenow]=\"progress()\"\n\t\taria-valuemin=\"0\"\n\t\taria-valuemax=\"100\"\n\t>\n\t\t<div class=\"hub-toast__progress-bar\" [style.width.%]=\"progress()\"></div>\n\t</div>\n}\n", styles: [":where(:host){--hub-toast-min-width: 18rem;--hub-toast-max-width: 26rem;--hub-toast-padding-x: var(--hub-ref-space-3, 1rem);--hub-toast-padding-y: var(--hub-ref-space-3, 1rem);--hub-toast-border-radius: var(--hub-ref-radius-md, .375rem);--hub-toast-border-width: var(--hub-ref-border-width, 1px);--hub-toast-shadow: var(--hub-sys-shadow-md, 0 .25rem .75rem rgba(0, 0, 0, .1));--hub-toast-gap: var(--hub-ref-space-1, .25rem);--hub-toast-accent-width: .25rem;--hub-toast-font-size: var(--hub-ref-font-size-base, 1rem);--hub-toast-title-font-size: var(--hub-ref-font-size-base, 1rem);--hub-toast-title-font-weight: 600;--hub-toast-bg: var(--hub-sys-surface-page, #fff);--hub-toast-color: var(--hub-sys-text-primary, #212529);--hub-toast-border: var(--hub-sys-border-color-default, #dee2e6);--hub-toast-accent: var(--hub-sys-border-color-default, #dee2e6);--hub-toast-progress-bg: color-mix(in srgb, var(--hub-toast-accent) 30%, transparent);--hub-toast-progress-height: .25rem;--hub-toast-close-opacity: .5;--hub-toast-close-opacity-hover: 1}:where(:host[data-type]){--hub-toast-bg: color-mix(in srgb, var(--hub-toast-accent) 14%, var(--hub-sys-surface-page, #fff));--hub-toast-color: color-mix(in srgb, var(--hub-toast-accent) 72%, var(--hub-sys-text-primary, #212529));--hub-toast-border: color-mix(in srgb, var(--hub-toast-accent) 30%, transparent)}:where(:host[data-type=success]){--hub-toast-bg: var(--hub-sys-color-success-subtle);--hub-toast-color: var(--hub-sys-color-success-emphasis);--hub-toast-border: var(--hub-sys-color-success-border-subtle);--hub-toast-accent: var(--hub-sys-color-success)}:where(:host[data-type=warning]){--hub-toast-bg: var(--hub-sys-color-warning-subtle);--hub-toast-color: var(--hub-sys-color-warning-emphasis);--hub-toast-border: var(--hub-sys-color-warning-border-subtle);--hub-toast-accent: var(--hub-sys-color-warning)}:where(:host[data-type=info]){--hub-toast-bg: var(--hub-sys-color-info-subtle);--hub-toast-color: var(--hub-sys-color-info-emphasis);--hub-toast-border: var(--hub-sys-color-info-border-subtle);--hub-toast-accent: var(--hub-sys-color-info)}:where(:host[data-type=error]){--hub-toast-bg: var(--hub-sys-color-danger-subtle);--hub-toast-color: var(--hub-sys-color-danger-emphasis);--hub-toast-border: var(--hub-sys-color-danger-border-subtle);--hub-toast-accent: var(--hub-sys-color-danger)}:host{position:relative;display:flex;align-items:flex-start;min-width:var(--hub-toast-min-width);max-width:var(--hub-toast-max-width);padding:var(--hub-toast-padding-y) var(--hub-toast-padding-x);border-radius:var(--hub-toast-border-radius);border:var(--hub-toast-border-width) solid var(--hub-toast-border);border-inline-start-width:var(--hub-toast-accent-width);border-inline-start-color:var(--hub-toast-accent);background:var(--hub-toast-bg);color:var(--hub-toast-color);box-shadow:var(--hub-toast-shadow);cursor:default;overflow:hidden}.hub-toast__body{flex:1;display:flex;flex-direction:column;gap:var(--hub-toast-gap)}.hub-toast__title{font-size:var(--hub-toast-title-font-size);font-weight:var(--hub-toast-title-font-weight);line-height:1.25}.hub-toast__message{font-size:var(--hub-toast-font-size);line-height:1.5}.hub-toast__close{flex:0 0 auto;padding:0;margin-inline-start:var(--hub-toast-padding-x);background:none;border:none;cursor:pointer;font-size:1.25rem;line-height:1;color:inherit;opacity:var(--hub-toast-close-opacity);transition:opacity .15s ease}.hub-toast__close:hover{opacity:var(--hub-toast-close-opacity-hover)}.hub-toast__progress{position:absolute;inset-inline:0;bottom:0;height:var(--hub-toast-progress-height);background:transparent}.hub-toast__progress-bar{height:100%;background:var(--hub-toast-progress-bg);transition:width 50ms linear}\n"] }]
399
+ }], ctorParameters: () => [], propDecorators: { data: [{ type: i0.Input, args: [{ isSignal: true, alias: "data", required: true }] }], closed: [{ type: i0.Output, args: ["closed"] }] } });
400
+
401
+ /**
402
+ * Fixed-corner container that renders all active toasts.
403
+ *
404
+ * Mounted once by `ToastService._ensureContainerMounted()` and appended
405
+ * directly to `document.body` — never declared in user templates.
406
+ * The `positionClass` from the first toast's config drives the CSS class
407
+ * that positions the container in the viewport corner.
408
+ */
409
+ class ToastContainerComponent {
410
+ toastService = inject(ToastService);
411
+ /** Active toast list from the service signal. */
412
+ toasts = this.toastService.toasts;
413
+ /**
414
+ * Position class derived from the first active toast's config.
415
+ * Uses the first toast so the container position stays stable across updates.
416
+ */
417
+ positionClass = () => {
418
+ const list = this.toasts();
419
+ return list.length > 0 ? list[0].config.positionClass : 'toast-top-right';
420
+ };
421
+ /** Delegates toast removal to `ToastService`. */
422
+ onClosed(toastId) {
423
+ this.toastService.remove(toastId);
424
+ }
425
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.1", ngImport: i0, type: ToastContainerComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
426
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.0.1", type: ToastContainerComponent, isStandalone: true, selector: "hub-toast-container", host: { properties: { "class": "positionClass()" }, classAttribute: "hub-toast-container" }, ngImport: i0, template: "@for (toast of toasts(); track toast.toastId) {\n\t<hub-toast [data]=\"toast\" (closed)=\"onClosed($event)\" />\n}\n", styles: [":host{--hub-toast-container-gap: var(--hub-ref-space-2, .5rem);--hub-toast-container-z-index: 1050;--hub-toast-container-offset: var(--hub-ref-space-3, 1rem)}:host{position:fixed;z-index:var(--hub-toast-container-z-index);display:flex;flex-direction:column;gap:var(--hub-toast-container-gap);pointer-events:none}:host hub-toast{pointer-events:all}:host.toast-top-right{top:var(--hub-toast-container-offset);inset-inline-end:var(--hub-toast-container-offset);align-items:flex-end}:host.toast-top-left{top:var(--hub-toast-container-offset);inset-inline-start:var(--hub-toast-container-offset);align-items:flex-start}:host.toast-top-center{top:var(--hub-toast-container-offset);left:50%;transform:translate(-50%);align-items:center}:host.toast-bottom-right{bottom:var(--hub-toast-container-offset);inset-inline-end:var(--hub-toast-container-offset);align-items:flex-end;flex-direction:column-reverse}:host.toast-bottom-left{bottom:var(--hub-toast-container-offset);inset-inline-start:var(--hub-toast-container-offset);align-items:flex-start;flex-direction:column-reverse}:host.toast-bottom-center{bottom:var(--hub-toast-container-offset);left:50%;transform:translate(-50%);align-items:center;flex-direction:column-reverse}\n"], dependencies: [{ kind: "component", type: ToastComponent, selector: "hub-toast", inputs: ["data"], outputs: ["closed"] }], animations: [toastAnimation], changeDetection: i0.ChangeDetectionStrategy.OnPush });
427
+ }
428
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.1", ngImport: i0, type: ToastContainerComponent, decorators: [{
429
+ type: Component,
430
+ args: [{ selector: 'hub-toast-container', changeDetection: ChangeDetectionStrategy.OnPush, animations: [toastAnimation], imports: [ToastComponent], host: {
431
+ class: 'hub-toast-container',
432
+ '[class]': 'positionClass()'
433
+ }, template: "@for (toast of toasts(); track toast.toastId) {\n\t<hub-toast [data]=\"toast\" (closed)=\"onClosed($event)\" />\n}\n", styles: [":host{--hub-toast-container-gap: var(--hub-ref-space-2, .5rem);--hub-toast-container-z-index: 1050;--hub-toast-container-offset: var(--hub-ref-space-3, 1rem)}:host{position:fixed;z-index:var(--hub-toast-container-z-index);display:flex;flex-direction:column;gap:var(--hub-toast-container-gap);pointer-events:none}:host hub-toast{pointer-events:all}:host.toast-top-right{top:var(--hub-toast-container-offset);inset-inline-end:var(--hub-toast-container-offset);align-items:flex-end}:host.toast-top-left{top:var(--hub-toast-container-offset);inset-inline-start:var(--hub-toast-container-offset);align-items:flex-start}:host.toast-top-center{top:var(--hub-toast-container-offset);left:50%;transform:translate(-50%);align-items:center}:host.toast-bottom-right{bottom:var(--hub-toast-container-offset);inset-inline-end:var(--hub-toast-container-offset);align-items:flex-end;flex-direction:column-reverse}:host.toast-bottom-left{bottom:var(--hub-toast-container-offset);inset-inline-start:var(--hub-toast-container-offset);align-items:flex-start;flex-direction:column-reverse}:host.toast-bottom-center{bottom:var(--hub-toast-container-offset);left:50%;transform:translate(-50%);align-items:center;flex-direction:column-reverse}\n"] }]
434
+ }] });
435
+
436
+ var toastContainer_component = /*#__PURE__*/Object.freeze({
437
+ __proto__: null,
438
+ ToastContainerComponent: ToastContainerComponent
439
+ });
440
+
441
+ /*
442
+ * Public API Surface of ng-hub-ui-toast
443
+ */
444
+
445
+ /**
446
+ * Generated bundle index. Do not edit.
447
+ */
448
+
449
+ export { HUB_TOAST_CONFIG, HUB_TOAST_DEFAULT_CONFIG, ToastComponent, ToastConfigService, ToastContainerComponent, ToastService, provideToast };
450
+ //# sourceMappingURL=ng-hub-ui-toast.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ng-hub-ui-toast.mjs","sources":["../../../projects/toast/src/lib/services/toast-config.service.ts","../../../projects/toast/src/lib/services/toast.service.ts","../../../projects/toast/src/lib/animations/toast.animations.ts","../../../projects/toast/src/lib/components/toast/toast.component.ts","../../../projects/toast/src/lib/components/toast/toast.component.html","../../../projects/toast/src/lib/components/toast-container/toast-container.component.ts","../../../projects/toast/src/lib/components/toast-container/toast-container.component.html","../../../projects/toast/src/public-api.ts","../../../projects/toast/src/ng-hub-ui-toast.ts"],"sourcesContent":["import { inject, Injectable, InjectionToken, Provider } from '@angular/core';\nimport type { HubToastConfig, HubToastPosition } from '../models/toast.types';\n\n/** Default configuration applied to every toast unless overridden. */\nexport const HUB_TOAST_DEFAULT_CONFIG: HubToastConfig = {\n\ttimeOut: 5000,\n\textendedTimeOut: 2500,\n\tcloseButton: true,\n\tprogressBar: false,\n\ttapToDismiss: true,\n\tdisableTimeOut: false,\n\tnewestOnTop: true,\n\tpositionClass: 'toast-top-right' as HubToastPosition,\n\tmaxOpened: 0,\n\tautoDismiss: false,\n\tpreventDuplicates: false\n};\n\n/** Injection token for the global toast configuration. */\nexport const HUB_TOAST_CONFIG = new InjectionToken<Partial<HubToastConfig>>('HUB_TOAST_CONFIG');\n\n/**\n * Registers the toast library providers.\n * Call inside `ApplicationConfig.providers` or a route's `providers` array.\n *\n * @param config - Partial global defaults merged over {@link HUB_TOAST_DEFAULT_CONFIG}.\n *\n * @example\n * ```typescript\n * export const appConfig: ApplicationConfig = {\n * providers: [provideToast({ timeOut: 3000, progressBar: true })]\n * };\n * ```\n */\nexport function provideToast(config: Partial<HubToastConfig> = {}): Provider[] {\n\treturn [{ provide: HUB_TOAST_CONFIG, useValue: config }];\n}\n\n/**\n * Resolves per-toast config by merging global defaults, the provider override,\n * and any per-call overrides. Injected by `ToastService`.\n */\n@Injectable({ providedIn: 'root' })\nexport class ToastConfigService {\n\tprivate readonly _override = inject(HUB_TOAST_CONFIG, { optional: true }) ?? {};\n\n\t/** Returns the merged global config (default ← provider override). */\n\tget defaults(): HubToastConfig {\n\t\treturn { ...HUB_TOAST_DEFAULT_CONFIG, ...this._override };\n\t}\n\n\t/**\n\t * Merges global defaults with per-call overrides into a final config.\n\t *\n\t * @param perCall - Per-call partial overrides.\n\t * @returns Fully resolved config for one toast.\n\t */\n\tresolve(perCall: Partial<HubToastConfig> = {}): HubToastConfig {\n\t\treturn { ...this.defaults, ...perCall };\n\t}\n}\n","import { ApplicationRef, ComponentRef, createComponent, inject, Injectable, signal } from '@angular/core';\nimport { Subject } from 'rxjs';\nimport type { HubToastConfig, HubToastData, HubToastRef, HubToastType } from '../models/toast.types';\nimport { ToastConfigService } from './toast-config.service';\n\n/** Monotonically increasing id counter. */\nlet nextId = 0;\n\n/**\n * Core service for displaying toast notifications.\n * Manages the active toast stack as a signal and lazily mounts the\n * container overlay on the first toast call.\n *\n * @example\n * ```typescript\n * constructor(private toastr: ToastService) {}\n *\n * save() {\n * this.toastr.success('Record saved', 'Success');\n * }\n * ```\n */\n@Injectable({ providedIn: 'root' })\nexport class ToastService {\n\tprivate readonly _config = inject(ToastConfigService);\n\tprivate readonly _appRef = inject(ApplicationRef);\n\n\t/** Read-only signal of all currently active toasts. */\n\treadonly toasts = signal<HubToastData[]>([]);\n\n\tprivate _containerMounted = false;\n\t/** Reference to the lazily created container, kept for explicit CD triggers. */\n\tprivate _containerRef: ComponentRef<unknown> | null = null;\n\n\t// ─── Public shorthand methods ───────────────────────────────────────────\n\n\t/**\n\t * Shows a success toast.\n\t * @param message - Notification body.\n\t * @param title - Optional heading.\n\t * @param config - Per-call config overrides.\n\t */\n\tsuccess(message: string, title = '', config: Partial<HubToastConfig> = {}): HubToastRef {\n\t\treturn this.show(message, title, config, 'success');\n\t}\n\n\t/**\n\t * Shows an error toast.\n\t * @param message - Notification body.\n\t * @param title - Optional heading.\n\t * @param config - Per-call config overrides.\n\t */\n\terror(message: string, title = '', config: Partial<HubToastConfig> = {}): HubToastRef {\n\t\treturn this.show(message, title, config, 'error');\n\t}\n\n\t/**\n\t * Shows a warning toast.\n\t * @param message - Notification body.\n\t * @param title - Optional heading.\n\t * @param config - Per-call config overrides.\n\t */\n\twarning(message: string, title = '', config: Partial<HubToastConfig> = {}): HubToastRef {\n\t\treturn this.show(message, title, config, 'warning');\n\t}\n\n\t/**\n\t * Shows an informational toast.\n\t * @param message - Notification body.\n\t * @param title - Optional heading.\n\t * @param config - Per-call config overrides.\n\t */\n\tinfo(message: string, title = '', config: Partial<HubToastConfig> = {}): HubToastRef {\n\t\treturn this.show(message, title, config, 'info');\n\t}\n\n\t/**\n\t * Shows a toast with a custom or built-in type.\n\t * The type string is applied as `data-type` on the toast host element\n\t * and drives the SCSS `@each` accent loop.\n\t *\n\t * @param message - Notification body.\n\t * @param title - Optional heading.\n\t * @param config - Per-call config overrides.\n\t * @param type - Semantic type or any custom string.\n\t */\n\tshow(\n\t\tmessage: string,\n\t\ttitle = '',\n\t\tconfig: Partial<HubToastConfig> = {},\n\t\ttype: HubToastType | (string & {}) = 'info'\n\t): HubToastRef {\n\t\tconst resolved = this._config.resolve(config);\n\n\t\tif (resolved.preventDuplicates) {\n\t\t\tconst duplicate = this.toasts().some((t) => t.message === message && t.type === type);\n\t\t\tif (duplicate) {\n\t\t\t\treturn this._refForExisting(message, type);\n\t\t\t}\n\t\t}\n\n\t\tif (resolved.maxOpened > 0 && this.toasts().length >= resolved.maxOpened) {\n\t\t\tif (resolved.autoDismiss) {\n\t\t\t\tconst oldest = resolved.newestOnTop ? this.toasts()[this.toasts().length - 1] : this.toasts()[0];\n\t\t\t\tthis._removeById(oldest.toastId);\n\t\t\t} else {\n\t\t\t\treturn this._buildRef({ toastId: -1 } as any);\n\t\t\t}\n\t\t}\n\n\t\tconst data: HubToastData = {\n\t\t\ttoastId: ++nextId,\n\t\t\ttype,\n\t\t\tmessage,\n\t\t\ttitle,\n\t\t\tconfig: resolved,\n\t\t\tonShown$: new Subject<void>(),\n\t\t\tonHidden$: new Subject<void>(),\n\t\t\tonTap$: new Subject<void>()\n\t\t};\n\n\t\tif (resolved.newestOnTop) {\n\t\t\tthis.toasts.update((list) => [data, ...list]);\n\t\t} else {\n\t\t\tthis.toasts.update((list) => [...list, data]);\n\t\t}\n\n\t\tthis._ensureContainerMounted();\n\t\tthis._syncContainer();\n\t\treturn this._buildRef(data);\n\t}\n\n\t/**\n\t * Removes a specific toast by id.\n\t * @param toastId - The id returned by the show method.\n\t */\n\tremove(toastId: number): void {\n\t\tthis._removeById(toastId);\n\t}\n\n\t/** Removes all active toasts immediately. */\n\tclear(): void {\n\t\tthis.toasts().forEach((t) => {\n\t\t\tt.onHidden$.next();\n\t\t\tt.onHidden$.complete();\n\t\t});\n\t\tthis.toasts.set([]);\n\t\tthis._syncContainer();\n\t}\n\n\t// ─── Internal helpers ────────────────────────────────────────────────────\n\n\tprivate _removeById(toastId: number): void {\n\t\tconst toast = this.toasts().find((t) => t.toastId === toastId);\n\t\tif (toast) {\n\t\t\ttoast.onHidden$.next();\n\t\t\ttoast.onHidden$.complete();\n\t\t\tthis.toasts.update((list) => list.filter((t) => t.toastId !== toastId));\n\t\t\tthis._syncContainer();\n\t\t}\n\t}\n\n\tprivate _refForExisting(message: string, type: string): HubToastRef {\n\t\tconst existing = this.toasts().find((t) => t.message === message && t.type === type);\n\t\treturn existing ? this._buildRef(existing) : this._buildRef({ toastId: -1 } as any);\n\t}\n\n\tprivate _buildRef(data: HubToastData): HubToastRef {\n\t\tconst svc = this;\n\t\treturn {\n\t\t\ttoastId: data.toastId,\n\t\t\tonShown: data.onShown$?.asObservable() ?? new Subject<void>().asObservable(),\n\t\t\tonHidden: data.onHidden$?.asObservable() ?? new Subject<void>().asObservable(),\n\t\t\tonTap: data.onTap$?.asObservable() ?? new Subject<void>().asObservable(),\n\t\t\tmanualClose() {\n\t\t\t\tsvc.remove(data.toastId);\n\t\t\t},\n\t\t\tresetTimeout() {\n\t\t\t\tconst toast = svc.toasts().find((t) => t.toastId === data.toastId);\n\t\t\t\tif (toast) {\n\t\t\t\t\ttoast.onShown$.next();\n\t\t\t\t}\n\t\t\t}\n\t\t};\n\t}\n\n\t/**\n\t * Lazily mounts the `ToastContainerComponent` via Angular's `createComponent`.\n\t * Called on the first toast — subsequent calls are no-ops.\n\t */\n\tprivate _ensureContainerMounted(): void {\n\t\tif (this._containerMounted) {\n\t\t\treturn;\n\t\t}\n\t\tthis._containerMounted = true;\n\n\t\timport('../components/toast-container/toast-container.component').then(({ ToastContainerComponent }) => {\n\t\t\tconst ref = createComponent(ToastContainerComponent, {\n\t\t\t\tenvironmentInjector: this._appRef.injector\n\t\t\t});\n\t\t\tthis._containerRef = ref;\n\t\t\tthis._appRef.attachView(ref.hostView);\n\t\t\tdocument.body.appendChild(ref.location.nativeElement);\n\t\t\t// Initial render: signal may already hold toasts queued before the import resolved.\n\t\t\tref.changeDetectorRef.detectChanges();\n\t\t});\n\t}\n\n\t/**\n\t * Explicitly runs change detection on the container.\n\t *\n\t * Views created via `createComponent` + `attachView` are not reachable by\n\t * Angular's signal-based \"mark ancestors dirty\" traversal, so they do not\n\t * update automatically when a signal changes. Calling `detectChanges()`\n\t * directly on the container's `ChangeDetectorRef` is the reliable alternative.\n\t */\n\tprivate _syncContainer(): void {\n\t\tthis._containerRef?.changeDetectorRef.detectChanges();\n\t}\n}\n","import { animate, keyframes, state, style, transition, trigger } from '@angular/animations';\n\n/**\n * Slide-in/out animation for individual toasts.\n * Enter: slides in from the inline-end edge with a fade.\n * Leave: fades out with a slight upward shift.\n */\nexport const toastAnimation = trigger('toastState', [\n\tstate('in', style({ opacity: 1, transform: 'translateX(0)' })),\n\ttransition(':enter', [\n\t\tanimate(\n\t\t\t'200ms ease-out',\n\t\t\tkeyframes([\n\t\t\t\tstyle({ opacity: 0, transform: 'translateX(100%)', offset: 0 }),\n\t\t\t\tstyle({ opacity: 1, transform: 'translateX(0)', offset: 1 })\n\t\t\t])\n\t\t)\n\t]),\n\ttransition(':leave', [\n\t\tanimate(\n\t\t\t'150ms ease-in',\n\t\t\tkeyframes([\n\t\t\t\tstyle({ opacity: 1, transform: 'translateY(0)', offset: 0 }),\n\t\t\t\tstyle({ opacity: 0, transform: 'translateY(-0.5rem)', offset: 1 })\n\t\t\t])\n\t\t)\n\t])\n]);\n","import {\n\tChangeDetectionStrategy,\n\tComponent,\n\tcomputed,\n\teffect,\n\tinput,\n\tOnDestroy,\n\toutput,\n\tsignal\n} from '@angular/core';\nimport { toastAnimation } from '../../animations/toast.animations';\nimport { HubToastData, HubToastType } from '../../models/toast.types';\n\n/** Built-in type names that have exact DS token coverage via `@each`. */\nconst BUILT_IN_TYPES = new Set<string>(['success', 'error', 'warning', 'info']);\n\n/**\n * Renders a single toast notification.\n *\n * Driven by a {@link HubToastData} input. Manages its own auto-dismiss timer\n * via `signal` + `effect` and emits `(closed)` with the toast id when done.\n *\n * The `data-type` host attribute drives the `@each` SCSS accent loop;\n * `--hub-toast-accent` is set inline only for custom types.\n */\n@Component({\n\tselector: 'hub-toast',\n\ttemplateUrl: './toast.component.html',\n\tstyleUrl: './toast.component.scss',\n\tchangeDetection: ChangeDetectionStrategy.OnPush,\n\tanimations: [toastAnimation],\n\thost: {\n\t\tclass: 'hub-toast',\n\t\t'[@toastState]': '\"in\"',\n\t\t'[attr.data-type]': 'data().type',\n\t\t'[style.--hub-toast-accent]': 'accentToken()',\n\t\t'(click)': 'onTap()'\n\t}\n})\nexport class ToastComponent implements OnDestroy {\n\t/** Toast data provided by `ToastContainerComponent`. */\n\treadonly data = input.required<HubToastData>();\n\n\t/** Emits the toast id when this toast should be dismissed. */\n\treadonly closed = output<number>();\n\n\t/** Remaining progress as a percentage (100 → 0). Used by the progress bar. */\n\treadonly progress = signal(100);\n\n\t/**\n\t * Inline accent token. Null for built-in types (covered by `@each`);\n\t * `var(--hub-sys-color-<type>)` for custom types so `color-mix` derives\n\t * the other tokens automatically.\n\t */\n\treadonly accentToken = computed<string | null>(() => {\n\t\tconst type = this.data().type;\n\t\treturn BUILT_IN_TYPES.has(type) ? null : `var(--hub-sys-color-${type})`;\n\t});\n\n\tprivate _timerId: ReturnType<typeof setTimeout> | null = null;\n\tprivate _intervalId: ReturnType<typeof setInterval> | null = null;\n\n\tconstructor() {\n\t\teffect(() => {\n\t\t\tconst cfg = this.data().config;\n\t\t\tthis._clearTimers();\n\t\t\tif (cfg.disableTimeOut === true || cfg.disableTimeOut === 'timeOut') {\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tif (cfg.timeOut > 0) {\n\t\t\t\tthis._startTimer(cfg.timeOut);\n\t\t\t}\n\t\t});\n\n\t\teffect(() => {\n\t\t\tthis.data().onShown$.next();\n\t\t});\n\t}\n\n\t/** Called by `(mouseenter)` binding in the template. */\n\tonMouseEnter(): void {\n\t\tconst cfg = this.data().config;\n\t\tif (cfg.disableTimeOut !== 'extendedTimeOut') {\n\t\t\tthis._clearTimers();\n\t\t}\n\t}\n\n\t/** Called by `(mouseleave)` binding in the template. */\n\tonMouseLeave(): void {\n\t\tconst cfg = this.data().config;\n\t\tif (cfg.extendedTimeOut > 0 && cfg.disableTimeOut !== 'extendedTimeOut') {\n\t\t\tthis._startTimer(cfg.extendedTimeOut);\n\t\t}\n\t}\n\n\t/** Called by the host `(click)` binding. */\n\tonTap(): void {\n\t\tconst cfg = this.data().config;\n\t\tthis.data().onTap$.next();\n\t\tif (cfg.tapToDismiss) {\n\t\t\tthis._dismiss();\n\t\t}\n\t}\n\n\t/** Called by the close button in the template. */\n\tonClose(event: Event): void {\n\t\tevent.stopPropagation();\n\t\tthis._dismiss();\n\t}\n\n\tngOnDestroy(): void {\n\t\tthis._clearTimers();\n\t}\n\n\tprivate _startTimer(duration: number): void {\n\t\tconst cfg = this.data().config;\n\t\tconst start = Date.now();\n\n\t\tif (cfg.progressBar) {\n\t\t\tthis._intervalId = setInterval(() => {\n\t\t\t\tconst elapsed = Date.now() - start;\n\t\t\t\tthis.progress.set(Math.max(0, 100 - (elapsed / duration) * 100));\n\t\t\t}, 50);\n\t\t}\n\n\t\tthis._timerId = setTimeout(() => {\n\t\t\tthis._dismiss();\n\t\t}, duration);\n\t}\n\n\tprivate _dismiss(): void {\n\t\tthis._clearTimers();\n\t\tthis.closed.emit(this.data().toastId);\n\t}\n\n\tprivate _clearTimers(): void {\n\t\tif (this._timerId !== null) {\n\t\t\tclearTimeout(this._timerId);\n\t\t\tthis._timerId = null;\n\t\t}\n\t\tif (this._intervalId !== null) {\n\t\t\tclearInterval(this._intervalId);\n\t\t\tthis._intervalId = null;\n\t\t}\n\t\tthis.progress.set(100);\n\t}\n}\n","<div\n\tclass=\"hub-toast__body\"\n\t(mouseenter)=\"onMouseEnter()\"\n\t(mouseleave)=\"onMouseLeave()\"\n>\n\t@if (data().title) {\n\t\t<div class=\"hub-toast__title\">{{ data().title }}</div>\n\t}\n\t<div class=\"hub-toast__message\">{{ data().message }}</div>\n</div>\n\n@if (data().config.closeButton) {\n\t<button\n\t\ttype=\"button\"\n\t\tclass=\"hub-toast__close\"\n\t\taria-label=\"Close\"\n\t\t(click)=\"onClose($event)\"\n\t>\n\t\t<span aria-hidden=\"true\">&times;</span>\n\t</button>\n}\n\n@if (data().config.progressBar) {\n\t<div\n\t\tclass=\"hub-toast__progress\"\n\t\trole=\"progressbar\"\n\t\t[attr.aria-valuenow]=\"progress()\"\n\t\taria-valuemin=\"0\"\n\t\taria-valuemax=\"100\"\n\t>\n\t\t<div class=\"hub-toast__progress-bar\" [style.width.%]=\"progress()\"></div>\n\t</div>\n}\n","import { ChangeDetectionStrategy, Component, inject } from '@angular/core';\nimport { toastAnimation } from '../../animations/toast.animations';\nimport { ToastService } from '../../services/toast.service';\nimport { ToastComponent } from '../toast/toast.component';\n\n/**\n * Fixed-corner container that renders all active toasts.\n *\n * Mounted once by `ToastService._ensureContainerMounted()` and appended\n * directly to `document.body` — never declared in user templates.\n * The `positionClass` from the first toast's config drives the CSS class\n * that positions the container in the viewport corner.\n */\n@Component({\n\tselector: 'hub-toast-container',\n\ttemplateUrl: './toast-container.component.html',\n\tstyleUrl: './toast-container.component.scss',\n\tchangeDetection: ChangeDetectionStrategy.OnPush,\n\tanimations: [toastAnimation],\n\timports: [ToastComponent],\n\thost: {\n\t\tclass: 'hub-toast-container',\n\t\t'[class]': 'positionClass()'\n\t}\n})\nexport class ToastContainerComponent {\n\tprotected readonly toastService = inject(ToastService);\n\n\t/** Active toast list from the service signal. */\n\tprotected readonly toasts = this.toastService.toasts;\n\n\t/**\n\t * Position class derived from the first active toast's config.\n\t * Uses the first toast so the container position stays stable across updates.\n\t */\n\tprotected readonly positionClass = () => {\n\t\tconst list = this.toasts();\n\t\treturn list.length > 0 ? list[0].config.positionClass : 'toast-top-right';\n\t};\n\n\t/** Delegates toast removal to `ToastService`. */\n\tprotected onClosed(toastId: number): void {\n\t\tthis.toastService.remove(toastId);\n\t}\n}\n","@for (toast of toasts(); track toast.toastId) {\n\t<hub-toast [data]=\"toast\" (closed)=\"onClosed($event)\" />\n}\n","/*\n * Public API Surface of ng-hub-ui-toast\n */\n\nexport { ToastService } from './lib/services/toast.service';\nexport { ToastConfigService, provideToast, HUB_TOAST_CONFIG, HUB_TOAST_DEFAULT_CONFIG } from './lib/services/toast-config.service';\nexport { ToastComponent } from './lib/components/toast/toast.component';\nexport { ToastContainerComponent } from './lib/components/toast-container/toast-container.component';\nexport type { HubToastRef, HubToastConfig, HubToastType, HubToastData, HubToastPosition } from './lib/models/toast.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 ADDED
@@ -0,0 +1,45 @@
1
+ {
2
+ "name": "ng-hub-ui-toast",
3
+ "version": "22.0.0",
4
+ "description": "Standalone Angular 22 toast/notification library, part of the ng-hub-ui family.",
5
+ "author": "Carlos Morcillo <carlos.morcillo@me.com> (https://www.carlosmorcillo.com)",
6
+ "repository": {
7
+ "type": "git",
8
+ "url": "git+https://github.com/carlos-morcillo/ng-hub-ui-toast.git"
9
+ },
10
+ "bugs": {
11
+ "url": "https://github.com/carlos-morcillo/ng-hub-ui-toast/issues",
12
+ "email": "carlos.morcillo@me.com"
13
+ },
14
+ "homepage": "https://hubui.dev/",
15
+ "keywords": [
16
+ "angular",
17
+ "toast",
18
+ "notification",
19
+ "ui-component",
20
+ "ng-hub-ui",
21
+ "signals",
22
+ "standalone"
23
+ ],
24
+ "peerDependencies": {
25
+ "@angular/animations": ">=22.0.0",
26
+ "@angular/common": ">=22.0.0",
27
+ "@angular/core": ">=22.0.0"
28
+ },
29
+ "dependencies": {
30
+ "tslib": "^2.3.0"
31
+ },
32
+ "sideEffects": false,
33
+ "module": "fesm2022/ng-hub-ui-toast.mjs",
34
+ "typings": "types/ng-hub-ui-toast.d.ts",
35
+ "exports": {
36
+ "./package.json": {
37
+ "default": "./package.json"
38
+ },
39
+ ".": {
40
+ "types": "./types/ng-hub-ui-toast.d.ts",
41
+ "default": "./fesm2022/ng-hub-ui-toast.mjs"
42
+ }
43
+ },
44
+ "type": "module"
45
+ }
@@ -0,0 +1,281 @@
1
+ import * as i0 from '@angular/core';
2
+ import { InjectionToken, Provider, OnDestroy } from '@angular/core';
3
+ import * as rxjs from 'rxjs';
4
+ import { Subject } from 'rxjs';
5
+ import * as ng_hub_ui_toast from 'ng-hub-ui-toast';
6
+
7
+ /**
8
+ * Built-in semantic toast types. Each maps to the matching
9
+ * `--hub-sys-color-<type>-*` design-system token family.
10
+ */
11
+ type HubToastType = 'success' | 'error' | 'warning' | 'info';
12
+ /**
13
+ * Position of the toast container on screen.
14
+ */
15
+ type HubToastPosition = 'toast-top-right' | 'toast-top-left' | 'toast-top-center' | 'toast-bottom-right' | 'toast-bottom-left' | 'toast-bottom-center';
16
+ /**
17
+ * Per-toast configuration. All fields are optional — missing fields
18
+ * fall back to the global defaults set via `provideToast()`.
19
+ */
20
+ interface HubToastConfig {
21
+ /** Duration in ms before auto-dismiss. 0 = persistent. @default 5000 */
22
+ timeOut: number;
23
+ /** Extra ms added while the user hovers. @default 2500 */
24
+ extendedTimeOut: number;
25
+ /** Show a close button. @default true */
26
+ closeButton: boolean;
27
+ /** Show a progress bar counting down to dismissal. @default false */
28
+ progressBar: boolean;
29
+ /** Close on click anywhere on the toast. @default true */
30
+ tapToDismiss: boolean;
31
+ /**
32
+ * Disable auto-dismiss.
33
+ * - `true` / `'timeOut'`: disable the initial timer.
34
+ * - `'extendedTimeOut'`: disable the hover extension only.
35
+ * @default false
36
+ */
37
+ disableTimeOut: boolean | 'timeOut' | 'extendedTimeOut';
38
+ /** Newest toast appears at the top of the stack. @default true */
39
+ newestOnTop: boolean;
40
+ /** Container position. @default 'toast-top-right' */
41
+ positionClass: HubToastPosition | string;
42
+ /** Max simultaneous toasts. 0 = unlimited. @default 0 */
43
+ maxOpened: number;
44
+ /** When maxOpened is reached, auto-remove the oldest. @default false */
45
+ autoDismiss: boolean;
46
+ /** Ignore duplicate messages already visible. @default false */
47
+ preventDuplicates: boolean;
48
+ }
49
+ /**
50
+ * Internal representation of one active toast.
51
+ * Created by `ToastService` and consumed by `ToastComponent`.
52
+ */
53
+ interface HubToastData {
54
+ /** Monotonically increasing identifier. */
55
+ toastId: number;
56
+ /** Semantic or custom type string. */
57
+ type: HubToastType | (string & {});
58
+ /** Notification body text. */
59
+ message: string;
60
+ /** Optional heading. */
61
+ title?: string;
62
+ /** Resolved config for this specific toast. */
63
+ config: HubToastConfig;
64
+ /** Subject fired once when the toast enters the DOM. */
65
+ onShown$: Subject<void>;
66
+ /** Subject fired once when the toast leaves the DOM. */
67
+ onHidden$: Subject<void>;
68
+ /** Subject fired when the user taps the toast. */
69
+ onTap$: Subject<void>;
70
+ }
71
+ /**
72
+ * Handle returned to callers of `ToastService`. Provides reactive
73
+ * observables for the toast lifecycle and imperative control methods.
74
+ */
75
+ interface HubToastRef {
76
+ /** Unique id of this toast instance. */
77
+ readonly toastId: number;
78
+ /** Emits once when the toast becomes visible. */
79
+ readonly onShown: rxjs.Observable<void>;
80
+ /** Emits once when the toast is removed from the DOM. */
81
+ readonly onHidden: rxjs.Observable<void>;
82
+ /** Emits each time the user clicks on the toast body. */
83
+ readonly onTap: rxjs.Observable<void>;
84
+ /** Immediately removes the toast. */
85
+ manualClose(): void;
86
+ /** Restart the auto-dismiss timer from zero. */
87
+ resetTimeout(): void;
88
+ }
89
+
90
+ /**
91
+ * Core service for displaying toast notifications.
92
+ * Manages the active toast stack as a signal and lazily mounts the
93
+ * container overlay on the first toast call.
94
+ *
95
+ * @example
96
+ * ```typescript
97
+ * constructor(private toastr: ToastService) {}
98
+ *
99
+ * save() {
100
+ * this.toastr.success('Record saved', 'Success');
101
+ * }
102
+ * ```
103
+ */
104
+ declare class ToastService {
105
+ private readonly _config;
106
+ private readonly _appRef;
107
+ /** Read-only signal of all currently active toasts. */
108
+ readonly toasts: i0.WritableSignal<HubToastData[]>;
109
+ private _containerMounted;
110
+ /** Reference to the lazily created container, kept for explicit CD triggers. */
111
+ private _containerRef;
112
+ /**
113
+ * Shows a success toast.
114
+ * @param message - Notification body.
115
+ * @param title - Optional heading.
116
+ * @param config - Per-call config overrides.
117
+ */
118
+ success(message: string, title?: string, config?: Partial<HubToastConfig>): HubToastRef;
119
+ /**
120
+ * Shows an error toast.
121
+ * @param message - Notification body.
122
+ * @param title - Optional heading.
123
+ * @param config - Per-call config overrides.
124
+ */
125
+ error(message: string, title?: string, config?: Partial<HubToastConfig>): HubToastRef;
126
+ /**
127
+ * Shows a warning toast.
128
+ * @param message - Notification body.
129
+ * @param title - Optional heading.
130
+ * @param config - Per-call config overrides.
131
+ */
132
+ warning(message: string, title?: string, config?: Partial<HubToastConfig>): HubToastRef;
133
+ /**
134
+ * Shows an informational toast.
135
+ * @param message - Notification body.
136
+ * @param title - Optional heading.
137
+ * @param config - Per-call config overrides.
138
+ */
139
+ info(message: string, title?: string, config?: Partial<HubToastConfig>): HubToastRef;
140
+ /**
141
+ * Shows a toast with a custom or built-in type.
142
+ * The type string is applied as `data-type` on the toast host element
143
+ * and drives the SCSS `@each` accent loop.
144
+ *
145
+ * @param message - Notification body.
146
+ * @param title - Optional heading.
147
+ * @param config - Per-call config overrides.
148
+ * @param type - Semantic type or any custom string.
149
+ */
150
+ show(message: string, title?: string, config?: Partial<HubToastConfig>, type?: HubToastType | (string & {})): HubToastRef;
151
+ /**
152
+ * Removes a specific toast by id.
153
+ * @param toastId - The id returned by the show method.
154
+ */
155
+ remove(toastId: number): void;
156
+ /** Removes all active toasts immediately. */
157
+ clear(): void;
158
+ private _removeById;
159
+ private _refForExisting;
160
+ private _buildRef;
161
+ /**
162
+ * Lazily mounts the `ToastContainerComponent` via Angular's `createComponent`.
163
+ * Called on the first toast — subsequent calls are no-ops.
164
+ */
165
+ private _ensureContainerMounted;
166
+ /**
167
+ * Explicitly runs change detection on the container.
168
+ *
169
+ * Views created via `createComponent` + `attachView` are not reachable by
170
+ * Angular's signal-based "mark ancestors dirty" traversal, so they do not
171
+ * update automatically when a signal changes. Calling `detectChanges()`
172
+ * directly on the container's `ChangeDetectorRef` is the reliable alternative.
173
+ */
174
+ private _syncContainer;
175
+ static ɵfac: i0.ɵɵFactoryDeclaration<ToastService, never>;
176
+ static ɵprov: i0.ɵɵInjectableDeclaration<ToastService>;
177
+ }
178
+
179
+ /** Default configuration applied to every toast unless overridden. */
180
+ declare const HUB_TOAST_DEFAULT_CONFIG: HubToastConfig;
181
+ /** Injection token for the global toast configuration. */
182
+ declare const HUB_TOAST_CONFIG: InjectionToken<Partial<HubToastConfig>>;
183
+ /**
184
+ * Registers the toast library providers.
185
+ * Call inside `ApplicationConfig.providers` or a route's `providers` array.
186
+ *
187
+ * @param config - Partial global defaults merged over {@link HUB_TOAST_DEFAULT_CONFIG}.
188
+ *
189
+ * @example
190
+ * ```typescript
191
+ * export const appConfig: ApplicationConfig = {
192
+ * providers: [provideToast({ timeOut: 3000, progressBar: true })]
193
+ * };
194
+ * ```
195
+ */
196
+ declare function provideToast(config?: Partial<HubToastConfig>): Provider[];
197
+ /**
198
+ * Resolves per-toast config by merging global defaults, the provider override,
199
+ * and any per-call overrides. Injected by `ToastService`.
200
+ */
201
+ declare class ToastConfigService {
202
+ private readonly _override;
203
+ /** Returns the merged global config (default ← provider override). */
204
+ get defaults(): HubToastConfig;
205
+ /**
206
+ * Merges global defaults with per-call overrides into a final config.
207
+ *
208
+ * @param perCall - Per-call partial overrides.
209
+ * @returns Fully resolved config for one toast.
210
+ */
211
+ resolve(perCall?: Partial<HubToastConfig>): HubToastConfig;
212
+ static ɵfac: i0.ɵɵFactoryDeclaration<ToastConfigService, never>;
213
+ static ɵprov: i0.ɵɵInjectableDeclaration<ToastConfigService>;
214
+ }
215
+
216
+ /**
217
+ * Renders a single toast notification.
218
+ *
219
+ * Driven by a {@link HubToastData} input. Manages its own auto-dismiss timer
220
+ * via `signal` + `effect` and emits `(closed)` with the toast id when done.
221
+ *
222
+ * The `data-type` host attribute drives the `@each` SCSS accent loop;
223
+ * `--hub-toast-accent` is set inline only for custom types.
224
+ */
225
+ declare class ToastComponent implements OnDestroy {
226
+ /** Toast data provided by `ToastContainerComponent`. */
227
+ readonly data: i0.InputSignal<HubToastData>;
228
+ /** Emits the toast id when this toast should be dismissed. */
229
+ readonly closed: i0.OutputEmitterRef<number>;
230
+ /** Remaining progress as a percentage (100 → 0). Used by the progress bar. */
231
+ readonly progress: i0.WritableSignal<number>;
232
+ /**
233
+ * Inline accent token. Null for built-in types (covered by `@each`);
234
+ * `var(--hub-sys-color-<type>)` for custom types so `color-mix` derives
235
+ * the other tokens automatically.
236
+ */
237
+ readonly accentToken: i0.Signal<string | null>;
238
+ private _timerId;
239
+ private _intervalId;
240
+ constructor();
241
+ /** Called by `(mouseenter)` binding in the template. */
242
+ onMouseEnter(): void;
243
+ /** Called by `(mouseleave)` binding in the template. */
244
+ onMouseLeave(): void;
245
+ /** Called by the host `(click)` binding. */
246
+ onTap(): void;
247
+ /** Called by the close button in the template. */
248
+ onClose(event: Event): void;
249
+ ngOnDestroy(): void;
250
+ private _startTimer;
251
+ private _dismiss;
252
+ private _clearTimers;
253
+ static ɵfac: i0.ɵɵFactoryDeclaration<ToastComponent, never>;
254
+ static ɵcmp: i0.ɵɵComponentDeclaration<ToastComponent, "hub-toast", never, { "data": { "alias": "data"; "required": true; "isSignal": true; }; }, { "closed": "closed"; }, never, never, true, never>;
255
+ }
256
+
257
+ /**
258
+ * Fixed-corner container that renders all active toasts.
259
+ *
260
+ * Mounted once by `ToastService._ensureContainerMounted()` and appended
261
+ * directly to `document.body` — never declared in user templates.
262
+ * The `positionClass` from the first toast's config drives the CSS class
263
+ * that positions the container in the viewport corner.
264
+ */
265
+ declare class ToastContainerComponent {
266
+ protected readonly toastService: ToastService;
267
+ /** Active toast list from the service signal. */
268
+ protected readonly toasts: i0.WritableSignal<ng_hub_ui_toast.HubToastData[]>;
269
+ /**
270
+ * Position class derived from the first active toast's config.
271
+ * Uses the first toast so the container position stays stable across updates.
272
+ */
273
+ protected readonly positionClass: () => string;
274
+ /** Delegates toast removal to `ToastService`. */
275
+ protected onClosed(toastId: number): void;
276
+ static ɵfac: i0.ɵɵFactoryDeclaration<ToastContainerComponent, never>;
277
+ static ɵcmp: i0.ɵɵComponentDeclaration<ToastContainerComponent, "hub-toast-container", never, {}, {}, never, never, true, never>;
278
+ }
279
+
280
+ export { HUB_TOAST_CONFIG, HUB_TOAST_DEFAULT_CONFIG, ToastComponent, ToastConfigService, ToastContainerComponent, ToastService, provideToast };
281
+ export type { HubToastConfig, HubToastData, HubToastPosition, HubToastRef, HubToastType };