@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 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)