@soft-toast/vue 1.1.0 → 1.1.1
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 +187 -99
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -1,17 +1,21 @@
|
|
|
1
1
|
# @soft-toast/vue
|
|
2
2
|
|
|
3
|
-
A toast notification library for Vue 3 with
|
|
3
|
+
A toast notification library for Vue 3 with fluid motion, stacked layout, and flexible customization.
|
|
4
4
|
|
|
5
5
|
## Features
|
|
6
6
|
|
|
7
|
-
- **Fluid Motion**: Smooth enter, exit, stack, and swipe interactions.
|
|
8
|
-
- **Stacked Layout**: Toasts stay compact
|
|
9
|
-
- **
|
|
10
|
-
- **Motion Presets**:
|
|
11
|
-
- **Rich Content**:
|
|
12
|
-
- **Promise
|
|
13
|
-
- **
|
|
14
|
-
- **
|
|
7
|
+
- **Fluid Motion**: Smooth enter, exit, stack, and swipe interactions powered by GSAP.
|
|
8
|
+
- **Stacked Layout**: Toasts stay compact and readable; hover or tap to expand the full stack.
|
|
9
|
+
- **10 Positions**: All corners, edges, and center positions.
|
|
10
|
+
- **Motion Presets**: `smooth`, `bouncy`, `subtle`, or `snappy`.
|
|
11
|
+
- **Rich Content**: Titles, descriptions, action buttons, progress bars, timestamps, and custom icons.
|
|
12
|
+
- **Promise Toasts**: Built-in loading → success / error lifecycle for async operations.
|
|
13
|
+
- **Flash Messages**: Queue toasts that survive page navigation via `sessionStorage`.
|
|
14
|
+
- **Sound Support**: Optional synthesized tones or custom audio URLs per toast.
|
|
15
|
+
- **Smart Deduplication**: Calling with the same `id` updates the existing toast instead of stacking.
|
|
16
|
+
- **Custom Slots**: Override `#icon`, `#title`, `#description`, `#action`, and `#close-button`.
|
|
17
|
+
- **Swipe to Dismiss**: Pointer-event gesture support for both touch and mouse.
|
|
18
|
+
- **TypeScript**: Full type safety with exported types.
|
|
15
19
|
|
|
16
20
|
## Compatibility
|
|
17
21
|
|
|
@@ -22,21 +26,14 @@ A toast notification library for Vue 3 with soft motion, compact defaults, and f
|
|
|
22
26
|
|
|
23
27
|
## Installation
|
|
24
28
|
|
|
25
|
-
### Vue 3
|
|
26
|
-
|
|
27
29
|
```bash
|
|
28
30
|
npm install @soft-toast/vue
|
|
31
|
+
# pnpm add @soft-toast/vue
|
|
32
|
+
# yarn add @soft-toast/vue
|
|
33
|
+
# bun add @soft-toast/vue
|
|
29
34
|
```
|
|
30
35
|
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
```bash
|
|
34
|
-
pnpm add @soft-toast/vue
|
|
35
|
-
yarn add @soft-toast/vue
|
|
36
|
-
bun add @soft-toast/vue
|
|
37
|
-
```
|
|
38
|
-
|
|
39
|
-
Register the Vue plugin in `main.ts` or `main.js`:
|
|
36
|
+
Register the plugin in `main.ts`:
|
|
40
37
|
|
|
41
38
|
```typescript
|
|
42
39
|
import { createApp } from "vue";
|
|
@@ -58,34 +55,16 @@ app.mount("#app");
|
|
|
58
55
|
|
|
59
56
|
### Nuxt 3 / Nuxt 4
|
|
60
57
|
|
|
61
|
-
For Nuxt apps, install the companion module:
|
|
62
|
-
|
|
63
58
|
```bash
|
|
64
59
|
npm install @soft-toast/nuxt
|
|
65
60
|
```
|
|
66
61
|
|
|
67
|
-
Package manager equivalents:
|
|
68
|
-
|
|
69
|
-
```bash
|
|
70
|
-
pnpm add @soft-toast/nuxt
|
|
71
|
-
yarn add @soft-toast/nuxt
|
|
72
|
-
bun add @soft-toast/nuxt
|
|
73
|
-
```
|
|
74
|
-
|
|
75
|
-
Add it to `nuxt.config.ts`:
|
|
76
|
-
|
|
77
|
-
```typescript
|
|
78
|
-
export default defineNuxtConfig({
|
|
79
|
-
modules: ["@soft-toast/nuxt"],
|
|
80
|
-
});
|
|
81
|
-
```
|
|
82
|
-
|
|
83
|
-
`softToast` is optional. Add it only when you want to override global defaults:
|
|
84
|
-
|
|
85
62
|
```typescript
|
|
63
|
+
// nuxt.config.ts
|
|
86
64
|
export default defineNuxtConfig({
|
|
87
65
|
modules: ["@soft-toast/nuxt"],
|
|
88
66
|
softToast: {
|
|
67
|
+
position: "top-right",
|
|
89
68
|
closeButton: true,
|
|
90
69
|
showProgress: true,
|
|
91
70
|
},
|
|
@@ -94,106 +73,197 @@ export default defineNuxtConfig({
|
|
|
94
73
|
|
|
95
74
|
## Quick Start
|
|
96
75
|
|
|
97
|
-
Use it anywhere in your Vue or Nuxt components:
|
|
98
|
-
|
|
99
76
|
```vue
|
|
100
|
-
<script setup>
|
|
77
|
+
<script setup lang="ts">
|
|
101
78
|
import { useSoftToast } from "@soft-toast/vue";
|
|
102
79
|
|
|
103
|
-
const
|
|
80
|
+
const toast = useSoftToast();
|
|
104
81
|
|
|
105
|
-
const
|
|
106
|
-
|
|
82
|
+
const save = () => {
|
|
83
|
+
toast.success("Profile saved!", {
|
|
107
84
|
description: "Your changes have been successfully saved.",
|
|
108
|
-
position: "top-right",
|
|
109
85
|
duration: 2500,
|
|
110
|
-
closeButton: true,
|
|
111
86
|
showProgress: true,
|
|
112
87
|
});
|
|
113
88
|
};
|
|
114
89
|
</script>
|
|
115
90
|
|
|
116
91
|
<template>
|
|
117
|
-
<button @click="
|
|
92
|
+
<button @click="save">Save Profile</button>
|
|
118
93
|
</template>
|
|
119
94
|
```
|
|
120
95
|
|
|
121
|
-
|
|
96
|
+
Plugin options are global defaults. Override them per toast as needed:
|
|
122
97
|
|
|
123
98
|
```typescript
|
|
124
|
-
|
|
99
|
+
toast.warning("File moved to trash", {
|
|
125
100
|
description: "You can restore it from the activity log.",
|
|
126
101
|
position: "bottom-right",
|
|
127
102
|
duration: Infinity,
|
|
128
103
|
closeButton: "top-left",
|
|
129
|
-
showProgress: false,
|
|
130
104
|
});
|
|
131
105
|
```
|
|
132
106
|
|
|
133
|
-
## API
|
|
107
|
+
## API
|
|
108
|
+
|
|
109
|
+
### `useSoftToast()`
|
|
110
|
+
|
|
111
|
+
Returns a composable instance bound to the Vue component lifecycle.
|
|
112
|
+
|
|
113
|
+
```typescript
|
|
114
|
+
const toast = useSoftToast();
|
|
115
|
+
```
|
|
116
|
+
|
|
117
|
+
### `softToast`
|
|
134
118
|
|
|
135
|
-
|
|
119
|
+
Singleton — works outside Vue components (e.g. in router guards, Pinia stores, API interceptors).
|
|
136
120
|
|
|
137
|
-
|
|
121
|
+
```typescript
|
|
122
|
+
import { softToast } from "@soft-toast/vue";
|
|
123
|
+
|
|
124
|
+
softToast.error("Session expired", { duration: 6000 });
|
|
125
|
+
```
|
|
138
126
|
|
|
139
|
-
|
|
127
|
+
### Methods
|
|
128
|
+
|
|
129
|
+
| Method | Description |
|
|
130
|
+
| ---------------------------------- | ----------------------------------------------------- |
|
|
131
|
+
| `toast.default(title, options?)` | Default (unstyled) toast |
|
|
132
|
+
| `toast.success(title, options?)` | Success toast |
|
|
133
|
+
| `toast.error(title, options?)` | Error toast |
|
|
134
|
+
| `toast.warning(title, options?)` | Warning toast |
|
|
135
|
+
| `toast.info(title, options?)` | Info toast |
|
|
136
|
+
| `toast.loading(title, options?)` | Persistent loading toast (use `update` to resolve it) |
|
|
137
|
+
| `toast.promise(promise, messages)` | Auto-transitions loading → success / error |
|
|
138
|
+
| `toast.custom(options)` | Full control — pass any `ToastOptions` directly |
|
|
139
|
+
| `toast.update(id, options)` | Update a visible toast by ID |
|
|
140
|
+
| `toast.dismiss(id?)` | Dismiss one toast by ID, or all toasts if omitted |
|
|
141
|
+
| `toast.dismissAll()` | Dismiss all visible toasts |
|
|
142
|
+
| `toast.pause(id)` | Pause the auto-close timer |
|
|
143
|
+
| `toast.resume(id)` | Resume the auto-close timer |
|
|
144
|
+
| `toast.flash(title, options?)` | Queue a toast that shows after the next navigation |
|
|
145
|
+
| `toast.showFlashes()` | Consume and show pending flash messages |
|
|
146
|
+
| `toast.hasFlashes()` | Returns `true` if there are pending flash messages |
|
|
147
|
+
|
|
148
|
+
### `promise` example
|
|
140
149
|
|
|
141
150
|
```typescript
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
151
|
+
toast.promise(api.saveProfile(data), {
|
|
152
|
+
loading: "Saving…",
|
|
153
|
+
success: "Profile saved!",
|
|
154
|
+
error: (err) => `Failed: ${err.message}`,
|
|
155
|
+
description: {
|
|
156
|
+
success: "Your changes are live.",
|
|
157
|
+
error: "Please try again.",
|
|
148
158
|
},
|
|
149
159
|
});
|
|
150
160
|
```
|
|
151
161
|
|
|
152
|
-
###
|
|
162
|
+
### `update` example
|
|
153
163
|
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
| `position` | `ToastPosition` | `'top-right'` | Position on screen (e.g. `'top'`, `'bottom-left'`, `'right'`) |
|
|
157
|
-
| `duration` | `number` | `4000` | Auto-close delay in ms (`Infinity` disables auto-close) |
|
|
158
|
-
| `preset` | `'smooth' \| 'bouncy' \| 'subtle' \| 'snappy'` | `'smooth'` | Motion style |
|
|
159
|
-
| `description` | `string \| VNode` | `undefined` | Secondary text below the title |
|
|
160
|
-
| `action` | `ToastAction \| ToastAction[]` | `undefined` | Interactive button config (`label`, `onClick`) |
|
|
161
|
-
| `showProgress` | `boolean` | `false` | Shows a decreasing progress bar |
|
|
162
|
-
| `closeButton` | `boolean \| 'top-left' \| 'top-right'` | `false` | Shows a dismiss button for this toast |
|
|
163
|
-
| `showTimestamp` | `boolean` | `false` | Shows the time the toast was created |
|
|
164
|
-
| `icon` | `string \| VNode \| Component` | `undefined` | Custom icon string (Iconify `line-md` support) or Component |
|
|
164
|
+
```typescript
|
|
165
|
+
const id = toast.loading("Uploading…");
|
|
165
166
|
|
|
166
|
-
|
|
167
|
+
await uploadFile(file);
|
|
167
168
|
|
|
168
|
-
|
|
169
|
+
toast.update(id, {
|
|
170
|
+
type: "success",
|
|
171
|
+
title: "Upload complete!",
|
|
172
|
+
duration: 3000,
|
|
173
|
+
});
|
|
174
|
+
```
|
|
175
|
+
|
|
176
|
+
## Toast Options
|
|
177
|
+
|
|
178
|
+
| Option | Type | Default | Description |
|
|
179
|
+
| --------------- | ---------------------------------------------- | ------------- | --------------------------------------------------------- |
|
|
180
|
+
| `id` | `string` | auto | Custom ID — same ID updates the existing toast |
|
|
181
|
+
| `position` | `ToastPosition` | `'top-right'` | Screen position |
|
|
182
|
+
| `duration` | `number` | `4000` | Auto-close delay in ms (`Infinity` disables auto-close) |
|
|
183
|
+
| `description` | `string \| VNode` | `undefined` | Secondary text below the title |
|
|
184
|
+
| `action` | `ToastAction \| ToastAction[]` | `undefined` | One or more action buttons |
|
|
185
|
+
| `icon` | `string \| VNode \| Component` | `undefined` | Iconify icon string, VNode, or Vue component |
|
|
186
|
+
| `preset` | `'smooth' \| 'bouncy' \| 'subtle' \| 'snappy'` | `'smooth'` | Motion style |
|
|
187
|
+
| `showProgress` | `boolean` | `false` | Decreasing progress bar |
|
|
188
|
+
| `closeButton` | `boolean \| 'top-left' \| 'top-right'` | `false` | Dismiss button position |
|
|
189
|
+
| `showTimestamp` | `boolean` | `false` | Shows creation time |
|
|
190
|
+
| `sound` | `boolean \| string` | `undefined` | `true` for built-in tone, or a URL to a custom audio file |
|
|
191
|
+
| `soundVolume` | `number` | `0.5` | Playback volume (0–1) |
|
|
192
|
+
| `classNames` | `ToastClassNames` | `undefined` | Custom CSS classes for individual parts of the toast |
|
|
193
|
+
| `fillColor` | `string` | `undefined` | Custom fill/accent color |
|
|
194
|
+
| `borderColor` | `string` | `undefined` | Custom border color |
|
|
195
|
+
| `borderWidth` | `number` | `undefined` | Custom border width in px |
|
|
196
|
+
| `onDismiss` | `(id: string) => void` | `undefined` | Called when the toast is manually dismissed |
|
|
197
|
+
| `onAutoClose` | `(id: string) => void` | `undefined` | Called when the toast auto-closes |
|
|
198
|
+
|
|
199
|
+
### `ToastAction`
|
|
200
|
+
|
|
201
|
+
```typescript
|
|
202
|
+
{
|
|
203
|
+
label: string;
|
|
204
|
+
onClick: () => void | Promise<void>;
|
|
205
|
+
successLabel?: string; // Replaces button with this text on success, then auto-closes
|
|
206
|
+
primary?: boolean; // Applies primary button styling
|
|
207
|
+
class?: string; // Custom CSS class
|
|
208
|
+
}
|
|
209
|
+
```
|
|
210
|
+
|
|
211
|
+
## Icons
|
|
212
|
+
|
|
213
|
+
Supports **Iconify** icon strings and custom Vue components:
|
|
169
214
|
|
|
170
215
|
```typescript
|
|
171
|
-
//
|
|
172
|
-
|
|
216
|
+
// Iconify icon string
|
|
217
|
+
toast.success("Deployed!", { icon: "line-md:rocket-launch" });
|
|
173
218
|
|
|
174
|
-
//
|
|
219
|
+
// Custom Vue component
|
|
175
220
|
import MyIcon from "./MyIcon.vue";
|
|
176
|
-
|
|
221
|
+
toast.info("Update available", { icon: MyIcon });
|
|
177
222
|
```
|
|
178
223
|
|
|
179
|
-
##
|
|
224
|
+
## Flash Messages
|
|
180
225
|
|
|
181
|
-
|
|
226
|
+
Queue a toast that survives a page navigation and shows on the next load:
|
|
182
227
|
|
|
183
|
-
|
|
228
|
+
```typescript
|
|
229
|
+
const toast = useSoftToast();
|
|
230
|
+
|
|
231
|
+
const save = async () => {
|
|
232
|
+
await api.save();
|
|
233
|
+
toast.flash("Changes saved!", { type: "success" });
|
|
234
|
+
router.push("/dashboard");
|
|
235
|
+
};
|
|
236
|
+
```
|
|
184
237
|
|
|
185
|
-
|
|
238
|
+
In `App.vue` or your root layout, consume pending flashes on mount:
|
|
239
|
+
|
|
240
|
+
```vue
|
|
241
|
+
<script setup lang="ts">
|
|
242
|
+
import { onMounted } from "vue";
|
|
243
|
+
import { useSoftToast } from "@soft-toast/vue";
|
|
244
|
+
|
|
245
|
+
const toast = useSoftToast();
|
|
246
|
+
onMounted(() => toast.showFlashes());
|
|
247
|
+
</script>
|
|
248
|
+
```
|
|
249
|
+
|
|
250
|
+
Flash messages expire automatically after 30 seconds if not consumed.
|
|
251
|
+
|
|
252
|
+
## Custom Slots
|
|
253
|
+
|
|
254
|
+
Override any part of the toast UI via named slots on `<SoftToastContainer>`.
|
|
255
|
+
|
|
256
|
+
When using slots, disable the plugin's auto-mounted container and place it manually in your layout:
|
|
186
257
|
|
|
187
258
|
```typescript
|
|
188
|
-
app.use(SoftToastPlugin, {
|
|
189
|
-
autoMount: false,
|
|
190
|
-
});
|
|
259
|
+
app.use(SoftToastPlugin, { autoMount: false });
|
|
191
260
|
```
|
|
192
261
|
|
|
193
262
|
```vue
|
|
263
|
+
<!-- App.vue or layout -->
|
|
194
264
|
<SoftToastContainer>
|
|
195
265
|
<template #icon="{ toast }">
|
|
196
|
-
<
|
|
266
|
+
<img src="/logo.png" alt="" />
|
|
197
267
|
</template>
|
|
198
268
|
|
|
199
269
|
<template #title="{ toast }">
|
|
@@ -204,42 +274,60 @@ app.use(SoftToastPlugin, {
|
|
|
204
274
|
<p>{{ toast.description }}</p>
|
|
205
275
|
</template>
|
|
206
276
|
|
|
207
|
-
<!-- Override the close button -->
|
|
208
|
-
<template #close-button="{ dismiss }">
|
|
209
|
-
<button @click="dismiss" class="my-custom-close-btn">Close</button>
|
|
210
|
-
</template>
|
|
211
|
-
|
|
212
|
-
<!-- Override the action button -->
|
|
213
277
|
<template #action="{ toast, execute }">
|
|
214
278
|
<button
|
|
215
279
|
v-for="action in Array.isArray(toast.action) ? toast.action : [toast.action]"
|
|
216
280
|
:key="action.label"
|
|
217
281
|
@click="execute(action)"
|
|
218
|
-
class="my-custom-action-btn"
|
|
219
282
|
>
|
|
220
283
|
{{ action.label }}
|
|
221
284
|
</button>
|
|
222
285
|
</template>
|
|
286
|
+
|
|
287
|
+
<template #close-button="{ dismiss }">
|
|
288
|
+
<button @click="dismiss">Close</button>
|
|
289
|
+
</template>
|
|
223
290
|
</SoftToastContainer>
|
|
224
291
|
```
|
|
225
292
|
|
|
226
|
-
Use `slotFilter`
|
|
293
|
+
Use `slotFilter` to apply custom slots only to specific toasts. Toasts that don't match keep the default rendering:
|
|
227
294
|
|
|
228
295
|
```vue
|
|
229
|
-
<SoftToastContainer :slot-filter="(toast) => toast.id
|
|
296
|
+
<SoftToastContainer :slot-filter="(toast) => toast.id.startsWith('custom-')">
|
|
230
297
|
<template #title="{ toast }">
|
|
231
298
|
<strong>{{ toast.title }}</strong>
|
|
232
299
|
</template>
|
|
233
300
|
</SoftToastContainer>
|
|
234
301
|
```
|
|
235
302
|
|
|
303
|
+
## Container Props
|
|
304
|
+
|
|
305
|
+
| Prop | Type | Default | Description |
|
|
306
|
+
| ---------------- | ---------------------------------------------- | --------------- | ---------------------------------------------------- |
|
|
307
|
+
| `position` | `ToastPosition` | `'top-right'` | Default position for all toasts |
|
|
308
|
+
| `duration` | `number` | `5000` | Default auto-close delay |
|
|
309
|
+
| `theme` | `'light' \| 'dark'` | `'light'` | Color theme |
|
|
310
|
+
| `preset` | `'smooth' \| 'bouncy' \| 'subtle' \| 'snappy'` | `'smooth'` | Default motion preset |
|
|
311
|
+
| `closeButton` | `boolean \| 'top-left' \| 'top-right'` | `false` | Show close button on all toasts |
|
|
312
|
+
| `showProgress` | `boolean` | `false` | Show progress bar on all toasts |
|
|
313
|
+
| `showTimestamp` | `boolean` | `false` | Show timestamp on all toasts |
|
|
314
|
+
| `closeOnEscape` | `boolean` | `true` | Dismiss the most recent toast on `Escape` |
|
|
315
|
+
| `swipeToDismiss` | `boolean` | `true` | Enable swipe-to-dismiss gesture |
|
|
316
|
+
| `maxQueue` | `number` | `Infinity` | Maximum number of toasts visible at once |
|
|
317
|
+
| `queueOverflow` | `'drop-oldest' \| 'drop-newest'` | `'drop-oldest'` | What to drop when queue is full |
|
|
318
|
+
| `gap` | `number` | `12` | Gap in px between expanded toasts |
|
|
319
|
+
| `offset` | `number \| string` | `'24px'` | Distance from the screen edge |
|
|
320
|
+
| `dir` | `'ltr' \| 'rtl'` | `'ltr'` | Text direction |
|
|
321
|
+
| `slotFilter` | `(toast: Toast) => boolean` | `undefined` | Function to select which toasts receive custom slots |
|
|
322
|
+
|
|
236
323
|
## Positions
|
|
237
324
|
|
|
238
325
|
10 available positions:
|
|
239
|
-
|
|
326
|
+
|
|
327
|
+
`top-left` · `top-center` · `top-right` · `bottom-left` · `bottom-center` · `bottom-right` · `top` · `bottom` · `left` · `right`
|
|
240
328
|
|
|
241
329
|
## License
|
|
242
330
|
|
|
243
331
|
MIT License.
|
|
244
332
|
|
|
245
|
-
> **Note on GSAP:** This library uses [GSAP](https://greensock.com/gsap/) for animations. GSAP is
|
|
333
|
+
> **Note on GSAP:** This library uses [GSAP](https://greensock.com/gsap/) for animations. GSAP is free for most uses under the [Standard No-Charge License](https://greensock.com/standard-license/).
|