snackng 0.2.0 → 1.0.2
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 +93 -48
- package/fesm2022/snackng.mjs +87 -25
- package/fesm2022/snackng.mjs.map +1 -1
- package/package.json +1 -1
- package/types/snackng.d.ts +37 -5
package/README.md
CHANGED
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
[](https://deepwiki.com/xgreymx/snackng)
|
|
2
|
+
|
|
1
3
|
# snackng
|
|
2
4
|
|
|
3
5
|
Toasts for Angular with a glass design, **zero UI dependencies** and CSS-variable theming.
|
|
@@ -20,7 +22,7 @@ no theme tokens to define.
|
|
|
20
22
|
import { Component, inject } from '@angular/core';
|
|
21
23
|
import { SnackngService } from 'snackng';
|
|
22
24
|
|
|
23
|
-
@Component({
|
|
25
|
+
@Component({/* … */})
|
|
24
26
|
export class SyncPage {
|
|
25
27
|
private readonly toast = inject(SnackngService);
|
|
26
28
|
|
|
@@ -50,8 +52,8 @@ this.toast.info('Import running…', { title: 'In progress', duration: 0, dismis
|
|
|
50
52
|
const ref = this.toast.success('Movement deleted.', {
|
|
51
53
|
action: { label: 'Undo', handler: () => this.restore() },
|
|
52
54
|
});
|
|
53
|
-
ref.afterDismissed.then(reason => {}); // 'timeout' | 'action' | 'manual' | 'replaced'
|
|
54
|
-
ref.dismiss();
|
|
55
|
+
ref.afterDismissed.then((reason) => {}); // 'timeout' | 'action' | 'manual' | 'replaced'
|
|
56
|
+
ref.dismiss(); // close it yourself
|
|
55
57
|
|
|
56
58
|
// Somewhere other than the top-right.
|
|
57
59
|
this.toast.info('Down here.', { position: 'bottom-center' });
|
|
@@ -77,15 +79,15 @@ its internal MDC surface with `!important` — or ship Tailwind classes that ren
|
|
|
77
79
|
markup unless the consuming app happens to run the same CSS pipeline. snackng ships **compiled,
|
|
78
80
|
encapsulated CSS** and owns its own overlay, so it has neither problem.
|
|
79
81
|
|
|
80
|
-
|
|
|
81
|
-
|
|
82
|
-
| Peer dependencies
|
|
83
|
-
| Global CSS you must add
|
|
84
|
-
| Stacks multiple toasts
|
|
85
|
-
| Handles bursts
|
|
86
|
-
| SSR
|
|
87
|
-
| Respects `prefers-reduced-motion` | yes
|
|
88
|
-
| Angular
|
|
82
|
+
| | snackng |
|
|
83
|
+
| --------------------------------- | --------------------------------------------------------------- |
|
|
84
|
+
| Peer dependencies | `@angular/core`, `@angular/common`, `@angular/platform-browser` |
|
|
85
|
+
| Global CSS you must add | none |
|
|
86
|
+
| Stacks multiple toasts | yes (`MatSnackBar` shows one at a time) |
|
|
87
|
+
| Handles bursts | queued and staggered, never dropped |
|
|
88
|
+
| SSR | safe — no-ops on the server |
|
|
89
|
+
| Respects `prefers-reduced-motion` | yes |
|
|
90
|
+
| Angular | 21 and up |
|
|
89
91
|
|
|
90
92
|
## API
|
|
91
93
|
|
|
@@ -106,21 +108,58 @@ const ref = toast.success('Item deleted', {
|
|
|
106
108
|
action: { label: 'Undo', handler: () => restore() },
|
|
107
109
|
});
|
|
108
110
|
|
|
109
|
-
ref.afterDismissed.then(reason => console.log(reason)); // 'timeout' | 'action' | 'manual' | 'replaced'
|
|
111
|
+
ref.afterDismissed.then((reason) => console.log(reason)); // 'timeout' | 'action' | 'manual' | 'replaced'
|
|
110
112
|
ref.dismiss();
|
|
111
113
|
```
|
|
112
114
|
|
|
113
115
|
### Options
|
|
114
116
|
|
|
115
|
-
| Option
|
|
116
|
-
|
|
117
|
-
| `title`
|
|
118
|
-
| `duration`
|
|
119
|
-
| `action`
|
|
120
|
-
| `position`
|
|
121
|
-
| `dismissible` | `boolean`
|
|
122
|
-
| `politeness`
|
|
123
|
-
| `
|
|
117
|
+
| Option | Type | Default | |
|
|
118
|
+
| ------------- | -------------------------------------- | ----------- | ----------------------------------------- |
|
|
119
|
+
| `title` | `string` | — | Bold line above the message |
|
|
120
|
+
| `duration` | `number` | `5000` | ms before auto-dismiss; `0` keeps it open |
|
|
121
|
+
| `action` | `{ label, handler?, dismissOnClick? }` | — | Snackbar-style button |
|
|
122
|
+
| `position` | `SnackngPosition` | `'top-end'` | `top`/`bottom` × `start`/`center`/`end` |
|
|
123
|
+
| `dismissible` | `boolean` | `false` | Show the close button |
|
|
124
|
+
| `politeness` | `'polite' \| 'assertive' \| 'off'` | by type | Screen-reader urgency |
|
|
125
|
+
| `style` | `SnackngStyle` | `'glass'` | Glass preset — see below |
|
|
126
|
+
| `effect` | `SnackngEffect` | `'drift'` | Surface light effect — see below |
|
|
127
|
+
| `panelClass` | `string \| string[]` | — | Extra classes on the toast element |
|
|
128
|
+
|
|
129
|
+
### Glass presets & surface effects
|
|
130
|
+
|
|
131
|
+
Don't want to hand-tune opacity and blur to get a look? Pick a **preset**. Each is a bundle of
|
|
132
|
+
glass settings, applied per call, globally, or via a chained shortcut — all three are equivalent:
|
|
133
|
+
|
|
134
|
+
```ts
|
|
135
|
+
toast.success('Saved', { style: 'solid' }); // per call
|
|
136
|
+
toast.success.solid('Saved'); // chained sugar (built-in types)
|
|
137
|
+
provideSnackng({ style: 'solid' }); // global default for every toast
|
|
138
|
+
```
|
|
139
|
+
|
|
140
|
+
| `style` | Look |
|
|
141
|
+
| --------------------- | -------------------------------------------------------------------------------- |
|
|
142
|
+
| `'glass'` _(default)_ | Rich translucent glass with depth |
|
|
143
|
+
| `'solid'` | Nearly opaque, a whisper of glass |
|
|
144
|
+
| `'translucent'` | More of the background shows through |
|
|
145
|
+
| `'transparent'` | Barely-there tint, very see-through |
|
|
146
|
+
| `'frosted'` | Heavy blur, high tint — classic frosted glass |
|
|
147
|
+
| `'flat'` | Fully opaque, no blur — flat design, and the cleanest `backdrop-filter` fallback |
|
|
148
|
+
|
|
149
|
+
Define your own by dropping a `.sng-style--<name>` rule in your CSS (setting `--sng-p-*`
|
|
150
|
+
private aliases) and passing `{ style: '<name>' }`.
|
|
151
|
+
|
|
152
|
+
`effect` adds subtle motion/light to the glass surface — it never touches the enter/exit
|
|
153
|
+
animations:
|
|
154
|
+
|
|
155
|
+
| `effect` | Behaviour |
|
|
156
|
+
| --------------------- | --------------------------------------------------- |
|
|
157
|
+
| `'drift'` _(default)_ | A slow specular highlight drifts across the surface |
|
|
158
|
+
| `'glare'` | The highlight follows the pointer (hover) |
|
|
159
|
+
| `'both'` | Drift when idle, glare on hover |
|
|
160
|
+
| `'none'` | Static glass, no light motion |
|
|
161
|
+
|
|
162
|
+
Both `drift` and the drift half of `both` stop under `prefers-reduced-motion: reduce`.
|
|
124
163
|
|
|
125
164
|
### Global defaults
|
|
126
165
|
|
|
@@ -132,13 +171,15 @@ import { provideSnackng } from 'snackng';
|
|
|
132
171
|
bootstrapApplication(App, {
|
|
133
172
|
providers: [
|
|
134
173
|
provideSnackng({
|
|
135
|
-
duration: 4000,
|
|
174
|
+
duration: 4000, // ms; 0 = stay until dismissed
|
|
136
175
|
position: 'bottom-end',
|
|
137
|
-
max: 5,
|
|
138
|
-
overflow: 'queue',
|
|
139
|
-
stagger: 90,
|
|
176
|
+
max: 5, // visible at once
|
|
177
|
+
overflow: 'queue', // 'queue' | 'dismiss-oldest'
|
|
178
|
+
stagger: 90, // ms between releases; 0 = all at once
|
|
140
179
|
pauseOnHover: true,
|
|
141
180
|
dismissible: false,
|
|
181
|
+
style: 'glass', // default glass preset for every toast
|
|
182
|
+
effect: 'drift', // 'drift' | 'glare' | 'both' | 'none'
|
|
142
183
|
types: {
|
|
143
184
|
deploy: { icon: '<svg viewBox="0 0 24 24"><path d="..."/></svg>' },
|
|
144
185
|
},
|
|
@@ -190,27 +231,31 @@ variables anywhere they inherit to the toast (`:root` is simplest).
|
|
|
190
231
|
}
|
|
191
232
|
```
|
|
192
233
|
|
|
193
|
-
| Variable
|
|
194
|
-
|
|
195
|
-
| `--snackng-{type}-bg`
|
|
196
|
-
| `--snackng-{type}-ink`
|
|
197
|
-
| `--snackng-{type}-solid`
|
|
198
|
-
| `--snackng-
|
|
199
|
-
| `--snackng-
|
|
200
|
-
| `--snackng-
|
|
201
|
-
| `--snackng-
|
|
202
|
-
| `--snackng-
|
|
203
|
-
| `--snackng-
|
|
204
|
-
| `--snackng-
|
|
205
|
-
| `--snackng-
|
|
206
|
-
| `--snackng-
|
|
207
|
-
| `--snackng-
|
|
208
|
-
| `--snackng-
|
|
209
|
-
| `--snackng-
|
|
210
|
-
| `--snackng-
|
|
211
|
-
| `--snackng-
|
|
212
|
-
| `--snackng-
|
|
213
|
-
| `--snackng-
|
|
234
|
+
| Variable | Default |
|
|
235
|
+
| ------------------------------------------------------ | ------------------------------------------------------ |
|
|
236
|
+
| `--snackng-{type}-bg` | glass gradient per type |
|
|
237
|
+
| `--snackng-{type}-ink` | `#ffffff` |
|
|
238
|
+
| `--snackng-{type}-solid` | opaque fallback where `backdrop-filter` is unsupported |
|
|
239
|
+
| `--snackng-tint` | `0.86` — tint opacity (how much shows through) |
|
|
240
|
+
| `--snackng-radius` | `14px` |
|
|
241
|
+
| `--snackng-blur` | `20px` |
|
|
242
|
+
| `--snackng-saturate` | `180%` |
|
|
243
|
+
| `--snackng-brightness` | `1.06` |
|
|
244
|
+
| `--snackng-gloss` | `0.22` — static highlight strength |
|
|
245
|
+
| `--snackng-spec-strength` / `--snackng-spec-size` | `0.15` / `30%` — drift & glare reflection |
|
|
246
|
+
| `--snackng-drift-duration` | `4s` |
|
|
247
|
+
| `--snackng-border` | `1px solid rgba(255,255,255,.22)` |
|
|
248
|
+
| `--snackng-shadow` | layered drop + inner highlight |
|
|
249
|
+
| `--snackng-min-width` / `--snackng-max-width` | `340px` / `460px` |
|
|
250
|
+
| `--snackng-padding` | `14px 18px` |
|
|
251
|
+
| `--snackng-gap` | `14px` |
|
|
252
|
+
| `--snackng-font` | `'Manrope', system-ui, sans-serif` |
|
|
253
|
+
| `--snackng-title-size` / `--snackng-title-weight` | `14px` / `700` |
|
|
254
|
+
| `--snackng-message-size` / `--snackng-message-weight` | `13px` / `400` |
|
|
255
|
+
| `--snackng-line-height` | `1.45` |
|
|
256
|
+
| `--snackng-enter-duration` / `--snackng-exit-duration` | `400ms` / `200ms` |
|
|
257
|
+
| `--snackng-z` | `2000` |
|
|
258
|
+
| `--snackng-stack-gap` / `--snackng-stack-padding` | `12px` / `16px` |
|
|
214
259
|
|
|
215
260
|
`{type}` is `success`, `warning`, `danger`, `info`, or any custom type you register.
|
|
216
261
|
|
|
@@ -220,7 +265,7 @@ snackng does **not** depend on daisyUI or Tailwind. If you happen to use them, o
|
|
|
220
265
|
re-points the toast at your daisy theme, so it follows theme switching and dark mode for free:
|
|
221
266
|
|
|
222
267
|
```css
|
|
223
|
-
@import
|
|
268
|
+
@import 'snackng/themes/daisy.css';
|
|
224
269
|
```
|
|
225
270
|
|
|
226
271
|
That maps `--snackng-*` onto daisyUI's `--color-success`, `--color-error-content`, `--radius-box`
|
package/fesm2022/snackng.mjs
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import * as i0 from '@angular/core';
|
|
2
|
-
import { InjectionToken, makeEnvironmentProviders, inject, DOCUMENT, Injectable, signal, computed, input, ElementRef, effect, ChangeDetectionStrategy, Component, afterEveryRender, ApplicationRef, EnvironmentInjector, PLATFORM_ID, createComponent } from '@angular/core';
|
|
2
|
+
import { InjectionToken, makeEnvironmentProviders, inject, DOCUMENT, Injectable, signal, computed, input, ElementRef, NgZone, effect, ChangeDetectionStrategy, Component, afterEveryRender, ApplicationRef, EnvironmentInjector, PLATFORM_ID, createComponent } from '@angular/core';
|
|
3
3
|
import { isPlatformBrowser } from '@angular/common';
|
|
4
4
|
import { DomSanitizer } from '@angular/platform-browser';
|
|
5
5
|
|
|
@@ -11,6 +11,8 @@ const SNACKNG_DEFAULTS = {
|
|
|
11
11
|
stagger: 90,
|
|
12
12
|
pauseOnHover: true,
|
|
13
13
|
dismissible: false,
|
|
14
|
+
style: 'glass',
|
|
15
|
+
effect: 'drift',
|
|
14
16
|
types: {},
|
|
15
17
|
};
|
|
16
18
|
const SNACKNG_CONFIG = new InjectionToken('snackng.config', {
|
|
@@ -97,6 +99,7 @@ const SNACKNG_ICONS = {
|
|
|
97
99
|
danger: 'M12 2C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2m1 15h-2v-2h2zm0-4h-2V7h2z',
|
|
98
100
|
info: 'M12 2C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2m1 15h-2v-6h2zm0-8h-2V7h2z',
|
|
99
101
|
};
|
|
102
|
+
const SNACKNG_CLOSE_ICON = 'M19 6.41 17.59 5 12 10.59 6.41 5 5 6.41 10.59 12 5 17.59 6.41 19 12 13.41 17.59 19 19 17.59 13.41 12z';
|
|
100
103
|
|
|
101
104
|
/**
|
|
102
105
|
* Toast state, and the pacing of how toasts reach the screen.
|
|
@@ -130,8 +133,8 @@ class SnackngStore {
|
|
|
130
133
|
}, ...(ngDevMode ? [{ debugName: "stacks" }] : /* istanbul ignore next */ []));
|
|
131
134
|
add(item) {
|
|
132
135
|
const id = `sng-${++this.counter}`;
|
|
133
|
-
const afterDismissed = new Promise(resolve => this.resolvers.set(id, resolve));
|
|
134
|
-
this.queue.update(queue => [...queue, { ...item, id, leaving: false }]);
|
|
136
|
+
const afterDismissed = new Promise((resolve) => this.resolvers.set(id, resolve));
|
|
137
|
+
this.queue.update((queue) => [...queue, { ...item, id, leaving: false }]);
|
|
135
138
|
this.pump();
|
|
136
139
|
return { id, afterDismissed };
|
|
137
140
|
}
|
|
@@ -140,24 +143,24 @@ class SnackngStore {
|
|
|
140
143
|
* still queued is dropped outright, having never been shown.
|
|
141
144
|
*/
|
|
142
145
|
dismiss(id, reason = 'manual') {
|
|
143
|
-
if (this.queue().some(item => item.id === id)) {
|
|
144
|
-
this.queue.update(queue => queue.filter(item => item.id !== id));
|
|
146
|
+
if (this.queue().some((item) => item.id === id)) {
|
|
147
|
+
this.queue.update((queue) => queue.filter((item) => item.id !== id));
|
|
145
148
|
this.resolve(id, reason);
|
|
146
149
|
return;
|
|
147
150
|
}
|
|
148
|
-
const target = this.items().find(item => item.id === id);
|
|
151
|
+
const target = this.items().find((item) => item.id === id);
|
|
149
152
|
if (!target || target.leaving) {
|
|
150
153
|
return;
|
|
151
154
|
}
|
|
152
155
|
this.reasons.set(id, reason);
|
|
153
|
-
this.items.update(items => items.map(i => (i.id === id ? { ...i, leaving: true } : i)));
|
|
156
|
+
this.items.update((items) => items.map((i) => (i.id === id ? { ...i, leaving: true } : i)));
|
|
154
157
|
}
|
|
155
158
|
/** Removes the item and resolves its `afterDismissed` promise. */
|
|
156
159
|
finalize(id) {
|
|
157
160
|
if (!this.resolvers.has(id)) {
|
|
158
161
|
return;
|
|
159
162
|
}
|
|
160
|
-
this.items.update(items => items.filter(item => item.id !== id));
|
|
163
|
+
this.items.update((items) => items.filter((item) => item.id !== id));
|
|
161
164
|
this.resolve(id, this.reasons.get(id) ?? 'manual');
|
|
162
165
|
this.reasons.delete(id);
|
|
163
166
|
this.pump();
|
|
@@ -186,7 +189,7 @@ class SnackngStore {
|
|
|
186
189
|
if (onScreen.length >= this.config.max) {
|
|
187
190
|
if (this.config.overflow === 'dismiss-oldest') {
|
|
188
191
|
// Evicting an already-leaving toast frees nothing.
|
|
189
|
-
const victim = onScreen.find(item => !item.leaving);
|
|
192
|
+
const victim = onScreen.find((item) => !item.leaving);
|
|
190
193
|
if (victim) {
|
|
191
194
|
this.dismiss(victim.id, 'replaced');
|
|
192
195
|
}
|
|
@@ -205,7 +208,7 @@ class SnackngStore {
|
|
|
205
208
|
}
|
|
206
209
|
const [next, ...rest] = this.queue();
|
|
207
210
|
this.queue.set(rest);
|
|
208
|
-
this.items.update(items => [...items, next]);
|
|
211
|
+
this.items.update((items) => [...items, next]);
|
|
209
212
|
this.lastReleaseAt = Date.now();
|
|
210
213
|
}
|
|
211
214
|
}
|
|
@@ -233,21 +236,36 @@ class SnackngToast {
|
|
|
233
236
|
config = inject(SNACKNG_CONFIG);
|
|
234
237
|
sanitizer = inject(DomSanitizer);
|
|
235
238
|
element = inject(ElementRef);
|
|
239
|
+
zone = inject(NgZone);
|
|
236
240
|
timer;
|
|
237
241
|
exitTimer;
|
|
238
242
|
armed = false;
|
|
239
243
|
remaining = 0;
|
|
240
244
|
startedAt = 0;
|
|
245
|
+
glareBound = false;
|
|
241
246
|
hostClasses = computed(() => {
|
|
242
247
|
const item = this.item();
|
|
243
|
-
|
|
248
|
+
const classes = [
|
|
244
249
|
'sng-toast',
|
|
245
250
|
`sng--${item.type}`,
|
|
246
251
|
`sng-at--${item.position}`,
|
|
252
|
+
`sng-style--${item.style}`,
|
|
247
253
|
item.leaving ? 'sng-leaving' : 'sng-entering',
|
|
248
254
|
...item.panelClass,
|
|
249
|
-
]
|
|
255
|
+
];
|
|
256
|
+
if (item.effect === 'drift' || item.effect === 'both') {
|
|
257
|
+
classes.push('sng-fx--drift');
|
|
258
|
+
}
|
|
259
|
+
if (item.effect === 'glare' || item.effect === 'both') {
|
|
260
|
+
classes.push('sng-fx--glare');
|
|
261
|
+
}
|
|
262
|
+
return classes.join(' ');
|
|
250
263
|
}, ...(ngDevMode ? [{ debugName: "hostClasses" }] : /* istanbul ignore next */ []));
|
|
264
|
+
/** Whether this toast tracks the pointer for the cursor glare effect. */
|
|
265
|
+
glareEnabled = computed(() => {
|
|
266
|
+
const fx = this.item().effect;
|
|
267
|
+
return fx === 'glare' || fx === 'both';
|
|
268
|
+
}, ...(ngDevMode ? [{ debugName: "glareEnabled" }] : /* istanbul ignore next */ []));
|
|
251
269
|
/** Null when the type brings its own SVG; unknown types fall back to `info`. */
|
|
252
270
|
iconPath = computed(() => {
|
|
253
271
|
const type = this.item().type;
|
|
@@ -256,6 +274,9 @@ class SnackngToast {
|
|
|
256
274
|
}
|
|
257
275
|
return SNACKNG_ICONS[type] ?? SNACKNG_ICONS.info;
|
|
258
276
|
}, ...(ngDevMode ? [{ debugName: "iconPath" }] : /* istanbul ignore next */ []));
|
|
277
|
+
closeIconPath = computed(() => {
|
|
278
|
+
return SNACKNG_CLOSE_ICON;
|
|
279
|
+
}, ...(ngDevMode ? [{ debugName: "closeIconPath" }] : /* istanbul ignore next */ []));
|
|
259
280
|
/** Custom types may supply raw SVG markup, trusted as authored config. */
|
|
260
281
|
iconSvg = computed(() => {
|
|
261
282
|
const custom = this.config.types[this.item().type]?.icon;
|
|
@@ -271,7 +292,36 @@ class SnackngToast {
|
|
|
271
292
|
this.startTimer();
|
|
272
293
|
}
|
|
273
294
|
});
|
|
295
|
+
// Bind pointer tracking once, only if this toast uses the glare effect.
|
|
296
|
+
effect(() => {
|
|
297
|
+
if (this.glareEnabled() && !this.glareBound) {
|
|
298
|
+
this.bindGlare();
|
|
299
|
+
}
|
|
300
|
+
});
|
|
301
|
+
}
|
|
302
|
+
/** Writes the cursor position into CSS vars the glare layer reads. Runs
|
|
303
|
+
* outside Angular — it only mutates style, no change detection needed. */
|
|
304
|
+
bindGlare() {
|
|
305
|
+
this.glareBound = true;
|
|
306
|
+
const el = this.element.nativeElement;
|
|
307
|
+
this.zone.runOutsideAngular(() => {
|
|
308
|
+
el.addEventListener('pointermove', this.onGlareMove);
|
|
309
|
+
el.addEventListener('pointerleave', this.onGlareLeave);
|
|
310
|
+
});
|
|
274
311
|
}
|
|
312
|
+
onGlareMove = (event) => {
|
|
313
|
+
const el = this.element.nativeElement;
|
|
314
|
+
const rect = el.getBoundingClientRect();
|
|
315
|
+
if (!rect.width || !rect.height) {
|
|
316
|
+
return;
|
|
317
|
+
}
|
|
318
|
+
el.style.setProperty('--sng-glare-x', `${((event.clientX - rect.left) / rect.width) * 100}%`);
|
|
319
|
+
el.style.setProperty('--sng-glare-y', `${((event.clientY - rect.top) / rect.height) * 100}%`);
|
|
320
|
+
el.style.setProperty('--sng-glare-op', '1');
|
|
321
|
+
};
|
|
322
|
+
onGlareLeave = () => {
|
|
323
|
+
this.element.nativeElement.style.setProperty('--sng-glare-op', '0');
|
|
324
|
+
};
|
|
275
325
|
onAction() {
|
|
276
326
|
const action = this.item().action;
|
|
277
327
|
action?.handler?.();
|
|
@@ -328,9 +378,14 @@ class SnackngToast {
|
|
|
328
378
|
ngOnDestroy() {
|
|
329
379
|
this.stopTimer();
|
|
330
380
|
clearTimeout(this.exitTimer);
|
|
381
|
+
if (this.glareBound) {
|
|
382
|
+
const el = this.element.nativeElement;
|
|
383
|
+
el.removeEventListener('pointermove', this.onGlareMove);
|
|
384
|
+
el.removeEventListener('pointerleave', this.onGlareLeave);
|
|
385
|
+
}
|
|
331
386
|
}
|
|
332
387
|
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.18", ngImport: i0, type: SnackngToast, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
333
|
-
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.18", type: SnackngToast, isStandalone: true, selector: "sng-toast", inputs: { item: { classPropertyName: "item", publicName: "item", isSignal: true, isRequired: true, transformFunction: null } }, host: { listeners: { "mouseenter": "pause()", "mouseleave": "resume()", "focusin": "pause()", "focusout": "resume()" }, properties: { "class": "hostClasses()" } }, ngImport: i0, template: "@let toast = item();\n\n<!-- No aria-live here: SnackngLiveRegion announces instead. -->\n<div class=\"sng-accent\" aria-hidden=\"true\"></div>\n\n<div class=\"sng-icon\" aria-hidden=\"true\">\n @if (iconPath(); as path) {\n <svg viewBox=\"0 0 24 24\" focusable=\"false\">\n <path [attr.d]=\"path\" fill=\"currentColor\" />\n </svg>\n } @else {\n <span class=\"sng-icon-custom\" [innerHTML]=\"iconSvg()\"></span>\n }\n</div>\n\n<div class=\"sng-body\">\n @if (toast.title) {\n <p class=\"sng-title\">{{ toast.title }}</p>\n }\n <p class=\"sng-message\">{{ toast.message }}</p>\n</div>\n\n@if (toast.action; as action) {\n <button type=\"button\" class=\"sng-action\" (click)=\"onAction()\">{{ action.label }}</button>\n}\n\n@if (toast.dismissible) {\n <button type=\"button\" class=\"sng-close\" (click)=\"onDismiss()\" aria-label=\"Close notification\">\n <svg viewBox=\"0 0 24 24\" aria-hidden=\"true\" focusable=\"false\">\n <path\n d=\"M19 6.41 17.59 5 12 10.59 6.41 5 5 6.41 10.59 12 5 17.59 6.41 19 12 13.41 17.59 19 19 17.59 13.41 12z\"\n fill=\"currentColor\" />\n </svg>\n </button>\n}\n", styles: [":host{--sng-bg: var(--snackng-info-bg, linear-gradient(135deg, rgba(71, 85, 105, .9), rgba(40, 51, 69, .84)));--sng-ink: var(--snackng-info-ink, #ffffff);--sng-solid: var(--snackng-info-solid, #47556a);--sng-shift: var(--snackng-shift, 28px);position:relative;display:flex;align-items:center;gap:var(--snackng-gap, 14px);box-sizing:border-box;min-width:var(--snackng-min-width, 340px);max-width:var(--snackng-max-width, 460px);padding:var(--snackng-padding, 14px 18px 14px 22px);overflow:hidden;pointer-events:auto;border-radius:var(--snackng-radius, 14px);border:var(--snackng-border, 1px solid rgba(255, 255, 255, .22));background-image:var(--sng-bg);backdrop-filter:blur(var(--snackng-blur, 16px)) saturate(var(--snackng-saturate, 180%));-webkit-backdrop-filter:blur(var(--snackng-blur, 16px)) saturate(var(--snackng-saturate, 180%));box-shadow:var( --snackng-shadow, 0 12px 32px -10px rgba(17, 52, 111, .55), inset 0 1px 0 rgba(255, 255, 255, .28) );color:var(--sng-ink);font-family:var(--snackng-font, \"Manrope\", system-ui, sans-serif);line-height:var(--snackng-line-height, 1.45)}:host(.sng--success){--sng-bg: var(--snackng-success-bg, linear-gradient(135deg, rgba(33, 80, 159, .9), rgba(17, 52, 111, .84)));--sng-ink: var(--snackng-success-ink, #ffffff);--sng-solid: var(--snackng-success-solid, #21509f)}:host(.sng--danger){--sng-bg: var(--snackng-danger-bg, linear-gradient(135deg, rgba(192, 57, 43, .9), rgba(140, 35, 29, .84)));--sng-ink: var(--snackng-danger-ink, #ffffff);--sng-solid: var(--snackng-danger-solid, #c0392b)}:host(.sng--warning){--sng-bg: var(--snackng-warning-bg, linear-gradient(135deg, rgba(252, 211, 77, .92), rgba(245, 182, 43, .9)));--sng-ink: var(--snackng-warning-ink, #4a3200);--sng-solid: var(--snackng-warning-solid, #fcd34d);text-shadow:none}@supports not ((backdrop-filter: blur(1px)) or (-webkit-backdrop-filter: blur(1px))){:host{background-color:var(--sng-solid)}}@media(max-width:480px){:host{min-width:0;max-width:100%;width:100%}}.sng-accent{position:absolute;inset:0 auto 0 0;width:var(--snackng-accent-width, 4px);background:color-mix(in srgb,var(--sng-ink) 55%,transparent)}.sng-icon{flex:0 0 auto;display:flex;align-items:center;justify-content:center;padding:11px;border-radius:12px;background:color-mix(in srgb,var(--sng-ink) 16%,transparent);color:var(--sng-ink)}.sng-icon svg{display:block;width:24px;height:24px;fill:currentColor}.sng-icon-custom{display:flex}.sng-icon-custom ::ng-deep svg{display:block;width:24px;height:24px;fill:currentColor}.sng-body{display:flex;flex-direction:column;gap:2px;min-width:0;flex:1 1 auto}.sng-title{margin:0;font-size:var(--snackng-title-size, 14px);font-weight:var(--snackng-title-weight, 700);line-height:1.3;letter-spacing:.1px;color:var(--sng-ink)}.sng-message{margin:0;font-size:var(--snackng-message-size, 13px);font-weight:var(--snackng-message-weight, 400);color:color-mix(in srgb,var(--sng-ink) 85%,transparent)}.sng-action{flex:0 0 auto;padding:7px 12px;border:1px solid color-mix(in srgb,var(--sng-ink) 32%,transparent);border-radius:8px;background:color-mix(in srgb,var(--sng-ink) 12%,transparent);color:var(--sng-ink);font:inherit;font-size:var(--snackng-action-size, 13px);font-weight:var(--snackng-action-weight, 600);cursor:pointer;transition:background .15s ease}.sng-action:hover{background:color-mix(in srgb,var(--sng-ink) 22%,transparent)}.sng-close{flex:0 0 auto;display:flex;padding:4px;border:0;border-radius:6px;background:none;color:color-mix(in srgb,var(--sng-ink) 70%,transparent);cursor:pointer}.sng-close:hover{background:color-mix(in srgb,var(--sng-ink) 14%,transparent);color:var(--sng-ink)}.sng-close svg{display:block;width:18px;height:18px}.sng-action:focus-visible,.sng-close:focus-visible{outline:2px solid var(--sng-ink);outline-offset:2px}@keyframes sng-in-right{0%{opacity:0;transform:translate(var(--sng-shift))}to{opacity:1;transform:translate(0)}}@keyframes sng-out-right{0%{opacity:1;transform:translate(0)}to{opacity:0;transform:translate(var(--sng-shift))}}@keyframes sng-in-left{0%{opacity:0;transform:translate(calc(-1 * var(--sng-shift)))}to{opacity:1;transform:translate(0)}}@keyframes sng-out-left{0%{opacity:1;transform:translate(0)}to{opacity:0;transform:translate(calc(-1 * var(--sng-shift)))}}@keyframes sng-in-top{0%{opacity:0;transform:translateY(calc(-1 * var(--sng-shift)))}to{opacity:1;transform:translateY(0)}}@keyframes sng-out-top{0%{opacity:1;transform:translateY(0)}to{opacity:0;transform:translateY(calc(-1 * var(--sng-shift)))}}@keyframes sng-in-bottom{0%{opacity:0;transform:translateY(var(--sng-shift))}to{opacity:1;transform:translateY(0)}}@keyframes sng-out-bottom{0%{opacity:1;transform:translateY(0)}to{opacity:0;transform:translateY(var(--sng-shift))}}:host(.sng-entering){animation-duration:var(--snackng-enter-duration, .4s);animation-timing-function:cubic-bezier(0,0,.2,1);animation-fill-mode:both}:host(.sng-leaving){animation-duration:var(--snackng-exit-duration, .2s);animation-timing-function:cubic-bezier(.4,0,1,1);animation-fill-mode:both}:host(.sng-at--top-end.sng-entering),:host(.sng-at--bottom-end.sng-entering){animation-name:sng-in-right}:host(.sng-at--top-end.sng-leaving),:host(.sng-at--bottom-end.sng-leaving){animation-name:sng-out-right}:host(.sng-at--top-start.sng-entering),:host(.sng-at--bottom-start.sng-entering){animation-name:sng-in-left}:host(.sng-at--top-start.sng-leaving),:host(.sng-at--bottom-start.sng-leaving){animation-name:sng-out-left}:host(.sng-at--top-center.sng-entering){animation-name:sng-in-top}:host(.sng-at--top-center.sng-leaving){animation-name:sng-out-top}:host(.sng-at--bottom-center.sng-entering){animation-name:sng-in-bottom}:host(.sng-at--bottom-center.sng-leaving){animation-name:sng-out-bottom}@media(prefers-reduced-motion:reduce){:host{--sng-shift: 0px;backdrop-filter:blur(var(--snackng-blur-reduced, 10px));-webkit-backdrop-filter:blur(var(--snackng-blur-reduced, 10px))}}\n"], changeDetection: i0.ChangeDetectionStrategy.OnPush });
|
|
388
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.18", type: SnackngToast, isStandalone: true, selector: "sng-toast", inputs: { item: { classPropertyName: "item", publicName: "item", isSignal: true, isRequired: true, transformFunction: null } }, host: { listeners: { "mouseenter": "pause()", "mouseleave": "resume()", "focusin": "pause()", "focusout": "resume()" }, properties: { "class": "hostClasses()" } }, ngImport: i0, template: "@let toast = item();\n\n<!-- No aria-live here: SnackngLiveRegion announces instead. -->\n@if (glareEnabled()) {\n <div class=\"sng-glare\" aria-hidden=\"true\"></div>\n}\n\n<div class=\"sng-icon\" aria-hidden=\"true\">\n @if (iconPath(); as path) {\n <svg viewBox=\"0 0 24 24\" focusable=\"false\">\n <path [attr.d]=\"path\" fill=\"currentColor\" />\n </svg>\n } @else {\n <span class=\"sng-icon-custom\" [innerHTML]=\"iconSvg()\"></span>\n }\n</div>\n\n<div class=\"sng-body\">\n <div class=\"sng-header\">\n @if (toast.title) {\n <p class=\"sng-title\">{{ toast.title }}</p>\n }\n\n @if (closeIconPath(); as closePath) {\n <div class=\"sng-close-icon\" (click)=\"onDismiss()\">\n <svg viewBox=\"0 0 24 24\" focusable=\"false\">\n <path [attr.d]=\"closePath\" fill=\"currentColor\" />\n </svg>\n </div>\n }\n </div>\n\n <p class=\"sng-message\">{{ toast.message }}</p>\n</div>\n\n@if (toast.action; as action) {\n <button type=\"button\" class=\"sng-action\" (click)=\"onAction()\">{{ action.label }}</button>\n}\n\n@if (toast.dismissible) {\n <button type=\"button\" class=\"sng-close\" (click)=\"onDismiss()\" aria-label=\"Close notification\">\n <svg viewBox=\"0 0 24 24\" aria-hidden=\"true\" focusable=\"false\">\n <path\n d=\"M19 6.41 17.59 5 12 10.59 6.41 5 5 6.41 10.59 12 5 17.59 6.41 19 12 13.41 17.59 19 19 17.59 13.41 12z\"\n fill=\"currentColor\"\n />\n </svg>\n </button>\n}\n", styles: [":host{--sng-c1: 71 85 105;--sng-c2: 40 51 69;--sng-tint: var(--snackng-tint, var(--sng-p-tint, .86));--sng-blur: var(--snackng-blur, var(--sng-p-blur, 20px));--sng-spec: var(--snackng-spec-strength, var(--sng-p-spec, .15));--sng-spec-size: var(--snackng-spec-size, var(--sng-p-spec-size, 30%));--sng-drift-dur: var(--snackng-drift-duration, var(--sng-p-drift-dur, 4s));--sng-bg: var( --snackng-info-bg, linear-gradient( 135deg, rgb(var(--sng-c1) / var(--sng-tint)), rgb(var(--sng-c2) / calc(var(--sng-tint) - .06)) ) );--sng-ink: var(--snackng-info-ink, #ffffff);--sng-solid: var(--snackng-info-solid, #47556a);--sng-shift: var(--snackng-shift, 28px);position:relative;display:flex;align-items:center;gap:var(--snackng-gap, 14px);box-sizing:border-box;min-width:var(--snackng-min-width, 340px);max-width:var(--snackng-max-width, 460px);padding:var(--snackng-padding, 14px 18px);overflow:hidden;isolation:isolate;pointer-events:auto;border-radius:var(--snackng-radius, 14px);border:var(--snackng-border, 1px solid rgba(255, 255, 255, .22));background-image:var(--sng-bg);backdrop-filter:blur(var(--sng-blur)) saturate(var(--snackng-saturate, 180%)) brightness(var(--snackng-brightness, 1.06));-webkit-backdrop-filter:blur(var(--sng-blur)) saturate(var(--snackng-saturate, 180%)) brightness(var(--snackng-brightness, 1.06));box-shadow:var( --snackng-shadow, 0 1px 1px rgba(0, 0, 0, .05), 0 14px 34px -12px rgba(10, 25, 60, .6), inset 0 1px 0 rgba(255, 255, 255, .32), inset 0 -1px 1px rgba(0, 0, 0, .18) );color:var(--sng-ink);font-family:var(--snackng-font, \"Manrope\", system-ui, sans-serif);line-height:var(--snackng-line-height, 1.45)}:host(.sng--success){--sng-c1: 33 80 159;--sng-c2: 17 52 111;--sng-bg: var( --snackng-success-bg, linear-gradient( 135deg, rgb(var(--sng-c1) / var(--sng-tint)), rgb(var(--sng-c2) / calc(var(--sng-tint) - .06)) ) );--sng-ink: var(--snackng-success-ink, #ffffff);--sng-solid: var(--snackng-success-solid, #21509f)}:host(.sng--danger){--sng-c1: 192 57 43;--sng-c2: 140 35 29;--sng-bg: var( --snackng-danger-bg, linear-gradient( 135deg, rgb(var(--sng-c1) / var(--sng-tint)), rgb(var(--sng-c2) / calc(var(--sng-tint) - .06)) ) );--sng-ink: var(--snackng-danger-ink, #ffffff);--sng-solid: var(--snackng-danger-solid, #c0392b)}:host(.sng--warning){--sng-c1: 180 83 9;--sng-c2: 146 64 14;--sng-bg: var( --snackng-warning-bg, linear-gradient( 135deg, rgb(var(--sng-c1) / var(--sng-tint)), rgb(var(--sng-c2) / calc(var(--sng-tint) - .06)) ) );--sng-ink: var(--snackng-warning-ink, #ffffff);--sng-solid: var(--snackng-warning-solid, #b45309)}:host(.sng-style--solid){--sng-p-tint: .96;--sng-p-spec: .06;--sng-p-spec-size: 28%}:host(.sng-style--translucent){--sng-p-tint: .74;--sng-p-spec: .12;--sng-p-spec-size: 40%}:host(.sng-style--transparent){--sng-p-tint: .3;--sng-p-spec: .1;--sng-p-spec-size: 30%}:host(.sng-style--frosted){--sng-p-tint: .92;--sng-p-blur: 30px;--sng-p-spec: .1}:host(.sng-style--flat){--sng-p-tint: 1;--sng-p-blur: 0px;--sng-p-spec: 0}@supports not ((backdrop-filter: blur(1px)) or (-webkit-backdrop-filter: blur(1px))){:host{background-color:var(--sng-solid)}}@media(max-width:480px){:host{min-width:0;max-width:100%;width:100%}}:host:before{content:\"\";position:absolute;inset:0;z-index:-1;pointer-events:none;background:linear-gradient(135deg,rgb(255 255 255 / var(--snackng-gloss, .22)) 0%,rgb(255 255 255 / calc(var(--snackng-gloss, .22) * .28)) 26%,transparent 52%)}:host:after{content:\"\";position:absolute;inset:-35%;z-index:-1;pointer-events:none;opacity:0;filter:blur(12px);background:radial-gradient(circle at 34% 24%,rgb(255 255 255 / var(--sng-spec)) 0%,transparent var(--sng-spec-size))}:host(.sng-fx--drift):after{opacity:1;animation-name:sng-sheen-drift;animation-duration:var(--sng-drift-dur);animation-timing-function:ease-in-out;animation-iteration-count:infinite;animation-direction:alternate}@keyframes sng-sheen-drift{0%{transform:translate(-6%,-5%) scale(1)}to{transform:translate(10%,8%) scale(1.12)}}.sng-glare{position:absolute;inset:0;z-index:-1;pointer-events:none;opacity:0;filter:blur(12px);transition:opacity .25s ease;background:radial-gradient(circle at var(--sng-glare-x, 50%) var(--sng-glare-y, 50%),rgb(255 255 255 / calc(var(--sng-spec) + .06)) 0%,transparent var(--sng-spec-size))}:host(.sng-fx--glare) .sng-glare{opacity:var(--sng-glare-op, 0)}.sng-close-icon{flex:0 0 auto;display:flex;align-items:center;justify-content:center;padding:4px;border-radius:8px;border:0;background:transparent;color:color-mix(in srgb,var(--sng-ink) 70%,transparent);cursor:pointer;transition:background-color .15s ease,color .15s ease,transform .1s ease}.sng-close-icon:hover{background:color-mix(in srgb,var(--sng-ink) 12%,transparent);color:var(--sng-ink)}.sng-close-icon:active{background:color-mix(in srgb,var(--sng-ink) 22%,transparent);transform:scale(.92)}.sng-close-icon svg{display:block;width:18px;height:18px;fill:currentColor}.sng-icon{flex:0 0 auto;display:flex;align-items:center;justify-content:center;padding:11px;border-radius:12px;background:color-mix(in srgb,var(--sng-ink) 16%,transparent);color:var(--sng-ink)}.sng-icon svg{display:block;width:24px;height:24px;fill:currentColor}.sng-icon-custom{display:flex}.sng-icon-custom ::ng-deep svg{display:block;width:24px;height:24px;fill:currentColor}.sng-body{display:flex;flex-direction:column;gap:2px;min-width:0;flex:1 1 auto}.sng-header{display:flex;flex-direction:row;justify-content:space-between;align-items:center}.sng-title{margin:0;font-size:var(--snackng-title-size, 14px);font-weight:var(--snackng-title-weight, 700);line-height:1.3;letter-spacing:.1px;color:var(--sng-ink)}.sng-message{margin:0;font-size:var(--snackng-message-size, 13px);font-weight:var(--snackng-message-weight, 400);color:color-mix(in srgb,var(--sng-ink) 85%,transparent)}.sng-action{flex:0 0 auto;padding:7px 12px;border:1px solid color-mix(in srgb,var(--sng-ink) 32%,transparent);border-radius:8px;background:color-mix(in srgb,var(--sng-ink) 12%,transparent);color:var(--sng-ink);font:inherit;font-size:var(--snackng-action-size, 13px);font-weight:var(--snackng-action-weight, 600);cursor:pointer;transition:background .15s ease}.sng-action:hover{background:color-mix(in srgb,var(--sng-ink) 22%,transparent)}.sng-close{flex:0 0 auto;display:flex;padding:4px;border:0;border-radius:6px;background:none;color:color-mix(in srgb,var(--sng-ink) 70%,transparent);cursor:pointer}.sng-close:hover{background:color-mix(in srgb,var(--sng-ink) 14%,transparent);color:var(--sng-ink)}.sng-close svg{display:block;width:18px;height:18px}.sng-action:focus-visible,.sng-close:focus-visible{outline:2px solid var(--sng-ink);outline-offset:2px}@keyframes sng-in-right{0%{opacity:0;transform:translate(var(--sng-shift))}to{opacity:1;transform:translate(0)}}@keyframes sng-out-right{0%{opacity:1;transform:translate(0)}to{opacity:0;transform:translate(var(--sng-shift))}}@keyframes sng-in-left{0%{opacity:0;transform:translate(calc(-1 * var(--sng-shift)))}to{opacity:1;transform:translate(0)}}@keyframes sng-out-left{0%{opacity:1;transform:translate(0)}to{opacity:0;transform:translate(calc(-1 * var(--sng-shift)))}}@keyframes sng-in-top{0%{opacity:0;transform:translateY(calc(-1 * var(--sng-shift)))}to{opacity:1;transform:translateY(0)}}@keyframes sng-out-top{0%{opacity:1;transform:translateY(0)}to{opacity:0;transform:translateY(calc(-1 * var(--sng-shift)))}}@keyframes sng-in-bottom{0%{opacity:0;transform:translateY(var(--sng-shift))}to{opacity:1;transform:translateY(0)}}@keyframes sng-out-bottom{0%{opacity:1;transform:translateY(0)}to{opacity:0;transform:translateY(var(--sng-shift))}}:host(.sng-entering){animation-duration:var(--snackng-enter-duration, .4s);animation-timing-function:cubic-bezier(0,0,.2,1);animation-fill-mode:both}:host(.sng-leaving){animation-duration:var(--snackng-exit-duration, .2s);animation-timing-function:cubic-bezier(.4,0,1,1);animation-fill-mode:both}:host(.sng-at--top-end.sng-entering),:host(.sng-at--bottom-end.sng-entering){animation-name:sng-in-right}:host(.sng-at--top-end.sng-leaving),:host(.sng-at--bottom-end.sng-leaving){animation-name:sng-out-right}:host(.sng-at--top-start.sng-entering),:host(.sng-at--bottom-start.sng-entering){animation-name:sng-in-left}:host(.sng-at--top-start.sng-leaving),:host(.sng-at--bottom-start.sng-leaving){animation-name:sng-out-left}:host(.sng-at--top-center.sng-entering){animation-name:sng-in-top}:host(.sng-at--top-center.sng-leaving){animation-name:sng-out-top}:host(.sng-at--bottom-center.sng-entering){animation-name:sng-in-bottom}:host(.sng-at--bottom-center.sng-leaving){animation-name:sng-out-bottom}@media(prefers-reduced-motion:reduce){:host{--sng-shift: 0px;--sng-blur: var(--snackng-blur-reduced, 10px)}:host(.sng-fx--drift):after{animation:none}}\n"], changeDetection: i0.ChangeDetectionStrategy.OnPush });
|
|
334
389
|
}
|
|
335
390
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.18", ngImport: i0, type: SnackngToast, decorators: [{
|
|
336
391
|
type: Component,
|
|
@@ -340,7 +395,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.18", ngImpo
|
|
|
340
395
|
'(mouseleave)': 'resume()',
|
|
341
396
|
'(focusin)': 'pause()',
|
|
342
397
|
'(focusout)': 'resume()',
|
|
343
|
-
}, template: "@let toast = item();\n\n<!-- No aria-live here: SnackngLiveRegion announces instead. -->\n<div class=\"sng-accent\" aria-hidden=\"true\"></div>\n\n<div class=\"sng-icon\" aria-hidden=\"true\">\n @if (iconPath(); as path) {\n <svg viewBox=\"0 0 24 24\" focusable=\"false\">\n <path [attr.d]=\"path\" fill=\"currentColor\" />\n </svg>\n } @else {\n <span class=\"sng-icon-custom\" [innerHTML]=\"iconSvg()\"></span>\n }\n</div>\n\n<div class=\"sng-body\">\n @if (toast.title) {\n <p class=\"sng-title\">{{ toast.title }}</p>\n }\n <p class=\"sng-message\">{{ toast.message }}</p>\n</div>\n\n@if (toast.action; as action) {\n <button type=\"button\" class=\"sng-action\" (click)=\"onAction()\">{{ action.label }}</button>\n}\n\n@if (toast.dismissible) {\n <button type=\"button\" class=\"sng-close\" (click)=\"onDismiss()\" aria-label=\"Close notification\">\n <svg viewBox=\"0 0 24 24\" aria-hidden=\"true\" focusable=\"false\">\n <path\n d=\"M19 6.41 17.59 5 12 10.59 6.41 5 5 6.41 10.59 12 5 17.59 6.41 19 12 13.41 17.59 19 19 17.59 13.41 12z\"\n fill=\"currentColor\" />\n </svg>\n </button>\n}\n", styles: [":host{--sng-bg: var(--snackng-info-bg, linear-gradient(135deg, rgba(71, 85, 105, .9), rgba(40, 51, 69, .84)));--sng-ink: var(--snackng-info-ink, #ffffff);--sng-solid: var(--snackng-info-solid, #47556a);--sng-shift: var(--snackng-shift, 28px);position:relative;display:flex;align-items:center;gap:var(--snackng-gap, 14px);box-sizing:border-box;min-width:var(--snackng-min-width, 340px);max-width:var(--snackng-max-width, 460px);padding:var(--snackng-padding, 14px 18px 14px 22px);overflow:hidden;pointer-events:auto;border-radius:var(--snackng-radius, 14px);border:var(--snackng-border, 1px solid rgba(255, 255, 255, .22));background-image:var(--sng-bg);backdrop-filter:blur(var(--snackng-blur, 16px)) saturate(var(--snackng-saturate, 180%));-webkit-backdrop-filter:blur(var(--snackng-blur, 16px)) saturate(var(--snackng-saturate, 180%));box-shadow:var( --snackng-shadow, 0 12px 32px -10px rgba(17, 52, 111, .55), inset 0 1px 0 rgba(255, 255, 255, .28) );color:var(--sng-ink);font-family:var(--snackng-font, \"Manrope\", system-ui, sans-serif);line-height:var(--snackng-line-height, 1.45)}:host(.sng--success){--sng-bg: var(--snackng-success-bg, linear-gradient(135deg, rgba(33, 80, 159, .9), rgba(17, 52, 111, .84)));--sng-ink: var(--snackng-success-ink, #ffffff);--sng-solid: var(--snackng-success-solid, #21509f)}:host(.sng--danger){--sng-bg: var(--snackng-danger-bg, linear-gradient(135deg, rgba(192, 57, 43, .9), rgba(140, 35, 29, .84)));--sng-ink: var(--snackng-danger-ink, #ffffff);--sng-solid: var(--snackng-danger-solid, #c0392b)}:host(.sng--warning){--sng-bg: var(--snackng-warning-bg, linear-gradient(135deg, rgba(252, 211, 77, .92), rgba(245, 182, 43, .9)));--sng-ink: var(--snackng-warning-ink, #4a3200);--sng-solid: var(--snackng-warning-solid, #fcd34d);text-shadow:none}@supports not ((backdrop-filter: blur(1px)) or (-webkit-backdrop-filter: blur(1px))){:host{background-color:var(--sng-solid)}}@media(max-width:480px){:host{min-width:0;max-width:100%;width:100%}}.sng-accent{position:absolute;inset:0 auto 0 0;width:var(--snackng-accent-width, 4px);background:color-mix(in srgb,var(--sng-ink) 55%,transparent)}.sng-icon{flex:0 0 auto;display:flex;align-items:center;justify-content:center;padding:11px;border-radius:12px;background:color-mix(in srgb,var(--sng-ink) 16%,transparent);color:var(--sng-ink)}.sng-icon svg{display:block;width:24px;height:24px;fill:currentColor}.sng-icon-custom{display:flex}.sng-icon-custom ::ng-deep svg{display:block;width:24px;height:24px;fill:currentColor}.sng-body{display:flex;flex-direction:column;gap:2px;min-width:0;flex:1 1 auto}.sng-title{margin:0;font-size:var(--snackng-title-size, 14px);font-weight:var(--snackng-title-weight, 700);line-height:1.3;letter-spacing:.1px;color:var(--sng-ink)}.sng-message{margin:0;font-size:var(--snackng-message-size, 13px);font-weight:var(--snackng-message-weight, 400);color:color-mix(in srgb,var(--sng-ink) 85%,transparent)}.sng-action{flex:0 0 auto;padding:7px 12px;border:1px solid color-mix(in srgb,var(--sng-ink) 32%,transparent);border-radius:8px;background:color-mix(in srgb,var(--sng-ink) 12%,transparent);color:var(--sng-ink);font:inherit;font-size:var(--snackng-action-size, 13px);font-weight:var(--snackng-action-weight, 600);cursor:pointer;transition:background .15s ease}.sng-action:hover{background:color-mix(in srgb,var(--sng-ink) 22%,transparent)}.sng-close{flex:0 0 auto;display:flex;padding:4px;border:0;border-radius:6px;background:none;color:color-mix(in srgb,var(--sng-ink) 70%,transparent);cursor:pointer}.sng-close:hover{background:color-mix(in srgb,var(--sng-ink) 14%,transparent);color:var(--sng-ink)}.sng-close svg{display:block;width:18px;height:18px}.sng-action:focus-visible,.sng-close:focus-visible{outline:2px solid var(--sng-ink);outline-offset:2px}@keyframes sng-in-right{0%{opacity:0;transform:translate(var(--sng-shift))}to{opacity:1;transform:translate(0)}}@keyframes sng-out-right{0%{opacity:1;transform:translate(0)}to{opacity:0;transform:translate(var(--sng-shift))}}@keyframes sng-in-left{0%{opacity:0;transform:translate(calc(-1 * var(--sng-shift)))}to{opacity:1;transform:translate(0)}}@keyframes sng-out-left{0%{opacity:1;transform:translate(0)}to{opacity:0;transform:translate(calc(-1 * var(--sng-shift)))}}@keyframes sng-in-top{0%{opacity:0;transform:translateY(calc(-1 * var(--sng-shift)))}to{opacity:1;transform:translateY(0)}}@keyframes sng-out-top{0%{opacity:1;transform:translateY(0)}to{opacity:0;transform:translateY(calc(-1 * var(--sng-shift)))}}@keyframes sng-in-bottom{0%{opacity:0;transform:translateY(var(--sng-shift))}to{opacity:1;transform:translateY(0)}}@keyframes sng-out-bottom{0%{opacity:1;transform:translateY(0)}to{opacity:0;transform:translateY(var(--sng-shift))}}:host(.sng-entering){animation-duration:var(--snackng-enter-duration, .4s);animation-timing-function:cubic-bezier(0,0,.2,1);animation-fill-mode:both}:host(.sng-leaving){animation-duration:var(--snackng-exit-duration, .2s);animation-timing-function:cubic-bezier(.4,0,1,1);animation-fill-mode:both}:host(.sng-at--top-end.sng-entering),:host(.sng-at--bottom-end.sng-entering){animation-name:sng-in-right}:host(.sng-at--top-end.sng-leaving),:host(.sng-at--bottom-end.sng-leaving){animation-name:sng-out-right}:host(.sng-at--top-start.sng-entering),:host(.sng-at--bottom-start.sng-entering){animation-name:sng-in-left}:host(.sng-at--top-start.sng-leaving),:host(.sng-at--bottom-start.sng-leaving){animation-name:sng-out-left}:host(.sng-at--top-center.sng-entering){animation-name:sng-in-top}:host(.sng-at--top-center.sng-leaving){animation-name:sng-out-top}:host(.sng-at--bottom-center.sng-entering){animation-name:sng-in-bottom}:host(.sng-at--bottom-center.sng-leaving){animation-name:sng-out-bottom}@media(prefers-reduced-motion:reduce){:host{--sng-shift: 0px;backdrop-filter:blur(var(--snackng-blur-reduced, 10px));-webkit-backdrop-filter:blur(var(--snackng-blur-reduced, 10px))}}\n"] }]
|
|
398
|
+
}, template: "@let toast = item();\n\n<!-- No aria-live here: SnackngLiveRegion announces instead. -->\n@if (glareEnabled()) {\n <div class=\"sng-glare\" aria-hidden=\"true\"></div>\n}\n\n<div class=\"sng-icon\" aria-hidden=\"true\">\n @if (iconPath(); as path) {\n <svg viewBox=\"0 0 24 24\" focusable=\"false\">\n <path [attr.d]=\"path\" fill=\"currentColor\" />\n </svg>\n } @else {\n <span class=\"sng-icon-custom\" [innerHTML]=\"iconSvg()\"></span>\n }\n</div>\n\n<div class=\"sng-body\">\n <div class=\"sng-header\">\n @if (toast.title) {\n <p class=\"sng-title\">{{ toast.title }}</p>\n }\n\n @if (closeIconPath(); as closePath) {\n <div class=\"sng-close-icon\" (click)=\"onDismiss()\">\n <svg viewBox=\"0 0 24 24\" focusable=\"false\">\n <path [attr.d]=\"closePath\" fill=\"currentColor\" />\n </svg>\n </div>\n }\n </div>\n\n <p class=\"sng-message\">{{ toast.message }}</p>\n</div>\n\n@if (toast.action; as action) {\n <button type=\"button\" class=\"sng-action\" (click)=\"onAction()\">{{ action.label }}</button>\n}\n\n@if (toast.dismissible) {\n <button type=\"button\" class=\"sng-close\" (click)=\"onDismiss()\" aria-label=\"Close notification\">\n <svg viewBox=\"0 0 24 24\" aria-hidden=\"true\" focusable=\"false\">\n <path\n d=\"M19 6.41 17.59 5 12 10.59 6.41 5 5 6.41 10.59 12 5 17.59 6.41 19 12 13.41 17.59 19 19 17.59 13.41 12z\"\n fill=\"currentColor\"\n />\n </svg>\n </button>\n}\n", styles: [":host{--sng-c1: 71 85 105;--sng-c2: 40 51 69;--sng-tint: var(--snackng-tint, var(--sng-p-tint, .86));--sng-blur: var(--snackng-blur, var(--sng-p-blur, 20px));--sng-spec: var(--snackng-spec-strength, var(--sng-p-spec, .15));--sng-spec-size: var(--snackng-spec-size, var(--sng-p-spec-size, 30%));--sng-drift-dur: var(--snackng-drift-duration, var(--sng-p-drift-dur, 4s));--sng-bg: var( --snackng-info-bg, linear-gradient( 135deg, rgb(var(--sng-c1) / var(--sng-tint)), rgb(var(--sng-c2) / calc(var(--sng-tint) - .06)) ) );--sng-ink: var(--snackng-info-ink, #ffffff);--sng-solid: var(--snackng-info-solid, #47556a);--sng-shift: var(--snackng-shift, 28px);position:relative;display:flex;align-items:center;gap:var(--snackng-gap, 14px);box-sizing:border-box;min-width:var(--snackng-min-width, 340px);max-width:var(--snackng-max-width, 460px);padding:var(--snackng-padding, 14px 18px);overflow:hidden;isolation:isolate;pointer-events:auto;border-radius:var(--snackng-radius, 14px);border:var(--snackng-border, 1px solid rgba(255, 255, 255, .22));background-image:var(--sng-bg);backdrop-filter:blur(var(--sng-blur)) saturate(var(--snackng-saturate, 180%)) brightness(var(--snackng-brightness, 1.06));-webkit-backdrop-filter:blur(var(--sng-blur)) saturate(var(--snackng-saturate, 180%)) brightness(var(--snackng-brightness, 1.06));box-shadow:var( --snackng-shadow, 0 1px 1px rgba(0, 0, 0, .05), 0 14px 34px -12px rgba(10, 25, 60, .6), inset 0 1px 0 rgba(255, 255, 255, .32), inset 0 -1px 1px rgba(0, 0, 0, .18) );color:var(--sng-ink);font-family:var(--snackng-font, \"Manrope\", system-ui, sans-serif);line-height:var(--snackng-line-height, 1.45)}:host(.sng--success){--sng-c1: 33 80 159;--sng-c2: 17 52 111;--sng-bg: var( --snackng-success-bg, linear-gradient( 135deg, rgb(var(--sng-c1) / var(--sng-tint)), rgb(var(--sng-c2) / calc(var(--sng-tint) - .06)) ) );--sng-ink: var(--snackng-success-ink, #ffffff);--sng-solid: var(--snackng-success-solid, #21509f)}:host(.sng--danger){--sng-c1: 192 57 43;--sng-c2: 140 35 29;--sng-bg: var( --snackng-danger-bg, linear-gradient( 135deg, rgb(var(--sng-c1) / var(--sng-tint)), rgb(var(--sng-c2) / calc(var(--sng-tint) - .06)) ) );--sng-ink: var(--snackng-danger-ink, #ffffff);--sng-solid: var(--snackng-danger-solid, #c0392b)}:host(.sng--warning){--sng-c1: 180 83 9;--sng-c2: 146 64 14;--sng-bg: var( --snackng-warning-bg, linear-gradient( 135deg, rgb(var(--sng-c1) / var(--sng-tint)), rgb(var(--sng-c2) / calc(var(--sng-tint) - .06)) ) );--sng-ink: var(--snackng-warning-ink, #ffffff);--sng-solid: var(--snackng-warning-solid, #b45309)}:host(.sng-style--solid){--sng-p-tint: .96;--sng-p-spec: .06;--sng-p-spec-size: 28%}:host(.sng-style--translucent){--sng-p-tint: .74;--sng-p-spec: .12;--sng-p-spec-size: 40%}:host(.sng-style--transparent){--sng-p-tint: .3;--sng-p-spec: .1;--sng-p-spec-size: 30%}:host(.sng-style--frosted){--sng-p-tint: .92;--sng-p-blur: 30px;--sng-p-spec: .1}:host(.sng-style--flat){--sng-p-tint: 1;--sng-p-blur: 0px;--sng-p-spec: 0}@supports not ((backdrop-filter: blur(1px)) or (-webkit-backdrop-filter: blur(1px))){:host{background-color:var(--sng-solid)}}@media(max-width:480px){:host{min-width:0;max-width:100%;width:100%}}:host:before{content:\"\";position:absolute;inset:0;z-index:-1;pointer-events:none;background:linear-gradient(135deg,rgb(255 255 255 / var(--snackng-gloss, .22)) 0%,rgb(255 255 255 / calc(var(--snackng-gloss, .22) * .28)) 26%,transparent 52%)}:host:after{content:\"\";position:absolute;inset:-35%;z-index:-1;pointer-events:none;opacity:0;filter:blur(12px);background:radial-gradient(circle at 34% 24%,rgb(255 255 255 / var(--sng-spec)) 0%,transparent var(--sng-spec-size))}:host(.sng-fx--drift):after{opacity:1;animation-name:sng-sheen-drift;animation-duration:var(--sng-drift-dur);animation-timing-function:ease-in-out;animation-iteration-count:infinite;animation-direction:alternate}@keyframes sng-sheen-drift{0%{transform:translate(-6%,-5%) scale(1)}to{transform:translate(10%,8%) scale(1.12)}}.sng-glare{position:absolute;inset:0;z-index:-1;pointer-events:none;opacity:0;filter:blur(12px);transition:opacity .25s ease;background:radial-gradient(circle at var(--sng-glare-x, 50%) var(--sng-glare-y, 50%),rgb(255 255 255 / calc(var(--sng-spec) + .06)) 0%,transparent var(--sng-spec-size))}:host(.sng-fx--glare) .sng-glare{opacity:var(--sng-glare-op, 0)}.sng-close-icon{flex:0 0 auto;display:flex;align-items:center;justify-content:center;padding:4px;border-radius:8px;border:0;background:transparent;color:color-mix(in srgb,var(--sng-ink) 70%,transparent);cursor:pointer;transition:background-color .15s ease,color .15s ease,transform .1s ease}.sng-close-icon:hover{background:color-mix(in srgb,var(--sng-ink) 12%,transparent);color:var(--sng-ink)}.sng-close-icon:active{background:color-mix(in srgb,var(--sng-ink) 22%,transparent);transform:scale(.92)}.sng-close-icon svg{display:block;width:18px;height:18px;fill:currentColor}.sng-icon{flex:0 0 auto;display:flex;align-items:center;justify-content:center;padding:11px;border-radius:12px;background:color-mix(in srgb,var(--sng-ink) 16%,transparent);color:var(--sng-ink)}.sng-icon svg{display:block;width:24px;height:24px;fill:currentColor}.sng-icon-custom{display:flex}.sng-icon-custom ::ng-deep svg{display:block;width:24px;height:24px;fill:currentColor}.sng-body{display:flex;flex-direction:column;gap:2px;min-width:0;flex:1 1 auto}.sng-header{display:flex;flex-direction:row;justify-content:space-between;align-items:center}.sng-title{margin:0;font-size:var(--snackng-title-size, 14px);font-weight:var(--snackng-title-weight, 700);line-height:1.3;letter-spacing:.1px;color:var(--sng-ink)}.sng-message{margin:0;font-size:var(--snackng-message-size, 13px);font-weight:var(--snackng-message-weight, 400);color:color-mix(in srgb,var(--sng-ink) 85%,transparent)}.sng-action{flex:0 0 auto;padding:7px 12px;border:1px solid color-mix(in srgb,var(--sng-ink) 32%,transparent);border-radius:8px;background:color-mix(in srgb,var(--sng-ink) 12%,transparent);color:var(--sng-ink);font:inherit;font-size:var(--snackng-action-size, 13px);font-weight:var(--snackng-action-weight, 600);cursor:pointer;transition:background .15s ease}.sng-action:hover{background:color-mix(in srgb,var(--sng-ink) 22%,transparent)}.sng-close{flex:0 0 auto;display:flex;padding:4px;border:0;border-radius:6px;background:none;color:color-mix(in srgb,var(--sng-ink) 70%,transparent);cursor:pointer}.sng-close:hover{background:color-mix(in srgb,var(--sng-ink) 14%,transparent);color:var(--sng-ink)}.sng-close svg{display:block;width:18px;height:18px}.sng-action:focus-visible,.sng-close:focus-visible{outline:2px solid var(--sng-ink);outline-offset:2px}@keyframes sng-in-right{0%{opacity:0;transform:translate(var(--sng-shift))}to{opacity:1;transform:translate(0)}}@keyframes sng-out-right{0%{opacity:1;transform:translate(0)}to{opacity:0;transform:translate(var(--sng-shift))}}@keyframes sng-in-left{0%{opacity:0;transform:translate(calc(-1 * var(--sng-shift)))}to{opacity:1;transform:translate(0)}}@keyframes sng-out-left{0%{opacity:1;transform:translate(0)}to{opacity:0;transform:translate(calc(-1 * var(--sng-shift)))}}@keyframes sng-in-top{0%{opacity:0;transform:translateY(calc(-1 * var(--sng-shift)))}to{opacity:1;transform:translateY(0)}}@keyframes sng-out-top{0%{opacity:1;transform:translateY(0)}to{opacity:0;transform:translateY(calc(-1 * var(--sng-shift)))}}@keyframes sng-in-bottom{0%{opacity:0;transform:translateY(var(--sng-shift))}to{opacity:1;transform:translateY(0)}}@keyframes sng-out-bottom{0%{opacity:1;transform:translateY(0)}to{opacity:0;transform:translateY(var(--sng-shift))}}:host(.sng-entering){animation-duration:var(--snackng-enter-duration, .4s);animation-timing-function:cubic-bezier(0,0,.2,1);animation-fill-mode:both}:host(.sng-leaving){animation-duration:var(--snackng-exit-duration, .2s);animation-timing-function:cubic-bezier(.4,0,1,1);animation-fill-mode:both}:host(.sng-at--top-end.sng-entering),:host(.sng-at--bottom-end.sng-entering){animation-name:sng-in-right}:host(.sng-at--top-end.sng-leaving),:host(.sng-at--bottom-end.sng-leaving){animation-name:sng-out-right}:host(.sng-at--top-start.sng-entering),:host(.sng-at--bottom-start.sng-entering){animation-name:sng-in-left}:host(.sng-at--top-start.sng-leaving),:host(.sng-at--bottom-start.sng-leaving){animation-name:sng-out-left}:host(.sng-at--top-center.sng-entering){animation-name:sng-in-top}:host(.sng-at--top-center.sng-leaving){animation-name:sng-out-top}:host(.sng-at--bottom-center.sng-entering){animation-name:sng-in-bottom}:host(.sng-at--bottom-center.sng-leaving){animation-name:sng-out-bottom}@media(prefers-reduced-motion:reduce){:host{--sng-shift: 0px;--sng-blur: var(--snackng-blur-reduced, 10px)}:host(.sng-fx--drift):after{animation:none}}\n"] }]
|
|
344
399
|
}], ctorParameters: () => [], propDecorators: { item: [{ type: i0.Input, args: [{ isSignal: true, alias: "item", required: true }] }] } });
|
|
345
400
|
|
|
346
401
|
const SHIFT_DURATION = 260;
|
|
@@ -423,17 +478,22 @@ class SnackngService {
|
|
|
423
478
|
* Useful for a "3 more" affordance.
|
|
424
479
|
*/
|
|
425
480
|
pending = this.store.pending;
|
|
426
|
-
success(
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
481
|
+
/** Callable shortcut with per-preset sugar, e.g. `success.solid('Saved')`. */
|
|
482
|
+
success = this.variant('success');
|
|
483
|
+
warning = this.variant('warning');
|
|
484
|
+
danger = this.variant('danger');
|
|
485
|
+
info = this.variant('info');
|
|
486
|
+
/** Builds a callable variant carrying a method per built-in preset. */
|
|
487
|
+
variant(type) {
|
|
488
|
+
const withStyle = (style) => (message, options) => this.show(type, message, style ? { ...options, style } : options);
|
|
489
|
+
return Object.assign(withStyle(), {
|
|
490
|
+
glass: withStyle('glass'),
|
|
491
|
+
solid: withStyle('solid'),
|
|
492
|
+
translucent: withStyle('translucent'),
|
|
493
|
+
transparent: withStyle('transparent'),
|
|
494
|
+
frosted: withStyle('frosted'),
|
|
495
|
+
flat: withStyle('flat'),
|
|
496
|
+
});
|
|
437
497
|
}
|
|
438
498
|
show(type, message, options = {}) {
|
|
439
499
|
// Client-only: no-op rather than throw on `document` during SSR.
|
|
@@ -450,6 +510,8 @@ class SnackngService {
|
|
|
450
510
|
position: options.position ?? this.config.position,
|
|
451
511
|
dismissible: options.dismissible ?? this.config.dismissible,
|
|
452
512
|
action: options.action,
|
|
513
|
+
style: options.style ?? this.config.style,
|
|
514
|
+
effect: options.effect ?? this.config.effect,
|
|
453
515
|
panelClass: normalizeClasses(options.panelClass),
|
|
454
516
|
politeness,
|
|
455
517
|
});
|
package/fesm2022/snackng.mjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"snackng.mjs","sources":["../../../projects/snackng/src/lib/snackng.config.ts","../../../projects/snackng/src/lib/live-region.ts","../../../projects/snackng/src/lib/icons.ts","../../../projects/snackng/src/lib/snackng.store.ts","../../../projects/snackng/src/lib/snackng-toast/snackng-toast.ts","../../../projects/snackng/src/lib/snackng-toast/snackng-toast.html","../../../projects/snackng/src/lib/snackng-host/snackng-host.ts","../../../projects/snackng/src/lib/snackng-host/snackng-host.html","../../../projects/snackng/src/lib/snackng.service.ts","../../../projects/snackng/src/public-api.ts","../../../projects/snackng/src/snackng.ts"],"sourcesContent":["import { InjectionToken, EnvironmentProviders, makeEnvironmentProviders } from '@angular/core';\nimport { SnackngOverflow, SnackngPosition, SnackngTypeDef } from './snackng.model';\n\nexport interface SnackngConfig {\n duration: number;\n position: SnackngPosition;\n /** Maximum toasts on screen at once. Extras are handled per `overflow`. */\n max: number;\n /**\n * What happens when a toast arrives while `max` are already visible.\n * `'queue'` waits for a free slot — nothing is ever lost, but a flood becomes\n * a long parade. `'dismiss-oldest'` evicts the oldest to make room, so the\n * newest always wins; useful when an interceptor can emit toasts in bursts.\n */\n overflow: SnackngOverflow;\n /**\n * Milliseconds between releasing consecutive queued toasts. Staggering keeps\n * a burst from firing every enter animation at the same instant. `0` releases\n * as many as fit at once.\n */\n stagger: number;\n pauseOnHover: boolean;\n dismissible: boolean;\n types: Record<string, SnackngTypeDef>;\n}\n\nexport const SNACKNG_DEFAULTS: SnackngConfig = {\n duration: 5000,\n position: 'top-end',\n max: 5,\n overflow: 'queue',\n stagger: 90,\n pauseOnHover: true,\n dismissible: false,\n types: {},\n};\n\nexport const SNACKNG_CONFIG = new InjectionToken<SnackngConfig>('snackng.config', {\n providedIn: 'root',\n factory: () => SNACKNG_DEFAULTS,\n});\n\n/**\n * Optional. Without it the defaults above apply, so `npm i snackng` and calling\n * the service is enough to get working toasts.\n */\nexport function provideSnackng(config: Partial<SnackngConfig> = {}): EnvironmentProviders {\n return makeEnvironmentProviders([\n {\n provide: SNACKNG_CONFIG,\n useValue: {\n ...SNACKNG_DEFAULTS,\n ...config,\n types: { ...SNACKNG_DEFAULTS.types, ...config.types },\n } satisfies SnackngConfig,\n },\n ]);\n}\n","import { DOCUMENT, Injectable, OnDestroy, inject } from '@angular/core';\nimport { SnackngPoliteness } from './snackng.model';\n\n/**\n * Announces toast text to screen readers, in place of the CDK `LiveAnnouncer`.\n *\n * The toast markup carries no `aria-live` of its own: text is copied into this\n * off-screen region instead. Keep it that way — a live region nested inside\n * another throws `HierarchyRequestError` when the outer one relocates its\n * content.\n */\n@Injectable({ providedIn: 'root' })\nexport class SnackngLiveRegion implements OnDestroy {\n private readonly document = inject(DOCUMENT);\n private region?: HTMLElement;\n private clearTimer?: ReturnType<typeof setTimeout>;\n\n announce(message: string, politeness: SnackngPoliteness = 'polite'): void {\n if (politeness === 'off' || !message) {\n return;\n }\n\n const region = this.ensureRegion();\n region.setAttribute('aria-live', politeness);\n\n // Clearing first forces assistive tech to treat an identical repeated\n // message as a new announcement.\n region.textContent = '';\n clearTimeout(this.clearTimer);\n this.clearTimer = setTimeout(() => (region.textContent = message), 100);\n }\n\n private ensureRegion(): HTMLElement {\n if (this.region?.isConnected) {\n return this.region;\n }\n\n const region = this.document.createElement('div');\n region.setAttribute('aria-atomic', 'true');\n region.setAttribute('aria-live', 'polite');\n region.classList.add('sng-live-region');\n // Visually hidden but still announced: `display:none` would silence it.\n region.style.cssText =\n 'position:absolute;width:1px;height:1px;margin:-1px;padding:0;overflow:hidden;' +\n 'clip:rect(0 0 0 0);clip-path:inset(50%);white-space:nowrap;border:0;';\n\n this.document.body.appendChild(region);\n this.region = region;\n return region;\n }\n\n ngOnDestroy(): void {\n clearTimeout(this.clearTimer);\n this.region?.remove();\n this.region = undefined;\n }\n}\n","import { SnackngBuiltInType } from './snackng.model';\n\n/**\n * Path data for the built-in glyphs, inlined so the package never requires an\n * icon font. Bound with `[attr.d]`, so no sanitizer is involved.\n */\nexport const SNACKNG_ICONS: Record<SnackngBuiltInType, string> = {\n success: 'M9 16.17 4.83 12l-1.42 1.41L9 19 21 7l-1.41-1.41z',\n warning: 'M1 21h22L12 2zm12-3h-2v-2h2zm0-4h-2v-4h2z',\n danger:\n 'M12 2C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2m1 15h-2v-2h2zm0-4h-2V7h2z',\n info: 'M12 2C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2m1 15h-2v-6h2zm0-8h-2V7h2z',\n};\n","import { Injectable, OnDestroy, computed, inject, signal } from '@angular/core';\nimport { SNACKNG_CONFIG } from './snackng.config';\nimport {\n SnackngAction,\n SnackngDismissReason,\n SnackngPoliteness,\n SnackngPosition,\n SnackngType,\n} from './snackng.model';\n\nexport interface SnackngItem {\n readonly id: string;\n readonly type: SnackngType;\n readonly message: string;\n readonly title?: string;\n readonly duration: number;\n readonly position: SnackngPosition;\n readonly dismissible: boolean;\n readonly action?: SnackngAction;\n readonly panelClass: readonly string[];\n readonly politeness: SnackngPoliteness;\n /** Set while the exit animation runs; the item is still in the DOM. */\n readonly leaving: boolean;\n}\n\nexport interface SnackngStack {\n readonly position: SnackngPosition;\n readonly items: readonly SnackngItem[];\n}\n\n/**\n * Toast state, and the pacing of how toasts reach the screen.\n *\n * Producer/consumer: `add` only enqueues; `pump` is the sole consumer moving\n * items into view as capacity allows. Releasing directly would let a burst\n * render in one tick, appearing and evicting itself in the same frame.\n *\n * Separate from `SnackngService` so the service can create the host while the\n * host reads state, without an import cycle.\n */\n@Injectable({ providedIn: 'root' })\nexport class SnackngStore implements OnDestroy {\n private readonly config = inject(SNACKNG_CONFIG);\n private readonly resolvers = new Map<string, (reason: SnackngDismissReason) => void>();\n private readonly reasons = new Map<string, SnackngDismissReason>();\n private pumpTimer?: ReturnType<typeof setTimeout>;\n private lastReleaseAt = 0;\n private counter = 0;\n\n /** Toasts currently rendered (including those playing their exit animation). */\n readonly items = signal<readonly SnackngItem[]>([]);\n /** Toasts accepted but still waiting for a free slot. */\n readonly queue = signal<readonly SnackngItem[]>([]);\n readonly pending = computed(() => this.queue().length);\n\n readonly stacks = computed<readonly SnackngStack[]>(() => {\n const byPosition = new Map<SnackngPosition, SnackngItem[]>();\n for (const item of this.items()) {\n const bucket = byPosition.get(item.position);\n bucket ? bucket.push(item) : byPosition.set(item.position, [item]);\n }\n return [...byPosition].map(([position, items]) => ({ position, items }));\n });\n\n add(item: Omit<SnackngItem, 'id' | 'leaving'>): {\n id: string;\n afterDismissed: Promise<SnackngDismissReason>;\n } {\n const id = `sng-${++this.counter}`;\n const afterDismissed = new Promise<SnackngDismissReason>(resolve =>\n this.resolvers.set(id, resolve),\n );\n\n this.queue.update(queue => [...queue, { ...item, id, leaving: false }]);\n this.pump();\n\n return { id, afterDismissed };\n }\n\n /**\n * Starts the exit animation; the item leaves the DOM on `finalize`. A toast\n * still queued is dropped outright, having never been shown.\n */\n dismiss(id: string, reason: SnackngDismissReason = 'manual'): void {\n if (this.queue().some(item => item.id === id)) {\n this.queue.update(queue => queue.filter(item => item.id !== id));\n this.resolve(id, reason);\n return;\n }\n\n const target = this.items().find(item => item.id === id);\n if (!target || target.leaving) {\n return;\n }\n this.reasons.set(id, reason);\n this.items.update(items => items.map(i => (i.id === id ? { ...i, leaving: true } : i)));\n }\n\n /** Removes the item and resolves its `afterDismissed` promise. */\n finalize(id: string): void {\n if (!this.resolvers.has(id)) {\n return;\n }\n this.items.update(items => items.filter(item => item.id !== id));\n this.resolve(id, this.reasons.get(id) ?? 'manual');\n this.reasons.delete(id);\n this.pump();\n }\n\n dismissAll(reason: SnackngDismissReason = 'manual'): void {\n // Drain the queue first, or it would march onto a screen just cleared.\n clearTimeout(this.pumpTimer);\n this.pumpTimer = undefined;\n for (const item of this.queue()) {\n this.resolve(item.id, reason);\n }\n this.queue.set([]);\n\n for (const item of this.items()) {\n this.dismiss(item.id, reason);\n }\n }\n\n /** The consumer. Runs on every add and every finalize. */\n private pump(): void {\n if (this.pumpTimer) {\n return; // a staggered release is already pending\n }\n\n while (this.queue().length) {\n // Counts leaving toasts too: they hold their slot until finalize removes\n // them, so ignoring them reads an expiring batch as free space.\n const onScreen = this.items();\n\n if (onScreen.length >= this.config.max) {\n if (this.config.overflow === 'dismiss-oldest') {\n // Evicting an already-leaving toast frees nothing.\n const victim = onScreen.find(item => !item.leaving);\n if (victim) {\n this.dismiss(victim.id, 'replaced');\n }\n }\n return;\n }\n\n // Spaced by elapsed time, not queue length: callers add one at a time, so\n // a burst arrives as N pumps of one item and would never wait.\n const wait = this.config.stagger - (Date.now() - this.lastReleaseAt);\n if (this.config.stagger > 0 && wait > 0) {\n this.pumpTimer = setTimeout(() => {\n this.pumpTimer = undefined;\n this.pump();\n }, wait);\n return;\n }\n\n const [next, ...rest] = this.queue();\n this.queue.set(rest);\n this.items.update(items => [...items, next]);\n this.lastReleaseAt = Date.now();\n }\n }\n\n private resolve(id: string, reason: SnackngDismissReason): void {\n this.resolvers.get(id)?.(reason);\n this.resolvers.delete(id);\n }\n\n ngOnDestroy(): void {\n clearTimeout(this.pumpTimer);\n }\n}\n","import {\n ChangeDetectionStrategy,\n Component,\n ElementRef,\n OnDestroy,\n computed,\n effect,\n inject,\n input,\n} from '@angular/core';\nimport { DomSanitizer, SafeHtml } from '@angular/platform-browser';\nimport { SNACKNG_CONFIG } from '../snackng.config';\nimport { SNACKNG_ICONS } from '../icons';\nimport { SnackngBuiltInType } from '../snackng.model';\nimport { SnackngItem, SnackngStore } from '../snackng.store';\n\nconst EXIT_DURATION = 200;\n/** Guards against `animationend` never firing (reduced motion, jsdom, backgrounded tab). */\nconst EXIT_FALLBACK = EXIT_DURATION + 150;\n\n@Component({\n selector: 'sng-toast',\n templateUrl: './snackng-toast.html',\n styleUrl: './snackng-toast.css',\n changeDetection: ChangeDetectionStrategy.OnPush,\n host: {\n '[class]': 'hostClasses()',\n '(mouseenter)': 'pause()',\n '(mouseleave)': 'resume()',\n '(focusin)': 'pause()',\n '(focusout)': 'resume()',\n },\n})\nexport class SnackngToast implements OnDestroy {\n readonly item = input.required<SnackngItem>();\n\n private readonly store = inject(SnackngStore);\n private readonly config = inject(SNACKNG_CONFIG);\n private readonly sanitizer = inject(DomSanitizer);\n private readonly element = inject<ElementRef<HTMLElement>>(ElementRef);\n\n private timer?: ReturnType<typeof setTimeout>;\n private exitTimer?: ReturnType<typeof setTimeout>;\n private armed = false;\n private remaining = 0;\n private startedAt = 0;\n\n protected readonly hostClasses = computed(() => {\n const item = this.item();\n return [\n 'sng-toast',\n `sng--${item.type}`,\n `sng-at--${item.position}`,\n item.leaving ? 'sng-leaving' : 'sng-entering',\n ...item.panelClass,\n ].join(' ');\n });\n\n /** Null when the type brings its own SVG; unknown types fall back to `info`. */\n protected readonly iconPath = computed<string | null>(() => {\n const type = this.item().type;\n if (this.config.types[type]?.icon) {\n return null;\n }\n return SNACKNG_ICONS[type as SnackngBuiltInType] ?? SNACKNG_ICONS.info;\n });\n\n /** Custom types may supply raw SVG markup, trusted as authored config. */\n protected readonly iconSvg = computed<SafeHtml | null>(() => {\n const custom = this.config.types[this.item().type]?.icon;\n return custom ? this.sanitizer.bypassSecurityTrustHtml(custom) : null;\n });\n\n constructor() {\n effect(() => {\n if (this.item().leaving) {\n this.stopTimer();\n this.scheduleFinalize();\n } else {\n this.startTimer();\n }\n });\n }\n\n protected onAction(): void {\n const action = this.item().action;\n action?.handler?.();\n if (action?.dismissOnClick !== false) {\n this.store.dismiss(this.item().id, 'action');\n }\n }\n\n protected onDismiss(): void {\n this.store.dismiss(this.item().id, 'manual');\n }\n\n protected pause(): void {\n if (!this.config.pauseOnHover || !this.timer) {\n return;\n }\n this.remaining -= Date.now() - this.startedAt;\n clearTimeout(this.timer);\n this.timer = undefined;\n }\n\n protected resume(): void {\n if (!this.config.pauseOnHover || this.timer || this.remaining <= 0 || this.item().leaving) {\n return;\n }\n this.arm(this.remaining);\n }\n\n /** Arms once per toast: re-running on later item updates would reset a paused timer. */\n private startTimer(): void {\n const duration = this.item().duration;\n if (this.armed || duration <= 0) {\n return;\n }\n this.armed = true;\n this.remaining = duration;\n this.arm(duration);\n }\n\n private arm(ms: number): void {\n this.startedAt = Date.now();\n this.timer = setTimeout(() => this.store.dismiss(this.item().id, 'timeout'), ms);\n }\n\n private stopTimer(): void {\n clearTimeout(this.timer);\n this.timer = undefined;\n }\n\n private scheduleFinalize(): void {\n if (this.exitTimer) {\n return;\n }\n const finalize = () => {\n clearTimeout(this.exitTimer);\n this.store.finalize(this.item().id);\n };\n this.element.nativeElement.addEventListener('animationend', finalize, { once: true });\n this.exitTimer = setTimeout(finalize, EXIT_FALLBACK);\n }\n\n ngOnDestroy(): void {\n this.stopTimer();\n clearTimeout(this.exitTimer);\n }\n}\n","@let toast = item();\n\n<!-- No aria-live here: SnackngLiveRegion announces instead. -->\n<div class=\"sng-accent\" aria-hidden=\"true\"></div>\n\n<div class=\"sng-icon\" aria-hidden=\"true\">\n @if (iconPath(); as path) {\n <svg viewBox=\"0 0 24 24\" focusable=\"false\">\n <path [attr.d]=\"path\" fill=\"currentColor\" />\n </svg>\n } @else {\n <span class=\"sng-icon-custom\" [innerHTML]=\"iconSvg()\"></span>\n }\n</div>\n\n<div class=\"sng-body\">\n @if (toast.title) {\n <p class=\"sng-title\">{{ toast.title }}</p>\n }\n <p class=\"sng-message\">{{ toast.message }}</p>\n</div>\n\n@if (toast.action; as action) {\n <button type=\"button\" class=\"sng-action\" (click)=\"onAction()\">{{ action.label }}</button>\n}\n\n@if (toast.dismissible) {\n <button type=\"button\" class=\"sng-close\" (click)=\"onDismiss()\" aria-label=\"Close notification\">\n <svg viewBox=\"0 0 24 24\" aria-hidden=\"true\" focusable=\"false\">\n <path\n d=\"M19 6.41 17.59 5 12 10.59 6.41 5 5 6.41 10.59 12 5 17.59 6.41 19 12 13.41 17.59 19 19 17.59 13.41 12z\"\n fill=\"currentColor\" />\n </svg>\n </button>\n}\n","import {\n ChangeDetectionStrategy,\n Component,\n ElementRef,\n afterEveryRender,\n inject,\n} from '@angular/core';\nimport { SnackngToast } from '../snackng-toast/snackng-toast';\nimport { SnackngStore } from '../snackng.store';\n\nconst SHIFT_DURATION = 260;\nconst SHIFT_EASING = 'cubic-bezier(0.4, 0, 0.2, 1)';\n\n/** Guarded: test environments such as jsdom render without `matchMedia`. */\nfunction prefersReducedMotion(): boolean {\n return (\n typeof matchMedia === 'function' && matchMedia('(prefers-reduced-motion: reduce)').matches\n );\n}\n\n@Component({\n selector: 'sng-host',\n imports: [SnackngToast],\n templateUrl: './snackng-host.html',\n styleUrl: './snackng-host.css',\n changeDetection: ChangeDetectionStrategy.OnPush,\n})\nexport class SnackngHost {\n private readonly store = inject(SnackngStore);\n private readonly element = inject<ElementRef<HTMLElement>>(ElementRef);\n\n protected readonly stacks = this.store.stacks;\n\n private readonly offsets = new Map<string, number>();\n private readonly shifts = new Map<string, Animation>();\n\n constructor() {\n afterEveryRender(() => this.absorbLayoutShift());\n }\n\n /**\n * FLIP: offset each slot back to where it was, then animate that offset away,\n * so neighbours glide into a freed gap instead of snapping.\n *\n * Measured with `offsetTop` rather than `getBoundingClientRect`: the latter\n * includes transforms and would read an in-flight shift instead of the layout\n * beneath it. Stacks are full height and top-anchored, so a slot's offsetTop\n * only changes when it truly moves. Applied to the slot, never the toast, to\n * leave the toast's own enter/exit transform alone.\n */\n private absorbLayoutShift(): void {\n const slots = this.element.nativeElement.querySelectorAll<HTMLElement>('.sng-slot');\n const present = new Set<string>();\n\n for (const slot of slots) {\n const id = slot.dataset['sngId'];\n if (!id) {\n continue;\n }\n present.add(id);\n\n const top = slot.offsetTop;\n const previous = this.offsets.get(id);\n this.offsets.set(id, top);\n\n if (previous === undefined || previous === top || prefersReducedMotion()) {\n continue;\n }\n\n this.shifts.get(id)?.cancel();\n this.shifts.set(\n id,\n slot.animate([{ transform: `translateY(${previous - top}px)` }, { transform: 'none' }], {\n duration: SHIFT_DURATION,\n easing: SHIFT_EASING,\n }),\n );\n }\n\n for (const id of [...this.offsets.keys()]) {\n if (!present.has(id)) {\n this.offsets.delete(id);\n this.shifts.delete(id);\n }\n }\n }\n}\n","@for (stack of stacks(); track stack.position) {\n <div class=\"sng-stack\" [class]=\"'sng-stack--' + stack.position\">\n @for (item of stack.items; track item.id) {\n <div class=\"sng-slot\" [attr.data-sng-id]=\"item.id\">\n <sng-toast [item]=\"item\" />\n </div>\n }\n </div>\n}\n","import {\n ApplicationRef,\n ComponentRef,\n DOCUMENT,\n EnvironmentInjector,\n Injectable,\n OnDestroy,\n PLATFORM_ID,\n createComponent,\n inject,\n} from '@angular/core';\nimport { isPlatformBrowser } from '@angular/common';\nimport { SNACKNG_CONFIG } from './snackng.config';\nimport { SnackngLiveRegion } from './live-region';\nimport { SnackngHost } from './snackng-host/snackng-host';\nimport { SnackngStore } from './snackng.store';\nimport {\n SnackngDismissReason,\n SnackngOptions,\n SnackngPoliteness,\n SnackngRef,\n SnackngType,\n} from './snackng.model';\n\nconst NOOP_REF: SnackngRef = {\n id: 'sng-noop',\n dismiss: () => {},\n afterDismissed: Promise.resolve<SnackngDismissReason>('manual'),\n};\n\n@Injectable({ providedIn: 'root' })\nexport class SnackngService implements OnDestroy {\n private readonly store = inject(SnackngStore);\n private readonly config = inject(SNACKNG_CONFIG);\n private readonly liveRegion = inject(SnackngLiveRegion);\n private readonly appRef = inject(ApplicationRef);\n private readonly injector = inject(EnvironmentInjector);\n private readonly document = inject(DOCUMENT);\n private readonly isBrowser = isPlatformBrowser(inject(PLATFORM_ID));\n\n private host?: ComponentRef<SnackngHost>;\n\n /**\n * Toasts accepted but still waiting for a free slot under `overflow: 'queue'`.\n * Useful for a \"3 more\" affordance.\n */\n readonly pending = this.store.pending;\n\n success(message: string, options?: SnackngOptions): SnackngRef {\n return this.show('success', message, options);\n }\n\n warning(message: string, options?: SnackngOptions): SnackngRef {\n return this.show('warning', message, options);\n }\n\n danger(message: string, options?: SnackngOptions): SnackngRef {\n return this.show('danger', message, options);\n }\n\n info(message: string, options?: SnackngOptions): SnackngRef {\n return this.show('info', message, options);\n }\n\n show(type: SnackngType, message: string, options: SnackngOptions = {}): SnackngRef {\n // Client-only: no-op rather than throw on `document` during SSR.\n if (!this.isBrowser) {\n return NOOP_REF;\n }\n\n this.ensureHost();\n\n const politeness = this.resolvePoliteness(type, options);\n const { id, afterDismissed } = this.store.add({\n type,\n message,\n title: options.title,\n duration: options.duration ?? this.config.duration,\n position: options.position ?? this.config.position,\n dismissible: options.dismissible ?? this.config.dismissible,\n action: options.action,\n panelClass: normalizeClasses(options.panelClass),\n politeness,\n });\n\n this.liveRegion.announce([options.title, message].filter(Boolean).join('. '), politeness);\n\n return {\n id,\n afterDismissed,\n dismiss: () => this.store.dismiss(id, 'manual'),\n };\n }\n\n dismissAll(): void {\n this.store.dismissAll();\n }\n\n private resolvePoliteness(type: SnackngType, options: SnackngOptions): SnackngPoliteness {\n if (options.politeness) {\n return options.politeness;\n }\n const configured = this.config.types[type]?.politeness;\n if (configured) {\n return configured;\n }\n // Errors interrupt; everything else waits for a pause.\n return type === 'danger' ? 'assertive' : 'polite';\n }\n\n private ensureHost(): void {\n if (this.host) {\n return;\n }\n this.host = createComponent(SnackngHost, { environmentInjector: this.injector });\n this.appRef.attachView(this.host.hostView);\n this.document.body.appendChild(this.host.location.nativeElement);\n }\n\n ngOnDestroy(): void {\n if (this.host) {\n this.appRef.detachView(this.host.hostView);\n this.host.destroy();\n this.host = undefined;\n }\n }\n}\n\nfunction normalizeClasses(panelClass: string | string[] | undefined): string[] {\n if (!panelClass) {\n return [];\n }\n return Array.isArray(panelClass) ? panelClass : [panelClass];\n}\n","/*\n * Public API Surface of snackng\n */\n\nexport { SnackngService } from './lib/snackng.service';\nexport { provideSnackng, SNACKNG_CONFIG, SNACKNG_DEFAULTS } from './lib/snackng.config';\nexport type { SnackngConfig } from './lib/snackng.config';\nexport type {\n SnackngAction,\n SnackngBuiltInType,\n SnackngDismissReason,\n SnackngOptions,\n SnackngPoliteness,\n SnackngPosition,\n SnackngRef,\n SnackngType,\n SnackngTypeDef,\n} from './lib/snackng.model';\n","/**\n * Generated bundle index. Do not edit.\n */\n\nexport * from './public-api';\n"],"names":[],"mappings":";;;;;AA0BO,MAAM,gBAAgB,GAAkB;AAC7C,IAAA,QAAQ,EAAE,IAAI;AACd,IAAA,QAAQ,EAAE,SAAS;AACnB,IAAA,GAAG,EAAE,CAAC;AACN,IAAA,QAAQ,EAAE,OAAO;AACjB,IAAA,OAAO,EAAE,EAAE;AACX,IAAA,YAAY,EAAE,IAAI;AAClB,IAAA,WAAW,EAAE,KAAK;AAClB,IAAA,KAAK,EAAE,EAAE;;MAGE,cAAc,GAAG,IAAI,cAAc,CAAgB,gBAAgB,EAAE;AAChF,IAAA,UAAU,EAAE,MAAM;AAClB,IAAA,OAAO,EAAE,MAAM,gBAAgB;AAChC,CAAA;AAED;;;AAGG;AACG,SAAU,cAAc,CAAC,MAAA,GAAiC,EAAE,EAAA;AAChE,IAAA,OAAO,wBAAwB,CAAC;AAC9B,QAAA;AACE,YAAA,OAAO,EAAE,cAAc;AACvB,YAAA,QAAQ,EAAE;AACR,gBAAA,GAAG,gBAAgB;AACnB,gBAAA,GAAG,MAAM;gBACT,KAAK,EAAE,EAAE,GAAG,gBAAgB,CAAC,KAAK,EAAE,GAAG,MAAM,CAAC,KAAK,EAAE;AAC9B,aAAA;AAC1B,SAAA;AACF,KAAA,CAAC;AACJ;;ACtDA;;;;;;;AAOG;MAEU,iBAAiB,CAAA;AACX,IAAA,QAAQ,GAAG,MAAM,CAAC,QAAQ,CAAC;AACpC,IAAA,MAAM;AACN,IAAA,UAAU;AAElB,IAAA,QAAQ,CAAC,OAAe,EAAE,UAAA,GAAgC,QAAQ,EAAA;AAChE,QAAA,IAAI,UAAU,KAAK,KAAK,IAAI,CAAC,OAAO,EAAE;YACpC;QACF;AAEA,QAAA,MAAM,MAAM,GAAG,IAAI,CAAC,YAAY,EAAE;AAClC,QAAA,MAAM,CAAC,YAAY,CAAC,WAAW,EAAE,UAAU,CAAC;;;AAI5C,QAAA,MAAM,CAAC,WAAW,GAAG,EAAE;AACvB,QAAA,YAAY,CAAC,IAAI,CAAC,UAAU,CAAC;AAC7B,QAAA,IAAI,CAAC,UAAU,GAAG,UAAU,CAAC,OAAO,MAAM,CAAC,WAAW,GAAG,OAAO,CAAC,EAAE,GAAG,CAAC;IACzE;IAEQ,YAAY,GAAA;AAClB,QAAA,IAAI,IAAI,CAAC,MAAM,EAAE,WAAW,EAAE;YAC5B,OAAO,IAAI,CAAC,MAAM;QACpB;QAEA,MAAM,MAAM,GAAG,IAAI,CAAC,QAAQ,CAAC,aAAa,CAAC,KAAK,CAAC;AACjD,QAAA,MAAM,CAAC,YAAY,CAAC,aAAa,EAAE,MAAM,CAAC;AAC1C,QAAA,MAAM,CAAC,YAAY,CAAC,WAAW,EAAE,QAAQ,CAAC;AAC1C,QAAA,MAAM,CAAC,SAAS,CAAC,GAAG,CAAC,iBAAiB,CAAC;;QAEvC,MAAM,CAAC,KAAK,CAAC,OAAO;YAClB,+EAA+E;AAC/E,gBAAA,sEAAsE;QAExE,IAAI,CAAC,QAAQ,CAAC,IAAI,CAAC,WAAW,CAAC,MAAM,CAAC;AACtC,QAAA,IAAI,CAAC,MAAM,GAAG,MAAM;AACpB,QAAA,OAAO,MAAM;IACf;IAEA,WAAW,GAAA;AACT,QAAA,YAAY,CAAC,IAAI,CAAC,UAAU,CAAC;AAC7B,QAAA,IAAI,CAAC,MAAM,EAAE,MAAM,EAAE;AACrB,QAAA,IAAI,CAAC,MAAM,GAAG,SAAS;IACzB;wGA3CW,iBAAiB,EAAA,IAAA,EAAA,EAAA,EAAA,MAAA,EAAA,EAAA,CAAA,eAAA,CAAA,UAAA,EAAA,CAAA;AAAjB,IAAA,OAAA,KAAA,GAAA,EAAA,CAAA,qBAAA,CAAA,EAAA,UAAA,EAAA,QAAA,EAAA,OAAA,EAAA,SAAA,EAAA,QAAA,EAAA,EAAA,EAAA,IAAA,EAAA,iBAAiB,cADJ,MAAM,EAAA,CAAA;;4FACnB,iBAAiB,EAAA,UAAA,EAAA,CAAA;kBAD7B,UAAU;mBAAC,EAAE,UAAU,EAAE,MAAM,EAAE;;;ACTlC;;;AAGG;AACI,MAAM,aAAa,GAAuC;AAC/D,IAAA,OAAO,EAAE,mDAAmD;AAC5D,IAAA,OAAO,EAAE,2CAA2C;AACpD,IAAA,MAAM,EACJ,6FAA6F;AAC/F,IAAA,IAAI,EAAE,6FAA6F;CACpG;;ACkBD;;;;;;;;;AASG;MAEU,YAAY,CAAA;AACN,IAAA,MAAM,GAAG,MAAM,CAAC,cAAc,CAAC;AAC/B,IAAA,SAAS,GAAG,IAAI,GAAG,EAAkD;AACrE,IAAA,OAAO,GAAG,IAAI,GAAG,EAAgC;AAC1D,IAAA,SAAS;IACT,aAAa,GAAG,CAAC;IACjB,OAAO,GAAG,CAAC;;AAGV,IAAA,KAAK,GAAG,MAAM,CAAyB,EAAE,4EAAC;;AAE1C,IAAA,KAAK,GAAG,MAAM,CAAyB,EAAE,4EAAC;AAC1C,IAAA,OAAO,GAAG,QAAQ,CAAC,MAAM,IAAI,CAAC,KAAK,EAAE,CAAC,MAAM,8EAAC;AAE7C,IAAA,MAAM,GAAG,QAAQ,CAA0B,MAAK;AACvD,QAAA,MAAM,UAAU,GAAG,IAAI,GAAG,EAAkC;QAC5D,KAAK,MAAM,IAAI,IAAI,IAAI,CAAC,KAAK,EAAE,EAAE;YAC/B,MAAM,MAAM,GAAG,UAAU,CAAC,GAAG,CAAC,IAAI,CAAC,QAAQ,CAAC;YAC5C,MAAM,GAAG,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC,GAAG,UAAU,CAAC,GAAG,CAAC,IAAI,CAAC,QAAQ,EAAE,CAAC,IAAI,CAAC,CAAC;QACpE;QACA,OAAO,CAAC,GAAG,UAAU,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,QAAQ,EAAE,KAAK,CAAC,MAAM,EAAE,QAAQ,EAAE,KAAK,EAAE,CAAC,CAAC;AAC1E,IAAA,CAAC,6EAAC;AAEF,IAAA,GAAG,CAAC,IAAyC,EAAA;QAI3C,MAAM,EAAE,GAAG,CAAA,IAAA,EAAO,EAAE,IAAI,CAAC,OAAO,EAAE;QAClC,MAAM,cAAc,GAAG,IAAI,OAAO,CAAuB,OAAO,IAC9D,IAAI,CAAC,SAAS,CAAC,GAAG,CAAC,EAAE,EAAE,OAAO,CAAC,CAChC;QAED,IAAI,CAAC,KAAK,CAAC,MAAM,CAAC,KAAK,IAAI,CAAC,GAAG,KAAK,EAAE,EAAE,GAAG,IAAI,EAAE,EAAE,EAAE,OAAO,EAAE,KAAK,EAAE,CAAC,CAAC;QACvE,IAAI,CAAC,IAAI,EAAE;AAEX,QAAA,OAAO,EAAE,EAAE,EAAE,cAAc,EAAE;IAC/B;AAEA;;;AAGG;AACH,IAAA,OAAO,CAAC,EAAU,EAAE,MAAA,GAA+B,QAAQ,EAAA;AACzD,QAAA,IAAI,IAAI,CAAC,KAAK,EAAE,CAAC,IAAI,CAAC,IAAI,IAAI,IAAI,CAAC,EAAE,KAAK,EAAE,CAAC,EAAE;YAC7C,IAAI,CAAC,KAAK,CAAC,MAAM,CAAC,KAAK,IAAI,KAAK,CAAC,MAAM,CAAC,IAAI,IAAI,IAAI,CAAC,EAAE,KAAK,EAAE,CAAC,CAAC;AAChE,YAAA,IAAI,CAAC,OAAO,CAAC,EAAE,EAAE,MAAM,CAAC;YACxB;QACF;AAEA,QAAA,MAAM,MAAM,GAAG,IAAI,CAAC,KAAK,EAAE,CAAC,IAAI,CAAC,IAAI,IAAI,IAAI,CAAC,EAAE,KAAK,EAAE,CAAC;AACxD,QAAA,IAAI,CAAC,MAAM,IAAI,MAAM,CAAC,OAAO,EAAE;YAC7B;QACF;QACA,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,EAAE,EAAE,MAAM,CAAC;AAC5B,QAAA,IAAI,CAAC,KAAK,CAAC,MAAM,CAAC,KAAK,IAAI,KAAK,CAAC,GAAG,CAAC,CAAC,KAAK,CAAC,CAAC,EAAE,KAAK,EAAE,GAAG,EAAE,GAAG,CAAC,EAAE,OAAO,EAAE,IAAI,EAAE,GAAG,CAAC,CAAC,CAAC,CAAC;IACzF;;AAGA,IAAA,QAAQ,CAAC,EAAU,EAAA;QACjB,IAAI,CAAC,IAAI,CAAC,SAAS,CAAC,GAAG,CAAC,EAAE,CAAC,EAAE;YAC3B;QACF;QACA,IAAI,CAAC,KAAK,CAAC,MAAM,CAAC,KAAK,IAAI,KAAK,CAAC,MAAM,CAAC,IAAI,IAAI,IAAI,CAAC,EAAE,KAAK,EAAE,CAAC,CAAC;AAChE,QAAA,IAAI,CAAC,OAAO,CAAC,EAAE,EAAE,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,EAAE,CAAC,IAAI,QAAQ,CAAC;AAClD,QAAA,IAAI,CAAC,OAAO,CAAC,MAAM,CAAC,EAAE,CAAC;QACvB,IAAI,CAAC,IAAI,EAAE;IACb;IAEA,UAAU,CAAC,SAA+B,QAAQ,EAAA;;AAEhD,QAAA,YAAY,CAAC,IAAI,CAAC,SAAS,CAAC;AAC5B,QAAA,IAAI,CAAC,SAAS,GAAG,SAAS;QAC1B,KAAK,MAAM,IAAI,IAAI,IAAI,CAAC,KAAK,EAAE,EAAE;YAC/B,IAAI,CAAC,OAAO,CAAC,IAAI,CAAC,EAAE,EAAE,MAAM,CAAC;QAC/B;AACA,QAAA,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,EAAE,CAAC;QAElB,KAAK,MAAM,IAAI,IAAI,IAAI,CAAC,KAAK,EAAE,EAAE;YAC/B,IAAI,CAAC,OAAO,CAAC,IAAI,CAAC,EAAE,EAAE,MAAM,CAAC;QAC/B;IACF;;IAGQ,IAAI,GAAA;AACV,QAAA,IAAI,IAAI,CAAC,SAAS,EAAE;AAClB,YAAA,OAAO;QACT;AAEA,QAAA,OAAO,IAAI,CAAC,KAAK,EAAE,CAAC,MAAM,EAAE;;;AAG1B,YAAA,MAAM,QAAQ,GAAG,IAAI,CAAC,KAAK,EAAE;YAE7B,IAAI,QAAQ,CAAC,MAAM,IAAI,IAAI,CAAC,MAAM,CAAC,GAAG,EAAE;gBACtC,IAAI,IAAI,CAAC,MAAM,CAAC,QAAQ,KAAK,gBAAgB,EAAE;;AAE7C,oBAAA,MAAM,MAAM,GAAG,QAAQ,CAAC,IAAI,CAAC,IAAI,IAAI,CAAC,IAAI,CAAC,OAAO,CAAC;oBACnD,IAAI,MAAM,EAAE;wBACV,IAAI,CAAC,OAAO,CAAC,MAAM,CAAC,EAAE,EAAE,UAAU,CAAC;oBACrC;gBACF;gBACA;YACF;;;AAIA,YAAA,MAAM,IAAI,GAAG,IAAI,CAAC,MAAM,CAAC,OAAO,IAAI,IAAI,CAAC,GAAG,EAAE,GAAG,IAAI,CAAC,aAAa,CAAC;AACpE,YAAA,IAAI,IAAI,CAAC,MAAM,CAAC,OAAO,GAAG,CAAC,IAAI,IAAI,GAAG,CAAC,EAAE;AACvC,gBAAA,IAAI,CAAC,SAAS,GAAG,UAAU,CAAC,MAAK;AAC/B,oBAAA,IAAI,CAAC,SAAS,GAAG,SAAS;oBAC1B,IAAI,CAAC,IAAI,EAAE;gBACb,CAAC,EAAE,IAAI,CAAC;gBACR;YACF;YAEA,MAAM,CAAC,IAAI,EAAE,GAAG,IAAI,CAAC,GAAG,IAAI,CAAC,KAAK,EAAE;AACpC,YAAA,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,IAAI,CAAC;AACpB,YAAA,IAAI,CAAC,KAAK,CAAC,MAAM,CAAC,KAAK,IAAI,CAAC,GAAG,KAAK,EAAE,IAAI,CAAC,CAAC;AAC5C,YAAA,IAAI,CAAC,aAAa,GAAG,IAAI,CAAC,GAAG,EAAE;QACjC;IACF;IAEQ,OAAO,CAAC,EAAU,EAAE,MAA4B,EAAA;QACtD,IAAI,CAAC,SAAS,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,MAAM,CAAC;AAChC,QAAA,IAAI,CAAC,SAAS,CAAC,MAAM,CAAC,EAAE,CAAC;IAC3B;IAEA,WAAW,GAAA;AACT,QAAA,YAAY,CAAC,IAAI,CAAC,SAAS,CAAC;IAC9B;wGAjIW,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,SAAA,EAAA,QAAA,EAAA,EAAA,EAAA,IAAA,EAAA,YAAY,cADC,MAAM,EAAA,CAAA;;4FACnB,YAAY,EAAA,UAAA,EAAA,CAAA;kBADxB,UAAU;mBAAC,EAAE,UAAU,EAAE,MAAM,EAAE;;;ACxBlC,MAAM,aAAa,GAAG,GAAG;AACzB;AACA,MAAM,aAAa,GAAG,aAAa,GAAG,GAAG;MAe5B,YAAY,CAAA;AACd,IAAA,IAAI,GAAG,KAAK,CAAC,QAAQ,0EAAe;AAE5B,IAAA,KAAK,GAAG,MAAM,CAAC,YAAY,CAAC;AAC5B,IAAA,MAAM,GAAG,MAAM,CAAC,cAAc,CAAC;AAC/B,IAAA,SAAS,GAAG,MAAM,CAAC,YAAY,CAAC;AAChC,IAAA,OAAO,GAAG,MAAM,CAA0B,UAAU,CAAC;AAE9D,IAAA,KAAK;AACL,IAAA,SAAS;IACT,KAAK,GAAG,KAAK;IACb,SAAS,GAAG,CAAC;IACb,SAAS,GAAG,CAAC;AAEF,IAAA,WAAW,GAAG,QAAQ,CAAC,MAAK;AAC7C,QAAA,MAAM,IAAI,GAAG,IAAI,CAAC,IAAI,EAAE;QACxB,OAAO;YACL,WAAW;YACX,CAAA,KAAA,EAAQ,IAAI,CAAC,IAAI,CAAA,CAAE;YACnB,CAAA,QAAA,EAAW,IAAI,CAAC,QAAQ,CAAA,CAAE;YAC1B,IAAI,CAAC,OAAO,GAAG,aAAa,GAAG,cAAc;YAC7C,GAAG,IAAI,CAAC,UAAU;AACnB,SAAA,CAAC,IAAI,CAAC,GAAG,CAAC;AACb,IAAA,CAAC,kFAAC;;AAGiB,IAAA,QAAQ,GAAG,QAAQ,CAAgB,MAAK;QACzD,MAAM,IAAI,GAAG,IAAI,CAAC,IAAI,EAAE,CAAC,IAAI;QAC7B,IAAI,IAAI,CAAC,MAAM,CAAC,KAAK,CAAC,IAAI,CAAC,EAAE,IAAI,EAAE;AACjC,YAAA,OAAO,IAAI;QACb;QACA,OAAO,aAAa,CAAC,IAA0B,CAAC,IAAI,aAAa,CAAC,IAAI;AACxE,IAAA,CAAC,+EAAC;;AAGiB,IAAA,OAAO,GAAG,QAAQ,CAAkB,MAAK;AAC1D,QAAA,MAAM,MAAM,GAAG,IAAI,CAAC,MAAM,CAAC,KAAK,CAAC,IAAI,CAAC,IAAI,EAAE,CAAC,IAAI,CAAC,EAAE,IAAI;AACxD,QAAA,OAAO,MAAM,GAAG,IAAI,CAAC,SAAS,CAAC,uBAAuB,CAAC,MAAM,CAAC,GAAG,IAAI;AACvE,IAAA,CAAC,8EAAC;AAEF,IAAA,WAAA,GAAA;QACE,MAAM,CAAC,MAAK;AACV,YAAA,IAAI,IAAI,CAAC,IAAI,EAAE,CAAC,OAAO,EAAE;gBACvB,IAAI,CAAC,SAAS,EAAE;gBAChB,IAAI,CAAC,gBAAgB,EAAE;YACzB;iBAAO;gBACL,IAAI,CAAC,UAAU,EAAE;YACnB;AACF,QAAA,CAAC,CAAC;IACJ;IAEU,QAAQ,GAAA;QAChB,MAAM,MAAM,GAAG,IAAI,CAAC,IAAI,EAAE,CAAC,MAAM;AACjC,QAAA,MAAM,EAAE,OAAO,IAAI;AACnB,QAAA,IAAI,MAAM,EAAE,cAAc,KAAK,KAAK,EAAE;AACpC,YAAA,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,IAAI,CAAC,IAAI,EAAE,CAAC,EAAE,EAAE,QAAQ,CAAC;QAC9C;IACF;IAEU,SAAS,GAAA;AACjB,QAAA,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,IAAI,CAAC,IAAI,EAAE,CAAC,EAAE,EAAE,QAAQ,CAAC;IAC9C;IAEU,KAAK,GAAA;AACb,QAAA,IAAI,CAAC,IAAI,CAAC,MAAM,CAAC,YAAY,IAAI,CAAC,IAAI,CAAC,KAAK,EAAE;YAC5C;QACF;QACA,IAAI,CAAC,SAAS,IAAI,IAAI,CAAC,GAAG,EAAE,GAAG,IAAI,CAAC,SAAS;AAC7C,QAAA,YAAY,CAAC,IAAI,CAAC,KAAK,CAAC;AACxB,QAAA,IAAI,CAAC,KAAK,GAAG,SAAS;IACxB;IAEU,MAAM,GAAA;QACd,IAAI,CAAC,IAAI,CAAC,MAAM,CAAC,YAAY,IAAI,IAAI,CAAC,KAAK,IAAI,IAAI,CAAC,SAAS,IAAI,CAAC,IAAI,IAAI,CAAC,IAAI,EAAE,CAAC,OAAO,EAAE;YACzF;QACF;AACA,QAAA,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC,SAAS,CAAC;IAC1B;;IAGQ,UAAU,GAAA;QAChB,MAAM,QAAQ,GAAG,IAAI,CAAC,IAAI,EAAE,CAAC,QAAQ;QACrC,IAAI,IAAI,CAAC,KAAK,IAAI,QAAQ,IAAI,CAAC,EAAE;YAC/B;QACF;AACA,QAAA,IAAI,CAAC,KAAK,GAAG,IAAI;AACjB,QAAA,IAAI,CAAC,SAAS,GAAG,QAAQ;AACzB,QAAA,IAAI,CAAC,GAAG,CAAC,QAAQ,CAAC;IACpB;AAEQ,IAAA,GAAG,CAAC,EAAU,EAAA;AACpB,QAAA,IAAI,CAAC,SAAS,GAAG,IAAI,CAAC,GAAG,EAAE;QAC3B,IAAI,CAAC,KAAK,GAAG,UAAU,CAAC,MAAM,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,IAAI,CAAC,IAAI,EAAE,CAAC,EAAE,EAAE,SAAS,CAAC,EAAE,EAAE,CAAC;IAClF;IAEQ,SAAS,GAAA;AACf,QAAA,YAAY,CAAC,IAAI,CAAC,KAAK,CAAC;AACxB,QAAA,IAAI,CAAC,KAAK,GAAG,SAAS;IACxB;IAEQ,gBAAgB,GAAA;AACtB,QAAA,IAAI,IAAI,CAAC,SAAS,EAAE;YAClB;QACF;QACA,MAAM,QAAQ,GAAG,MAAK;AACpB,YAAA,YAAY,CAAC,IAAI,CAAC,SAAS,CAAC;AAC5B,YAAA,IAAI,CAAC,KAAK,CAAC,QAAQ,CAAC,IAAI,CAAC,IAAI,EAAE,CAAC,EAAE,CAAC;AACrC,QAAA,CAAC;AACD,QAAA,IAAI,CAAC,OAAO,CAAC,aAAa,CAAC,gBAAgB,CAAC,cAAc,EAAE,QAAQ,EAAE,EAAE,IAAI,EAAE,IAAI,EAAE,CAAC;QACrF,IAAI,CAAC,SAAS,GAAG,UAAU,CAAC,QAAQ,EAAE,aAAa,CAAC;IACtD;IAEA,WAAW,GAAA;QACT,IAAI,CAAC,SAAS,EAAE;AAChB,QAAA,YAAY,CAAC,IAAI,CAAC,SAAS,CAAC;IAC9B;wGAnHW,YAAY,EAAA,IAAA,EAAA,EAAA,EAAA,MAAA,EAAA,EAAA,CAAA,eAAA,CAAA,SAAA,EAAA,CAAA;AAAZ,IAAA,OAAA,IAAA,GAAA,EAAA,CAAA,oBAAA,CAAA,EAAA,UAAA,EAAA,QAAA,EAAA,OAAA,EAAA,SAAA,EAAA,IAAA,EAAA,YAAY,yWCjCzB,6nCAmCA,EAAA,MAAA,EAAA,CAAA,8wLAAA,CAAA,EAAA,eAAA,EAAA,EAAA,CAAA,uBAAA,CAAA,MAAA,EAAA,CAAA;;4FDFa,YAAY,EAAA,UAAA,EAAA,CAAA;kBAbxB,SAAS;AACE,YAAA,IAAA,EAAA,CAAA,EAAA,QAAA,EAAA,WAAW,EAAA,eAAA,EAGJ,uBAAuB,CAAC,MAAM,EAAA,IAAA,EACzC;AACJ,wBAAA,SAAS,EAAE,eAAe;AAC1B,wBAAA,cAAc,EAAE,SAAS;AACzB,wBAAA,cAAc,EAAE,UAAU;AAC1B,wBAAA,WAAW,EAAE,SAAS;AACtB,wBAAA,YAAY,EAAE,UAAU;AACzB,qBAAA,EAAA,QAAA,EAAA,6nCAAA,EAAA,MAAA,EAAA,CAAA,8wLAAA,CAAA,EAAA;;;AErBH,MAAM,cAAc,GAAG,GAAG;AAC1B,MAAM,YAAY,GAAG,8BAA8B;AAEnD;AACA,SAAS,oBAAoB,GAAA;AAC3B,IAAA,QACE,OAAO,UAAU,KAAK,UAAU,IAAI,UAAU,CAAC,kCAAkC,CAAC,CAAC,OAAO;AAE9F;MASa,WAAW,CAAA;AACL,IAAA,KAAK,GAAG,MAAM,CAAC,YAAY,CAAC;AAC5B,IAAA,OAAO,GAAG,MAAM,CAA0B,UAAU,CAAC;AAEnD,IAAA,MAAM,GAAG,IAAI,CAAC,KAAK,CAAC,MAAM;AAE5B,IAAA,OAAO,GAAG,IAAI,GAAG,EAAkB;AACnC,IAAA,MAAM,GAAG,IAAI,GAAG,EAAqB;AAEtD,IAAA,WAAA,GAAA;QACE,gBAAgB,CAAC,MAAM,IAAI,CAAC,iBAAiB,EAAE,CAAC;IAClD;AAEA;;;;;;;;;AASG;IACK,iBAAiB,GAAA;AACvB,QAAA,MAAM,KAAK,GAAG,IAAI,CAAC,OAAO,CAAC,aAAa,CAAC,gBAAgB,CAAc,WAAW,CAAC;AACnF,QAAA,MAAM,OAAO,GAAG,IAAI,GAAG,EAAU;AAEjC,QAAA,KAAK,MAAM,IAAI,IAAI,KAAK,EAAE;YACxB,MAAM,EAAE,GAAG,IAAI,CAAC,OAAO,CAAC,OAAO,CAAC;YAChC,IAAI,CAAC,EAAE,EAAE;gBACP;YACF;AACA,YAAA,OAAO,CAAC,GAAG,CAAC,EAAE,CAAC;AAEf,YAAA,MAAM,GAAG,GAAG,IAAI,CAAC,SAAS;YAC1B,MAAM,QAAQ,GAAG,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,EAAE,CAAC;YACrC,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,EAAE,EAAE,GAAG,CAAC;YAEzB,IAAI,QAAQ,KAAK,SAAS,IAAI,QAAQ,KAAK,GAAG,IAAI,oBAAoB,EAAE,EAAE;gBACxE;YACF;YAEA,IAAI,CAAC,MAAM,CAAC,GAAG,CAAC,EAAE,CAAC,EAAE,MAAM,EAAE;AAC7B,YAAA,IAAI,CAAC,MAAM,CAAC,GAAG,CACb,EAAE,EACF,IAAI,CAAC,OAAO,CAAC,CAAC,EAAE,SAAS,EAAE,CAAA,WAAA,EAAc,QAAQ,GAAG,GAAG,CAAA,GAAA,CAAK,EAAE,EAAE,EAAE,SAAS,EAAE,MAAM,EAAE,CAAC,EAAE;AACtF,gBAAA,QAAQ,EAAE,cAAc;AACxB,gBAAA,MAAM,EAAE,YAAY;AACrB,aAAA,CAAC,CACH;QACH;AAEA,QAAA,KAAK,MAAM,EAAE,IAAI,CAAC,GAAG,IAAI,CAAC,OAAO,CAAC,IAAI,EAAE,CAAC,EAAE;YACzC,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,EAAE,CAAC,EAAE;AACpB,gBAAA,IAAI,CAAC,OAAO,CAAC,MAAM,CAAC,EAAE,CAAC;AACvB,gBAAA,IAAI,CAAC,MAAM,CAAC,MAAM,CAAC,EAAE,CAAC;YACxB;QACF;IACF;wGA1DW,WAAW,EAAA,IAAA,EAAA,EAAA,EAAA,MAAA,EAAA,EAAA,CAAA,eAAA,CAAA,SAAA,EAAA,CAAA;4FAAX,WAAW,EAAA,YAAA,EAAA,IAAA,EAAA,QAAA,EAAA,UAAA,EAAA,QAAA,EAAA,EAAA,EAAA,QAAA,EC3BxB,qTASA,EAAA,MAAA,EAAA,CAAA,k0BAAA,CAAA,EAAA,YAAA,EAAA,CAAA,EAAA,IAAA,EAAA,WAAA,EAAA,IAAA,EDaY,YAAY,EAAA,QAAA,EAAA,WAAA,EAAA,MAAA,EAAA,CAAA,MAAA,CAAA,EAAA,CAAA,EAAA,eAAA,EAAA,EAAA,CAAA,uBAAA,CAAA,MAAA,EAAA,CAAA;;4FAKX,WAAW,EAAA,UAAA,EAAA,CAAA;kBAPvB,SAAS;AACE,YAAA,IAAA,EAAA,CAAA,EAAA,QAAA,EAAA,UAAU,WACX,CAAC,YAAY,CAAC,EAAA,eAAA,EAGN,uBAAuB,CAAC,MAAM,EAAA,QAAA,EAAA,qTAAA,EAAA,MAAA,EAAA,CAAA,k0BAAA,CAAA,EAAA;;;AEDjD,MAAM,QAAQ,GAAe;AAC3B,IAAA,EAAE,EAAE,UAAU;AACd,IAAA,OAAO,EAAE,MAAK,EAAE,CAAC;AACjB,IAAA,cAAc,EAAE,OAAO,CAAC,OAAO,CAAuB,QAAQ,CAAC;CAChE;MAGY,cAAc,CAAA;AACR,IAAA,KAAK,GAAG,MAAM,CAAC,YAAY,CAAC;AAC5B,IAAA,MAAM,GAAG,MAAM,CAAC,cAAc,CAAC;AAC/B,IAAA,UAAU,GAAG,MAAM,CAAC,iBAAiB,CAAC;AACtC,IAAA,MAAM,GAAG,MAAM,CAAC,cAAc,CAAC;AAC/B,IAAA,QAAQ,GAAG,MAAM,CAAC,mBAAmB,CAAC;AACtC,IAAA,QAAQ,GAAG,MAAM,CAAC,QAAQ,CAAC;IAC3B,SAAS,GAAG,iBAAiB,CAAC,MAAM,CAAC,WAAW,CAAC,CAAC;AAE3D,IAAA,IAAI;AAEZ;;;AAGG;AACM,IAAA,OAAO,GAAG,IAAI,CAAC,KAAK,CAAC,OAAO;IAErC,OAAO,CAAC,OAAe,EAAE,OAAwB,EAAA;QAC/C,OAAO,IAAI,CAAC,IAAI,CAAC,SAAS,EAAE,OAAO,EAAE,OAAO,CAAC;IAC/C;IAEA,OAAO,CAAC,OAAe,EAAE,OAAwB,EAAA;QAC/C,OAAO,IAAI,CAAC,IAAI,CAAC,SAAS,EAAE,OAAO,EAAE,OAAO,CAAC;IAC/C;IAEA,MAAM,CAAC,OAAe,EAAE,OAAwB,EAAA;QAC9C,OAAO,IAAI,CAAC,IAAI,CAAC,QAAQ,EAAE,OAAO,EAAE,OAAO,CAAC;IAC9C;IAEA,IAAI,CAAC,OAAe,EAAE,OAAwB,EAAA;QAC5C,OAAO,IAAI,CAAC,IAAI,CAAC,MAAM,EAAE,OAAO,EAAE,OAAO,CAAC;IAC5C;AAEA,IAAA,IAAI,CAAC,IAAiB,EAAE,OAAe,EAAE,UAA0B,EAAE,EAAA;;AAEnE,QAAA,IAAI,CAAC,IAAI,CAAC,SAAS,EAAE;AACnB,YAAA,OAAO,QAAQ;QACjB;QAEA,IAAI,CAAC,UAAU,EAAE;QAEjB,MAAM,UAAU,GAAG,IAAI,CAAC,iBAAiB,CAAC,IAAI,EAAE,OAAO,CAAC;QACxD,MAAM,EAAE,EAAE,EAAE,cAAc,EAAE,GAAG,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC;YAC5C,IAAI;YACJ,OAAO;YACP,KAAK,EAAE,OAAO,CAAC,KAAK;YACpB,QAAQ,EAAE,OAAO,CAAC,QAAQ,IAAI,IAAI,CAAC,MAAM,CAAC,QAAQ;YAClD,QAAQ,EAAE,OAAO,CAAC,QAAQ,IAAI,IAAI,CAAC,MAAM,CAAC,QAAQ;YAClD,WAAW,EAAE,OAAO,CAAC,WAAW,IAAI,IAAI,CAAC,MAAM,CAAC,WAAW;YAC3D,MAAM,EAAE,OAAO,CAAC,MAAM;AACtB,YAAA,UAAU,EAAE,gBAAgB,CAAC,OAAO,CAAC,UAAU,CAAC;YAChD,UAAU;AACX,SAAA,CAAC;QAEF,IAAI,CAAC,UAAU,CAAC,QAAQ,CAAC,CAAC,OAAO,CAAC,KAAK,EAAE,OAAO,CAAC,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,UAAU,CAAC;QAEzF,OAAO;YACL,EAAE;YACF,cAAc;AACd,YAAA,OAAO,EAAE,MAAM,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,EAAE,EAAE,QAAQ,CAAC;SAChD;IACH;IAEA,UAAU,GAAA;AACR,QAAA,IAAI,CAAC,KAAK,CAAC,UAAU,EAAE;IACzB;IAEQ,iBAAiB,CAAC,IAAiB,EAAE,OAAuB,EAAA;AAClE,QAAA,IAAI,OAAO,CAAC,UAAU,EAAE;YACtB,OAAO,OAAO,CAAC,UAAU;QAC3B;AACA,QAAA,MAAM,UAAU,GAAG,IAAI,CAAC,MAAM,CAAC,KAAK,CAAC,IAAI,CAAC,EAAE,UAAU;QACtD,IAAI,UAAU,EAAE;AACd,YAAA,OAAO,UAAU;QACnB;;QAEA,OAAO,IAAI,KAAK,QAAQ,GAAG,WAAW,GAAG,QAAQ;IACnD;IAEQ,UAAU,GAAA;AAChB,QAAA,IAAI,IAAI,CAAC,IAAI,EAAE;YACb;QACF;AACA,QAAA,IAAI,CAAC,IAAI,GAAG,eAAe,CAAC,WAAW,EAAE,EAAE,mBAAmB,EAAE,IAAI,CAAC,QAAQ,EAAE,CAAC;QAChF,IAAI,CAAC,MAAM,CAAC,UAAU,CAAC,IAAI,CAAC,IAAI,CAAC,QAAQ,CAAC;AAC1C,QAAA,IAAI,CAAC,QAAQ,CAAC,IAAI,CAAC,WAAW,CAAC,IAAI,CAAC,IAAI,CAAC,QAAQ,CAAC,aAAa,CAAC;IAClE;IAEA,WAAW,GAAA;AACT,QAAA,IAAI,IAAI,CAAC,IAAI,EAAE;YACb,IAAI,CAAC,MAAM,CAAC,UAAU,CAAC,IAAI,CAAC,IAAI,CAAC,QAAQ,CAAC;AAC1C,YAAA,IAAI,CAAC,IAAI,CAAC,OAAO,EAAE;AACnB,YAAA,IAAI,CAAC,IAAI,GAAG,SAAS;QACvB;IACF;wGA9FW,cAAc,EAAA,IAAA,EAAA,EAAA,EAAA,MAAA,EAAA,EAAA,CAAA,eAAA,CAAA,UAAA,EAAA,CAAA;AAAd,IAAA,OAAA,KAAA,GAAA,EAAA,CAAA,qBAAA,CAAA,EAAA,UAAA,EAAA,QAAA,EAAA,OAAA,EAAA,SAAA,EAAA,QAAA,EAAA,EAAA,EAAA,IAAA,EAAA,cAAc,cADD,MAAM,EAAA,CAAA;;4FACnB,cAAc,EAAA,UAAA,EAAA,CAAA;kBAD1B,UAAU;mBAAC,EAAE,UAAU,EAAE,MAAM,EAAE;;AAkGlC,SAAS,gBAAgB,CAAC,UAAyC,EAAA;IACjE,IAAI,CAAC,UAAU,EAAE;AACf,QAAA,OAAO,EAAE;IACX;AACA,IAAA,OAAO,KAAK,CAAC,OAAO,CAAC,UAAU,CAAC,GAAG,UAAU,GAAG,CAAC,UAAU,CAAC;AAC9D;;ACrIA;;AAEG;;ACFH;;AAEG;;;;"}
|
|
1
|
+
{"version":3,"file":"snackng.mjs","sources":["../../../projects/snackng/src/lib/snackng.config.ts","../../../projects/snackng/src/lib/live-region.ts","../../../projects/snackng/src/lib/icons.ts","../../../projects/snackng/src/lib/snackng.store.ts","../../../projects/snackng/src/lib/snackng-toast/snackng-toast.ts","../../../projects/snackng/src/lib/snackng-toast/snackng-toast.html","../../../projects/snackng/src/lib/snackng-host/snackng-host.ts","../../../projects/snackng/src/lib/snackng-host/snackng-host.html","../../../projects/snackng/src/lib/snackng.service.ts","../../../projects/snackng/src/public-api.ts","../../../projects/snackng/src/snackng.ts"],"sourcesContent":["import { InjectionToken, EnvironmentProviders, makeEnvironmentProviders } from '@angular/core';\nimport {\n SnackngEffect,\n SnackngOverflow,\n SnackngPosition,\n SnackngStyle,\n SnackngTypeDef,\n} from './snackng.model';\n\nexport interface SnackngConfig {\n duration: number;\n position: SnackngPosition;\n /** Maximum toasts on screen at once. Extras are handled per `overflow`. */\n max: number;\n /**\n * What happens when a toast arrives while `max` are already visible.\n * `'queue'` waits for a free slot — nothing is ever lost, but a flood becomes\n * a long parade. `'dismiss-oldest'` evicts the oldest to make room, so the\n * newest always wins; useful when an interceptor can emit toasts in bursts.\n */\n overflow: SnackngOverflow;\n /**\n * Milliseconds between releasing consecutive queued toasts. Staggering keeps\n * a burst from firing every enter animation at the same instant. `0` releases\n * as many as fit at once.\n */\n stagger: number;\n pauseOnHover: boolean;\n dismissible: boolean;\n /** Default glass preset for every toast. Overridable per call via `options.style`. */\n style: SnackngStyle;\n /** Default surface light effect. Overridable per call via `options.effect`. */\n effect: SnackngEffect;\n types: Record<string, SnackngTypeDef>;\n}\n\nexport const SNACKNG_DEFAULTS: SnackngConfig = {\n duration: 5000,\n position: 'top-end',\n max: 5,\n overflow: 'queue',\n stagger: 90,\n pauseOnHover: true,\n dismissible: false,\n style: 'glass',\n effect: 'drift',\n types: {},\n};\n\nexport const SNACKNG_CONFIG = new InjectionToken<SnackngConfig>('snackng.config', {\n providedIn: 'root',\n factory: () => SNACKNG_DEFAULTS,\n});\n\n/**\n * Optional. Without it the defaults above apply, so `npm i snackng` and calling\n * the service is enough to get working toasts.\n */\nexport function provideSnackng(config: Partial<SnackngConfig> = {}): EnvironmentProviders {\n return makeEnvironmentProviders([\n {\n provide: SNACKNG_CONFIG,\n useValue: {\n ...SNACKNG_DEFAULTS,\n ...config,\n types: { ...SNACKNG_DEFAULTS.types, ...config.types },\n } satisfies SnackngConfig,\n },\n ]);\n}\n","import { DOCUMENT, Injectable, OnDestroy, inject } from '@angular/core';\nimport { SnackngPoliteness } from './snackng.model';\n\n/**\n * Announces toast text to screen readers, in place of the CDK `LiveAnnouncer`.\n *\n * The toast markup carries no `aria-live` of its own: text is copied into this\n * off-screen region instead. Keep it that way — a live region nested inside\n * another throws `HierarchyRequestError` when the outer one relocates its\n * content.\n */\n@Injectable({ providedIn: 'root' })\nexport class SnackngLiveRegion implements OnDestroy {\n private readonly document = inject(DOCUMENT);\n private region?: HTMLElement;\n private clearTimer?: ReturnType<typeof setTimeout>;\n\n announce(message: string, politeness: SnackngPoliteness = 'polite'): void {\n if (politeness === 'off' || !message) {\n return;\n }\n\n const region = this.ensureRegion();\n region.setAttribute('aria-live', politeness);\n\n // Clearing first forces assistive tech to treat an identical repeated\n // message as a new announcement.\n region.textContent = '';\n clearTimeout(this.clearTimer);\n this.clearTimer = setTimeout(() => (region.textContent = message), 100);\n }\n\n private ensureRegion(): HTMLElement {\n if (this.region?.isConnected) {\n return this.region;\n }\n\n const region = this.document.createElement('div');\n region.setAttribute('aria-atomic', 'true');\n region.setAttribute('aria-live', 'polite');\n region.classList.add('sng-live-region');\n // Visually hidden but still announced: `display:none` would silence it.\n region.style.cssText =\n 'position:absolute;width:1px;height:1px;margin:-1px;padding:0;overflow:hidden;' +\n 'clip:rect(0 0 0 0);clip-path:inset(50%);white-space:nowrap;border:0;';\n\n this.document.body.appendChild(region);\n this.region = region;\n return region;\n }\n\n ngOnDestroy(): void {\n clearTimeout(this.clearTimer);\n this.region?.remove();\n this.region = undefined;\n }\n}\n","import { SnackngBuiltInType } from './snackng.model';\n\n/**\n * Path data for the built-in glyphs, inlined so the package never requires an\n * icon font. Bound with `[attr.d]`, so no sanitizer is involved.\n */\nexport const SNACKNG_ICONS: Record<SnackngBuiltInType, string> = {\n success: 'M9 16.17 4.83 12l-1.42 1.41L9 19 21 7l-1.41-1.41z',\n warning: 'M1 21h22L12 2zm12-3h-2v-2h2zm0-4h-2v-4h2z',\n danger:\n 'M12 2C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2m1 15h-2v-2h2zm0-4h-2V7h2z',\n info: 'M12 2C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2m1 15h-2v-6h2zm0-8h-2V7h2z',\n};\n\nexport const SNACKNG_CLOSE_ICON =\n 'M19 6.41 17.59 5 12 10.59 6.41 5 5 6.41 10.59 12 5 17.59 6.41 19 12 13.41 17.59 19 19 17.59 13.41 12z';\n\n","import { Injectable, OnDestroy, computed, inject, signal } from '@angular/core';\nimport { SNACKNG_CONFIG } from './snackng.config';\nimport {\n SnackngAction,\n SnackngDismissReason,\n SnackngEffect,\n SnackngPoliteness,\n SnackngPosition,\n SnackngStyle,\n SnackngType,\n} from './snackng.model';\n\nexport interface SnackngItem {\n readonly id: string;\n readonly type: SnackngType;\n readonly message: string;\n readonly title?: string;\n readonly duration: number;\n readonly position: SnackngPosition;\n readonly dismissible: boolean;\n readonly action?: SnackngAction;\n readonly style: SnackngStyle;\n readonly effect: SnackngEffect;\n readonly panelClass: readonly string[];\n readonly politeness: SnackngPoliteness;\n /** Set while the exit animation runs; the item is still in the DOM. */\n readonly leaving: boolean;\n}\n\nexport interface SnackngStack {\n readonly position: SnackngPosition;\n readonly items: readonly SnackngItem[];\n}\n\n/**\n * Toast state, and the pacing of how toasts reach the screen.\n *\n * Producer/consumer: `add` only enqueues; `pump` is the sole consumer moving\n * items into view as capacity allows. Releasing directly would let a burst\n * render in one tick, appearing and evicting itself in the same frame.\n *\n * Separate from `SnackngService` so the service can create the host while the\n * host reads state, without an import cycle.\n */\n@Injectable({ providedIn: 'root' })\nexport class SnackngStore implements OnDestroy {\n private readonly config = inject(SNACKNG_CONFIG);\n private readonly resolvers = new Map<string, (reason: SnackngDismissReason) => void>();\n private readonly reasons = new Map<string, SnackngDismissReason>();\n private pumpTimer?: ReturnType<typeof setTimeout>;\n private lastReleaseAt = 0;\n private counter = 0;\n\n /** Toasts currently rendered (including those playing their exit animation). */\n readonly items = signal<readonly SnackngItem[]>([]);\n /** Toasts accepted but still waiting for a free slot. */\n readonly queue = signal<readonly SnackngItem[]>([]);\n readonly pending = computed(() => this.queue().length);\n\n readonly stacks = computed<readonly SnackngStack[]>(() => {\n const byPosition = new Map<SnackngPosition, SnackngItem[]>();\n for (const item of this.items()) {\n const bucket = byPosition.get(item.position);\n bucket ? bucket.push(item) : byPosition.set(item.position, [item]);\n }\n return [...byPosition].map(([position, items]) => ({ position, items }));\n });\n\n add(item: Omit<SnackngItem, 'id' | 'leaving'>): {\n id: string;\n afterDismissed: Promise<SnackngDismissReason>;\n } {\n const id = `sng-${++this.counter}`;\n const afterDismissed = new Promise<SnackngDismissReason>((resolve) =>\n this.resolvers.set(id, resolve),\n );\n\n this.queue.update((queue) => [...queue, { ...item, id, leaving: false }]);\n this.pump();\n\n return { id, afterDismissed };\n }\n\n /**\n * Starts the exit animation; the item leaves the DOM on `finalize`. A toast\n * still queued is dropped outright, having never been shown.\n */\n dismiss(id: string, reason: SnackngDismissReason = 'manual'): void {\n if (this.queue().some((item) => item.id === id)) {\n this.queue.update((queue) => queue.filter((item) => item.id !== id));\n this.resolve(id, reason);\n return;\n }\n\n const target = this.items().find((item) => item.id === id);\n if (!target || target.leaving) {\n return;\n }\n this.reasons.set(id, reason);\n this.items.update((items) => items.map((i) => (i.id === id ? { ...i, leaving: true } : i)));\n }\n\n /** Removes the item and resolves its `afterDismissed` promise. */\n finalize(id: string): void {\n if (!this.resolvers.has(id)) {\n return;\n }\n this.items.update((items) => items.filter((item) => item.id !== id));\n this.resolve(id, this.reasons.get(id) ?? 'manual');\n this.reasons.delete(id);\n this.pump();\n }\n\n dismissAll(reason: SnackngDismissReason = 'manual'): void {\n // Drain the queue first, or it would march onto a screen just cleared.\n clearTimeout(this.pumpTimer);\n this.pumpTimer = undefined;\n for (const item of this.queue()) {\n this.resolve(item.id, reason);\n }\n this.queue.set([]);\n\n for (const item of this.items()) {\n this.dismiss(item.id, reason);\n }\n }\n\n /** The consumer. Runs on every add and every finalize. */\n private pump(): void {\n if (this.pumpTimer) {\n return; // a staggered release is already pending\n }\n\n while (this.queue().length) {\n // Counts leaving toasts too: they hold their slot until finalize removes\n // them, so ignoring them reads an expiring batch as free space.\n const onScreen = this.items();\n\n if (onScreen.length >= this.config.max) {\n if (this.config.overflow === 'dismiss-oldest') {\n // Evicting an already-leaving toast frees nothing.\n const victim = onScreen.find((item) => !item.leaving);\n if (victim) {\n this.dismiss(victim.id, 'replaced');\n }\n }\n return;\n }\n\n // Spaced by elapsed time, not queue length: callers add one at a time, so\n // a burst arrives as N pumps of one item and would never wait.\n const wait = this.config.stagger - (Date.now() - this.lastReleaseAt);\n if (this.config.stagger > 0 && wait > 0) {\n this.pumpTimer = setTimeout(() => {\n this.pumpTimer = undefined;\n this.pump();\n }, wait);\n return;\n }\n\n const [next, ...rest] = this.queue();\n this.queue.set(rest);\n this.items.update((items) => [...items, next]);\n this.lastReleaseAt = Date.now();\n }\n }\n\n private resolve(id: string, reason: SnackngDismissReason): void {\n this.resolvers.get(id)?.(reason);\n this.resolvers.delete(id);\n }\n\n ngOnDestroy(): void {\n clearTimeout(this.pumpTimer);\n }\n}\n","import {\n ChangeDetectionStrategy,\n Component,\n ElementRef,\n NgZone,\n OnDestroy,\n computed,\n effect,\n inject,\n input,\n} from '@angular/core';\nimport { DomSanitizer, SafeHtml } from '@angular/platform-browser';\nimport { SNACKNG_CONFIG } from '../snackng.config';\nimport { SNACKNG_CLOSE_ICON, SNACKNG_ICONS } from '../icons';\nimport { SnackngBuiltInType } from '../snackng.model';\nimport { SnackngItem, SnackngStore } from '../snackng.store';\n\nconst EXIT_DURATION = 200;\n/** Guards against `animationend` never firing (reduced motion, jsdom, backgrounded tab). */\nconst EXIT_FALLBACK = EXIT_DURATION + 150;\n\n@Component({\n selector: 'sng-toast',\n templateUrl: './snackng-toast.html',\n styleUrl: './snackng-toast.css',\n changeDetection: ChangeDetectionStrategy.OnPush,\n host: {\n '[class]': 'hostClasses()',\n '(mouseenter)': 'pause()',\n '(mouseleave)': 'resume()',\n '(focusin)': 'pause()',\n '(focusout)': 'resume()',\n },\n})\nexport class SnackngToast implements OnDestroy {\n readonly item = input.required<SnackngItem>();\n\n private readonly store = inject(SnackngStore);\n private readonly config = inject(SNACKNG_CONFIG);\n private readonly sanitizer = inject(DomSanitizer);\n private readonly element = inject<ElementRef<HTMLElement>>(ElementRef);\n private readonly zone = inject(NgZone);\n\n private timer?: ReturnType<typeof setTimeout>;\n private exitTimer?: ReturnType<typeof setTimeout>;\n private armed = false;\n private remaining = 0;\n private startedAt = 0;\n private glareBound = false;\n\n protected readonly hostClasses = computed(() => {\n const item = this.item();\n const classes = [\n 'sng-toast',\n `sng--${item.type}`,\n `sng-at--${item.position}`,\n `sng-style--${item.style}`,\n item.leaving ? 'sng-leaving' : 'sng-entering',\n ...item.panelClass,\n ];\n if (item.effect === 'drift' || item.effect === 'both') {\n classes.push('sng-fx--drift');\n }\n if (item.effect === 'glare' || item.effect === 'both') {\n classes.push('sng-fx--glare');\n }\n return classes.join(' ');\n });\n\n /** Whether this toast tracks the pointer for the cursor glare effect. */\n protected readonly glareEnabled = computed(() => {\n const fx = this.item().effect;\n return fx === 'glare' || fx === 'both';\n });\n\n /** Null when the type brings its own SVG; unknown types fall back to `info`. */\n protected readonly iconPath = computed<string | null>(() => {\n const type = this.item().type;\n if (this.config.types[type]?.icon) {\n return null;\n }\n return SNACKNG_ICONS[type as SnackngBuiltInType] ?? SNACKNG_ICONS.info;\n });\n\n protected readonly closeIconPath = computed<string | null>(() => {\n return SNACKNG_CLOSE_ICON;\n });\n\n /** Custom types may supply raw SVG markup, trusted as authored config. */\n protected readonly iconSvg = computed<SafeHtml | null>(() => {\n const custom = this.config.types[this.item().type]?.icon;\n return custom ? this.sanitizer.bypassSecurityTrustHtml(custom) : null;\n });\n\n constructor() {\n effect(() => {\n if (this.item().leaving) {\n this.stopTimer();\n this.scheduleFinalize();\n } else {\n this.startTimer();\n }\n });\n\n // Bind pointer tracking once, only if this toast uses the glare effect.\n effect(() => {\n if (this.glareEnabled() && !this.glareBound) {\n this.bindGlare();\n }\n });\n }\n\n /** Writes the cursor position into CSS vars the glare layer reads. Runs\n * outside Angular — it only mutates style, no change detection needed. */\n private bindGlare(): void {\n this.glareBound = true;\n const el = this.element.nativeElement;\n this.zone.runOutsideAngular(() => {\n el.addEventListener('pointermove', this.onGlareMove);\n el.addEventListener('pointerleave', this.onGlareLeave);\n });\n }\n\n private readonly onGlareMove = (event: PointerEvent): void => {\n const el = this.element.nativeElement;\n const rect = el.getBoundingClientRect();\n if (!rect.width || !rect.height) {\n return;\n }\n el.style.setProperty('--sng-glare-x', `${((event.clientX - rect.left) / rect.width) * 100}%`);\n el.style.setProperty('--sng-glare-y', `${((event.clientY - rect.top) / rect.height) * 100}%`);\n el.style.setProperty('--sng-glare-op', '1');\n };\n\n private readonly onGlareLeave = (): void => {\n this.element.nativeElement.style.setProperty('--sng-glare-op', '0');\n };\n\n protected onAction(): void {\n const action = this.item().action;\n action?.handler?.();\n if (action?.dismissOnClick !== false) {\n this.store.dismiss(this.item().id, 'action');\n }\n }\n\n protected onDismiss(): void {\n this.store.dismiss(this.item().id, 'manual');\n }\n\n protected pause(): void {\n if (!this.config.pauseOnHover || !this.timer) {\n return;\n }\n this.remaining -= Date.now() - this.startedAt;\n clearTimeout(this.timer);\n this.timer = undefined;\n }\n\n protected resume(): void {\n if (!this.config.pauseOnHover || this.timer || this.remaining <= 0 || this.item().leaving) {\n return;\n }\n this.arm(this.remaining);\n }\n\n /** Arms once per toast: re-running on later item updates would reset a paused timer. */\n private startTimer(): void {\n const duration = this.item().duration;\n if (this.armed || duration <= 0) {\n return;\n }\n this.armed = true;\n this.remaining = duration;\n this.arm(duration);\n }\n\n private arm(ms: number): void {\n this.startedAt = Date.now();\n this.timer = setTimeout(() => this.store.dismiss(this.item().id, 'timeout'), ms);\n }\n\n private stopTimer(): void {\n clearTimeout(this.timer);\n this.timer = undefined;\n }\n\n private scheduleFinalize(): void {\n if (this.exitTimer) {\n return;\n }\n const finalize = () => {\n clearTimeout(this.exitTimer);\n this.store.finalize(this.item().id);\n };\n this.element.nativeElement.addEventListener('animationend', finalize, { once: true });\n this.exitTimer = setTimeout(finalize, EXIT_FALLBACK);\n }\n\n ngOnDestroy(): void {\n this.stopTimer();\n clearTimeout(this.exitTimer);\n if (this.glareBound) {\n const el = this.element.nativeElement;\n el.removeEventListener('pointermove', this.onGlareMove);\n el.removeEventListener('pointerleave', this.onGlareLeave);\n }\n }\n}\n","@let toast = item();\n\n<!-- No aria-live here: SnackngLiveRegion announces instead. -->\n@if (glareEnabled()) {\n <div class=\"sng-glare\" aria-hidden=\"true\"></div>\n}\n\n<div class=\"sng-icon\" aria-hidden=\"true\">\n @if (iconPath(); as path) {\n <svg viewBox=\"0 0 24 24\" focusable=\"false\">\n <path [attr.d]=\"path\" fill=\"currentColor\" />\n </svg>\n } @else {\n <span class=\"sng-icon-custom\" [innerHTML]=\"iconSvg()\"></span>\n }\n</div>\n\n<div class=\"sng-body\">\n <div class=\"sng-header\">\n @if (toast.title) {\n <p class=\"sng-title\">{{ toast.title }}</p>\n }\n\n @if (closeIconPath(); as closePath) {\n <div class=\"sng-close-icon\" (click)=\"onDismiss()\">\n <svg viewBox=\"0 0 24 24\" focusable=\"false\">\n <path [attr.d]=\"closePath\" fill=\"currentColor\" />\n </svg>\n </div>\n }\n </div>\n\n <p class=\"sng-message\">{{ toast.message }}</p>\n</div>\n\n@if (toast.action; as action) {\n <button type=\"button\" class=\"sng-action\" (click)=\"onAction()\">{{ action.label }}</button>\n}\n\n@if (toast.dismissible) {\n <button type=\"button\" class=\"sng-close\" (click)=\"onDismiss()\" aria-label=\"Close notification\">\n <svg viewBox=\"0 0 24 24\" aria-hidden=\"true\" focusable=\"false\">\n <path\n d=\"M19 6.41 17.59 5 12 10.59 6.41 5 5 6.41 10.59 12 5 17.59 6.41 19 12 13.41 17.59 19 19 17.59 13.41 12z\"\n fill=\"currentColor\"\n />\n </svg>\n </button>\n}\n","import {\n ChangeDetectionStrategy,\n Component,\n ElementRef,\n afterEveryRender,\n inject,\n} from '@angular/core';\nimport { SnackngToast } from '../snackng-toast/snackng-toast';\nimport { SnackngStore } from '../snackng.store';\n\nconst SHIFT_DURATION = 260;\nconst SHIFT_EASING = 'cubic-bezier(0.4, 0, 0.2, 1)';\n\n/** Guarded: test environments such as jsdom render without `matchMedia`. */\nfunction prefersReducedMotion(): boolean {\n return (\n typeof matchMedia === 'function' && matchMedia('(prefers-reduced-motion: reduce)').matches\n );\n}\n\n@Component({\n selector: 'sng-host',\n imports: [SnackngToast],\n templateUrl: './snackng-host.html',\n styleUrl: './snackng-host.css',\n changeDetection: ChangeDetectionStrategy.OnPush,\n})\nexport class SnackngHost {\n private readonly store = inject(SnackngStore);\n private readonly element = inject<ElementRef<HTMLElement>>(ElementRef);\n\n protected readonly stacks = this.store.stacks;\n\n private readonly offsets = new Map<string, number>();\n private readonly shifts = new Map<string, Animation>();\n\n constructor() {\n afterEveryRender(() => this.absorbLayoutShift());\n }\n\n /**\n * FLIP: offset each slot back to where it was, then animate that offset away,\n * so neighbours glide into a freed gap instead of snapping.\n *\n * Measured with `offsetTop` rather than `getBoundingClientRect`: the latter\n * includes transforms and would read an in-flight shift instead of the layout\n * beneath it. Stacks are full height and top-anchored, so a slot's offsetTop\n * only changes when it truly moves. Applied to the slot, never the toast, to\n * leave the toast's own enter/exit transform alone.\n */\n private absorbLayoutShift(): void {\n const slots = this.element.nativeElement.querySelectorAll<HTMLElement>('.sng-slot');\n const present = new Set<string>();\n\n for (const slot of slots) {\n const id = slot.dataset['sngId'];\n if (!id) {\n continue;\n }\n present.add(id);\n\n const top = slot.offsetTop;\n const previous = this.offsets.get(id);\n this.offsets.set(id, top);\n\n if (previous === undefined || previous === top || prefersReducedMotion()) {\n continue;\n }\n\n this.shifts.get(id)?.cancel();\n this.shifts.set(\n id,\n slot.animate([{ transform: `translateY(${previous - top}px)` }, { transform: 'none' }], {\n duration: SHIFT_DURATION,\n easing: SHIFT_EASING,\n }),\n );\n }\n\n for (const id of [...this.offsets.keys()]) {\n if (!present.has(id)) {\n this.offsets.delete(id);\n this.shifts.delete(id);\n }\n }\n }\n}\n","@for (stack of stacks(); track stack.position) {\n <div class=\"sng-stack\" [class]=\"'sng-stack--' + stack.position\">\n @for (item of stack.items; track item.id) {\n <div class=\"sng-slot\" [attr.data-sng-id]=\"item.id\">\n <sng-toast [item]=\"item\" />\n </div>\n }\n </div>\n}\n","import {\n ApplicationRef,\n ComponentRef,\n DOCUMENT,\n EnvironmentInjector,\n Injectable,\n OnDestroy,\n PLATFORM_ID,\n createComponent,\n inject,\n} from '@angular/core';\nimport { isPlatformBrowser } from '@angular/common';\nimport { SNACKNG_CONFIG } from './snackng.config';\nimport { SnackngLiveRegion } from './live-region';\nimport { SnackngHost } from './snackng-host/snackng-host';\nimport { SnackngStore } from './snackng.store';\nimport {\n SnackngDismissReason,\n SnackngOptions,\n SnackngPoliteness,\n SnackngRef,\n SnackngStyle,\n SnackngType,\n} from './snackng.model';\n\nconst NOOP_REF: SnackngRef = {\n id: 'sng-noop',\n dismiss: () => {},\n afterDismissed: Promise.resolve<SnackngDismissReason>('manual'),\n};\n\n/**\n * A variant shortcut (`success`, `danger`, …). Callable directly, and also\n * carries a method per built-in glass preset so you can pick a look inline:\n * `snackng.success.solid('Saved')` is sugar for\n * `snackng.success('Saved', { style: 'solid' })`.\n */\nexport interface SnackngVariantFn {\n (message: string, options?: SnackngOptions): SnackngRef;\n glass(message: string, options?: SnackngOptions): SnackngRef;\n solid(message: string, options?: SnackngOptions): SnackngRef;\n translucent(message: string, options?: SnackngOptions): SnackngRef;\n transparent(message: string, options?: SnackngOptions): SnackngRef;\n frosted(message: string, options?: SnackngOptions): SnackngRef;\n flat(message: string, options?: SnackngOptions): SnackngRef;\n}\n\n@Injectable({ providedIn: 'root' })\nexport class SnackngService implements OnDestroy {\n private readonly store = inject(SnackngStore);\n private readonly config = inject(SNACKNG_CONFIG);\n private readonly liveRegion = inject(SnackngLiveRegion);\n private readonly appRef = inject(ApplicationRef);\n private readonly injector = inject(EnvironmentInjector);\n private readonly document = inject(DOCUMENT);\n private readonly isBrowser = isPlatformBrowser(inject(PLATFORM_ID));\n\n private host?: ComponentRef<SnackngHost>;\n\n /**\n * Toasts accepted but still waiting for a free slot under `overflow: 'queue'`.\n * Useful for a \"3 more\" affordance.\n */\n readonly pending = this.store.pending;\n\n /** Callable shortcut with per-preset sugar, e.g. `success.solid('Saved')`. */\n readonly success = this.variant('success');\n readonly warning = this.variant('warning');\n readonly danger = this.variant('danger');\n readonly info = this.variant('info');\n\n /** Builds a callable variant carrying a method per built-in preset. */\n private variant(type: SnackngType): SnackngVariantFn {\n const withStyle =\n (style?: SnackngStyle) =>\n (message: string, options?: SnackngOptions): SnackngRef =>\n this.show(type, message, style ? { ...options, style } : options);\n\n return Object.assign(withStyle(), {\n glass: withStyle('glass'),\n solid: withStyle('solid'),\n translucent: withStyle('translucent'),\n transparent: withStyle('transparent'),\n frosted: withStyle('frosted'),\n flat: withStyle('flat'),\n });\n }\n\n show(type: SnackngType, message: string, options: SnackngOptions = {}): SnackngRef {\n // Client-only: no-op rather than throw on `document` during SSR.\n if (!this.isBrowser) {\n return NOOP_REF;\n }\n\n this.ensureHost();\n\n const politeness = this.resolvePoliteness(type, options);\n const { id, afterDismissed } = this.store.add({\n type,\n message,\n title: options.title,\n duration: options.duration ?? this.config.duration,\n position: options.position ?? this.config.position,\n dismissible: options.dismissible ?? this.config.dismissible,\n action: options.action,\n style: options.style ?? this.config.style,\n effect: options.effect ?? this.config.effect,\n panelClass: normalizeClasses(options.panelClass),\n politeness,\n });\n\n this.liveRegion.announce([options.title, message].filter(Boolean).join('. '), politeness);\n\n return {\n id,\n afterDismissed,\n dismiss: () => this.store.dismiss(id, 'manual'),\n };\n }\n\n dismissAll(): void {\n this.store.dismissAll();\n }\n\n private resolvePoliteness(type: SnackngType, options: SnackngOptions): SnackngPoliteness {\n if (options.politeness) {\n return options.politeness;\n }\n const configured = this.config.types[type]?.politeness;\n if (configured) {\n return configured;\n }\n // Errors interrupt; everything else waits for a pause.\n return type === 'danger' ? 'assertive' : 'polite';\n }\n\n private ensureHost(): void {\n if (this.host) {\n return;\n }\n this.host = createComponent(SnackngHost, { environmentInjector: this.injector });\n this.appRef.attachView(this.host.hostView);\n this.document.body.appendChild(this.host.location.nativeElement);\n }\n\n ngOnDestroy(): void {\n if (this.host) {\n this.appRef.detachView(this.host.hostView);\n this.host.destroy();\n this.host = undefined;\n }\n }\n}\n\nfunction normalizeClasses(panelClass: string | string[] | undefined): string[] {\n if (!panelClass) {\n return [];\n }\n return Array.isArray(panelClass) ? panelClass : [panelClass];\n}\n","/*\n * Public API Surface of snackng\n */\n\nexport { SnackngService } from './lib/snackng.service';\nexport type { SnackngVariantFn } from './lib/snackng.service';\nexport { provideSnackng, SNACKNG_CONFIG, SNACKNG_DEFAULTS } from './lib/snackng.config';\nexport type { SnackngConfig } from './lib/snackng.config';\nexport type {\n SnackngAction,\n SnackngBuiltInStyle,\n SnackngBuiltInType,\n SnackngDismissReason,\n SnackngEffect,\n SnackngOptions,\n SnackngPoliteness,\n SnackngPosition,\n SnackngRef,\n SnackngStyle,\n SnackngType,\n SnackngTypeDef,\n} from './lib/snackng.model';\n","/**\n * Generated bundle index. Do not edit.\n */\n\nexport * from './public-api';\n"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "snackng",
|
|
3
|
-
"version": "0.2
|
|
3
|
+
"version": "1.0.2",
|
|
4
4
|
"description": "The easiest way to show toasts and snackbars in Angular with a glass design, zero UI dependencies and CSS-variable theming. No Material, no CDK, no Tailwind required.",
|
|
5
5
|
"author": "xgreymx",
|
|
6
6
|
"keywords": [
|
package/types/snackng.d.ts
CHANGED
|
@@ -7,6 +7,12 @@ type SnackngType = SnackngBuiltInType | (string & {});
|
|
|
7
7
|
type SnackngPosition = 'top-start' | 'top-center' | 'top-end' | 'bottom-start' | 'bottom-center' | 'bottom-end';
|
|
8
8
|
type SnackngPoliteness = 'polite' | 'assertive' | 'off';
|
|
9
9
|
type SnackngOverflow = 'queue' | 'dismiss-oldest';
|
|
10
|
+
/** Built-in glass presets. `(string & {})` keeps autocomplete while allowing a
|
|
11
|
+
* custom preset defined in CSS via a `.sng-style--<name>` rule. */
|
|
12
|
+
type SnackngBuiltInStyle = 'glass' | 'solid' | 'translucent' | 'transparent' | 'frosted' | 'flat';
|
|
13
|
+
type SnackngStyle = SnackngBuiltInStyle | (string & {});
|
|
14
|
+
/** Idle/interactive light effect on the glass surface. */
|
|
15
|
+
type SnackngEffect = 'none' | 'drift' | 'glare' | 'both';
|
|
10
16
|
/** `'replaced'` means the toast was evicted under `overflow: 'dismiss-oldest'`. */
|
|
11
17
|
type SnackngDismissReason = 'timeout' | 'action' | 'manual' | 'replaced';
|
|
12
18
|
interface SnackngAction {
|
|
@@ -39,6 +45,10 @@ interface SnackngOptions {
|
|
|
39
45
|
dismissible?: boolean;
|
|
40
46
|
/** Overrides the politeness derived from the type. */
|
|
41
47
|
politeness?: SnackngPoliteness;
|
|
48
|
+
/** Glass preset for this toast. Overrides the global `style`. Defaults to `'glass'`. */
|
|
49
|
+
style?: SnackngStyle;
|
|
50
|
+
/** Surface light effect for this toast. Overrides the global `effect`. Defaults to `'drift'`. */
|
|
51
|
+
effect?: SnackngEffect;
|
|
42
52
|
panelClass?: string | string[];
|
|
43
53
|
}
|
|
44
54
|
interface SnackngRef {
|
|
@@ -47,6 +57,21 @@ interface SnackngRef {
|
|
|
47
57
|
readonly afterDismissed: Promise<SnackngDismissReason>;
|
|
48
58
|
}
|
|
49
59
|
|
|
60
|
+
/**
|
|
61
|
+
* A variant shortcut (`success`, `danger`, …). Callable directly, and also
|
|
62
|
+
* carries a method per built-in glass preset so you can pick a look inline:
|
|
63
|
+
* `snackng.success.solid('Saved')` is sugar for
|
|
64
|
+
* `snackng.success('Saved', { style: 'solid' })`.
|
|
65
|
+
*/
|
|
66
|
+
interface SnackngVariantFn {
|
|
67
|
+
(message: string, options?: SnackngOptions): SnackngRef;
|
|
68
|
+
glass(message: string, options?: SnackngOptions): SnackngRef;
|
|
69
|
+
solid(message: string, options?: SnackngOptions): SnackngRef;
|
|
70
|
+
translucent(message: string, options?: SnackngOptions): SnackngRef;
|
|
71
|
+
transparent(message: string, options?: SnackngOptions): SnackngRef;
|
|
72
|
+
frosted(message: string, options?: SnackngOptions): SnackngRef;
|
|
73
|
+
flat(message: string, options?: SnackngOptions): SnackngRef;
|
|
74
|
+
}
|
|
50
75
|
declare class SnackngService implements OnDestroy {
|
|
51
76
|
private readonly store;
|
|
52
77
|
private readonly config;
|
|
@@ -61,10 +86,13 @@ declare class SnackngService implements OnDestroy {
|
|
|
61
86
|
* Useful for a "3 more" affordance.
|
|
62
87
|
*/
|
|
63
88
|
readonly pending: i0.Signal<number>;
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
89
|
+
/** Callable shortcut with per-preset sugar, e.g. `success.solid('Saved')`. */
|
|
90
|
+
readonly success: SnackngVariantFn;
|
|
91
|
+
readonly warning: SnackngVariantFn;
|
|
92
|
+
readonly danger: SnackngVariantFn;
|
|
93
|
+
readonly info: SnackngVariantFn;
|
|
94
|
+
/** Builds a callable variant carrying a method per built-in preset. */
|
|
95
|
+
private variant;
|
|
68
96
|
show(type: SnackngType, message: string, options?: SnackngOptions): SnackngRef;
|
|
69
97
|
dismissAll(): void;
|
|
70
98
|
private resolvePoliteness;
|
|
@@ -94,6 +122,10 @@ interface SnackngConfig {
|
|
|
94
122
|
stagger: number;
|
|
95
123
|
pauseOnHover: boolean;
|
|
96
124
|
dismissible: boolean;
|
|
125
|
+
/** Default glass preset for every toast. Overridable per call via `options.style`. */
|
|
126
|
+
style: SnackngStyle;
|
|
127
|
+
/** Default surface light effect. Overridable per call via `options.effect`. */
|
|
128
|
+
effect: SnackngEffect;
|
|
97
129
|
types: Record<string, SnackngTypeDef>;
|
|
98
130
|
}
|
|
99
131
|
declare const SNACKNG_DEFAULTS: SnackngConfig;
|
|
@@ -105,4 +137,4 @@ declare const SNACKNG_CONFIG: InjectionToken<SnackngConfig>;
|
|
|
105
137
|
declare function provideSnackng(config?: Partial<SnackngConfig>): EnvironmentProviders;
|
|
106
138
|
|
|
107
139
|
export { SNACKNG_CONFIG, SNACKNG_DEFAULTS, SnackngService, provideSnackng };
|
|
108
|
-
export type { SnackngAction, SnackngBuiltInType, SnackngConfig, SnackngDismissReason, SnackngOptions, SnackngPoliteness, SnackngPosition, SnackngRef, SnackngType, SnackngTypeDef };
|
|
140
|
+
export type { SnackngAction, SnackngBuiltInStyle, SnackngBuiltInType, SnackngConfig, SnackngDismissReason, SnackngEffect, SnackngOptions, SnackngPoliteness, SnackngPosition, SnackngRef, SnackngStyle, SnackngType, SnackngTypeDef, SnackngVariantFn };
|