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 +201 -0
- package/fesm2022/ng-hub-ui-toast.mjs +450 -0
- package/fesm2022/ng-hub-ui-toast.mjs.map +1 -0
- package/package.json +45 -0
- package/types/ng-hub-ui-toast.d.ts +281 -0
package/README.md
ADDED
|
@@ -0,0 +1,201 @@
|
|
|
1
|
+
# ng-hub-ui-toast
|
|
2
|
+
|
|
3
|
+
[](https://www.npmjs.com/package/ng-hub-ui-toast)
|
|
4
|
+
[](https://angular.dev)
|
|
5
|
+
[](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\">×</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\">×</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\">×</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":";;;;;AAGA;AACO,MAAM,wBAAwB,GAAmB;AACvD,IAAA,OAAO,EAAE,IAAI;AACb,IAAA,eAAe,EAAE,IAAI;AACrB,IAAA,WAAW,EAAE,IAAI;AACjB,IAAA,WAAW,EAAE,KAAK;AAClB,IAAA,YAAY,EAAE,IAAI;AAClB,IAAA,cAAc,EAAE,KAAK;AACrB,IAAA,WAAW,EAAE,IAAI;AACjB,IAAA,aAAa,EAAE,iBAAqC;AACpD,IAAA,SAAS,EAAE,CAAC;AACZ,IAAA,WAAW,EAAE,KAAK;AAClB,IAAA,iBAAiB,EAAE;;AAGpB;MACa,gBAAgB,GAAG,IAAI,cAAc,CAA0B,kBAAkB;AAE9F;;;;;;;;;;;;AAYG;AACG,SAAU,YAAY,CAAC,MAAA,GAAkC,EAAE,EAAA;IAChE,OAAO,CAAC,EAAE,OAAO,EAAE,gBAAgB,EAAE,QAAQ,EAAE,MAAM,EAAE,CAAC;AACzD;AAEA;;;AAGG;MAEU,kBAAkB,CAAA;AACb,IAAA,SAAS,GAAG,MAAM,CAAC,gBAAgB,EAAE,EAAE,QAAQ,EAAE,IAAI,EAAE,CAAC,IAAI,EAAE;;AAG/E,IAAA,IAAI,QAAQ,GAAA;QACX,OAAO,EAAE,GAAG,wBAAwB,EAAE,GAAG,IAAI,CAAC,SAAS,EAAE;IAC1D;AAEA;;;;;AAKG;IACH,OAAO,CAAC,UAAmC,EAAE,EAAA;QAC5C,OAAO,EAAE,GAAG,IAAI,CAAC,QAAQ,EAAE,GAAG,OAAO,EAAE;IACxC;uGAhBY,kBAAkB,EAAA,IAAA,EAAA,EAAA,EAAA,MAAA,EAAA,EAAA,CAAA,eAAA,CAAA,UAAA,EAAA,CAAA;AAAlB,IAAA,OAAA,KAAA,GAAA,EAAA,CAAA,qBAAA,CAAA,EAAA,UAAA,EAAA,QAAA,EAAA,OAAA,EAAA,QAAA,EAAA,QAAA,EAAA,EAAA,EAAA,IAAA,EAAA,kBAAkB,cADL,MAAM,EAAA,CAAA;;2FACnB,kBAAkB,EAAA,UAAA,EAAA,CAAA;kBAD9B,UAAU;mBAAC,EAAE,UAAU,EAAE,MAAM,EAAE;;;ACrClC;AACA,IAAI,MAAM,GAAG,CAAC;AAEd;;;;;;;;;;;;;AAaG;MAEU,YAAY,CAAA;AACP,IAAA,OAAO,GAAG,MAAM,CAAC,kBAAkB,CAAC;AACpC,IAAA,OAAO,GAAG,MAAM,CAAC,cAAc,CAAC;;IAGxC,MAAM,GAAG,MAAM,CAAiB,EAAE;+EAAC;IAEpC,iBAAiB,GAAG,KAAK;;IAEzB,aAAa,GAAiC,IAAI;;AAI1D;;;;;AAKG;IACH,OAAO,CAAC,OAAe,EAAE,KAAK,GAAG,EAAE,EAAE,SAAkC,EAAE,EAAA;AACxE,QAAA,OAAO,IAAI,CAAC,IAAI,CAAC,OAAO,EAAE,KAAK,EAAE,MAAM,EAAE,SAAS,CAAC;IACpD;AAEA;;;;;AAKG;IACH,KAAK,CAAC,OAAe,EAAE,KAAK,GAAG,EAAE,EAAE,SAAkC,EAAE,EAAA;AACtE,QAAA,OAAO,IAAI,CAAC,IAAI,CAAC,OAAO,EAAE,KAAK,EAAE,MAAM,EAAE,OAAO,CAAC;IAClD;AAEA;;;;;AAKG;IACH,OAAO,CAAC,OAAe,EAAE,KAAK,GAAG,EAAE,EAAE,SAAkC,EAAE,EAAA;AACxE,QAAA,OAAO,IAAI,CAAC,IAAI,CAAC,OAAO,EAAE,KAAK,EAAE,MAAM,EAAE,SAAS,CAAC;IACpD;AAEA;;;;;AAKG;IACH,IAAI,CAAC,OAAe,EAAE,KAAK,GAAG,EAAE,EAAE,SAAkC,EAAE,EAAA;AACrE,QAAA,OAAO,IAAI,CAAC,IAAI,CAAC,OAAO,EAAE,KAAK,EAAE,MAAM,EAAE,MAAM,CAAC;IACjD;AAEA;;;;;;;;;AASG;IACH,IAAI,CACH,OAAe,EACf,KAAK,GAAG,EAAE,EACV,MAAA,GAAkC,EAAE,EACpC,IAAA,GAAqC,MAAM,EAAA;QAE3C,MAAM,QAAQ,GAAG,IAAI,CAAC,OAAO,CAAC,OAAO,CAAC,MAAM,CAAC;AAE7C,QAAA,IAAI,QAAQ,CAAC,iBAAiB,EAAE;YAC/B,MAAM,SAAS,GAAG,IAAI,CAAC,MAAM,EAAE,CAAC,IAAI,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,OAAO,KAAK,OAAO,IAAI,CAAC,CAAC,IAAI,KAAK,IAAI,CAAC;YACrF,IAAI,SAAS,EAAE;gBACd,OAAO,IAAI,CAAC,eAAe,CAAC,OAAO,EAAE,IAAI,CAAC;YAC3C;QACD;AAEA,QAAA,IAAI,QAAQ,CAAC,SAAS,GAAG,CAAC,IAAI,IAAI,CAAC,MAAM,EAAE,CAAC,MAAM,IAAI,QAAQ,CAAC,SAAS,EAAE;AACzE,YAAA,IAAI,QAAQ,CAAC,WAAW,EAAE;AACzB,gBAAA,MAAM,MAAM,GAAG,QAAQ,CAAC,WAAW,GAAG,IAAI,CAAC,MAAM,EAAE,CAAC,IAAI,CAAC,MAAM,EAAE,CAAC,MAAM,GAAG,CAAC,CAAC,GAAG,IAAI,CAAC,MAAM,EAAE,CAAC,CAAC,CAAC;AAChG,gBAAA,IAAI,CAAC,WAAW,CAAC,MAAM,CAAC,OAAO,CAAC;YACjC;iBAAO;gBACN,OAAO,IAAI,CAAC,SAAS,CAAC,EAAE,OAAO,EAAE,CAAC,CAAC,EAAS,CAAC;YAC9C;QACD;AAEA,QAAA,MAAM,IAAI,GAAiB;YAC1B,OAAO,EAAE,EAAE,MAAM;YACjB,IAAI;YACJ,OAAO;YACP,KAAK;AACL,YAAA,MAAM,EAAE,QAAQ;YAChB,QAAQ,EAAE,IAAI,OAAO,EAAQ;YAC7B,SAAS,EAAE,IAAI,OAAO,EAAQ;YAC9B,MAAM,EAAE,IAAI,OAAO;SACnB;AAED,QAAA,IAAI,QAAQ,CAAC,WAAW,EAAE;AACzB,YAAA,IAAI,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC,IAAI,KAAK,CAAC,IAAI,EAAE,GAAG,IAAI,CAAC,CAAC;QAC9C;aAAO;AACN,YAAA,IAAI,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC,IAAI,KAAK,CAAC,GAAG,IAAI,EAAE,IAAI,CAAC,CAAC;QAC9C;QAEA,IAAI,CAAC,uBAAuB,EAAE;QAC9B,IAAI,CAAC,cAAc,EAAE;AACrB,QAAA,OAAO,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC;IAC5B;AAEA;;;AAGG;AACH,IAAA,MAAM,CAAC,OAAe,EAAA;AACrB,QAAA,IAAI,CAAC,WAAW,CAAC,OAAO,CAAC;IAC1B;;IAGA,KAAK,GAAA;QACJ,IAAI,CAAC,MAAM,EAAE,CAAC,OAAO,CAAC,CAAC,CAAC,KAAI;AAC3B,YAAA,CAAC,CAAC,SAAS,CAAC,IAAI,EAAE;AAClB,YAAA,CAAC,CAAC,SAAS,CAAC,QAAQ,EAAE;AACvB,QAAA,CAAC,CAAC;AACF,QAAA,IAAI,CAAC,MAAM,CAAC,GAAG,CAAC,EAAE,CAAC;QACnB,IAAI,CAAC,cAAc,EAAE;IACtB;;AAIQ,IAAA,WAAW,CAAC,OAAe,EAAA;QAClC,MAAM,KAAK,GAAG,IAAI,CAAC,MAAM,EAAE,CAAC,IAAI,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,OAAO,KAAK,OAAO,CAAC;QAC9D,IAAI,KAAK,EAAE;AACV,YAAA,KAAK,CAAC,SAAS,CAAC,IAAI,EAAE;AACtB,YAAA,KAAK,CAAC,SAAS,CAAC,QAAQ,EAAE;YAC1B,IAAI,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC,IAAI,KAAK,IAAI,CAAC,MAAM,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,OAAO,KAAK,OAAO,CAAC,CAAC;YACvE,IAAI,CAAC,cAAc,EAAE;QACtB;IACD;IAEQ,eAAe,CAAC,OAAe,EAAE,IAAY,EAAA;QACpD,MAAM,QAAQ,GAAG,IAAI,CAAC,MAAM,EAAE,CAAC,IAAI,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,OAAO,KAAK,OAAO,IAAI,CAAC,CAAC,IAAI,KAAK,IAAI,CAAC;QACpF,OAAO,QAAQ,GAAG,IAAI,CAAC,SAAS,CAAC,QAAQ,CAAC,GAAG,IAAI,CAAC,SAAS,CAAC,EAAE,OAAO,EAAE,CAAC,CAAC,EAAS,CAAC;IACpF;AAEQ,IAAA,SAAS,CAAC,IAAkB,EAAA;QACnC,MAAM,GAAG,GAAG,IAAI;QAChB,OAAO;YACN,OAAO,EAAE,IAAI,CAAC,OAAO;AACrB,YAAA,OAAO,EAAE,IAAI,CAAC,QAAQ,EAAE,YAAY,EAAE,IAAI,IAAI,OAAO,EAAQ,CAAC,YAAY,EAAE;AAC5E,YAAA,QAAQ,EAAE,IAAI,CAAC,SAAS,EAAE,YAAY,EAAE,IAAI,IAAI,OAAO,EAAQ,CAAC,YAAY,EAAE;AAC9E,YAAA,KAAK,EAAE,IAAI,CAAC,MAAM,EAAE,YAAY,EAAE,IAAI,IAAI,OAAO,EAAQ,CAAC,YAAY,EAAE;YACxE,WAAW,GAAA;AACV,gBAAA,GAAG,CAAC,MAAM,CAAC,IAAI,CAAC,OAAO,CAAC;YACzB,CAAC;YACD,YAAY,GAAA;gBACX,MAAM,KAAK,GAAG,GAAG,CAAC,MAAM,EAAE,CAAC,IAAI,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,OAAO,KAAK,IAAI,CAAC,OAAO,CAAC;gBAClE,IAAI,KAAK,EAAE;AACV,oBAAA,KAAK,CAAC,QAAQ,CAAC,IAAI,EAAE;gBACtB;YACD;SACA;IACF;AAEA;;;AAGG;IACK,uBAAuB,GAAA;AAC9B,QAAA,IAAI,IAAI,CAAC,iBAAiB,EAAE;YAC3B;QACD;AACA,QAAA,IAAI,CAAC,iBAAiB,GAAG,IAAI;QAE7B,wEAAiE,CAAC,IAAI,CAAC,CAAC,EAAE,uBAAuB,EAAE,KAAI;AACtG,YAAA,MAAM,GAAG,GAAG,eAAe,CAAC,uBAAuB,EAAE;AACpD,gBAAA,mBAAmB,EAAE,IAAI,CAAC,OAAO,CAAC;AAClC,aAAA,CAAC;AACF,YAAA,IAAI,CAAC,aAAa,GAAG,GAAG;YACxB,IAAI,CAAC,OAAO,CAAC,UAAU,CAAC,GAAG,CAAC,QAAQ,CAAC;YACrC,QAAQ,CAAC,IAAI,CAAC,WAAW,CAAC,GAAG,CAAC,QAAQ,CAAC,aAAa,CAAC;;AAErD,YAAA,GAAG,CAAC,iBAAiB,CAAC,aAAa,EAAE;AACtC,QAAA,CAAC,CAAC;IACH;AAEA;;;;;;;AAOG;IACK,cAAc,GAAA;AACrB,QAAA,IAAI,CAAC,aAAa,EAAE,iBAAiB,CAAC,aAAa,EAAE;IACtD;uGAnMY,YAAY,EAAA,IAAA,EAAA,EAAA,EAAA,MAAA,EAAA,EAAA,CAAA,eAAA,CAAA,UAAA,EAAA,CAAA;AAAZ,IAAA,OAAA,KAAA,GAAA,EAAA,CAAA,qBAAA,CAAA,EAAA,UAAA,EAAA,QAAA,EAAA,OAAA,EAAA,QAAA,EAAA,QAAA,EAAA,EAAA,EAAA,IAAA,EAAA,YAAY,cADC,MAAM,EAAA,CAAA;;2FACnB,YAAY,EAAA,UAAA,EAAA,CAAA;kBADxB,UAAU;mBAAC,EAAE,UAAU,EAAE,MAAM,EAAE;;;ACpBlC;;;;AAIG;AACI,MAAM,cAAc,GAAG,OAAO,CAAC,YAAY,EAAE;AACnD,IAAA,KAAK,CAAC,IAAI,EAAE,KAAK,CAAC,EAAE,OAAO,EAAE,CAAC,EAAE,SAAS,EAAE,eAAe,EAAE,CAAC,CAAC;IAC9D,UAAU,CAAC,QAAQ,EAAE;AACpB,QAAA,OAAO,CACN,gBAAgB,EAChB,SAAS,CAAC;AACT,YAAA,KAAK,CAAC,EAAE,OAAO,EAAE,CAAC,EAAE,SAAS,EAAE,kBAAkB,EAAE,MAAM,EAAE,CAAC,EAAE,CAAC;AAC/D,YAAA,KAAK,CAAC,EAAE,OAAO,EAAE,CAAC,EAAE,SAAS,EAAE,eAAe,EAAE,MAAM,EAAE,CAAC,EAAE;AAC3D,SAAA,CAAC;KAEH,CAAC;IACF,UAAU,CAAC,QAAQ,EAAE;AACpB,QAAA,OAAO,CACN,eAAe,EACf,SAAS,CAAC;AACT,YAAA,KAAK,CAAC,EAAE,OAAO,EAAE,CAAC,EAAE,SAAS,EAAE,eAAe,EAAE,MAAM,EAAE,CAAC,EAAE,CAAC;AAC5D,YAAA,KAAK,CAAC,EAAE,OAAO,EAAE,CAAC,EAAE,SAAS,EAAE,qBAAqB,EAAE,MAAM,EAAE,CAAC,EAAE;AACjE,SAAA,CAAC;KAEH;AACD,CAAA,CAAC;;ACdF;AACA,MAAM,cAAc,GAAG,IAAI,GAAG,CAAS,CAAC,SAAS,EAAE,OAAO,EAAE,SAAS,EAAE,MAAM,CAAC,CAAC;AAE/E;;;;;;;;AAQG;MAeU,cAAc,CAAA;;IAEjB,IAAI,GAAG,KAAK,CAAC,QAAQ;6EAAgB;;IAGrC,MAAM,GAAG,MAAM,EAAU;;IAGzB,QAAQ,GAAG,MAAM,CAAC,GAAG;iFAAC;AAE/B;;;;AAIG;AACM,IAAA,WAAW,GAAG,QAAQ,CAAgB,MAAK;QACnD,MAAM,IAAI,GAAG,IAAI,CAAC,IAAI,EAAE,CAAC,IAAI;AAC7B,QAAA,OAAO,cAAc,CAAC,GAAG,CAAC,IAAI,CAAC,GAAG,IAAI,GAAG,CAAA,oBAAA,EAAuB,IAAI,GAAG;IACxE,CAAC;oFAAC;IAEM,QAAQ,GAAyC,IAAI;IACrD,WAAW,GAA0C,IAAI;AAEjE,IAAA,WAAA,GAAA;QACC,MAAM,CAAC,MAAK;YACX,MAAM,GAAG,GAAG,IAAI,CAAC,IAAI,EAAE,CAAC,MAAM;YAC9B,IAAI,CAAC,YAAY,EAAE;AACnB,YAAA,IAAI,GAAG,CAAC,cAAc,KAAK,IAAI,IAAI,GAAG,CAAC,cAAc,KAAK,SAAS,EAAE;gBACpE;YACD;AACA,YAAA,IAAI,GAAG,CAAC,OAAO,GAAG,CAAC,EAAE;AACpB,gBAAA,IAAI,CAAC,WAAW,CAAC,GAAG,CAAC,OAAO,CAAC;YAC9B;AACD,QAAA,CAAC,CAAC;QAEF,MAAM,CAAC,MAAK;YACX,IAAI,CAAC,IAAI,EAAE,CAAC,QAAQ,CAAC,IAAI,EAAE;AAC5B,QAAA,CAAC,CAAC;IACH;;IAGA,YAAY,GAAA;QACX,MAAM,GAAG,GAAG,IAAI,CAAC,IAAI,EAAE,CAAC,MAAM;AAC9B,QAAA,IAAI,GAAG,CAAC,cAAc,KAAK,iBAAiB,EAAE;YAC7C,IAAI,CAAC,YAAY,EAAE;QACpB;IACD;;IAGA,YAAY,GAAA;QACX,MAAM,GAAG,GAAG,IAAI,CAAC,IAAI,EAAE,CAAC,MAAM;AAC9B,QAAA,IAAI,GAAG,CAAC,eAAe,GAAG,CAAC,IAAI,GAAG,CAAC,cAAc,KAAK,iBAAiB,EAAE;AACxE,YAAA,IAAI,CAAC,WAAW,CAAC,GAAG,CAAC,eAAe,CAAC;QACtC;IACD;;IAGA,KAAK,GAAA;QACJ,MAAM,GAAG,GAAG,IAAI,CAAC,IAAI,EAAE,CAAC,MAAM;QAC9B,IAAI,CAAC,IAAI,EAAE,CAAC,MAAM,CAAC,IAAI,EAAE;AACzB,QAAA,IAAI,GAAG,CAAC,YAAY,EAAE;YACrB,IAAI,CAAC,QAAQ,EAAE;QAChB;IACD;;AAGA,IAAA,OAAO,CAAC,KAAY,EAAA;QACnB,KAAK,CAAC,eAAe,EAAE;QACvB,IAAI,CAAC,QAAQ,EAAE;IAChB;IAEA,WAAW,GAAA;QACV,IAAI,CAAC,YAAY,EAAE;IACpB;AAEQ,IAAA,WAAW,CAAC,QAAgB,EAAA;QACnC,MAAM,GAAG,GAAG,IAAI,CAAC,IAAI,EAAE,CAAC,MAAM;AAC9B,QAAA,MAAM,KAAK,GAAG,IAAI,CAAC,GAAG,EAAE;AAExB,QAAA,IAAI,GAAG,CAAC,WAAW,EAAE;AACpB,YAAA,IAAI,CAAC,WAAW,GAAG,WAAW,CAAC,MAAK;gBACnC,MAAM,OAAO,GAAG,IAAI,CAAC,GAAG,EAAE,GAAG,KAAK;gBAClC,IAAI,CAAC,QAAQ,CAAC,GAAG,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,GAAG,GAAG,CAAC,OAAO,GAAG,QAAQ,IAAI,GAAG,CAAC,CAAC;YACjE,CAAC,EAAE,EAAE,CAAC;QACP;AAEA,QAAA,IAAI,CAAC,QAAQ,GAAG,UAAU,CAAC,MAAK;YAC/B,IAAI,CAAC,QAAQ,EAAE;QAChB,CAAC,EAAE,QAAQ,CAAC;IACb;IAEQ,QAAQ,GAAA;QACf,IAAI,CAAC,YAAY,EAAE;AACnB,QAAA,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC,IAAI,EAAE,CAAC,OAAO,CAAC;IACtC;IAEQ,YAAY,GAAA;AACnB,QAAA,IAAI,IAAI,CAAC,QAAQ,KAAK,IAAI,EAAE;AAC3B,YAAA,YAAY,CAAC,IAAI,CAAC,QAAQ,CAAC;AAC3B,YAAA,IAAI,CAAC,QAAQ,GAAG,IAAI;QACrB;AACA,QAAA,IAAI,IAAI,CAAC,WAAW,KAAK,IAAI,EAAE;AAC9B,YAAA,aAAa,CAAC,IAAI,CAAC,WAAW,CAAC;AAC/B,YAAA,IAAI,CAAC,WAAW,GAAG,IAAI;QACxB;AACA,QAAA,IAAI,CAAC,QAAQ,CAAC,GAAG,CAAC,GAAG,CAAC;IACvB;uGA1GY,cAAc,EAAA,IAAA,EAAA,EAAA,EAAA,MAAA,EAAA,EAAA,CAAA,eAAA,CAAA,SAAA,EAAA,CAAA;AAAd,IAAA,OAAA,IAAA,GAAA,EAAA,CAAA,oBAAA,CAAA,EAAA,UAAA,EAAA,QAAA,EAAA,OAAA,EAAA,QAAA,EAAA,IAAA,EAAA,cAAc,EAAA,YAAA,EAAA,IAAA,EAAA,QAAA,EAAA,WAAA,EAAA,MAAA,EAAA,EAAA,IAAA,EAAA,EAAA,iBAAA,EAAA,MAAA,EAAA,UAAA,EAAA,MAAA,EAAA,QAAA,EAAA,IAAA,EAAA,UAAA,EAAA,IAAA,EAAA,iBAAA,EAAA,IAAA,EAAA,EAAA,EAAA,OAAA,EAAA,EAAA,MAAA,EAAA,QAAA,EAAA,EAAA,IAAA,EAAA,EAAA,SAAA,EAAA,EAAA,OAAA,EAAA,SAAA,EAAA,EAAA,UAAA,EAAA,EAAA,aAAA,EAAA,QAAA,EAAA,gBAAA,EAAA,aAAA,EAAA,0BAAA,EAAA,eAAA,EAAA,EAAA,cAAA,EAAA,WAAA,EAAA,EAAA,QAAA,EAAA,EAAA,EAAA,QAAA,ECvC3B,gyBAiCA,EAAA,MAAA,EAAA,CAAA,qoHAAA,CAAA,EAAA,UAAA,EDHa,CAAC,cAAc,CAAC,EAAA,eAAA,EAAA,EAAA,CAAA,uBAAA,CAAA,MAAA,EAAA,CAAA;;2FAShB,cAAc,EAAA,UAAA,EAAA,CAAA;kBAd1B,SAAS;+BACC,WAAW,EAAA,eAAA,EAGJ,uBAAuB,CAAC,MAAM,cACnC,CAAC,cAAc,CAAC,EAAA,IAAA,EACtB;AACL,wBAAA,KAAK,EAAE,WAAW;AAClB,wBAAA,eAAe,EAAE,MAAM;AACvB,wBAAA,kBAAkB,EAAE,aAAa;AACjC,wBAAA,4BAA4B,EAAE,eAAe;AAC7C,wBAAA,SAAS,EAAE;AACX,qBAAA,EAAA,QAAA,EAAA,gyBAAA,EAAA,MAAA,EAAA,CAAA,qoHAAA,CAAA,EAAA;;;AEhCF;;;;;;;AAOG;MAaU,uBAAuB,CAAA;AAChB,IAAA,YAAY,GAAG,MAAM,CAAC,YAAY,CAAC;;AAGnC,IAAA,MAAM,GAAG,IAAI,CAAC,YAAY,CAAC,MAAM;AAEpD;;;AAGG;IACgB,aAAa,GAAG,MAAK;AACvC,QAAA,MAAM,IAAI,GAAG,IAAI,CAAC,MAAM,EAAE;QAC1B,OAAO,IAAI,CAAC,MAAM,GAAG,CAAC,GAAG,IAAI,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,aAAa,GAAG,iBAAiB;AAC1E,IAAA,CAAC;;AAGS,IAAA,QAAQ,CAAC,OAAe,EAAA;AACjC,QAAA,IAAI,CAAC,YAAY,CAAC,MAAM,CAAC,OAAO,CAAC;IAClC;uGAlBY,uBAAuB,EAAA,IAAA,EAAA,EAAA,EAAA,MAAA,EAAA,EAAA,CAAA,eAAA,CAAA,SAAA,EAAA,CAAA;AAAvB,IAAA,OAAA,IAAA,GAAA,EAAA,CAAA,oBAAA,CAAA,EAAA,UAAA,EAAA,QAAA,EAAA,OAAA,EAAA,QAAA,EAAA,IAAA,EAAA,uBAAuB,4KCzBpC,sHAGA,EAAA,MAAA,EAAA,CAAA,usCAAA,CAAA,EAAA,YAAA,EAAA,CAAA,EAAA,IAAA,EAAA,WAAA,EAAA,IAAA,EDgBW,cAAc,EAAA,QAAA,EAAA,WAAA,EAAA,MAAA,EAAA,CAAA,MAAA,CAAA,EAAA,OAAA,EAAA,CAAA,QAAA,CAAA,EAAA,CAAA,EAAA,UAAA,EADZ,CAAC,cAAc,CAAC,EAAA,eAAA,EAAA,EAAA,CAAA,uBAAA,CAAA,MAAA,EAAA,CAAA;;2FAOhB,uBAAuB,EAAA,UAAA,EAAA,CAAA;kBAZnC,SAAS;AACC,YAAA,IAAA,EAAA,CAAA,EAAA,QAAA,EAAA,qBAAqB,EAAA,eAAA,EAGd,uBAAuB,CAAC,MAAM,EAAA,UAAA,EACnC,CAAC,cAAc,CAAC,EAAA,OAAA,EACnB,CAAC,cAAc,CAAC,EAAA,IAAA,EACnB;AACL,wBAAA,KAAK,EAAE,qBAAqB;AAC5B,wBAAA,SAAS,EAAE;AACX,qBAAA,EAAA,QAAA,EAAA,sHAAA,EAAA,MAAA,EAAA,CAAA,usCAAA,CAAA,EAAA;;;;;;;;AEvBF;;AAEG;;ACFH;;AAEG;;;;"}
|
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 };
|