@cookified/toastify 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +296 -0
- package/dist/index.cjs +779 -0
- package/dist/index.d.cts +155 -0
- package/dist/index.d.ts +155 -0
- package/dist/index.js +742 -0
- package/package.json +89 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Cookified
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,296 @@
|
|
|
1
|
+
# Toastify
|
|
2
|
+
|
|
3
|
+
<p align="center">
|
|
4
|
+
<strong>A delightful toast notification library for React.</strong><br>
|
|
5
|
+
Designed with folder stack animations, tactile buttons, and pluggable Motion presets.
|
|
6
|
+
</p>
|
|
7
|
+
|
|
8
|
+
<p align="center">
|
|
9
|
+
<a href="https://www.npmjs.com/package/@cookified/toastify"><img src="https://img.shields.io/badge/react-19+-61dafb.svg?style=flat-square&logo=react" alt="React 19" /></a>
|
|
10
|
+
<a href="https://www.typescriptlang.org/"><img src="https://img.shields.io/badge/typescript-strict-3178c6.svg?style=flat-square&logo=typescript" alt="TypeScript" /></a>
|
|
11
|
+
<a href="https://motion.dev/"><img src="https://img.shields.io/badge/motion-13.2+-ff0055.svg?style=flat-square" alt="Motion" /></a>
|
|
12
|
+
<a href="./LICENSE"><img src="https://img.shields.io/badge/license-MIT-green.svg?style=flat-square" alt="MIT License" /></a>
|
|
13
|
+
</p>
|
|
14
|
+
|
|
15
|
+
---
|
|
16
|
+
|
|
17
|
+
## Highlights
|
|
18
|
+
|
|
19
|
+
- 📁 **Folder Stack Animations**: Stacked cards with spring compression that smoothly expand on hover.
|
|
20
|
+
- 🔌 **Pluggable Animation Components**: Animations are fully decoupled as first-class React components (`"stack" | "slide" | "fade"` or custom Motion component).
|
|
21
|
+
- 🎛️ **Tactile Action Buttons**: Subtle bottom shadow offering clean tactile feedback.
|
|
22
|
+
- ♿ **Full Screen Reader & Keyboard A11y**: Polite ARIA regions (`role="region"`), non-intrusive focus management, and keyboard dismissal.
|
|
23
|
+
- 📐 **Zero Layout Jumps**: Constant 56px height geometry across neutral, action, and loading states ensures zero layout thrashing.
|
|
24
|
+
- 🛡️ **Bounded Memory & DoS Safe**: Bounded toast buffer, duration sanitization, and safe exception containment.
|
|
25
|
+
- 🧩 **Headless Custom JSX**: `toast.custom((id) => ...)` for total layout freedom when you need bespoke cards.
|
|
26
|
+
|
|
27
|
+
---
|
|
28
|
+
|
|
29
|
+
## Installation
|
|
30
|
+
|
|
31
|
+
```bash
|
|
32
|
+
# pnpm
|
|
33
|
+
pnpm add @cookified/toastify motion lucide-react
|
|
34
|
+
|
|
35
|
+
# npm
|
|
36
|
+
npm install @cookified/toastify motion lucide-react
|
|
37
|
+
|
|
38
|
+
# bun
|
|
39
|
+
bun add @cookified/toastify motion lucide-react
|
|
40
|
+
|
|
41
|
+
# yarn
|
|
42
|
+
yarn add @cookified/toastify motion lucide-react
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
---
|
|
46
|
+
|
|
47
|
+
## Quick Start
|
|
48
|
+
|
|
49
|
+
### 1. Add `<Toaster />` to your application root
|
|
50
|
+
|
|
51
|
+
```tsx
|
|
52
|
+
import { Toaster } from "@cookified/toastify";
|
|
53
|
+
|
|
54
|
+
export default function App() {
|
|
55
|
+
return (
|
|
56
|
+
<>
|
|
57
|
+
<YourAppContent />
|
|
58
|
+
<Toaster position="bottom-right" />
|
|
59
|
+
</>
|
|
60
|
+
);
|
|
61
|
+
}
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
### 2. Dispatch notifications from anywhere
|
|
65
|
+
|
|
66
|
+
```tsx
|
|
67
|
+
import { toast } from "@cookified/toastify";
|
|
68
|
+
|
|
69
|
+
// Simple neutral toast
|
|
70
|
+
toast("Your changes have been saved");
|
|
71
|
+
|
|
72
|
+
// Toast with description and tactile action
|
|
73
|
+
toast("Event scheduled", {
|
|
74
|
+
description: "Monday, January at 4:00 PM",
|
|
75
|
+
action: {
|
|
76
|
+
label: "Undo",
|
|
77
|
+
onClick: () => console.log("Undo triggered"),
|
|
78
|
+
},
|
|
79
|
+
cancel: {
|
|
80
|
+
label: "Dismiss",
|
|
81
|
+
onClick: () => console.log("Dismissed"),
|
|
82
|
+
},
|
|
83
|
+
});
|
|
84
|
+
```
|
|
85
|
+
|
|
86
|
+
---
|
|
87
|
+
|
|
88
|
+
## Notification Types & Examples
|
|
89
|
+
|
|
90
|
+
### Status Variants
|
|
91
|
+
|
|
92
|
+
Each status variant displays an inverted high-contrast badge for instant optical hierarchy:
|
|
93
|
+
|
|
94
|
+
```tsx
|
|
95
|
+
toast.success("Payment confirmed", {
|
|
96
|
+
description: "Receipt #4092 emailed to your account",
|
|
97
|
+
});
|
|
98
|
+
|
|
99
|
+
toast.error("Deployment failed", {
|
|
100
|
+
description: "Missing environment variable API_SECRET",
|
|
101
|
+
action: {
|
|
102
|
+
label: "Retry",
|
|
103
|
+
onClick: () => retryDeployment(),
|
|
104
|
+
},
|
|
105
|
+
});
|
|
106
|
+
|
|
107
|
+
toast.warning("Storage limit approaching", {
|
|
108
|
+
description: "You have used 88% of available quota",
|
|
109
|
+
});
|
|
110
|
+
|
|
111
|
+
toast.info("Update available", {
|
|
112
|
+
description: "Version 2.4 is ready to install",
|
|
113
|
+
});
|
|
114
|
+
```
|
|
115
|
+
|
|
116
|
+
### Promise Toasts
|
|
117
|
+
|
|
118
|
+
Automatically tracks loading states and resolves to success or error:
|
|
119
|
+
|
|
120
|
+
```tsx
|
|
121
|
+
toast.promise(
|
|
122
|
+
uploadFilePromise(),
|
|
123
|
+
{
|
|
124
|
+
loading: "Uploading file to server...",
|
|
125
|
+
success: (data) => `Uploaded ${data.filename} successfully`,
|
|
126
|
+
error: "Failed to upload file. Please retry.",
|
|
127
|
+
action: {
|
|
128
|
+
label: "View",
|
|
129
|
+
onClick: () => navigateToFiles(),
|
|
130
|
+
},
|
|
131
|
+
}
|
|
132
|
+
);
|
|
133
|
+
```
|
|
134
|
+
|
|
135
|
+
### Custom Themes & Icons
|
|
136
|
+
|
|
137
|
+
```tsx
|
|
138
|
+
import { Sparkles } from "lucide-react";
|
|
139
|
+
|
|
140
|
+
toast("Custom Palette & Icon", {
|
|
141
|
+
description: "Per-toast CSS styling & custom icons",
|
|
142
|
+
icon: (
|
|
143
|
+
<span className="flex h-5 w-5 items-center justify-center rounded-full bg-amber-500 text-neutral-950">
|
|
144
|
+
<Sparkles size={11} strokeWidth={2.5} />
|
|
145
|
+
</span>
|
|
146
|
+
),
|
|
147
|
+
className: "border-amber-500/40 bg-amber-500/5",
|
|
148
|
+
});
|
|
149
|
+
```
|
|
150
|
+
|
|
151
|
+
### Headless Custom JSX Toasts
|
|
152
|
+
|
|
153
|
+
When you want total control over the rendered markup:
|
|
154
|
+
|
|
155
|
+
```tsx
|
|
156
|
+
toast.custom((id) => (
|
|
157
|
+
<div className="flex h-14 w-full items-center justify-between rounded-md border border-violet-500/40 bg-neutral-900 px-4 text-xs text-white shadow-xl">
|
|
158
|
+
<span>Custom rendered notification</span>
|
|
159
|
+
<button onClick={() => toast.dismiss(id)} className="cursor-pointer font-bold opacity-70 hover:opacity-100">
|
|
160
|
+
✕
|
|
161
|
+
</button>
|
|
162
|
+
</div>
|
|
163
|
+
));
|
|
164
|
+
```
|
|
165
|
+
|
|
166
|
+
---
|
|
167
|
+
|
|
168
|
+
## Pluggable Animation Components
|
|
169
|
+
|
|
170
|
+
Unlike traditional libraries with fixed CSS transitions, Toastify treats animations as first-class, pluggable React components.
|
|
171
|
+
|
|
172
|
+
### 1. Using Built-In Presets
|
|
173
|
+
|
|
174
|
+
```tsx
|
|
175
|
+
<Toaster animation="stack" /> {/* Signature folder stack (default) */}
|
|
176
|
+
<Toaster animation="slide" /> {/* Horizontal sliding edge transitions */}
|
|
177
|
+
<Toaster animation="fade" /> {/* Pure opacity dissolution */}
|
|
178
|
+
```
|
|
179
|
+
|
|
180
|
+
### 2. Creating a Custom Animation Component
|
|
181
|
+
|
|
182
|
+
Any React component adhering to `ToastAnimationProps` can be passed to `<Toaster animation={MyComponent} />`:
|
|
183
|
+
|
|
184
|
+
```tsx
|
|
185
|
+
import type { ToastAnimationProps } from "@cookified/toastify";
|
|
186
|
+
import { motion } from "motion/react";
|
|
187
|
+
|
|
188
|
+
export function BouncyAnimation({
|
|
189
|
+
children,
|
|
190
|
+
isDismissing,
|
|
191
|
+
index,
|
|
192
|
+
}: ToastAnimationProps) {
|
|
193
|
+
return (
|
|
194
|
+
<motion.div
|
|
195
|
+
initial={{ opacity: 0, scale: 0.85, y: 30 }}
|
|
196
|
+
animate={{
|
|
197
|
+
opacity: isDismissing ? 0 : 1,
|
|
198
|
+
scale: isDismissing ? 0.85 : 1 - index * 0.05,
|
|
199
|
+
y: isDismissing ? 40 : index * 8,
|
|
200
|
+
}}
|
|
201
|
+
transition={{ type: "spring", stiffness: 420, damping: 20 }}
|
|
202
|
+
>
|
|
203
|
+
{children}
|
|
204
|
+
</motion.div>
|
|
205
|
+
);
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
// Pass directly to Toaster:
|
|
209
|
+
<Toaster animation={BouncyAnimation} />
|
|
210
|
+
```
|
|
211
|
+
|
|
212
|
+
### 3. Tuning Spring Physics
|
|
213
|
+
|
|
214
|
+
You can customize the folder stack spring dynamics directly:
|
|
215
|
+
|
|
216
|
+
```tsx
|
|
217
|
+
<Toaster
|
|
218
|
+
animation="stack"
|
|
219
|
+
springConfig={{
|
|
220
|
+
stiffness: 240,
|
|
221
|
+
damping: 24,
|
|
222
|
+
mass: 0.9,
|
|
223
|
+
}}
|
|
224
|
+
/>
|
|
225
|
+
```
|
|
226
|
+
|
|
227
|
+
---
|
|
228
|
+
|
|
229
|
+
## API Reference
|
|
230
|
+
|
|
231
|
+
### `<Toaster />` Props
|
|
232
|
+
|
|
233
|
+
| Prop | Type | Default | Description |
|
|
234
|
+
| :--- | :--- | :--- | :--- |
|
|
235
|
+
| `position` | `ToastPosition` | `"bottom-right"` | Viewport anchor: `"top-left" \| "top-center" \| "top-right" \| "bottom-left" \| "bottom-center" \| "bottom-right"` |
|
|
236
|
+
| `animation` | `AnimationPreset \| ToastAnimationComponent` | `"stack"` | Animation preset string (`"stack" \| "slide" \| "fade"`) or custom React motion component |
|
|
237
|
+
| `theme` | `"light" \| "dark" \| "system"` | `"system"` | Visual theme mode |
|
|
238
|
+
| `duration` | `number \| false` | `3500` | Auto-close timeout in ms, or `false` to disable |
|
|
239
|
+
| `springConfig` | `SpringConfig` | `{ stiffness: 220, damping: 26, mass: 1.0 }` | Motion spring dynamics override |
|
|
240
|
+
| `icons` | `Partial<Record<ToastType, ReactNode>>` | `undefined` | Global custom icon overrides by toast variant |
|
|
241
|
+
| `toastOptions` | `{ className?, style?, duration? }` | `undefined` | Global toast card styling and defaults |
|
|
242
|
+
| `closeOnClick` | `boolean` | `false` | Whether clicking a toast card dismisses it |
|
|
243
|
+
| `visibleToasts` | `number` | `3` | Maximum number of visible toasts in the folder stack |
|
|
244
|
+
|
|
245
|
+
### `toast()` Dispatcher API
|
|
246
|
+
|
|
247
|
+
| Method | Signature | Description |
|
|
248
|
+
| :--- | :--- | :--- |
|
|
249
|
+
| `toast(title, options?)` | `(title: ReactNode, options?: ToastOptions) => string` | Dispatches a neutral notification |
|
|
250
|
+
| `toast.success(title, options?)` | `(title: ReactNode, options?: ToastOptions) => string` | Dispatches a success notification |
|
|
251
|
+
| `toast.error(title, options?)` | `(title: ReactNode, options?: ToastOptions) => string` | Dispatches an error notification |
|
|
252
|
+
| `toast.warning(title, options?)` | `(title: ReactNode, options?: ToastOptions) => string` | Dispatches a warning notification |
|
|
253
|
+
| `toast.info(title, options?)` | `(title: ReactNode, options?: ToastOptions) => string` | Dispatches an informational notification |
|
|
254
|
+
| `toast.loading(title, options?)` | `(title: ReactNode, options?: ToastOptions) => string` | Dispatches a persistent loading notification |
|
|
255
|
+
| `toast.promise(promise, options)` | `<T>(promise, options: ToastPromiseOptions<T>) => Promise<T>` | Dispatches a promise lifecycle notification |
|
|
256
|
+
| `toast.custom(renderFn, options?)` | `(renderFn: (id: string) => ReactNode, options?) => string` | Dispatches a headless custom JSX toast |
|
|
257
|
+
| `toast.dismiss(id?)` | `(id?: string) => void` | Dismisses a specific toast by ID, or all toasts if omitted |
|
|
258
|
+
| `toast.update(id, options)` | `(id: string, options: Partial<ToastOptions>) => void` | Dynamically updates an active toast |
|
|
259
|
+
|
|
260
|
+
### `ToastOptions`
|
|
261
|
+
|
|
262
|
+
```typescript
|
|
263
|
+
type ToastOptions = {
|
|
264
|
+
id?: string;
|
|
265
|
+
description?: ReactNode;
|
|
266
|
+
action?: {
|
|
267
|
+
label: string;
|
|
268
|
+
onClick: (event: React.MouseEvent<HTMLButtonElement>) => void;
|
|
269
|
+
};
|
|
270
|
+
cancel?: {
|
|
271
|
+
label: string;
|
|
272
|
+
onClick: (event: React.MouseEvent<HTMLButtonElement>) => void;
|
|
273
|
+
};
|
|
274
|
+
icon?: ReactNode;
|
|
275
|
+
duration?: number | false;
|
|
276
|
+
autoClose?: number | false;
|
|
277
|
+
className?: string;
|
|
278
|
+
style?: CSSProperties;
|
|
279
|
+
};
|
|
280
|
+
```
|
|
281
|
+
|
|
282
|
+
---
|
|
283
|
+
|
|
284
|
+
## Security & Reliability Hardening
|
|
285
|
+
|
|
286
|
+
- **DoS & Memory Leak Prevention**: The store maintains a strictly bounded maximum history (`MAX_TOASTS = 30`), discarding overflow if a runaway loop fires toasts continuously.
|
|
287
|
+
- **Duration Sanitization**: Negative numbers and non-finite timestamps are sanitized to safe fallback intervals.
|
|
288
|
+
- **Exception Shielding**: Action and cancel callbacks are wrapped with exception boundaries, guaranteeing that unhandled user errors never crash the toaster or leave orphaned timers.
|
|
289
|
+
- **Safe ID Generation**: Utilizes standard `crypto.randomUUID()` with entropy fallbacks for SSR, older browsers, and web workers.
|
|
290
|
+
- **Strict Form Isolation**: All buttons explicitly declare `type="button"`, preventing accidental form submissions when rendered inside `<form>` tags.
|
|
291
|
+
|
|
292
|
+
---
|
|
293
|
+
|
|
294
|
+
## License
|
|
295
|
+
|
|
296
|
+
MIT © [Cookified](https://github.com/cookified/toastify)
|