@soft-toast/vue 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.
Files changed (51) hide show
  1. package/LICENSE +31 -0
  2. package/README.md +210 -0
  3. package/dist/animations/gsapConfig.d.ts +42 -0
  4. package/dist/animations/gsapConfig.d.ts.map +1 -0
  5. package/dist/composables/useFlash.d.ts +41 -0
  6. package/dist/composables/useFlash.d.ts.map +1 -0
  7. package/dist/composables/useFlash.test.d.ts +2 -0
  8. package/dist/composables/useFlash.test.d.ts.map +1 -0
  9. package/dist/composables/useToast.d.ts +53 -0
  10. package/dist/composables/useToast.d.ts.map +1 -0
  11. package/dist/composables/useToast.test.d.ts +2 -0
  12. package/dist/composables/useToast.test.d.ts.map +1 -0
  13. package/dist/exports.test.d.ts +2 -0
  14. package/dist/exports.test.d.ts.map +1 -0
  15. package/dist/icons.d.ts +2 -0
  16. package/dist/icons.d.ts.map +1 -0
  17. package/dist/index.d.ts +8 -0
  18. package/dist/index.d.ts.map +1 -0
  19. package/dist/index.js +2100 -0
  20. package/dist/plugin.d.ts +7 -0
  21. package/dist/plugin.d.ts.map +1 -0
  22. package/dist/stores/toastStore.d.ts +25 -0
  23. package/dist/stores/toastStore.d.ts.map +1 -0
  24. package/dist/stores/toastStore.test.d.ts +2 -0
  25. package/dist/stores/toastStore.test.d.ts.map +1 -0
  26. package/dist/style.css +1 -0
  27. package/dist/types/index.d.ts +107 -0
  28. package/dist/types/index.d.ts.map +1 -0
  29. package/dist/utils/sound.d.ts +9 -0
  30. package/dist/utils/sound.d.ts.map +1 -0
  31. package/package.json +70 -0
  32. package/src/animations/gsapConfig.ts +303 -0
  33. package/src/components/ToastContainer.vue +36 -0
  34. package/src/components/ToastIcon.vue +33 -0
  35. package/src/components/ToastItem.vue +342 -0
  36. package/src/components/ToastProgress.vue +50 -0
  37. package/src/components/ToastRegion.vue +381 -0
  38. package/src/composables/useFlash.test.ts +164 -0
  39. package/src/composables/useFlash.ts +118 -0
  40. package/src/composables/useToast.test.ts +230 -0
  41. package/src/composables/useToast.ts +95 -0
  42. package/src/exports.test.ts +72 -0
  43. package/src/icons.ts +38 -0
  44. package/src/index.ts +25 -0
  45. package/src/plugin.ts +85 -0
  46. package/src/stores/toastStore.test.ts +129 -0
  47. package/src/stores/toastStore.ts +288 -0
  48. package/src/styles/toast.css +353 -0
  49. package/src/styles/variables.css +83 -0
  50. package/src/types/index.ts +115 -0
  51. package/src/utils/sound.ts +140 -0
@@ -0,0 +1,7 @@
1
+ import { App } from 'vue';
2
+ import { ToastPluginOptions } from './types';
3
+ export declare const SoftToastPlugin: {
4
+ install(app: App, options?: ToastPluginOptions): void;
5
+ };
6
+ export declare const getToastOptions: () => ToastPluginOptions;
7
+ //# sourceMappingURL=plugin.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"plugin.d.ts","sourceRoot":"","sources":["../src/plugin.ts"],"names":[],"mappings":"AAAA,OAAO,EAAgB,KAAK,GAAG,EAAE,MAAM,KAAK,CAAA;AAC5C,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,SAAS,CAAA;AAEjD,OAAO,oBAAoB,CAAA;AAwB3B,eAAO,MAAM,eAAe;iBACb,GAAG,YAAW,kBAAkB;CAqD9C,CAAA;AAGD,eAAO,MAAM,eAAe,0BAAsB,CAAA"}
@@ -0,0 +1,25 @@
1
+ import { Toast, ToastOptions, ToastType, ToastPosition, ToastPromiseMessages } from '../types';
2
+ import { ComputedRef } from 'vue';
3
+ export interface ToastStore {
4
+ toasts: ComputedRef<Toast[]>;
5
+ getToastsByPosition: (position: ToastPosition) => ComputedRef<Toast[]>;
6
+ add: (options: ToastOptions) => string;
7
+ update: (id: string, options: Partial<ToastOptions>) => void;
8
+ dismiss: (id?: string | {
9
+ type?: ToastType | ToastType[];
10
+ }) => void;
11
+ pause: (id: string) => void;
12
+ resume: (id: string) => void;
13
+ expand: (id: string) => void;
14
+ collapse: (id: string) => void;
15
+ success: (title: string, options?: Omit<ToastOptions, 'type' | 'title'>) => string;
16
+ error: (title: string, options?: Omit<ToastOptions, 'type' | 'title'>) => string;
17
+ warning: (title: string, options?: Omit<ToastOptions, 'type' | 'title'>) => string;
18
+ info: (title: string, options?: Omit<ToastOptions, 'type' | 'title'>) => string;
19
+ loading: (title: string, options?: Omit<ToastOptions, 'type' | 'title'>) => string;
20
+ promise: <T>(promiseFn: Promise<T>, messages: ToastPromiseMessages, options?: Omit<ToastOptions, 'type' | 'promise' | 'promiseMessages'>) => Promise<T>;
21
+ clearAll: () => void;
22
+ remove: (id: string) => void;
23
+ }
24
+ export declare const toastStore: ToastStore;
25
+ //# sourceMappingURL=toastStore.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"toastStore.d.ts","sourceRoot":"","sources":["../../src/stores/toastStore.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,KAAK,EAAE,YAAY,EAAE,SAAS,EAAE,aAAa,EAAE,oBAAoB,EAAE,MAAM,UAAU,CAAA;AAsPnG,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,KAAK,CAAA;AAEtC,MAAM,WAAW,UAAU;IACzB,MAAM,EAAE,WAAW,CAAC,KAAK,EAAE,CAAC,CAAA;IAC5B,mBAAmB,EAAE,CAAC,QAAQ,EAAE,aAAa,KAAK,WAAW,CAAC,KAAK,EAAE,CAAC,CAAA;IACtE,GAAG,EAAE,CAAC,OAAO,EAAE,YAAY,KAAK,MAAM,CAAA;IACtC,MAAM,EAAE,CAAC,EAAE,EAAE,MAAM,EAAE,OAAO,EAAE,OAAO,CAAC,YAAY,CAAC,KAAK,IAAI,CAAA;IAC5D,OAAO,EAAE,CAAC,EAAE,CAAC,EAAE,MAAM,GAAG;QAAE,IAAI,CAAC,EAAE,SAAS,GAAG,SAAS,EAAE,CAAA;KAAE,KAAK,IAAI,CAAA;IACnE,KAAK,EAAE,CAAC,EAAE,EAAE,MAAM,KAAK,IAAI,CAAA;IAC3B,MAAM,EAAE,CAAC,EAAE,EAAE,MAAM,KAAK,IAAI,CAAA;IAC5B,MAAM,EAAE,CAAC,EAAE,EAAE,MAAM,KAAK,IAAI,CAAA;IAC5B,QAAQ,EAAE,CAAC,EAAE,EAAE,MAAM,KAAK,IAAI,CAAA;IAC9B,OAAO,EAAE,CAAC,KAAK,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,IAAI,CAAC,YAAY,EAAE,MAAM,GAAG,OAAO,CAAC,KAAK,MAAM,CAAA;IAClF,KAAK,EAAE,CAAC,KAAK,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,IAAI,CAAC,YAAY,EAAE,MAAM,GAAG,OAAO,CAAC,KAAK,MAAM,CAAA;IAChF,OAAO,EAAE,CAAC,KAAK,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,IAAI,CAAC,YAAY,EAAE,MAAM,GAAG,OAAO,CAAC,KAAK,MAAM,CAAA;IAClF,IAAI,EAAE,CAAC,KAAK,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,IAAI,CAAC,YAAY,EAAE,MAAM,GAAG,OAAO,CAAC,KAAK,MAAM,CAAA;IAC/E,OAAO,EAAE,CAAC,KAAK,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,IAAI,CAAC,YAAY,EAAE,MAAM,GAAG,OAAO,CAAC,KAAK,MAAM,CAAA;IAClF,OAAO,EAAE,CAAC,CAAC,EAAE,SAAS,EAAE,OAAO,CAAC,CAAC,CAAC,EAAE,QAAQ,EAAE,oBAAoB,EAAE,OAAO,CAAC,EAAE,IAAI,CAAC,YAAY,EAAE,MAAM,GAAG,SAAS,GAAG,iBAAiB,CAAC,KAAK,OAAO,CAAC,CAAC,CAAC,CAAA;IACvJ,QAAQ,EAAE,MAAM,IAAI,CAAA;IACpB,MAAM,EAAE,CAAC,EAAE,EAAE,MAAM,KAAK,IAAI,CAAA;CAC7B;AAED,eAAO,MAAM,UAAU,EAAE,UAkBxB,CAAA"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=toastStore.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"toastStore.test.d.ts","sourceRoot":"","sources":["../../src/stores/toastStore.test.ts"],"names":[],"mappings":""}
package/dist/style.css ADDED
@@ -0,0 +1 @@
1
+ .soft-toast-stack[data-v-3699e296]{position:relative;pointer-events:auto;padding-top:32px;padding-bottom:32px;margin-top:-32px;margin-bottom:-32px;width:100%;max-width:100%;display:flex;flex-direction:column}.soft-toast-list[data-v-3699e296]{position:relative;width:100%;flex-shrink:0}.soft-toast-stack[data-direction=up] .soft-toast-list[data-v-3699e296]{margin-top:auto}.soft-toast-stack[data-direction=down] .soft-toast-list[data-v-3699e296]{margin-bottom:auto}.soft-toast-list[data-v-3699e296]>.soft-toast-item{position:absolute;width:100%}.soft-toast-container[data-position$=left] .soft-toast-list[data-v-3699e296]>.soft-toast-item{left:0;right:auto}.soft-toast-container[data-position$=right] .soft-toast-list[data-v-3699e296]>.soft-toast-item{right:0;left:auto}.soft-toast-container[data-position$=center] .soft-toast-list[data-v-3699e296]>.soft-toast-item{left:50%;transform:translate(-50%)}.soft-toast-stack[data-direction=down][data-v-3699e296] .soft-toast-list>.soft-toast-item{top:0;bottom:auto}.soft-toast-stack[data-direction=up][data-v-3699e296] .soft-toast-list>.soft-toast-item{top:auto;bottom:0}:root{--st-bg-default: #ffffff;--st-bg-success: #dcfce7;--st-bg-error: #fee2e2;--st-bg-warning: #fef3c7;--st-bg-info: #dbeafe;--st-text-default: #1f2937;--st-text-success: #166534;--st-text-error: #991b1b;--st-text-warning: #92400e;--st-text-info: #1e40af;--st-border-default: #e5e7eb;--st-border-success: #86efac;--st-border-error: #fca5a5;--st-border-warning: #fcd34d;--st-border-info: #93c5fd;--st-icon-success: #22c55e;--st-icon-error: #ef4444;--st-icon-warning: #f59e0b;--st-icon-info: #3b82f6;--st-shadow-sm: 0 1px 2px 0 rgb(0 0 0 / .05);--st-shadow-md: 0 4px 6px -1px rgb(0 0 0 / .1), 0 2px 4px -2px rgb(0 0 0 / .1);--st-shadow-lg: 0 10px 15px -3px rgb(0 0 0 / .1), 0 4px 6px -4px rgb(0 0 0 / .1);--st-shadow-glow: 0 0 20px rgb(0 0 0 / .05);--st-toast-min-width: 320px;--st-toast-max-width: 420px;--st-toast-padding: 16px;--st-toast-gap: 12px;--st-border-radius: 9999px;--st-border-radius-blob: 24px;--st-font-family: system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif;--st-font-size-sm: 12px;--st-font-size-base: 14px;--st-font-size-lg: 16px;--st-font-weight-normal: 400;--st-font-weight-medium: 500;--st-font-weight-semibold: 600;--st-transition-fast: .15s;--st-transition-base: .3s;--st-transition-slow: .5s;--st-z-index: 9999}[data-soft-toast-theme=dark]{--st-bg-default: #1f2937;--st-bg-success: #14532d;--st-bg-error: #7f1d1d;--st-bg-warning: #713f12;--st-bg-info: #1e3a8a;--st-text-default: #f9fafb;--st-text-success: #86efac;--st-text-error: #fca5a5;--st-text-warning: #fcd34d;--st-text-info: #93c5fd;--st-border-default: #374151;--st-border-success: #166534;--st-border-error: #991b1b;--st-border-warning: #92400e;--st-border-info: #1e40af;--st-shadow-sm: 0 1px 2px 0 rgb(0 0 0 / .3);--st-shadow-md: 0 4px 6px -1px rgb(0 0 0 / .4), 0 2px 4px -2px rgb(0 0 0 / .3);--st-shadow-lg: 0 10px 15px -3px rgb(0 0 0 / .4), 0 4px 6px -4px rgb(0 0 0 / .3);--st-shadow-glow: 0 0 30px rgb(0 0 0 / .3)}.soft-toast-container{position:fixed;z-index:var(--st-z-index);pointer-events:none;display:flex;flex-direction:column;gap:10px;width:min(var(--st-width, 356px),calc(100vw - 64px));max-width:calc(100vw - 64px)}.soft-toast-container[data-position=top-left]{top:32px;left:32px}.soft-toast-container[data-position=top-center]{top:32px;left:50%;transform:translate(-50%)}.soft-toast-container[data-position=top-right]{top:32px;right:32px}.soft-toast-container[data-position=bottom-left]{bottom:32px;left:32px}.soft-toast-container[data-position=bottom-center]{bottom:32px;left:50%;transform:translate(-50%)}.soft-toast-container[data-position=bottom-right]{bottom:32px;right:32px}.soft-toast-container[data-position=top]{top:32px;left:50%;transform:translate(-50%)}.soft-toast-container[data-position=bottom]{bottom:32px;left:50%;transform:translate(-50%)}.soft-toast-container[data-position=left]{top:50%;left:32px;transform:translateY(-50%)}.soft-toast-container[data-position=right]{top:50%;right:32px;transform:translateY(-50%)}.soft-toast-container[data-position=center]{top:50%;left:50%;transform:translate(-50%,-50%)}.soft-toast-item{pointer-events:auto;position:relative;box-sizing:border-box;width:100%;min-width:260px;max-width:360px;padding:12px 16px;border-radius:18px;font-family:var(--st-font-family);font-size:13.5px;line-height:1.45;cursor:default;-webkit-user-select:none;user-select:none;will-change:transform,opacity;touch-action:pan-y;background:#fffffffa;border:1px solid rgba(226,232,240,.8);box-shadow:0 10px 30px -5px #00000014,inset 0 1px #fff}.soft-toast-item--swipeable{cursor:grab}.soft-toast-item--swipeable:active{cursor:grabbing}.soft-toast-container[data-position=top-left] .soft-toast-item,.soft-toast-container[data-position=left] .soft-toast-item{border-top-left-radius:4px}.soft-toast-container[data-position=bottom-left] .soft-toast-item{border-bottom-left-radius:4px}.soft-toast-container[data-position=top-right] .soft-toast-item,.soft-toast-container[data-position=right] .soft-toast-item{border-top-right-radius:4px}.soft-toast-container[data-position=bottom-right] .soft-toast-item{border-bottom-right-radius:4px}.soft-toast-item[data-type=default]{--st-accent: #9ca3af;--st-icon-color: #64748b}.soft-toast-item[data-type=success]{--st-accent: #34d399;--st-icon-color: #059669}.soft-toast-item[data-type=error]{--st-accent: #fb7185;--st-icon-color: #e11d48}.soft-toast-item[data-type=warning]{--st-accent: #fbbf24;--st-icon-color: #d97706}.soft-toast-item[data-type=info]{--st-accent: #60a5fa;--st-icon-color: #3b82f6}.soft-toast-item[data-type=promise]{--st-accent: #a78bfa;--st-icon-color: #7c3aed}.soft-toast-content{display:flex;align-items:flex-start;gap:11px}.soft-toast-icon{flex-shrink:0;width:24px;height:24px;margin-top:1px;display:flex;align-items:center;justify-content:center;color:var(--st-icon-color, #64748b)}.soft-toast-icon svg,.soft-toast-icon .iconify,.soft-toast-icon-svg{display:block;flex:none;width:18px!important;height:18px!important;max-width:18px;max-height:18px;filter:drop-shadow(0 1px 2px rgba(0,0,0,.12))}.soft-toast-item[data-type=promise] .soft-toast-icon-svg{animation:st-spin .8s linear infinite}.soft-toast-spinner{width:18px;height:18px;border:2px solid rgba(139,92,246,.2);border-top-color:#8b5cf6;border-radius:50%;animation:st-spin .7s linear infinite}@keyframes st-spin{to{transform:rotate(360deg)}}.soft-toast-body{flex:1;min-width:0}.soft-toast-header-row{display:flex;align-items:center;gap:12px}.soft-toast-title{font-weight:600;font-size:14px;color:#111827;margin:0;line-height:1.4;letter-spacing:-.01em;padding-right:4px}.soft-toast-title--has-close{padding-right:24px}.soft-toast-description{font-size:13.5px;font-weight:400;color:#4b5563;margin:4px 0 0;line-height:1.5;max-height:120px;overflow-y:auto;padding-right:4px}.soft-toast-description::-webkit-scrollbar{width:4px}.soft-toast-description::-webkit-scrollbar-track{background:transparent}.soft-toast-description::-webkit-scrollbar-thumb{background:#0000001a;border-radius:4px}.soft-toast-description::-webkit-scrollbar-thumb:hover{background:#0003}.soft-toast-timestamp{display:inline-block;font-size:11px;color:#9ca3af;letter-spacing:.01em;white-space:nowrap;margin-top:6px}.soft-toast-action{display:flex;gap:8px;margin-top:12px}.soft-toast-action-button{flex:1;width:100%;padding:8px 16px;border-radius:12px;background:color-mix(in srgb,var(--st-accent) 15%,transparent);color:var(--st-icon-color);border:none;font-size:13.5px;font-weight:600;cursor:pointer;font-family:inherit;transition:all .18s ease;letter-spacing:.01em;text-align:center}.soft-toast-action-button:hover{background:color-mix(in srgb,var(--st-accent) 25%,transparent);transform:scale(1.02)}.soft-toast-action-button:active{transform:scale(.98)}.soft-toast-action-primary{background:var(--st-icon-color);color:#fff}.soft-toast-action-primary:hover{background:color-mix(in srgb,var(--st-icon-color) 85%,#000)}.soft-toast-close{position:absolute;top:10px;right:10px;width:22px;height:22px;border-radius:50%;border:none;background:#0000000a;color:#9ca3af;cursor:pointer;display:flex;align-items:center;justify-content:center;opacity:0;transition:opacity .18s ease,background .18s ease,color .18s ease,transform .18s ease;padding:0}.soft-toast-close:hover{background:#0000001a;color:#374151;transform:scale(1.1)}.soft-toast-item:hover .soft-toast-close{opacity:1}.soft-toast-close[data-position=top-left]{right:auto;left:10px}.st-close-line-1,.st-close-line-2{transition:transform .3s cubic-bezier(.4,0,.2,1),opacity .3s ease;transform-origin:center}.soft-toast-close:hover .st-close-line-1{transform:rotate(-45deg)}.soft-toast-close:hover .st-close-line-2{transform:rotate(45deg) scaleX(0);opacity:0}.soft-toast-progress{position:absolute;top:0;left:0;right:0;bottom:0;height:100%;background:transparent;z-index:0;pointer-events:none;border-radius:inherit;overflow:hidden}.soft-toast-progress-bar{height:100%;background:var(--st-accent);opacity:.12;transform-origin:left}[data-soft-toast-theme=dark] .soft-toast-item{background:#1c1c1e;border-color:#ffffff14;box-shadow:0 4px 24px -4px #00000080,0 1px 4px #0000004d}[data-soft-toast-theme=dark] .soft-toast-title{color:#f9fafb}[data-soft-toast-theme=dark] .soft-toast-description{color:#9ca3af}[data-soft-toast-theme=dark] .soft-toast-timestamp{color:#6b7280}[data-soft-toast-theme=dark] .soft-toast-item[data-type]{background:#1c1c1e;border-color:#ffffff14}[data-soft-toast-dir=rtl] .soft-toast-item:before{left:auto;right:0;border-radius:4px 0 0 4px}[data-soft-toast-dir=rtl] .soft-toast-content{flex-direction:row-reverse;padding-left:0;padding-right:10px}[data-soft-toast-dir=rtl] .soft-toast-body{align-items:flex-end;text-align:right}[data-soft-toast-dir=rtl] .soft-toast-close{right:auto;left:10px}@media (max-width: 640px){.soft-toast-container{left:12px!important;right:12px!important;width:auto;max-width:none;transform:none!important;align-items:stretch!important}.soft-toast-container[data-position^=top]{top:12px}.soft-toast-container[data-position^=bottom]{bottom:12px}.soft-toast-item{min-width:auto;max-width:none}.soft-toast-close{opacity:1}}@media (prefers-reduced-motion: reduce){.soft-toast-item,.soft-toast-action-button,.soft-toast-close,.soft-toast-spinner{transition:none!important;animation:none!important}}
@@ -0,0 +1,107 @@
1
+ import { VNode, Component } from 'vue';
2
+ export type ToastType = 'default' | 'success' | 'error' | 'warning' | 'info' | 'promise';
3
+ export type ToastPosition = 'top' | 'bottom' | 'left' | 'right' | 'center' | 'top-left' | 'top-center' | 'top-right' | 'bottom-left' | 'bottom-center' | 'bottom-right';
4
+ export type AnimationPreset = 'smooth' | 'bouncy' | 'subtle' | 'snappy';
5
+ export type QueueOverflow = 'drop-oldest' | 'drop-newest';
6
+ export interface ToastAction {
7
+ label: string;
8
+ onClick: () => void | Promise<void>;
9
+ successLabel?: string;
10
+ primary?: boolean;
11
+ class?: string;
12
+ }
13
+ export interface ToastPromiseMessages {
14
+ loading: string;
15
+ success: string;
16
+ error: string;
17
+ description?: {
18
+ success?: string;
19
+ error?: string;
20
+ };
21
+ action?: {
22
+ error?: ToastAction;
23
+ };
24
+ }
25
+ export interface ToastOptions {
26
+ id?: string;
27
+ type?: ToastType;
28
+ title?: string;
29
+ description?: string | VNode;
30
+ action?: ToastAction | ToastAction[];
31
+ icon?: string | VNode | Component;
32
+ duration?: number;
33
+ position?: ToastPosition;
34
+ classNames?: ToastClassNames;
35
+ fillColor?: string;
36
+ borderColor?: string;
37
+ borderWidth?: number;
38
+ preset?: AnimationPreset;
39
+ bounce?: number;
40
+ spring?: boolean;
41
+ showTimestamp?: boolean;
42
+ showProgress?: boolean;
43
+ closeButton?: boolean | 'top-left' | 'top-right';
44
+ sound?: boolean | string;
45
+ soundVolume?: number;
46
+ onDismiss?: (id: string) => void;
47
+ onAutoClose?: (id: string) => void;
48
+ promise?: Promise<unknown>;
49
+ promiseMessages?: ToastPromiseMessages;
50
+ }
51
+ export interface ToastClassNames {
52
+ wrapper?: string;
53
+ content?: string;
54
+ header?: string;
55
+ title?: string;
56
+ icon?: string;
57
+ description?: string;
58
+ actionWrapper?: string;
59
+ actionButton?: string;
60
+ }
61
+ export interface Toast extends Required<Pick<ToastOptions, 'id' | 'type' | 'duration' | 'position' | 'showTimestamp' | 'showProgress' | 'spring'>> {
62
+ title?: string;
63
+ description?: string | VNode;
64
+ action?: ToastAction | ToastAction[];
65
+ icon?: string | VNode | Component;
66
+ classNames?: ToastClassNames;
67
+ fillColor?: string;
68
+ borderColor?: string;
69
+ borderWidth?: number;
70
+ closeButton?: boolean | 'top-left' | 'top-right';
71
+ preset: AnimationPreset;
72
+ bounce: number;
73
+ createdAt: number;
74
+ remainingTime: number;
75
+ isPaused: boolean;
76
+ isExpanded: boolean;
77
+ isLeaving: boolean;
78
+ promise?: Promise<unknown>;
79
+ promiseMessages?: ToastPromiseMessages;
80
+ onDismiss?: (id: string) => void;
81
+ onAutoClose?: (id: string) => void;
82
+ }
83
+ export interface ToastContainerProps {
84
+ position?: ToastPosition;
85
+ duration?: number;
86
+ gap?: number;
87
+ offset?: number | string;
88
+ theme?: 'light' | 'dark';
89
+ toastOptions?: Partial<ToastOptions>;
90
+ spring?: boolean;
91
+ bounce?: number;
92
+ preset?: AnimationPreset;
93
+ closeOnEscape?: boolean;
94
+ closeButton?: boolean | 'top-left' | 'top-right';
95
+ showProgress?: boolean;
96
+ showTimestamp?: boolean;
97
+ sound?: boolean;
98
+ soundVolume?: number;
99
+ maxQueue?: number;
100
+ queueOverflow?: QueueOverflow;
101
+ dir?: 'ltr' | 'rtl';
102
+ swipeToDismiss?: boolean;
103
+ }
104
+ export interface ToastPluginOptions extends ToastContainerProps {
105
+ teleportTarget?: string;
106
+ }
107
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/types/index.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,9 @@
1
+ import { ToastType } from '../types';
2
+ /**
3
+ * Play a sound for the given toast.
4
+ * @param toastType The semantic toast type
5
+ * @param sound true = built-in tone, string = custom audio URL, false = silence
6
+ * @param volume 0–1 (clamped)
7
+ */
8
+ export declare const playToastSound: (toastType: ToastType, sound: boolean | string | undefined, volume?: number) => void;
9
+ //# sourceMappingURL=sound.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"sound.d.ts","sourceRoot":"","sources":["../../src/utils/sound.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,UAAU,CAAA;AAqGzC;;;;;GAKG;AACH,eAAO,MAAM,cAAc,GACzB,WAAW,SAAS,EACpB,OAAO,OAAO,GAAG,MAAM,GAAG,SAAS,EACnC,eAAY,KACX,IAsBF,CAAA"}
package/package.json ADDED
@@ -0,0 +1,70 @@
1
+ {
2
+ "name": "@soft-toast/vue",
3
+ "version": "1.0.0",
4
+ "description": "Soft, fluid toast notifications for Vue 3 — part of the @soft-toast family",
5
+ "type": "module",
6
+ "main": "./dist/index.js",
7
+ "module": "./dist/index.js",
8
+ "types": "./dist/index.d.ts",
9
+ "exports": {
10
+ ".": {
11
+ "import": "./dist/index.js",
12
+ "types": "./dist/index.d.ts"
13
+ },
14
+ "./styles": {
15
+ "import": "./dist/style.css"
16
+ },
17
+ "./style.css": "./dist/style.css",
18
+ "./dist/style.css": "./dist/style.css"
19
+ },
20
+ "files": [
21
+ "dist",
22
+ "src"
23
+ ],
24
+ "scripts": {
25
+ "build": "bun run build.ts",
26
+ "typecheck": "tsc --noEmit",
27
+ "test": "bun test",
28
+ "test:watch": "bun test --watch",
29
+ "prepublishOnly": "bun run build"
30
+ },
31
+ "peerDependencies": {
32
+ "vue": "^3.3.0"
33
+ },
34
+ "dependencies": {
35
+ "@iconify/vue": "5.0.1",
36
+ "gsap": "^3.12.0"
37
+ },
38
+ "devDependencies": {
39
+ "@types/bun": "latest",
40
+ "@types/node": "^20.0.0",
41
+ "@vitejs/plugin-vue": "^5.0.0",
42
+ "typescript": "^5.4.0",
43
+ "vite": "^5.0.0",
44
+ "vite-plugin-dts": "5.0.0",
45
+ "vue": "^3.4.0",
46
+ "vue-tsc": "^2.0.0"
47
+ },
48
+ "keywords": [
49
+ "vue",
50
+ "vue3",
51
+ "toast",
52
+ "notification",
53
+ "animation",
54
+ "soft",
55
+ "fluid"
56
+ ],
57
+ "author": "",
58
+ "license": "MIT",
59
+ "repository": {
60
+ "type": "git",
61
+ "url": "https://github.com/ggthkk/soft-toast"
62
+ },
63
+ "homepage": "https://soft-toast.netlify.app",
64
+ "bugs": {
65
+ "url": "https://github.com/ggthkk/soft-toast/issues"
66
+ },
67
+ "engines": {
68
+ "bun": ">=1.0.0"
69
+ }
70
+ }
@@ -0,0 +1,303 @@
1
+ import { gsap } from 'gsap'
2
+ import type { AnimationPreset } from '../types'
3
+
4
+ export const presetConfigs: Record<AnimationPreset, { ease: string; elasticEase: string; duration: number }> = {
5
+ smooth: {
6
+ ease: 'power3.out',
7
+ elasticEase: 'elastic.out(1, 0.5)',
8
+ duration: 0.65
9
+ },
10
+ bouncy: {
11
+ ease: 'back.out(2)',
12
+ elasticEase: 'elastic.out(1.4, 0.45)',
13
+ duration: 0.75
14
+ },
15
+ subtle: {
16
+ ease: 'power2.out',
17
+ elasticEase: 'back.out(1.2)',
18
+ duration: 0.45
19
+ },
20
+ snappy: {
21
+ ease: 'back.out(3)',
22
+ elasticEase: 'elastic.out(1.1, 0.35)',
23
+ duration: 0.5
24
+ }
25
+ }
26
+
27
+ gsap.defaults({ overwrite: 'auto' })
28
+
29
+ const SOFT_SETTLE_DURATION = 0.42
30
+ const QUICK_SETTLE_DURATION = 0.22
31
+ const SOFT_SETTLE_EASE = 'power2.out'
32
+
33
+ export const createElasticEase = (bounce: number) => {
34
+ const amplitude = 0.8 + (bounce * 0.8)
35
+ const period = 0.55 - (bounce * 0.15)
36
+ return `elastic.out(${amplitude.toFixed(2)}, ${period.toFixed(2)})`
37
+ }
38
+
39
+ const clamp = (value: number, min: number, max: number) =>
40
+ Math.max(min, Math.min(max, value))
41
+
42
+ const cloudSettleAnimation = (
43
+ element: HTMLElement,
44
+ target: {
45
+ y: number
46
+ scale: number
47
+ opacity: number
48
+ }
49
+ ) => {
50
+ const currentY = Number(gsap.getProperty(element, 'y')) || 0
51
+ const movement = target.y - currentY
52
+ const distance = Math.abs(movement)
53
+
54
+ if (distance < 0.5) {
55
+ return gsap.to(element, {
56
+ y: target.y,
57
+ scale: target.scale,
58
+ opacity: target.opacity,
59
+ force3D: true,
60
+ overwrite: true,
61
+ duration: QUICK_SETTLE_DURATION,
62
+ ease: SOFT_SETTLE_EASE
63
+ })
64
+ }
65
+
66
+ return gsap.to(element, {
67
+ y: target.y,
68
+ scale: target.scale,
69
+ opacity: target.opacity,
70
+ force3D: true,
71
+ overwrite: true,
72
+ duration: clamp(0.34 + distance * 0.003, 0.36, 0.46),
73
+ ease: 'sine.out'
74
+ })
75
+ }
76
+
77
+ export const landingAnimation = (
78
+ element: HTMLElement,
79
+ options: {
80
+ preset?: AnimationPreset
81
+ bounce?: number
82
+ spring?: boolean
83
+ direction?: 'up' | 'down'
84
+ } = {}
85
+ ) => {
86
+ const { direction = 'up', preset = 'smooth' } = options
87
+ const config = presetConfigs[preset] || presetConfigs.smooth
88
+ const yFrom = direction === 'down' ? -14 : 14
89
+ const landingDuration = Math.min(config.duration, 0.46)
90
+ const landingEase = preset === 'bouncy' ? 'back.out(1.35)' : SOFT_SETTLE_EASE
91
+
92
+ const tl = gsap.timeline()
93
+
94
+ // Find extra wrapper
95
+ const extra = element.querySelector('.soft-toast-extra') as HTMLElement
96
+
97
+ if (extra) {
98
+ gsap.set(extra, { height: 0, opacity: 0 })
99
+ }
100
+
101
+ // 1. Soft and bouncy "pudding" pop (overshoot effect)
102
+ tl.fromTo(element,
103
+ { y: yFrom, opacity: 0, scale: 0.94 },
104
+ {
105
+ y: 0, opacity: 1, scale: 1,
106
+ duration: landingDuration,
107
+ ease: landingEase,
108
+ transformOrigin: direction === 'down' ? 'top center' : 'bottom center'
109
+ }
110
+ )
111
+
112
+ // 2. Expand extra content softly
113
+ if (extra) {
114
+ tl.to(extra, {
115
+ height: 'auto',
116
+ opacity: 1,
117
+ duration: 0.36,
118
+ force3D: true,
119
+ ease: SOFT_SETTLE_EASE,
120
+ clearProps: 'overflow'
121
+ }, "-=0.32") // Start expanding seamlessly as the main pop slows down
122
+ }
123
+
124
+ return tl
125
+ }
126
+
127
+ // Morph animation: pill ↔ expanded (fluid expand/collapse)
128
+ export const morphAnimation = (
129
+ element: HTMLElement,
130
+ options: { preset?: AnimationPreset; bounce?: number; spring?: boolean } = {}
131
+ ) => {
132
+ const { preset = 'smooth', bounce = 0.4, spring = true } = options
133
+ const ease = spring ? createElasticEase(bounce) : presetConfigs[preset].ease
134
+
135
+ const tl = gsap.timeline()
136
+ tl.to(element, {
137
+ borderRadius: '20px',
138
+ scaleX: 1.015,
139
+ scaleY: 0.98,
140
+ duration: 0.18,
141
+ ease: 'power2.in'
142
+ }).to(element, {
143
+ borderRadius: '20px',
144
+ scaleX: 1, scaleY: 1,
145
+ duration: 0.55,
146
+ ease: ease
147
+ })
148
+
149
+ return tl
150
+ }
151
+
152
+ // Exit: Soft scale down + fade
153
+ export const exitAnimation = (
154
+ element: HTMLElement
155
+ ) => {
156
+ return gsap.to(element, {
157
+ scale: 0.8,
158
+ opacity: 0,
159
+ force3D: true,
160
+ overwrite: 'auto',
161
+ duration: 0.25,
162
+ ease: 'power2.out'
163
+ })
164
+ }
165
+
166
+ // Swipe exit: fly off horizontally in the direction of the gesture
167
+ export const swipeExitAnimation = (
168
+ element: HTMLElement,
169
+ toX: number // positive = fly right, negative = fly left
170
+ ) => {
171
+ return gsap.to(element, {
172
+ x: toX,
173
+ opacity: 0,
174
+ force3D: true,
175
+ overwrite: 'auto',
176
+ duration: 0.28,
177
+ ease: 'power3.out'
178
+ })
179
+ }
180
+
181
+ // Swipe snap-back: elastic spring return to rest position
182
+ export const swipeSnapBack = (element: HTMLElement) => {
183
+ return gsap.to(element, {
184
+ x: 0,
185
+ opacity: 1,
186
+ force3D: true,
187
+ overwrite: 'auto',
188
+ duration: 0.55,
189
+ ease: 'elastic.out(1, 0.55)'
190
+ })
191
+ }
192
+
193
+ export const expandAnimation = (element: HTMLElement) =>
194
+ gsap.to(element, { scale: 1.025, duration: 0.25, ease: 'back.out(2)' })
195
+
196
+ export const collapseAnimation = (element: HTMLElement) =>
197
+ gsap.to(element, { scale: 1, duration: 0.3, ease: 'power2.out' })
198
+
199
+ // Animate to a stacked-card position. index 0 = front.
200
+ // direction: 'up' means the stack peeks upward (bottom-anchored).
201
+ // direction: 'down' means the stack peeks downward (top-anchored).
202
+ export const positionAnimation = (
203
+ element: HTMLElement,
204
+ options: {
205
+ index: number
206
+ expanded: boolean
207
+ preset?: AnimationPreset
208
+ bounce?: number
209
+ spring?: boolean
210
+ direction?: 'up' | 'down'
211
+ expandedOffset?: number
212
+ liftPx?: number
213
+ scaleStep?: number
214
+ opacityStep?: number
215
+ maxVisible?: number
216
+ reposition?: boolean
217
+ }
218
+ ) => {
219
+ const {
220
+ index,
221
+ expanded,
222
+ preset = 'smooth',
223
+ bounce = 0.4,
224
+ spring = true,
225
+ direction = 'up',
226
+ expandedOffset = 0,
227
+ liftPx = 14,
228
+ scaleStep = 0.055,
229
+ opacityStep = 0.2,
230
+ maxVisible = 3,
231
+ reposition = false
232
+ } = options
233
+
234
+ const config = presetConfigs[preset] || presetConfigs.smooth
235
+ const sign = direction === 'up' ? -1 : 1
236
+
237
+ if (expanded) {
238
+ // Expanded hover and dismiss restacking share one soft settle timing.
239
+ const expandedTarget = {
240
+ y: expandedOffset * sign,
241
+ scale: 1,
242
+ opacity: 1
243
+ }
244
+
245
+ if (reposition && spring) {
246
+ return cloudSettleAnimation(element, expandedTarget)
247
+ }
248
+
249
+ return gsap.to(element, {
250
+ ...expandedTarget,
251
+ force3D: true,
252
+ overwrite: 'auto',
253
+ duration: 0.36,
254
+ ease: SOFT_SETTLE_EASE
255
+ })
256
+ }
257
+
258
+ const isHidden = index >= maxVisible
259
+ const clampedIdx = Math.min(index, maxVisible)
260
+ const targetY = clampedIdx * liftPx * sign
261
+ const targetScale = 1 - clampedIdx * scaleStep
262
+ const targetOpacity = isHidden ? 0 : Math.max(0, 1 - clampedIdx * opacityStep)
263
+
264
+ if (reposition && spring) {
265
+ return cloudSettleAnimation(element, {
266
+ y: targetY,
267
+ scale: targetScale,
268
+ opacity: targetOpacity
269
+ })
270
+ }
271
+
272
+ // Initial collapsed stacking keeps the selected preset feel. Repositioning
273
+ // paths above stay softer so hover and dismiss do not feel mismatched.
274
+ const restackEase = spring ? createElasticEase(bounce) : config.ease
275
+ return gsap.to(element, {
276
+ y: targetY,
277
+ scale: targetScale,
278
+ opacity: targetOpacity,
279
+ force3D: true,
280
+ overwrite: 'auto',
281
+ duration: config.duration * 0.85,
282
+ ease: restackEase
283
+ })
284
+ }
285
+
286
+ export const progressAnimation = (
287
+ element: HTMLElement,
288
+ duration: number,
289
+ onComplete?: () => void
290
+ ) =>
291
+ gsap.fromTo(element,
292
+ { scaleX: 1 },
293
+ { scaleX: 0, duration: duration / 1000, ease: 'linear', onComplete }
294
+ )
295
+
296
+ export const pauseAnimation = (animation: gsap.core.Tween | gsap.core.Timeline) =>
297
+ animation.pause()
298
+
299
+ export const resumeAnimation = (animation: gsap.core.Tween | gsap.core.Timeline) =>
300
+ animation.play()
301
+
302
+ export const killAnimations = (element: HTMLElement) =>
303
+ gsap.killTweensOf(element)
@@ -0,0 +1,36 @@
1
+ <script setup lang="ts">
2
+
3
+ import type { ToastContainerProps, ToastPosition } from "../types";
4
+ import ToastRegion from "./ToastRegion.vue";
5
+
6
+ const props = withDefaults(defineProps<ToastContainerProps>(), {
7
+ position: "top-right",
8
+ maxQueue: 10,
9
+ queueOverflow: "drop-oldest",
10
+ });
11
+
12
+ const positions: ToastPosition[] = [
13
+ "top", "bottom", "left", "right", "center",
14
+ "top-left",
15
+ "top-center",
16
+ "top-right",
17
+ "bottom-left",
18
+ "bottom-center",
19
+ "bottom-right",
20
+ ];
21
+ </script>
22
+
23
+ <template>
24
+ <Teleport to="body">
25
+ <ToastRegion
26
+ v-for="pos in positions"
27
+ :key="pos"
28
+ v-bind="props"
29
+ :position="pos"
30
+ >
31
+ <template v-for="(_, name) in $slots" #[name]="slotProps">
32
+ <slot :name="name" v-bind="slotProps || {}" />
33
+ </template>
34
+ </ToastRegion>
35
+ </Teleport>
36
+ </template>
@@ -0,0 +1,33 @@
1
+ <script setup lang="ts">
2
+ import { Icon } from '@iconify/vue';
3
+ import type { ToastType } from "../types";
4
+ import { registerToastIcons } from "../icons";
5
+
6
+ registerToastIcons();
7
+
8
+ interface Props {
9
+ type: ToastType;
10
+ customIcon?: string;
11
+ }
12
+
13
+ const props = defineProps<Props>();
14
+
15
+ const defaultIcons: Record<string, string> = {
16
+ success: 'lucide:circle-check',
17
+ error: 'lucide:circle-x',
18
+ warning: 'lucide:triangle-alert',
19
+ info: 'lucide:info',
20
+ default: 'lucide:bell',
21
+ };
22
+
23
+ const getIcon = () => {
24
+ if (props.customIcon) return props.customIcon;
25
+ return defaultIcons[props.type] || defaultIcons.default;
26
+ };
27
+ </script>
28
+
29
+ <template>
30
+ <span class="soft-toast-icon">
31
+ <Icon class="soft-toast-icon-svg" :icon="getIcon()" :width="18" :height="18" />
32
+ </span>
33
+ </template>