vyrn 2.0.9 → 3.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/Licence.md +9 -0
- package/package.json +1 -1
- package/rollup.config.js +0 -57
- package/src/components/ClientToastProvider.tsx +0 -42
- package/src/components/Toast.tsx +0 -184
- package/src/components/ToastContainer.tsx +0 -132
- package/src/components/ToastProvider.tsx +0 -105
- package/src/context/ToastContext.tsx +0 -15
- package/src/hooks/useToast.ts +0 -90
- package/src/index.ts +0 -9
- package/src/styles/toast.css +0 -238
- package/src/types/index.ts +0 -83
- package/src/utils/ToastProgressBar.tsx +0 -178
- package/src/utils/animations.ts +0 -14
- package/src/utils/constants.ts +0 -4
package/Licence.md
ADDED
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2025 vyan
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted to any person obtaining a copy of this software and associated documentation files (the "Software"), to view the Software for educational purposes. Use, modification, copying, merging, publishing, distribution, sublicensing, or selling of this Software is strictly prohibited without explicit written permission from the copyright holder.
|
|
6
|
+
|
|
7
|
+
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
|
|
8
|
+
|
|
9
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
package/package.json
CHANGED
package/rollup.config.js
DELETED
|
@@ -1,57 +0,0 @@
|
|
|
1
|
-
import typescript from 'rollup-plugin-typescript2';
|
|
2
|
-
import postcss from 'rollup-plugin-postcss';
|
|
3
|
-
import { terser } from 'rollup-plugin-terser';
|
|
4
|
-
import replace from '@rollup/plugin-replace';
|
|
5
|
-
import pkg from './package.json';
|
|
6
|
-
|
|
7
|
-
const external = [...Object.keys(pkg.peerDependencies || {})];
|
|
8
|
-
|
|
9
|
-
const plugins = [
|
|
10
|
-
// Add replace plugin to handle 'use client' directives
|
|
11
|
-
replace({
|
|
12
|
-
preventAssignment: true,
|
|
13
|
-
values: {
|
|
14
|
-
'use client': '',
|
|
15
|
-
},
|
|
16
|
-
}),
|
|
17
|
-
typescript({
|
|
18
|
-
typescript: require('typescript'),
|
|
19
|
-
tsconfig: 'tsconfig.json',
|
|
20
|
-
tsconfigOverride: {
|
|
21
|
-
compilerOptions: {
|
|
22
|
-
declaration: true,
|
|
23
|
-
declarationDir: 'dist',
|
|
24
|
-
},
|
|
25
|
-
},
|
|
26
|
-
}),
|
|
27
|
-
postcss({
|
|
28
|
-
extensions: ['.css'],
|
|
29
|
-
minimize: true,
|
|
30
|
-
inject: {
|
|
31
|
-
insertAt: 'top',
|
|
32
|
-
},
|
|
33
|
-
}),
|
|
34
|
-
terser(),
|
|
35
|
-
];
|
|
36
|
-
|
|
37
|
-
export default [
|
|
38
|
-
{
|
|
39
|
-
input: 'src/index.ts',
|
|
40
|
-
output: [
|
|
41
|
-
{
|
|
42
|
-
file: pkg.main,
|
|
43
|
-
format: 'cjs',
|
|
44
|
-
exports: 'named',
|
|
45
|
-
sourcemap: true,
|
|
46
|
-
},
|
|
47
|
-
{
|
|
48
|
-
file: pkg.module,
|
|
49
|
-
format: 'es',
|
|
50
|
-
exports: 'named',
|
|
51
|
-
sourcemap: true,
|
|
52
|
-
},
|
|
53
|
-
],
|
|
54
|
-
external,
|
|
55
|
-
plugins,
|
|
56
|
-
},
|
|
57
|
-
];
|
|
@@ -1,42 +0,0 @@
|
|
|
1
|
-
// ClientToastProvider.tsx
|
|
2
|
-
'use client';
|
|
3
|
-
|
|
4
|
-
import React from 'react';
|
|
5
|
-
import { ToastProvider } from './ToastProvider';
|
|
6
|
-
import { ToastPosition, ToastLayout, SwipeDirection } from '../types';
|
|
7
|
-
|
|
8
|
-
interface ClientToastProviderProps {
|
|
9
|
-
children: React.ReactNode;
|
|
10
|
-
position?: ToastPosition;
|
|
11
|
-
layout?: ToastLayout;
|
|
12
|
-
maxToasts?: number;
|
|
13
|
-
containerClassName?: string;
|
|
14
|
-
showCloseButton?: boolean;
|
|
15
|
-
swipeDirection?: SwipeDirection;
|
|
16
|
-
showProgressBar?: boolean;
|
|
17
|
-
}
|
|
18
|
-
|
|
19
|
-
export const ClientToastProvider: React.FC<ClientToastProviderProps> = ({
|
|
20
|
-
children,
|
|
21
|
-
position,
|
|
22
|
-
layout = 'normal',
|
|
23
|
-
maxToasts,
|
|
24
|
-
containerClassName,
|
|
25
|
-
showCloseButton = true,
|
|
26
|
-
swipeDirection = 'right',
|
|
27
|
-
showProgressBar = true,
|
|
28
|
-
}) => {
|
|
29
|
-
return (
|
|
30
|
-
<ToastProvider
|
|
31
|
-
defaultPosition={position}
|
|
32
|
-
defaultLayout={layout}
|
|
33
|
-
maxToasts={maxToasts}
|
|
34
|
-
containerClassName={containerClassName}
|
|
35
|
-
showCloseButton={showCloseButton}
|
|
36
|
-
swipeDirection={swipeDirection}
|
|
37
|
-
showProgressBar={showProgressBar}
|
|
38
|
-
>
|
|
39
|
-
{children}
|
|
40
|
-
</ToastProvider>
|
|
41
|
-
);
|
|
42
|
-
};
|
package/src/components/Toast.tsx
DELETED
|
@@ -1,184 +0,0 @@
|
|
|
1
|
-
'use client';
|
|
2
|
-
|
|
3
|
-
import React, { useEffect, useState } from 'react';
|
|
4
|
-
import { ToastProps } from '../types';
|
|
5
|
-
import { useToastContext } from '../context/ToastContext';
|
|
6
|
-
import { DEFAULT_DURATION } from '../utils/constants';
|
|
7
|
-
import { X, CheckCircle, AlertCircle, Info, AlertTriangle } from 'lucide-react';
|
|
8
|
-
import { motion, AnimatePresence } from 'framer-motion';
|
|
9
|
-
import ToastProgressBar from '../utils/ToastProgressBar';
|
|
10
|
-
|
|
11
|
-
interface ToastComponentProps extends ToastProps {
|
|
12
|
-
isExpanded: boolean;
|
|
13
|
-
isStacked: boolean;
|
|
14
|
-
showCloseButton?: boolean;
|
|
15
|
-
showProgressBar?: boolean;
|
|
16
|
-
}
|
|
17
|
-
|
|
18
|
-
export const Toast: React.FC<ToastComponentProps> = ({
|
|
19
|
-
id,
|
|
20
|
-
content,
|
|
21
|
-
type,
|
|
22
|
-
duration = DEFAULT_DURATION,
|
|
23
|
-
icon,
|
|
24
|
-
actions,
|
|
25
|
-
input,
|
|
26
|
-
onClose,
|
|
27
|
-
progress,
|
|
28
|
-
customStyles,
|
|
29
|
-
className = '',
|
|
30
|
-
isExpanded,
|
|
31
|
-
isStacked,
|
|
32
|
-
showCloseButton,
|
|
33
|
-
showProgressBar,
|
|
34
|
-
}) => {
|
|
35
|
-
const { removeToast, updateToast } = useToastContext();
|
|
36
|
-
const [inputValue, setInputValue] = useState('');
|
|
37
|
-
const [localProgress, setLocalProgress] = useState(progress || 100);
|
|
38
|
-
|
|
39
|
-
useEffect(() => {
|
|
40
|
-
if (typeof progress === 'undefined') {
|
|
41
|
-
const timer = setInterval(() => {
|
|
42
|
-
setLocalProgress((prev) => {
|
|
43
|
-
const newProgress = prev - 100 / (duration / 1000);
|
|
44
|
-
return newProgress > 0 ? newProgress : 0;
|
|
45
|
-
});
|
|
46
|
-
}, 1000);
|
|
47
|
-
|
|
48
|
-
return () => clearInterval(timer);
|
|
49
|
-
}
|
|
50
|
-
}, [duration, progress]);
|
|
51
|
-
|
|
52
|
-
useEffect(() => {
|
|
53
|
-
if (localProgress === 0) {
|
|
54
|
-
removeToast(id);
|
|
55
|
-
}
|
|
56
|
-
}, [localProgress, id, removeToast]);
|
|
57
|
-
|
|
58
|
-
useEffect(() => {
|
|
59
|
-
if (typeof progress !== 'undefined') {
|
|
60
|
-
setLocalProgress(progress);
|
|
61
|
-
}
|
|
62
|
-
}, [progress]);
|
|
63
|
-
|
|
64
|
-
const getIcon = () => {
|
|
65
|
-
if (icon) return icon;
|
|
66
|
-
switch (type) {
|
|
67
|
-
case 'success':
|
|
68
|
-
return <CheckCircle className="vyrn-toast-icon text-green-500" />;
|
|
69
|
-
case 'error':
|
|
70
|
-
return <AlertCircle className="vyrn-toast-icon text-red-500" />;
|
|
71
|
-
case 'info':
|
|
72
|
-
return <Info className="vyrn-toast-icon text-blue-500" />;
|
|
73
|
-
case 'warning':
|
|
74
|
-
return <AlertTriangle className="vyrn-toast-icon text-yellow-500" />;
|
|
75
|
-
default:
|
|
76
|
-
return null;
|
|
77
|
-
}
|
|
78
|
-
};
|
|
79
|
-
|
|
80
|
-
const handleClose = () => {
|
|
81
|
-
onClose?.();
|
|
82
|
-
removeToast(id);
|
|
83
|
-
};
|
|
84
|
-
|
|
85
|
-
const handleInputSubmit = (e: React.FormEvent) => {
|
|
86
|
-
e.preventDefault();
|
|
87
|
-
input?.onSubmit(inputValue);
|
|
88
|
-
setInputValue('');
|
|
89
|
-
};
|
|
90
|
-
|
|
91
|
-
return (
|
|
92
|
-
<motion.div
|
|
93
|
-
className={`vyrn-toast ${className} ${isStacked ? 'vyrn-toast-stacked' : ''} ${
|
|
94
|
-
type ? `vyrn-toast-${type}` : ''
|
|
95
|
-
}`}
|
|
96
|
-
style={{
|
|
97
|
-
...customStyles,
|
|
98
|
-
height: isExpanded ? 'auto' : isStacked ? '60px' : 'auto',
|
|
99
|
-
overflow: 'hidden',
|
|
100
|
-
}}
|
|
101
|
-
initial={{ opacity: 0, y: 50, scale: 0.3 }}
|
|
102
|
-
animate={{ opacity: 1, y: 0, scale: 1 }}
|
|
103
|
-
exit={{ opacity: 0, scale: 0.5, transition: { duration: 0.2 } }}
|
|
104
|
-
transition={{ type: 'spring', stiffness: 500, damping: 30 }}
|
|
105
|
-
>
|
|
106
|
-
<div className="vyrn-toast-content">
|
|
107
|
-
{getIcon()}
|
|
108
|
-
<div className="vyrn-toast-message">{content}</div>
|
|
109
|
-
</div>
|
|
110
|
-
<AnimatePresence>
|
|
111
|
-
{isExpanded && (
|
|
112
|
-
<motion.div
|
|
113
|
-
initial={{ opacity: 0, height: 0 }}
|
|
114
|
-
animate={{ opacity: 1, height: 'auto' }}
|
|
115
|
-
exit={{ opacity: 0, height: 0 }}
|
|
116
|
-
transition={{ duration: 0.3 }}
|
|
117
|
-
>
|
|
118
|
-
{actions && (
|
|
119
|
-
<div className="vyrn-toast-actions">
|
|
120
|
-
{actions.map((action, index) => (
|
|
121
|
-
<button
|
|
122
|
-
key={index}
|
|
123
|
-
onClick={(e) => {
|
|
124
|
-
e.stopPropagation();
|
|
125
|
-
action.onClick();
|
|
126
|
-
}}
|
|
127
|
-
className={`vyrn-toast-action vyrn-toast-action-${action.style || 'default'} ${
|
|
128
|
-
action.className || ''
|
|
129
|
-
}`}
|
|
130
|
-
>
|
|
131
|
-
{action.label}
|
|
132
|
-
</button>
|
|
133
|
-
))}
|
|
134
|
-
</div>
|
|
135
|
-
)}
|
|
136
|
-
{input && (
|
|
137
|
-
<form onSubmit={handleInputSubmit} className="vyrn-toast-input-form">
|
|
138
|
-
<input
|
|
139
|
-
type="text"
|
|
140
|
-
value={inputValue}
|
|
141
|
-
onChange={(e) => setInputValue(e.target.value)}
|
|
142
|
-
placeholder={input.placeholder}
|
|
143
|
-
className={`vyrn-toast-input ${input.className || ''}`}
|
|
144
|
-
onClick={(e) => e.stopPropagation()}
|
|
145
|
-
/>
|
|
146
|
-
<button
|
|
147
|
-
type="submit"
|
|
148
|
-
className={`vyrn-toast-input-submit ${input.submitClassName || ''}`}
|
|
149
|
-
onClick={(e) => e.stopPropagation()}
|
|
150
|
-
>
|
|
151
|
-
Submit
|
|
152
|
-
</button>
|
|
153
|
-
</form>
|
|
154
|
-
)}
|
|
155
|
-
</motion.div>
|
|
156
|
-
)}
|
|
157
|
-
</AnimatePresence>
|
|
158
|
-
{showCloseButton && (
|
|
159
|
-
<button
|
|
160
|
-
className="vyrn-toast-close"
|
|
161
|
-
onClick={(e) => {
|
|
162
|
-
e.stopPropagation();
|
|
163
|
-
handleClose();
|
|
164
|
-
}}
|
|
165
|
-
aria-label="Close"
|
|
166
|
-
>
|
|
167
|
-
<X size={18} />
|
|
168
|
-
</button>
|
|
169
|
-
)}
|
|
170
|
-
{showProgressBar && (
|
|
171
|
-
<ToastProgressBar
|
|
172
|
-
progress={localProgress}
|
|
173
|
-
type={type as 'success' | 'error' | 'warning' | 'info' | 'default'}
|
|
174
|
-
/>
|
|
175
|
-
)}
|
|
176
|
-
{/* <motion.div
|
|
177
|
-
className="vyrn-toast-progress"
|
|
178
|
-
initial={{ width: '100%' }}
|
|
179
|
-
animate={{ width: `${localProgress}%` }}
|
|
180
|
-
transition={{ duration: 0.5 }}
|
|
181
|
-
/> */}
|
|
182
|
-
</motion.div>
|
|
183
|
-
);
|
|
184
|
-
};
|
|
@@ -1,132 +0,0 @@
|
|
|
1
|
-
import React, { useState, useRef } from 'react';
|
|
2
|
-
import { Toast } from './Toast';
|
|
3
|
-
import { ToastProps, ToastPosition, ToastLayout } from '../types';
|
|
4
|
-
import { AnimatePresence, motion } from 'framer-motion';
|
|
5
|
-
|
|
6
|
-
interface ToastContainerProps {
|
|
7
|
-
toasts: ToastProps[];
|
|
8
|
-
position: ToastPosition;
|
|
9
|
-
layout: ToastLayout;
|
|
10
|
-
className?: string;
|
|
11
|
-
showCloseButton?: boolean;
|
|
12
|
-
swipeDirection?: 'left' | 'right' | 'up' | 'down';
|
|
13
|
-
showProgressBar?: boolean;
|
|
14
|
-
}
|
|
15
|
-
|
|
16
|
-
export const ToastContainer: React.FC<ToastContainerProps> = ({
|
|
17
|
-
toasts,
|
|
18
|
-
position,
|
|
19
|
-
layout,
|
|
20
|
-
className = '',
|
|
21
|
-
showCloseButton = true,
|
|
22
|
-
swipeDirection = 'right',
|
|
23
|
-
showProgressBar = true,
|
|
24
|
-
}) => {
|
|
25
|
-
const [isHovered, setIsHovered] = useState(false);
|
|
26
|
-
const containerRef = useRef<HTMLDivElement>(null);
|
|
27
|
-
const EXPANDED_OFFSET = 80;
|
|
28
|
-
const STACK_OFFSET = 3;
|
|
29
|
-
const NORMAL_OFFSET = 15; // Reduced from 80 to 45 for tighter spacing
|
|
30
|
-
|
|
31
|
-
const getPositionStyles = () => {
|
|
32
|
-
const styles: React.CSSProperties = { position: 'fixed', zIndex: 9999 };
|
|
33
|
-
if (position.includes('top')) styles.top = '1rem';
|
|
34
|
-
if (position.includes('bottom')) styles.bottom = '3rem';
|
|
35
|
-
if (position.includes('left')) styles.left = '1rem';
|
|
36
|
-
if (position.includes('right')) styles.right = '1rem';
|
|
37
|
-
if (position.includes('center')) {
|
|
38
|
-
styles.left = '50%';
|
|
39
|
-
styles.transform = 'translateX(-50%)';
|
|
40
|
-
}
|
|
41
|
-
return styles;
|
|
42
|
-
};
|
|
43
|
-
|
|
44
|
-
const getSwipeAnimation = () => {
|
|
45
|
-
const distance = 100;
|
|
46
|
-
switch (swipeDirection) {
|
|
47
|
-
case 'left':
|
|
48
|
-
return { x: -distance, opacity: 0 };
|
|
49
|
-
case 'right':
|
|
50
|
-
return { x: distance, opacity: 0 };
|
|
51
|
-
case 'up':
|
|
52
|
-
return { y: -distance, opacity: 0 };
|
|
53
|
-
case 'down':
|
|
54
|
-
return { y: distance, opacity: 0 };
|
|
55
|
-
default:
|
|
56
|
-
return { x: distance, opacity: 0 };
|
|
57
|
-
}
|
|
58
|
-
};
|
|
59
|
-
|
|
60
|
-
const getOffset = (index: number) => {
|
|
61
|
-
if (layout === 'normal') {
|
|
62
|
-
// Reduced offset for normal layout
|
|
63
|
-
return position.startsWith('top')
|
|
64
|
-
? index * (isHovered ? EXPANDED_OFFSET : NORMAL_OFFSET)
|
|
65
|
-
: -(index * (isHovered ? EXPANDED_OFFSET : NORMAL_OFFSET));
|
|
66
|
-
}
|
|
67
|
-
|
|
68
|
-
if (layout === 'stack') {
|
|
69
|
-
return isHovered ? index * EXPANDED_OFFSET : index * STACK_OFFSET;
|
|
70
|
-
}
|
|
71
|
-
|
|
72
|
-
return 0;
|
|
73
|
-
};
|
|
74
|
-
|
|
75
|
-
// Reverse toasts array for proper stacking order
|
|
76
|
-
const reversedToasts = [...toasts].reverse();
|
|
77
|
-
|
|
78
|
-
return (
|
|
79
|
-
<div
|
|
80
|
-
className={`vyrn-toast-container ${className}`}
|
|
81
|
-
style={getPositionStyles()}
|
|
82
|
-
ref={containerRef}
|
|
83
|
-
onMouseEnter={() => setIsHovered(true)}
|
|
84
|
-
onMouseLeave={() => setIsHovered(false)}
|
|
85
|
-
>
|
|
86
|
-
<AnimatePresence mode="sync">
|
|
87
|
-
{reversedToasts.map((toast, index) => (
|
|
88
|
-
<motion.div
|
|
89
|
-
key={toast.id}
|
|
90
|
-
layout
|
|
91
|
-
initial={{ opacity: 0, y: position.startsWith('top') ? -20 : 20, scale: 0.9 }}
|
|
92
|
-
animate={{
|
|
93
|
-
opacity: 1,
|
|
94
|
-
y: position.startsWith('top') ? getOffset(index) : -getOffset(index),
|
|
95
|
-
scale: isHovered ? 1.02 : 1,
|
|
96
|
-
zIndex: layout === 'stack' ? 9999 - index : 9999,
|
|
97
|
-
transition: {
|
|
98
|
-
type: 'spring',
|
|
99
|
-
stiffness: 500,
|
|
100
|
-
damping: 30,
|
|
101
|
-
},
|
|
102
|
-
}}
|
|
103
|
-
exit={getSwipeAnimation()}
|
|
104
|
-
drag={swipeDirection === 'left' || swipeDirection === 'right' ? 'x' : 'y'}
|
|
105
|
-
dragConstraints={{ left: 0, right: 0, top: 0, bottom: 0 }}
|
|
106
|
-
dragElastic={0.9}
|
|
107
|
-
onDragEnd={(_, info) => {
|
|
108
|
-
const threshold = 100;
|
|
109
|
-
if (Math.abs(info.offset.x) > threshold || Math.abs(info.offset.y) > threshold) {
|
|
110
|
-
toast.onClose?.();
|
|
111
|
-
}
|
|
112
|
-
}}
|
|
113
|
-
style={{
|
|
114
|
-
position: layout === 'normal' ? 'relative' : 'absolute',
|
|
115
|
-
width: '100%',
|
|
116
|
-
transformOrigin: position.startsWith('top') ? 'top' : 'bottom',
|
|
117
|
-
marginBottom: layout === 'normal' ? '0.25rem' : 0, // Reduced margin to 0.25rem
|
|
118
|
-
}}
|
|
119
|
-
>
|
|
120
|
-
<Toast
|
|
121
|
-
{...toast}
|
|
122
|
-
isExpanded={layout !== 'stack' || isHovered}
|
|
123
|
-
isStacked={layout === 'stack' && !isHovered}
|
|
124
|
-
showCloseButton={toast.showCloseButton ?? showCloseButton}
|
|
125
|
-
showProgressBar={toast.showProgressBar ?? showProgressBar}
|
|
126
|
-
/>
|
|
127
|
-
</motion.div>
|
|
128
|
-
))}
|
|
129
|
-
</AnimatePresence>
|
|
130
|
-
</div>
|
|
131
|
-
);
|
|
132
|
-
};
|
|
@@ -1,105 +0,0 @@
|
|
|
1
|
-
'use client';
|
|
2
|
-
|
|
3
|
-
import React, { useState, useCallback, useEffect, useMemo } from 'react';
|
|
4
|
-
import { createPortal } from 'react-dom';
|
|
5
|
-
import { ToastContext } from '../context/ToastContext';
|
|
6
|
-
import { ToastContainer } from './ToastContainer';
|
|
7
|
-
import { ToastProps, ToastProviderProps, ToastPosition, ToastLayout } from '../types';
|
|
8
|
-
import { MAX_TOASTS } from '../utils/constants';
|
|
9
|
-
import '../styles/toast.css';
|
|
10
|
-
|
|
11
|
-
export const ToastProvider: React.FC<ToastProviderProps> = ({
|
|
12
|
-
children,
|
|
13
|
-
defaultPosition = 'top-right',
|
|
14
|
-
defaultLayout = 'normal',
|
|
15
|
-
maxToasts = MAX_TOASTS,
|
|
16
|
-
containerClassName = '',
|
|
17
|
-
showCloseButton = true,
|
|
18
|
-
swipeDirection = 'right',
|
|
19
|
-
showProgressBar = true,
|
|
20
|
-
}) => {
|
|
21
|
-
const [toasts, setToasts] = useState<ToastProps[]>([]);
|
|
22
|
-
const [position, setPosition] = useState<ToastPosition>(defaultPosition);
|
|
23
|
-
const [layout, setLayout] = useState<ToastLayout>(defaultLayout);
|
|
24
|
-
|
|
25
|
-
const addToast = useCallback(
|
|
26
|
-
(toast: Omit<ToastProps, 'id'>) => {
|
|
27
|
-
const id = Math.random().toString(36).substr(2, 9);
|
|
28
|
-
setToasts((prevToasts) => {
|
|
29
|
-
const newToasts = [...prevToasts, { ...toast, id }];
|
|
30
|
-
return newToasts.slice(-maxToasts);
|
|
31
|
-
});
|
|
32
|
-
if (toast.soundEffect) {
|
|
33
|
-
new Audio(toast.soundEffect).play().catch((e) => console.error('Error playing sound:', e));
|
|
34
|
-
}
|
|
35
|
-
return id;
|
|
36
|
-
},
|
|
37
|
-
[maxToasts],
|
|
38
|
-
);
|
|
39
|
-
|
|
40
|
-
const removeToast = useCallback((id: string) => {
|
|
41
|
-
setToasts((prevToasts) => prevToasts.filter((toast) => toast.id !== id));
|
|
42
|
-
}, []);
|
|
43
|
-
|
|
44
|
-
const updateToast = useCallback((id: string, updates: Partial<Omit<ToastProps, 'id'>>) => {
|
|
45
|
-
setToasts((prevToasts) =>
|
|
46
|
-
prevToasts.map((toast) => (toast.id === id ? { ...toast, ...updates } : toast)),
|
|
47
|
-
);
|
|
48
|
-
}, []);
|
|
49
|
-
|
|
50
|
-
const clearAllToasts = useCallback(() => {
|
|
51
|
-
setToasts([]);
|
|
52
|
-
}, []);
|
|
53
|
-
|
|
54
|
-
const contextValue = useMemo(
|
|
55
|
-
() => ({
|
|
56
|
-
addToast,
|
|
57
|
-
removeToast,
|
|
58
|
-
updateToast,
|
|
59
|
-
clearAllToasts,
|
|
60
|
-
position,
|
|
61
|
-
setPosition,
|
|
62
|
-
layout,
|
|
63
|
-
setLayout,
|
|
64
|
-
showCloseButton,
|
|
65
|
-
swipeDirection,
|
|
66
|
-
showProgressBar,
|
|
67
|
-
}),
|
|
68
|
-
[
|
|
69
|
-
addToast,
|
|
70
|
-
removeToast,
|
|
71
|
-
updateToast,
|
|
72
|
-
clearAllToasts,
|
|
73
|
-
position,
|
|
74
|
-
layout,
|
|
75
|
-
showCloseButton,
|
|
76
|
-
swipeDirection,
|
|
77
|
-
showProgressBar,
|
|
78
|
-
],
|
|
79
|
-
);
|
|
80
|
-
|
|
81
|
-
const [isMounted, setIsMounted] = useState(false);
|
|
82
|
-
|
|
83
|
-
useEffect(() => {
|
|
84
|
-
setIsMounted(true);
|
|
85
|
-
}, []);
|
|
86
|
-
|
|
87
|
-
return (
|
|
88
|
-
<ToastContext.Provider value={contextValue}>
|
|
89
|
-
{children}
|
|
90
|
-
{isMounted &&
|
|
91
|
-
createPortal(
|
|
92
|
-
<ToastContainer
|
|
93
|
-
toasts={toasts}
|
|
94
|
-
position={position}
|
|
95
|
-
layout={layout}
|
|
96
|
-
className={containerClassName}
|
|
97
|
-
showCloseButton={showCloseButton}
|
|
98
|
-
swipeDirection={swipeDirection}
|
|
99
|
-
showProgressBar={showProgressBar}
|
|
100
|
-
/>,
|
|
101
|
-
document.body,
|
|
102
|
-
)}
|
|
103
|
-
</ToastContext.Provider>
|
|
104
|
-
);
|
|
105
|
-
};
|
|
@@ -1,15 +0,0 @@
|
|
|
1
|
-
'use client';
|
|
2
|
-
|
|
3
|
-
import React from 'react';
|
|
4
|
-
import { ToastContextValue } from '../types';
|
|
5
|
-
|
|
6
|
-
export const ToastContext = React.createContext<ToastContextValue | undefined>(undefined);
|
|
7
|
-
|
|
8
|
-
export const useToastContext = () => {
|
|
9
|
-
const context = React.useContext(ToastContext);
|
|
10
|
-
if (typeof context === 'undefined') {
|
|
11
|
-
throw new Error('useToastContext must be used within a ToastProvider');
|
|
12
|
-
}
|
|
13
|
-
return context;
|
|
14
|
-
};
|
|
15
|
-
|
package/src/hooks/useToast.ts
DELETED
|
@@ -1,90 +0,0 @@
|
|
|
1
|
-
'use client';
|
|
2
|
-
|
|
3
|
-
import { useCallback } from 'react';
|
|
4
|
-
import { useToastContext } from '../context/ToastContext';
|
|
5
|
-
import { ToastType, ToastProps, ToastPosition, ToastLayout } from '../types';
|
|
6
|
-
|
|
7
|
-
export const useToast = () => {
|
|
8
|
-
const {
|
|
9
|
-
addToast,
|
|
10
|
-
removeToast,
|
|
11
|
-
updateToast,
|
|
12
|
-
clearAllToasts,
|
|
13
|
-
position,
|
|
14
|
-
setPosition,
|
|
15
|
-
layout,
|
|
16
|
-
setLayout,
|
|
17
|
-
} = useToastContext();
|
|
18
|
-
|
|
19
|
-
const toast = useCallback(
|
|
20
|
-
(options: Omit<ToastProps, 'id'> & { className?: string }) => {
|
|
21
|
-
return addToast(options);
|
|
22
|
-
},
|
|
23
|
-
[addToast],
|
|
24
|
-
);
|
|
25
|
-
|
|
26
|
-
const update = useCallback(
|
|
27
|
-
(id: string, updates: Partial<Omit<ToastProps, 'id'>>) => {
|
|
28
|
-
updateToast(id, updates);
|
|
29
|
-
},
|
|
30
|
-
[updateToast],
|
|
31
|
-
);
|
|
32
|
-
|
|
33
|
-
const dismiss = useCallback(
|
|
34
|
-
(id: string) => {
|
|
35
|
-
removeToast(id);
|
|
36
|
-
},
|
|
37
|
-
[removeToast],
|
|
38
|
-
);
|
|
39
|
-
|
|
40
|
-
const clearAll = useCallback(() => {
|
|
41
|
-
clearAllToasts();
|
|
42
|
-
}, [clearAllToasts]);
|
|
43
|
-
|
|
44
|
-
const changePosition = useCallback(
|
|
45
|
-
(newPosition: ToastPosition) => {
|
|
46
|
-
setPosition(newPosition);
|
|
47
|
-
},
|
|
48
|
-
[setPosition],
|
|
49
|
-
);
|
|
50
|
-
|
|
51
|
-
const changeLayout = useCallback(
|
|
52
|
-
(newLayout: ToastLayout) => {
|
|
53
|
-
setLayout(newLayout);
|
|
54
|
-
},
|
|
55
|
-
[setLayout],
|
|
56
|
-
);
|
|
57
|
-
|
|
58
|
-
return {
|
|
59
|
-
toast,
|
|
60
|
-
update,
|
|
61
|
-
dismiss,
|
|
62
|
-
clearAll,
|
|
63
|
-
changePosition,
|
|
64
|
-
changeLayout,
|
|
65
|
-
position,
|
|
66
|
-
layout,
|
|
67
|
-
default: (
|
|
68
|
-
content: React.ReactNode,
|
|
69
|
-
options?: Partial<Omit<ToastProps, 'id' | 'type' | 'content'>>,
|
|
70
|
-
) => toast({ content, type: 'default', ...options }),
|
|
71
|
-
info: (
|
|
72
|
-
content: React.ReactNode,
|
|
73
|
-
options?: Partial<Omit<ToastProps, 'id' | 'type' | 'content'>>,
|
|
74
|
-
) => toast({ content, type: 'info', ...options }),
|
|
75
|
-
success: (
|
|
76
|
-
content: React.ReactNode,
|
|
77
|
-
options?: Partial<Omit<ToastProps, 'id' | 'type' | 'content'>>,
|
|
78
|
-
) => toast({ content, type: 'success', ...options }),
|
|
79
|
-
warning: (
|
|
80
|
-
content: React.ReactNode,
|
|
81
|
-
options?: Partial<Omit<ToastProps, 'id' | 'type' | 'content'>>,
|
|
82
|
-
) => toast({ content, type: 'warning', ...options }),
|
|
83
|
-
error: (
|
|
84
|
-
content: React.ReactNode,
|
|
85
|
-
options?: Partial<Omit<ToastProps, 'id' | 'type' | 'content'>>,
|
|
86
|
-
) => toast({ content, type: 'error', ...options }),
|
|
87
|
-
custom: (content: React.ReactNode, options?: Partial<Omit<ToastProps, 'id' | 'content'>>) =>
|
|
88
|
-
toast({ content, type: 'custom', ...options }),
|
|
89
|
-
};
|
|
90
|
-
};
|
package/src/index.ts
DELETED
|
@@ -1,9 +0,0 @@
|
|
|
1
|
-
'use client';
|
|
2
|
-
|
|
3
|
-
export { ToastProvider } from './components/ToastProvider';
|
|
4
|
-
export { useToast } from './hooks/useToast';
|
|
5
|
-
export type { ToastProps, ToastType, ToastPosition } from './types';
|
|
6
|
-
|
|
7
|
-
// Special export for Next.js
|
|
8
|
-
export { ClientToastProvider } from './components/ClientToastProvider';
|
|
9
|
-
|
package/src/styles/toast.css
DELETED
|
@@ -1,238 +0,0 @@
|
|
|
1
|
-
.vyrn-toast-container {
|
|
2
|
-
position: fixed;
|
|
3
|
-
z-index: 9999;
|
|
4
|
-
display: flex;
|
|
5
|
-
flex-direction: column;
|
|
6
|
-
max-width: 356px;
|
|
7
|
-
width: calc(100% - 24px);
|
|
8
|
-
offset: 32px;
|
|
9
|
-
pointer-events: none;
|
|
10
|
-
padding: 12px;
|
|
11
|
-
bottom: 0;
|
|
12
|
-
right: 0;
|
|
13
|
-
gap: 8px;
|
|
14
|
-
}
|
|
15
|
-
|
|
16
|
-
.vyrn-toast {
|
|
17
|
-
display: flex;
|
|
18
|
-
overflow: hidden;
|
|
19
|
-
position: relative;
|
|
20
|
-
pointer-events: auto;
|
|
21
|
-
border-radius: 6px;
|
|
22
|
-
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.1);
|
|
23
|
-
background-color: #ffffff;
|
|
24
|
-
width: 100%;
|
|
25
|
-
transition: all 0.2s ease;
|
|
26
|
-
}
|
|
27
|
-
|
|
28
|
-
.vyrn-toast-stacked {
|
|
29
|
-
height: 40px;
|
|
30
|
-
cursor: pointer;
|
|
31
|
-
transform-origin: top;
|
|
32
|
-
margin-bottom: 0;
|
|
33
|
-
}
|
|
34
|
-
|
|
35
|
-
.vyrn-toast-content {
|
|
36
|
-
display: flex;
|
|
37
|
-
align-items: center;
|
|
38
|
-
padding: 20px 20px;
|
|
39
|
-
min-height: 48px;
|
|
40
|
-
width: 100%;
|
|
41
|
-
}
|
|
42
|
-
|
|
43
|
-
.vyrn-toast-icon {
|
|
44
|
-
flex-shrink: 0;
|
|
45
|
-
margin-right: 8px;
|
|
46
|
-
width: 16px;
|
|
47
|
-
height: 16px;
|
|
48
|
-
}
|
|
49
|
-
|
|
50
|
-
.vyrn-toast-message {
|
|
51
|
-
flex-grow: 1;
|
|
52
|
-
font-size: 13px;
|
|
53
|
-
line-height: 1.5;
|
|
54
|
-
font-weight: 400;
|
|
55
|
-
color: #000000;
|
|
56
|
-
margin: 0;
|
|
57
|
-
padding: 0;
|
|
58
|
-
}
|
|
59
|
-
|
|
60
|
-
.vyrn-toast-actions {
|
|
61
|
-
display: flex;
|
|
62
|
-
flex-wrap: wrap;
|
|
63
|
-
gap: 8px;
|
|
64
|
-
padding: 0 16px 12px;
|
|
65
|
-
}
|
|
66
|
-
|
|
67
|
-
.vyrn-toast-action {
|
|
68
|
-
cursor: pointer;
|
|
69
|
-
padding: 6px 12px;
|
|
70
|
-
border-radius: 4px;
|
|
71
|
-
font-size: 13px;
|
|
72
|
-
font-weight: 500;
|
|
73
|
-
transition: all 0.2s ease;
|
|
74
|
-
}
|
|
75
|
-
|
|
76
|
-
.vyrn-toast-action-default {
|
|
77
|
-
background-color: #f1f3f5;
|
|
78
|
-
color: #495057;
|
|
79
|
-
}
|
|
80
|
-
|
|
81
|
-
.vyrn-toast-action-primary {
|
|
82
|
-
background-color: #339af0;
|
|
83
|
-
color: #ffffff;
|
|
84
|
-
}
|
|
85
|
-
|
|
86
|
-
.vyrn-toast-action-secondary {
|
|
87
|
-
background-color: #868e96;
|
|
88
|
-
color: #ffffff;
|
|
89
|
-
}
|
|
90
|
-
|
|
91
|
-
.vyrn-toast-action-danger {
|
|
92
|
-
background-color: #fa5252;
|
|
93
|
-
color: #ffffff;
|
|
94
|
-
}
|
|
95
|
-
|
|
96
|
-
.vyrn-toast-input-form {
|
|
97
|
-
display: flex;
|
|
98
|
-
padding: 0 16px 12px;
|
|
99
|
-
}
|
|
100
|
-
|
|
101
|
-
.vyrn-toast-input {
|
|
102
|
-
flex-grow: 1;
|
|
103
|
-
padding: 8px 12px;
|
|
104
|
-
border: 1px solid #e9ecef;
|
|
105
|
-
border-radius: 4px;
|
|
106
|
-
font-size: 13px;
|
|
107
|
-
}
|
|
108
|
-
|
|
109
|
-
.vyrn-toast-input-submit {
|
|
110
|
-
cursor: pointer;
|
|
111
|
-
padding: 8px 16px;
|
|
112
|
-
background-color: #339af0;
|
|
113
|
-
color: #ffffff;
|
|
114
|
-
border: none;
|
|
115
|
-
border-radius: 4px;
|
|
116
|
-
font-size: 13px;
|
|
117
|
-
font-weight: 500;
|
|
118
|
-
margin-left: 8px;
|
|
119
|
-
}
|
|
120
|
-
|
|
121
|
-
.vyrn-toast-close {
|
|
122
|
-
position: absolute;
|
|
123
|
-
top: 50%;
|
|
124
|
-
right: 12px;
|
|
125
|
-
transform: translateY(-50%);
|
|
126
|
-
background: none;
|
|
127
|
-
border: none;
|
|
128
|
-
cursor: pointer;
|
|
129
|
-
padding: 0;
|
|
130
|
-
display: flex;
|
|
131
|
-
align-items: center;
|
|
132
|
-
justify-content: center;
|
|
133
|
-
color: #868e96;
|
|
134
|
-
transition: color 0.2s ease;
|
|
135
|
-
}
|
|
136
|
-
|
|
137
|
-
.vyrn-toast-close:hover {
|
|
138
|
-
color: #495057;
|
|
139
|
-
}
|
|
140
|
-
|
|
141
|
-
.vyrn-toast-progress {
|
|
142
|
-
position: absolute;
|
|
143
|
-
bottom: 0;
|
|
144
|
-
left: 0;
|
|
145
|
-
height: 2px;
|
|
146
|
-
background-color: rgba(51, 154, 240, 0.5);
|
|
147
|
-
}
|
|
148
|
-
|
|
149
|
-
/* Toast types */
|
|
150
|
-
/* .vyrn-toast-success {
|
|
151
|
-
border-left: 3px solid #51cf66;
|
|
152
|
-
}
|
|
153
|
-
|
|
154
|
-
.vyrn-toast-error {
|
|
155
|
-
border-left: 3px solid #fa5252;
|
|
156
|
-
}
|
|
157
|
-
|
|
158
|
-
.vyrn-toast-info {
|
|
159
|
-
border-left: 3px solid #339af0;
|
|
160
|
-
} */
|
|
161
|
-
|
|
162
|
-
/* .vyrn-toast-warning {
|
|
163
|
-
border-left: 3px solid #fcc419;
|
|
164
|
-
} */
|
|
165
|
-
|
|
166
|
-
/* Dark mode */
|
|
167
|
-
.dark .vyrn-toast {
|
|
168
|
-
background-color: #222222;
|
|
169
|
-
}
|
|
170
|
-
|
|
171
|
-
.dark .vyrn-toast-message {
|
|
172
|
-
color: #e9ecef;
|
|
173
|
-
}
|
|
174
|
-
|
|
175
|
-
.dark .vyrn-toast-action-default {
|
|
176
|
-
background-color: #343a40;
|
|
177
|
-
color: #f1f3f5;
|
|
178
|
-
}
|
|
179
|
-
|
|
180
|
-
.dark .vyrn-toast-input {
|
|
181
|
-
background-color: #343a40;
|
|
182
|
-
border-color: #495057;
|
|
183
|
-
color: #e9ecef;
|
|
184
|
-
}
|
|
185
|
-
|
|
186
|
-
.dark .vyrn-toast-close {
|
|
187
|
-
color: #adb5bd;
|
|
188
|
-
}
|
|
189
|
-
|
|
190
|
-
.dark .vyrn-toast-close:hover {
|
|
191
|
-
color: #e9ecef;
|
|
192
|
-
}
|
|
193
|
-
|
|
194
|
-
/* Animations */
|
|
195
|
-
@keyframes vyrn-toast-enter {
|
|
196
|
-
from {
|
|
197
|
-
transform: translateY(100%);
|
|
198
|
-
opacity: 0;
|
|
199
|
-
}
|
|
200
|
-
to {
|
|
201
|
-
transform: translateY(0);
|
|
202
|
-
opacity: 1;
|
|
203
|
-
}
|
|
204
|
-
}
|
|
205
|
-
|
|
206
|
-
@keyframes vyrn-toast-exit {
|
|
207
|
-
from {
|
|
208
|
-
transform: translateY(0);
|
|
209
|
-
opacity: 1;
|
|
210
|
-
}
|
|
211
|
-
to {
|
|
212
|
-
transform: translateY(100%);
|
|
213
|
-
opacity: 0;
|
|
214
|
-
}
|
|
215
|
-
}
|
|
216
|
-
|
|
217
|
-
.vyrn-toast-enter {
|
|
218
|
-
animation: vyrn-toast-enter 0.3s ease-out;
|
|
219
|
-
}
|
|
220
|
-
|
|
221
|
-
.vyrn-toast-exit {
|
|
222
|
-
animation: vyrn-toast-exit 0.3s ease-in;
|
|
223
|
-
}
|
|
224
|
-
|
|
225
|
-
/* Responsive styles */
|
|
226
|
-
@media (max-width: 640px) {
|
|
227
|
-
.vyrn-toast-container {
|
|
228
|
-
width: calc(100% - 32px);
|
|
229
|
-
padding: 8px;
|
|
230
|
-
bottom: 0;
|
|
231
|
-
right: 0;
|
|
232
|
-
left: 0;
|
|
233
|
-
}
|
|
234
|
-
|
|
235
|
-
.vyrn-toast {
|
|
236
|
-
border-radius: 6px 6px 0 0;
|
|
237
|
-
}
|
|
238
|
-
}
|
package/src/types/index.ts
DELETED
|
@@ -1,83 +0,0 @@
|
|
|
1
|
-
import { ReactNode } from 'react';
|
|
2
|
-
|
|
3
|
-
export type ToastType = 'default' | 'info' | 'success' | 'warning' | 'error' | 'custom';
|
|
4
|
-
|
|
5
|
-
export type ToastPosition =
|
|
6
|
-
| 'top-right'
|
|
7
|
-
| 'top-left'
|
|
8
|
-
| 'bottom-right'
|
|
9
|
-
| 'bottom-left'
|
|
10
|
-
| 'top-center'
|
|
11
|
-
| 'bottom-center';
|
|
12
|
-
|
|
13
|
-
export type ToastLayout = 'stack' | 'normal';
|
|
14
|
-
|
|
15
|
-
export interface ToastAction {
|
|
16
|
-
label: string;
|
|
17
|
-
onClick: () => void;
|
|
18
|
-
style?: 'default' | 'primary' | 'secondary' | 'danger';
|
|
19
|
-
className?: string;
|
|
20
|
-
}
|
|
21
|
-
|
|
22
|
-
export interface ToastInput {
|
|
23
|
-
placeholder: string;
|
|
24
|
-
onSubmit: (value: string) => void;
|
|
25
|
-
submitClassName?: string;
|
|
26
|
-
className?: string;
|
|
27
|
-
}
|
|
28
|
-
|
|
29
|
-
export interface ToastProps {
|
|
30
|
-
id: string;
|
|
31
|
-
content: ReactNode;
|
|
32
|
-
type: ToastType;
|
|
33
|
-
duration?: number;
|
|
34
|
-
icon?: ReactNode;
|
|
35
|
-
actions?: ToastAction[];
|
|
36
|
-
input?: ToastInput;
|
|
37
|
-
onClose?: () => void;
|
|
38
|
-
progress?: number;
|
|
39
|
-
theme?: 'light' | 'dark' | 'custom';
|
|
40
|
-
customStyles?: React.CSSProperties;
|
|
41
|
-
soundEffect?: string;
|
|
42
|
-
animation?: 'slide' | 'fade' | 'bounce' | 'zoom';
|
|
43
|
-
className?: string;
|
|
44
|
-
showCloseButton?: boolean;
|
|
45
|
-
swipeClose?: boolean;
|
|
46
|
-
showProgressBar?: boolean;
|
|
47
|
-
}
|
|
48
|
-
|
|
49
|
-
export interface ToastContextValue {
|
|
50
|
-
addToast: (toast: Omit<ToastProps, 'id'>) => string;
|
|
51
|
-
removeToast: (id: string) => void;
|
|
52
|
-
updateToast: (id: string, updates: Partial<Omit<ToastProps, 'id'>>) => void;
|
|
53
|
-
clearAllToasts: () => void;
|
|
54
|
-
position: ToastPosition;
|
|
55
|
-
setPosition: (position: ToastPosition) => void;
|
|
56
|
-
layout: ToastLayout;
|
|
57
|
-
setLayout: (layout: ToastLayout) => void;
|
|
58
|
-
}
|
|
59
|
-
|
|
60
|
-
export interface ToastProviderProps {
|
|
61
|
-
children: React.ReactNode;
|
|
62
|
-
defaultPosition?: ToastPosition;
|
|
63
|
-
defaultLayout?: ToastLayout;
|
|
64
|
-
maxToasts?: number;
|
|
65
|
-
containerClassName?: string;
|
|
66
|
-
showCloseButton?: boolean;
|
|
67
|
-
swipeDirection?: SwipeDirection;
|
|
68
|
-
showProgressBar?: boolean;
|
|
69
|
-
}
|
|
70
|
-
|
|
71
|
-
export interface ToastTheme {
|
|
72
|
-
background: string;
|
|
73
|
-
text: string;
|
|
74
|
-
border: string;
|
|
75
|
-
progressBar: string;
|
|
76
|
-
}
|
|
77
|
-
|
|
78
|
-
export interface ToastGroupOptions {
|
|
79
|
-
groupId: string;
|
|
80
|
-
groupTitle: string;
|
|
81
|
-
}
|
|
82
|
-
|
|
83
|
-
export type SwipeDirection = 'left' | 'right' | 'up' | 'down';
|
|
@@ -1,178 +0,0 @@
|
|
|
1
|
-
import React from 'react';
|
|
2
|
-
import { motion } from 'framer-motion';
|
|
3
|
-
|
|
4
|
-
interface ToastProgressBarProps {
|
|
5
|
-
progress: number;
|
|
6
|
-
type?: 'success' | 'error' | 'warning' | 'info' | 'default';
|
|
7
|
-
}
|
|
8
|
-
|
|
9
|
-
const ToastProgressBar: React.FC<ToastProgressBarProps> = ({ progress, type = 'default' }) => {
|
|
10
|
-
// Dynamic color mapping based on toast type
|
|
11
|
-
const getColors = () => {
|
|
12
|
-
const colors = {
|
|
13
|
-
success: {
|
|
14
|
-
base: '#10B981',
|
|
15
|
-
highlight: '#34D399',
|
|
16
|
-
glow: 'rgba(16, 185, 129, 0.4)'
|
|
17
|
-
},
|
|
18
|
-
error: {
|
|
19
|
-
base: '#EF4444',
|
|
20
|
-
highlight: '#F87171',
|
|
21
|
-
glow: 'rgba(239, 68, 68, 0.4)'
|
|
22
|
-
},
|
|
23
|
-
warning: {
|
|
24
|
-
base: '#F59E0B',
|
|
25
|
-
highlight: '#FBBF24',
|
|
26
|
-
glow: 'rgba(245, 158, 11, 0.4)'
|
|
27
|
-
},
|
|
28
|
-
info: {
|
|
29
|
-
base: '#3B82F6',
|
|
30
|
-
highlight: '#60A5FA',
|
|
31
|
-
glow: 'rgba(59, 130, 246, 0.4)'
|
|
32
|
-
},
|
|
33
|
-
default: {
|
|
34
|
-
base: '#6366F1',
|
|
35
|
-
highlight: '#818CF8',
|
|
36
|
-
glow: 'rgba(99, 102, 241, 0.4)'
|
|
37
|
-
}
|
|
38
|
-
};
|
|
39
|
-
return colors[type] || colors.default;
|
|
40
|
-
};
|
|
41
|
-
|
|
42
|
-
const colors = getColors();
|
|
43
|
-
|
|
44
|
-
return (
|
|
45
|
-
<motion.div
|
|
46
|
-
style={{
|
|
47
|
-
position: 'absolute',
|
|
48
|
-
bottom: 0,
|
|
49
|
-
left: 0,
|
|
50
|
-
right: 0,
|
|
51
|
-
height: '2px',
|
|
52
|
-
overflow: 'hidden',
|
|
53
|
-
background: 'rgba(255, 255, 255, 0.1)',
|
|
54
|
-
backdropFilter: 'blur(8px)',
|
|
55
|
-
}}
|
|
56
|
-
>
|
|
57
|
-
{/* Backdrop blur line */}
|
|
58
|
-
<motion.div
|
|
59
|
-
style={{
|
|
60
|
-
position: 'absolute',
|
|
61
|
-
bottom: 0,
|
|
62
|
-
left: 0,
|
|
63
|
-
width: '100%',
|
|
64
|
-
height: '100%',
|
|
65
|
-
background: 'rgba(255, 255, 255, 0.05)',
|
|
66
|
-
backdropFilter: 'blur(2px)',
|
|
67
|
-
}}
|
|
68
|
-
/>
|
|
69
|
-
|
|
70
|
-
{/* Main progress line */}
|
|
71
|
-
<motion.div
|
|
72
|
-
style={{
|
|
73
|
-
position: 'absolute',
|
|
74
|
-
bottom: 0,
|
|
75
|
-
left: 0,
|
|
76
|
-
height: '100%',
|
|
77
|
-
background: `linear-gradient(90deg,
|
|
78
|
-
${colors.base},
|
|
79
|
-
${colors.highlight},
|
|
80
|
-
${colors.base}
|
|
81
|
-
)`,
|
|
82
|
-
backgroundSize: '200% 100%',
|
|
83
|
-
boxShadow: `0 0 10px ${colors.glow}`,
|
|
84
|
-
}}
|
|
85
|
-
initial={{ width: '100%', backgroundPosition: '0% 0%' }}
|
|
86
|
-
animate={{
|
|
87
|
-
width: `${progress}%`,
|
|
88
|
-
backgroundPosition: ['0% 0%', '100% 0%'],
|
|
89
|
-
}}
|
|
90
|
-
transition={{
|
|
91
|
-
width: {
|
|
92
|
-
duration: 0.4,
|
|
93
|
-
ease: [0.4, 0, 0.2, 1],
|
|
94
|
-
},
|
|
95
|
-
backgroundPosition: {
|
|
96
|
-
duration: 2,
|
|
97
|
-
repeat: Infinity,
|
|
98
|
-
ease: "linear"
|
|
99
|
-
}
|
|
100
|
-
}}
|
|
101
|
-
>
|
|
102
|
-
{/* Ultra-modern scanning effect */}
|
|
103
|
-
<motion.div
|
|
104
|
-
style={{
|
|
105
|
-
position: 'absolute',
|
|
106
|
-
top: 0,
|
|
107
|
-
left: 0,
|
|
108
|
-
width: '100%',
|
|
109
|
-
height: '100%',
|
|
110
|
-
background: `linear-gradient(90deg,
|
|
111
|
-
transparent 0%,
|
|
112
|
-
${colors.highlight}40 50%,
|
|
113
|
-
transparent 100%
|
|
114
|
-
)`,
|
|
115
|
-
transform: 'translateX(-50%)',
|
|
116
|
-
}}
|
|
117
|
-
animate={{
|
|
118
|
-
x: ['0%', '200%'],
|
|
119
|
-
}}
|
|
120
|
-
transition={{
|
|
121
|
-
duration: 1.5,
|
|
122
|
-
repeat: Infinity,
|
|
123
|
-
ease: "linear",
|
|
124
|
-
}}
|
|
125
|
-
/>
|
|
126
|
-
|
|
127
|
-
{/* Micro dots effect */}
|
|
128
|
-
<motion.div
|
|
129
|
-
style={{
|
|
130
|
-
position: 'absolute',
|
|
131
|
-
top: 0,
|
|
132
|
-
right: 0,
|
|
133
|
-
width: '2px',
|
|
134
|
-
height: '100%',
|
|
135
|
-
background: colors.highlight,
|
|
136
|
-
filter: 'blur(0.5px)',
|
|
137
|
-
}}
|
|
138
|
-
animate={{
|
|
139
|
-
opacity: [1, 0.5, 1],
|
|
140
|
-
scale: [1, 1.2, 1],
|
|
141
|
-
}}
|
|
142
|
-
transition={{
|
|
143
|
-
duration: 1,
|
|
144
|
-
repeat: Infinity,
|
|
145
|
-
ease: "easeInOut",
|
|
146
|
-
}}
|
|
147
|
-
/>
|
|
148
|
-
</motion.div>
|
|
149
|
-
|
|
150
|
-
{/* Ambient light effect */}
|
|
151
|
-
<motion.div
|
|
152
|
-
style={{
|
|
153
|
-
position: 'absolute',
|
|
154
|
-
bottom: 0,
|
|
155
|
-
left: 0,
|
|
156
|
-
width: `${progress}%`,
|
|
157
|
-
height: '100%',
|
|
158
|
-
background: `linear-gradient(90deg,
|
|
159
|
-
transparent,
|
|
160
|
-
${colors.glow},
|
|
161
|
-
transparent
|
|
162
|
-
)`,
|
|
163
|
-
filter: 'blur(4px)',
|
|
164
|
-
}}
|
|
165
|
-
animate={{
|
|
166
|
-
opacity: [0.3, 0.6, 0.3],
|
|
167
|
-
}}
|
|
168
|
-
transition={{
|
|
169
|
-
duration: 2,
|
|
170
|
-
repeat: Infinity,
|
|
171
|
-
ease: "easeInOut",
|
|
172
|
-
}}
|
|
173
|
-
/>
|
|
174
|
-
</motion.div>
|
|
175
|
-
);
|
|
176
|
-
};
|
|
177
|
-
|
|
178
|
-
export default ToastProgressBar;
|
package/src/utils/animations.ts
DELETED
|
@@ -1,14 +0,0 @@
|
|
|
1
|
-
export const fadeIn = `
|
|
2
|
-
@keyframes vyrnFadeIn {
|
|
3
|
-
from { opacity: 0; transform: translateY(20px); }
|
|
4
|
-
to { opacity: 1; transform: translateY(0); }
|
|
5
|
-
}
|
|
6
|
-
`;
|
|
7
|
-
|
|
8
|
-
export const fadeOut = `
|
|
9
|
-
@keyframes vyrnFadeOut {
|
|
10
|
-
from { opacity: 1; transform: translateY(0); }
|
|
11
|
-
to { opacity: 0; transform: translateY(-20px); }
|
|
12
|
-
}
|
|
13
|
-
`;
|
|
14
|
-
|
package/src/utils/constants.ts
DELETED