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 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
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "vyrn",
3
- "version": "2.0.9",
3
+ "version": "3.0.0",
4
4
  "description": "A modern, customizable toast library for React and Next.js",
5
5
  "main": "dist/index.js",
6
6
  "module": "dist/index.esm.js",
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
- };
@@ -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
-
@@ -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
-
@@ -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
- }
@@ -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;
@@ -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
-
@@ -1,4 +0,0 @@
1
- export const TOAST_TYPES = ['info', 'success', 'warning', 'error'] as const;
2
- export const DEFAULT_DURATION = 3000;
3
- export const MAX_TOASTS = 5;
4
-