ngx-gooey-toast 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md ADDED
@@ -0,0 +1,269 @@
1
+ # ngx-gooey-toast
2
+
3
+ > Morphing **pill → blob** toasts for Angular. Built on signals and a hand-rolled spring engine — **zero runtime dependencies**.
4
+
5
+ An Angular port of `goey-toast`: a toast that morphs from a compact pill into a
6
+ gooey blob. The spring/morph engine runs on `requestAnimationFrame` (no
7
+ framer-motion), and the only dependencies are Angular peer deps.
8
+
9
+ ## Features
10
+
11
+ - 🫧 **Morphing animation** — parametric SVG path (pill → blob), not a CSS filter.
12
+ - 📡 **Signals-first API** — imperative service (`success/error/info/…`) backed by Angular signals.
13
+ - 📦 **Zero runtime deps** — Angular peer deps + `tslib` only.
14
+ - 🖥️ **SSR-safe** — every browser global (`window`, `matchMedia`, `navigator.vibrate`, rAF) is feature-detected.
15
+ - ♿ **Accessible** — `aria-live` announcements, WCAG AA contrast, timers pause on hover **and** keyboard focus (WCAG 2.2.1), reduced-motion respected.
16
+ - ⏳ **Promise lifecycle** — `promise()` shows loading → success/error in place.
17
+ - 🔁 **In-place updates** — `update()` mutates a live toast (great for status flows).
18
+ - 🧲 **Coalescing** — collapse duplicate toasts into one with a count badge + pulse.
19
+ - 🕘 **History + replay** — dismissed toasts kept for replay (configurable).
20
+ - 📳 **Haptics** (opt-in), **RTL**, **6 positions**, swipe-to-dismiss.
21
+ - 🎚️ **Animation presets** — `smooth`, `bouncy`, `subtle`, `snappy`.
22
+
23
+ ## Requirements
24
+
25
+ Angular `^21.0.0` (`@angular/common`, `@angular/core`, `@angular/platform-browser`).
26
+
27
+ ## Install
28
+
29
+ ```bash
30
+ npm install ngx-gooey-toast
31
+ # or
32
+ pnpm add ngx-gooey-toast
33
+ # or
34
+ bun add ngx-gooey-toast
35
+ ```
36
+
37
+ ## Setup
38
+
39
+ `GooeyToastService` is `providedIn: 'root'` — **no module or provider** to wire up.
40
+ Render exactly **one** `<gooey-toaster />` at your app root and you're done.
41
+
42
+ > ⚠️ Render only **one** `<gooey-toaster />` per app. Its inputs write into the
43
+ > root-singleton service config, so multiple instances overwrite each other.
44
+
45
+ ```ts
46
+ // app.ts
47
+ import { ChangeDetectionStrategy, Component } from '@angular/core'
48
+ import { RouterOutlet } from '@angular/router'
49
+ import { GooeyToasterComponent } from 'ngx-gooey-toast'
50
+
51
+ @Component({
52
+ selector: 'app-root',
53
+ changeDetection: ChangeDetectionStrategy.OnPush,
54
+ imports: [RouterOutlet, GooeyToasterComponent],
55
+ template: `
56
+ <router-outlet />
57
+ <gooey-toaster position="top-right" theme="light" />
58
+ `,
59
+ })
60
+ export class App {}
61
+ ```
62
+
63
+ ## Usage
64
+
65
+ Inject the service anywhere and fire a toast:
66
+
67
+ ```ts
68
+ import { Component, inject } from '@angular/core'
69
+ import { GooeyToastService } from 'ngx-gooey-toast'
70
+
71
+ @Component({
72
+ selector: 'app-demo',
73
+ template: `<button (click)="save()">Save</button>`,
74
+ })
75
+ export class DemoComponent {
76
+ private readonly toast = inject(GooeyToastService)
77
+
78
+ save() {
79
+ this.toast.success('Saved!')
80
+ }
81
+ }
82
+ ```
83
+
84
+ ### Description + action
85
+
86
+ ```ts
87
+ this.toast.error('Upload failed', {
88
+ description: 'The file was too large.',
89
+ action: { label: 'Retry', onClick: () => this.upload() },
90
+ })
91
+ ```
92
+
93
+ ### Promise lifecycle
94
+
95
+ ```ts
96
+ this.toast.promise(this.api.save(form), {
97
+ loading: 'Saving…',
98
+ success: (res) => `Saved as ${res.name}`,
99
+ error: (err) => `Failed: ${err}`,
100
+ })
101
+ ```
102
+
103
+ ### Update in place
104
+
105
+ ```ts
106
+ // Opt out of coalescing for toasts you keep the id of and update later.
107
+ const id = this.toast.info('Connecting…', { coalesce: false })
108
+ // later…
109
+ this.toast.update(id, { title: 'Connected', type: 'success' })
110
+ ```
111
+
112
+ ### Dismiss
113
+
114
+ ```ts
115
+ this.toast.dismiss(id) // one toast by id
116
+ this.toast.dismiss({ type: 'error' }) // all of a type (or array of types)
117
+ this.toast.dismiss() // everything
118
+ ```
119
+
120
+ ## Toaster inputs (`<gooey-toaster>`)
121
+
122
+ | Input | Type | Default | Notes |
123
+ | --- | --- | --- | --- |
124
+ | `position` | `'top-left' \| 'top-center' \| 'top-right' \| 'bottom-left' \| 'bottom-center' \| 'bottom-right'` | `'top-right'` | Anchor corner/edge. |
125
+ | `theme` | `'light' \| 'dark'` | `'light'` | |
126
+ | `duration` | `number` | `4000` | Default auto-dismiss (ms). |
127
+ | `gap` | `number` | — | Space between stacked toasts. |
128
+ | `offset` | `number \| string` | — | Distance from the screen edge. |
129
+ | `visibleToasts` | `number` | `6` | Rest overflow into a queue. |
130
+ | `closeButton` | `boolean \| 'top-left' \| 'top-right'` | `false` | |
131
+ | `swipeToDismiss` | `boolean` | `true` | |
132
+ | `closeOnEscape` | `boolean` | `true` | |
133
+ | `dir` | `'ltr' \| 'rtl'` | `'ltr'` | |
134
+ | `preset` | `'smooth' \| 'bouncy' \| 'subtle' \| 'snappy'` | — | Animation preset. |
135
+ | `spring` | `boolean` | `true` | |
136
+ | `bounce` | `number` | — | Spring bounciness override. |
137
+ | `showProgress` | `boolean` | `false` | Progress bar for the timer. |
138
+ | `coalesceDuplicates` | `boolean` | `false` | Collapse dupes into a count badge. |
139
+ | `haptics` | `boolean` | `false` | Vibrate on arrival (mobile, opt-in). |
140
+ | `historyLimit` | `number` | `20` | Dismissed toasts kept for replay (`0` disables). |
141
+ | `maxQueue` | `number` | `Infinity` | Cap the overflow queue. |
142
+ | `queueOverflow` | `'drop-oldest' \| 'drop-newest'` | `'drop-oldest'` | When the queue is full. |
143
+ | `stackDirection` | `'newest-first' \| 'oldest-first'` | `'newest-first'` | Where new toasts enter. |
144
+ | `label` | `string` | `'Notifications'` | `aria-label` for the list. |
145
+
146
+ ## Per-toast options (`GooeyToastOptions`)
147
+
148
+ | Option | Type | Notes |
149
+ | --- | --- | --- |
150
+ | `description` | `string \| TemplateRef \| { html } \| { markdown }` | Rich content is sanitized. |
151
+ | `action` | `{ label, onClick, successLabel? }` | Action button. |
152
+ | `icon` | `GooeyContent` | Override the type icon. |
153
+ | `duration` | `number` | Auto-dismiss override (`Infinity` = stay open). |
154
+ | `id` | `string \| number` | Provide your own id (also enables `update()`). |
155
+ | `fillColor` / `borderColor` / `borderWidth` | `string` / `string` / `number` | Per-toast styling. |
156
+ | `preset` / `spring` / `bounce` | preset / `boolean` / `number` | Per-toast animation. |
157
+ | `showProgress` | `boolean` | Progress bar override. |
158
+ | `showTimestamp` | `boolean` | Show relative time. |
159
+ | `coalesce` | `boolean` | Override the global dedup behavior. |
160
+ | `onDismiss` / `onAutoClose` | `(id) => void` | Lifecycle callbacks. |
161
+
162
+ ## Service API (`GooeyToastService`)
163
+
164
+ All create methods return the toast id (`string | number`).
165
+
166
+ | Method | Description |
167
+ | --- | --- |
168
+ | `show(title, options?)` | Default toast. |
169
+ | `success / error / warning / info(title, options?)` | Typed toasts. |
170
+ | `promise(promise, data)` | Loading → success/error in place. |
171
+ | `update(id, options)` | Mutate a live toast. |
172
+ | `dismiss(id \| filter?)` | Dismiss one / by type / all. |
173
+ | `replay(id)` | Re-fire a dismissed toast. |
174
+ | `clearHistory()` | Clear the dismissed-toast history. |
175
+ | `mostRecentId()` | Id of the newest live toast. |
176
+
177
+ ## Accessibility
178
+
179
+ - Announcements via dual `aria-live` regions — `polite` for info/success,
180
+ `assertive` for warning/error.
181
+ - Auto-dismiss timers pause on hover **and** keyboard focus (WCAG 2.2.1).
182
+ - Light-theme phase colors are tuned to **WCAG AA** (≥ 4.5:1 on white).
183
+ - `prefers-reduced-motion` is respected (animations and haptics back off).
184
+
185
+ ## License
186
+
187
+ [MIT](./LICENSE)
188
+
189
+ ---
190
+
191
+ <details>
192
+ <summary>🇧🇷 Português</summary>
193
+
194
+ ### ngx-gooey-toast
195
+
196
+ Toasts que se transformam de **pílula → blob** para Angular. Feito com signals e
197
+ um motor de mola próprio — **zero dependências em runtime** (só peer deps do
198
+ Angular).
199
+
200
+ ### Requisitos
201
+
202
+ Angular `^21.0.0`.
203
+
204
+ ### Instalação
205
+
206
+ ```bash
207
+ npm install ngx-gooey-toast
208
+ # ou
209
+ pnpm add ngx-gooey-toast
210
+ # ou
211
+ bun add ngx-gooey-toast
212
+ ```
213
+
214
+ ### Configuração
215
+
216
+ O `GooeyToastService` é `providedIn: 'root'` — **sem módulo ou provider**.
217
+ Renderize **apenas um** `<gooey-toaster />` na raiz do app:
218
+
219
+ ```ts
220
+ import { ChangeDetectionStrategy, Component } from '@angular/core'
221
+ import { GooeyToasterComponent } from 'ngx-gooey-toast'
222
+
223
+ @Component({
224
+ selector: 'app-root',
225
+ changeDetection: ChangeDetectionStrategy.OnPush,
226
+ imports: [GooeyToasterComponent],
227
+ template: `<gooey-toaster position="top-right" theme="light" />`,
228
+ })
229
+ export class App {}
230
+ ```
231
+
232
+ ### Uso
233
+
234
+ Injete o serviço e dispare um toast:
235
+
236
+ ```ts
237
+ import { Component, inject } from '@angular/core'
238
+ import { GooeyToastService } from 'ngx-gooey-toast'
239
+
240
+ @Component({ /* … */ })
241
+ export class DemoComponent {
242
+ private readonly toast = inject(GooeyToastService)
243
+
244
+ save() {
245
+ this.toast.success('Salvo!', {
246
+ description: 'Suas alterações foram gravadas.',
247
+ action: { label: 'Desfazer', onClick: () => this.undo() },
248
+ })
249
+ }
250
+ }
251
+ ```
252
+
253
+ ### Principais métodos
254
+
255
+ - `show / success / error / warning / info(title, options?)` — cria um toast (retorna o id).
256
+ - `promise(promise, data)` — loading → sucesso/erro no mesmo toast.
257
+ - `update(id, options)` — altera um toast vivo (use `coalesce: false` ao criar).
258
+ - `dismiss(id | { type } )` — fecha um, por tipo, ou todos (`dismiss()`).
259
+
260
+ ### Acessibilidade
261
+
262
+ `aria-live` (polite/assertive), contraste WCAG AA, timers pausam no hover e no
263
+ foco do teclado (WCAG 2.2.1) e `prefers-reduced-motion` é respeitado.
264
+
265
+ ### Licença
266
+
267
+ MIT.
268
+
269
+ </details>