rn-toastify 1.0.11 → 2.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -21
- package/README.MD +221 -190
- package/babel.config.js +5 -5
- package/docs/demo.gif +0 -0
- package/example/App.js +193 -0
- package/index.js +60 -34
- package/jest.config.js +14 -14
- package/jest.setup.js +1 -1
- package/package.json +86 -43
- package/src/Toast.js +194 -114
- package/src/__tests__/Toast.test.js +54 -54
- package/src/components/BaseToast.js +163 -0
- package/src/components/CustomToast.js +58 -0
- package/src/components/CustomeToast.js +5 -28
- package/src/components/EmojiToast.js +142 -38
- package/src/components/ErrorToast.js +23 -52
- package/src/components/InfoToast.js +23 -0
- package/src/components/LoadingToast.js +24 -33
- package/src/components/ProgressBar.js +67 -0
- package/src/components/SuccessToast.js +23 -51
- package/src/components/WarningToast.js +23 -0
- package/src/components/icons/CheckIcon.js +98 -0
- package/src/components/icons/CrossIcon.js +84 -0
- package/src/components/icons/InfoIcon.js +71 -0
- package/src/components/icons/LoadingSpinner.js +78 -0
- package/src/components/icons/WarningIcon.js +84 -0
- package/src/components/icons/index.js +5 -0
- package/src/context/ToastContainer.js +223 -144
- package/src/context/ToastManager.js +150 -36
- package/src/hooks/useToast.js +123 -49
- package/src/types.d.ts +172 -0
- package/src/utils/Pixel/Index.js +28 -28
- package/src/utils/theme.js +81 -0
package/index.js
CHANGED
|
@@ -1,35 +1,61 @@
|
|
|
1
|
-
//
|
|
2
|
-
const useToastModule = require('./src/hooks/useToast');
|
|
3
|
-
const ToastModule = require('./src/Toast');
|
|
4
|
-
const ToastContainerModule = require('./src/context/ToastContainer');
|
|
5
|
-
const
|
|
6
|
-
const
|
|
7
|
-
const
|
|
8
|
-
const
|
|
9
|
-
const
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
const
|
|
13
|
-
const
|
|
14
|
-
const
|
|
15
|
-
const
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
const
|
|
19
|
-
const
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
1
|
+
// rn-toastify v2.0.0 — Production-ready toast notifications for React Native
|
|
2
|
+
const useToastModule = require('./src/hooks/useToast');
|
|
3
|
+
const ToastModule = require('./src/Toast');
|
|
4
|
+
const ToastContainerModule = require('./src/context/ToastContainer');
|
|
5
|
+
const ToastManagerModule = require('./src/context/ToastManager');
|
|
6
|
+
const SuccessToastModule = require('./src/components/SuccessToast');
|
|
7
|
+
const ErrorToastModule = require('./src/components/ErrorToast');
|
|
8
|
+
const InfoToastModule = require('./src/components/InfoToast');
|
|
9
|
+
const WarningToastModule = require('./src/components/WarningToast');
|
|
10
|
+
const LoadingToastModule = require('./src/components/LoadingToast');
|
|
11
|
+
const EmojiToastModule = require('./src/components/EmojiToast');
|
|
12
|
+
const CustomToastModule = require('./src/components/CustomToast');
|
|
13
|
+
const BaseToastModule = require('./src/components/BaseToast');
|
|
14
|
+
const ProgressBarModule = require('./src/components/ProgressBar');
|
|
15
|
+
const themeModule = require('./src/utils/theme');
|
|
16
|
+
|
|
17
|
+
// Extract default exports properly
|
|
18
|
+
const useToast = useToastModule.default || useToastModule;
|
|
19
|
+
const Toast = ToastModule.default || ToastModule;
|
|
20
|
+
const ToastContainer = ToastContainerModule.default || ToastContainerModule;
|
|
21
|
+
const ToastManager = ToastManagerModule.default || ToastManagerModule;
|
|
22
|
+
const SuccessToast = SuccessToastModule.default || SuccessToastModule;
|
|
23
|
+
const ErrorToast = ErrorToastModule.default || ErrorToastModule;
|
|
24
|
+
const InfoToast = InfoToastModule.default || InfoToastModule;
|
|
25
|
+
const WarningToast = WarningToastModule.default || WarningToastModule;
|
|
26
|
+
const LoadingToast = LoadingToastModule.default || LoadingToastModule;
|
|
27
|
+
const EmojiToast = EmojiToastModule.default || EmojiToastModule;
|
|
28
|
+
const CustomToast = CustomToastModule.default || CustomToastModule;
|
|
29
|
+
const BaseToast = BaseToastModule.default || BaseToastModule;
|
|
30
|
+
const ProgressBar = ProgressBarModule.default || ProgressBarModule;
|
|
31
|
+
|
|
32
|
+
// Primary default export
|
|
33
|
+
module.exports = useToast;
|
|
34
|
+
|
|
35
|
+
// Named exports
|
|
36
|
+
module.exports.useToast = useToast;
|
|
37
|
+
module.exports.Toast = Toast;
|
|
38
|
+
module.exports.ToastContainer = ToastContainer;
|
|
39
|
+
module.exports.ToastManager = ToastManager;
|
|
40
|
+
|
|
41
|
+
// Built-in toast components
|
|
42
|
+
module.exports.SuccessToast = SuccessToast;
|
|
43
|
+
module.exports.ErrorToast = ErrorToast;
|
|
44
|
+
module.exports.InfoToast = InfoToast;
|
|
45
|
+
module.exports.WarningToast = WarningToast;
|
|
46
|
+
module.exports.LoadingToast = LoadingToast;
|
|
47
|
+
module.exports.EmojiToast = EmojiToast;
|
|
48
|
+
module.exports.CustomToast = CustomToast;
|
|
49
|
+
module.exports.BaseToast = BaseToast;
|
|
50
|
+
module.exports.ProgressBar = ProgressBar;
|
|
51
|
+
|
|
52
|
+
// Theme utilities
|
|
53
|
+
module.exports.TOAST_COLORS = themeModule.TOAST_COLORS;
|
|
54
|
+
module.exports.TOAST_THEME = themeModule.TOAST_THEME;
|
|
55
|
+
module.exports.TOAST_DEFAULTS = themeModule.TOAST_DEFAULTS;
|
|
56
|
+
|
|
57
|
+
// Backward compatibility aliases
|
|
58
|
+
module.exports.CustomeToast = CustomToast;
|
|
59
|
+
|
|
60
|
+
// ES6 default
|
|
35
61
|
module.exports.default = useToast;
|
package/jest.config.js
CHANGED
|
@@ -1,14 +1,14 @@
|
|
|
1
|
-
module.exports = {
|
|
2
|
-
preset: 'react-native',
|
|
3
|
-
testEnvironment: 'node',
|
|
4
|
-
collectCoverage: true,
|
|
5
|
-
collectCoverageFrom: ['src/**/*.{ts,tsx}'],
|
|
6
|
-
setupFilesAfterEnv: [
|
|
7
|
-
'@testing-library/jest-native/extend-expect',
|
|
8
|
-
'./jest.setup.js'
|
|
9
|
-
],
|
|
10
|
-
transformIgnorePatterns: [
|
|
11
|
-
'node_modules/(?!(jest-)?react-native|@react-native|@react-navigation|@react-native-community|@expo)',
|
|
12
|
-
],
|
|
13
|
-
moduleFileExtensions: ['ts', 'tsx', 'js', 'jsx', 'json', 'node'],
|
|
14
|
-
};
|
|
1
|
+
module.exports = {
|
|
2
|
+
preset: 'react-native',
|
|
3
|
+
testEnvironment: 'node',
|
|
4
|
+
collectCoverage: true,
|
|
5
|
+
collectCoverageFrom: ['src/**/*.{ts,tsx}'],
|
|
6
|
+
setupFilesAfterEnv: [
|
|
7
|
+
'@testing-library/jest-native/extend-expect',
|
|
8
|
+
'./jest.setup.js'
|
|
9
|
+
],
|
|
10
|
+
transformIgnorePatterns: [
|
|
11
|
+
'node_modules/(?!(jest-)?react-native|@react-native|@react-navigation|@react-native-community|@expo)',
|
|
12
|
+
],
|
|
13
|
+
moduleFileExtensions: ['ts', 'tsx', 'js', 'jsx', 'json', 'node'],
|
|
14
|
+
};
|
package/jest.setup.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
import '@testing-library/jest-native/extend-expect';
|
|
1
|
+
import '@testing-library/jest-native/extend-expect';
|
package/package.json
CHANGED
|
@@ -1,44 +1,87 @@
|
|
|
1
|
-
{
|
|
2
|
-
"name": "rn-toastify",
|
|
3
|
-
"version": "
|
|
4
|
-
"description": "A
|
|
5
|
-
"main": "index.js",
|
|
6
|
-
"
|
|
7
|
-
|
|
8
|
-
"
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
"
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
"
|
|
17
|
-
"react
|
|
18
|
-
"
|
|
19
|
-
"
|
|
20
|
-
"
|
|
21
|
-
"alert",
|
|
22
|
-
"
|
|
23
|
-
"
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
"react"
|
|
29
|
-
"
|
|
30
|
-
"
|
|
31
|
-
"
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
"
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
"
|
|
38
|
-
"
|
|
39
|
-
"
|
|
40
|
-
"
|
|
41
|
-
"
|
|
42
|
-
"
|
|
43
|
-
|
|
1
|
+
{
|
|
2
|
+
"name": "rn-toastify",
|
|
3
|
+
"version": "2.0.0",
|
|
4
|
+
"description": "A professional, production-ready toast notification library for React Native. Featuring smooth spring animations, swipe-to-dismiss gestures, progress bars, queue management, and a beautiful design system with light/dark themes.",
|
|
5
|
+
"main": "index.js",
|
|
6
|
+
"types": "src/types.d.ts",
|
|
7
|
+
"repository": {
|
|
8
|
+
"type": "git",
|
|
9
|
+
"url": "https://github.com/muku534/react-native-toast"
|
|
10
|
+
},
|
|
11
|
+
"homepage": "https://github.com/muku534/react-native-toast#readme",
|
|
12
|
+
"scripts": {
|
|
13
|
+
"test": "jest"
|
|
14
|
+
},
|
|
15
|
+
"keywords": [
|
|
16
|
+
"react-native",
|
|
17
|
+
"react native",
|
|
18
|
+
"toast",
|
|
19
|
+
"toastify",
|
|
20
|
+
"notification",
|
|
21
|
+
"alert",
|
|
22
|
+
"snackbar",
|
|
23
|
+
"message",
|
|
24
|
+
"popup",
|
|
25
|
+
"react-native-toast",
|
|
26
|
+
"react-native-toastify",
|
|
27
|
+
"react-native-toast-message",
|
|
28
|
+
"react-native-notifications",
|
|
29
|
+
"rn-toast",
|
|
30
|
+
"rn-toastify",
|
|
31
|
+
"animated-toast",
|
|
32
|
+
"swipe-toast",
|
|
33
|
+
"promise-toast",
|
|
34
|
+
"success-toast",
|
|
35
|
+
"error-toast",
|
|
36
|
+
"info-toast",
|
|
37
|
+
"warning-toast",
|
|
38
|
+
"loading-toast",
|
|
39
|
+
"custom-toast",
|
|
40
|
+
"progress-bar",
|
|
41
|
+
"queue",
|
|
42
|
+
"dismiss",
|
|
43
|
+
"ios",
|
|
44
|
+
"android",
|
|
45
|
+
"cross-platform",
|
|
46
|
+
"reanimated",
|
|
47
|
+
"gesture",
|
|
48
|
+
"animation",
|
|
49
|
+
"spring-animation",
|
|
50
|
+
"dark-mode",
|
|
51
|
+
"theme",
|
|
52
|
+
"accessibility",
|
|
53
|
+
"production-ready",
|
|
54
|
+
"typescript",
|
|
55
|
+
"mobile",
|
|
56
|
+
"notification-library",
|
|
57
|
+
"alert-library",
|
|
58
|
+
"toast-notification",
|
|
59
|
+
"react-native-component"
|
|
60
|
+
],
|
|
61
|
+
"author": {
|
|
62
|
+
"name": "Mukesh Prajapati",
|
|
63
|
+
"url": "https://github.com/muku534"
|
|
64
|
+
},
|
|
65
|
+
"license": "MIT",
|
|
66
|
+
"peerDependencies": {
|
|
67
|
+
"react": ">=16.8.0",
|
|
68
|
+
"react-native": ">=0.60.0",
|
|
69
|
+
"react-native-reanimated": ">=3.0.0"
|
|
70
|
+
},
|
|
71
|
+
"peerDependenciesMeta": {
|
|
72
|
+
"react-native-reanimated": {
|
|
73
|
+
"optional": false
|
|
74
|
+
}
|
|
75
|
+
},
|
|
76
|
+
"dependencies": {
|
|
77
|
+
"events": "^3.3.0"
|
|
78
|
+
},
|
|
79
|
+
"devDependencies": {
|
|
80
|
+
"@testing-library/jest-native": "^5.4.3",
|
|
81
|
+
"@testing-library/react-native": "^12.5.2",
|
|
82
|
+
"@types/jest": "^28.1.0",
|
|
83
|
+
"@types/react-native": "^0.67.3",
|
|
84
|
+
"jest": "^28.1.0",
|
|
85
|
+
"react-test-renderer": "^17.0.2"
|
|
86
|
+
}
|
|
44
87
|
}
|
package/src/Toast.js
CHANGED
|
@@ -1,114 +1,194 @@
|
|
|
1
|
-
import React, { useEffect, useMemo } from 'react';
|
|
2
|
-
import { PanResponder, StyleSheet } from 'react-native';
|
|
3
|
-
import Animated, {
|
|
4
|
-
useSharedValue,
|
|
5
|
-
useAnimatedStyle,
|
|
6
|
-
withTiming,
|
|
7
|
-
withSpring,
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
}
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
{
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
{
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
};
|
|
97
|
-
|
|
98
|
-
const
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
}
|
|
113
|
-
|
|
114
|
-
|
|
1
|
+
import React, { useEffect, useMemo, useCallback, useRef } from 'react';
|
|
2
|
+
import { PanResponder, StyleSheet } from 'react-native';
|
|
3
|
+
import Animated, {
|
|
4
|
+
useSharedValue,
|
|
5
|
+
useAnimatedStyle,
|
|
6
|
+
withTiming,
|
|
7
|
+
withSpring,
|
|
8
|
+
withDelay,
|
|
9
|
+
runOnJS,
|
|
10
|
+
Easing,
|
|
11
|
+
interpolate,
|
|
12
|
+
cancelAnimation,
|
|
13
|
+
} from 'react-native-reanimated';
|
|
14
|
+
|
|
15
|
+
const SPRING_CONFIG = {
|
|
16
|
+
damping: 18,
|
|
17
|
+
stiffness: 160,
|
|
18
|
+
mass: 0.8,
|
|
19
|
+
};
|
|
20
|
+
|
|
21
|
+
const Toast = ({ visible, duration, position, children, onHide, style, theme }) => {
|
|
22
|
+
const opacity = useSharedValue(0);
|
|
23
|
+
const translateY = useSharedValue(position === 'top' ? -80 : position === 'center' ? 30 : 80);
|
|
24
|
+
const translateX = useSharedValue(0);
|
|
25
|
+
const scale = useSharedValue(0.85);
|
|
26
|
+
const hideCalledRef = useRef(false);
|
|
27
|
+
|
|
28
|
+
const positionStyle = useMemo(
|
|
29
|
+
() => {
|
|
30
|
+
if (position === 'top') return styles.top;
|
|
31
|
+
if (position === 'center') return styles.center;
|
|
32
|
+
return styles.bottom;
|
|
33
|
+
},
|
|
34
|
+
[position]
|
|
35
|
+
);
|
|
36
|
+
|
|
37
|
+
const safeOnHide = useCallback(() => {
|
|
38
|
+
if (!hideCalledRef.current) {
|
|
39
|
+
hideCalledRef.current = true;
|
|
40
|
+
onHide?.();
|
|
41
|
+
}
|
|
42
|
+
}, [onHide]);
|
|
43
|
+
|
|
44
|
+
useEffect(() => {
|
|
45
|
+
hideCalledRef.current = false;
|
|
46
|
+
|
|
47
|
+
if (visible) {
|
|
48
|
+
// Entrance animation — smooth spring with overshoot
|
|
49
|
+
opacity.value = withTiming(1, { duration: 280, easing: Easing.out(Easing.cubic) });
|
|
50
|
+
translateY.value = withSpring(0, SPRING_CONFIG);
|
|
51
|
+
scale.value = withSpring(1, { ...SPRING_CONFIG, damping: 14 });
|
|
52
|
+
translateX.value = 0;
|
|
53
|
+
|
|
54
|
+
// Auto-dismiss timer
|
|
55
|
+
if (duration && duration !== Infinity) {
|
|
56
|
+
const hideTimeout = setTimeout(() => {
|
|
57
|
+
hideToast();
|
|
58
|
+
}, duration);
|
|
59
|
+
|
|
60
|
+
return () => clearTimeout(hideTimeout);
|
|
61
|
+
}
|
|
62
|
+
} else {
|
|
63
|
+
hideToast();
|
|
64
|
+
}
|
|
65
|
+
}, [visible, duration]);
|
|
66
|
+
|
|
67
|
+
const hideToast = useCallback(() => {
|
|
68
|
+
const exitY = position === 'top' ? -60 : position === 'center' ? 20 : 60;
|
|
69
|
+
|
|
70
|
+
opacity.value = withTiming(0, {
|
|
71
|
+
duration: 250,
|
|
72
|
+
easing: Easing.in(Easing.cubic),
|
|
73
|
+
});
|
|
74
|
+
translateY.value = withTiming(exitY, {
|
|
75
|
+
duration: 250,
|
|
76
|
+
easing: Easing.in(Easing.cubic),
|
|
77
|
+
});
|
|
78
|
+
scale.value = withTiming(0.9, {
|
|
79
|
+
duration: 250,
|
|
80
|
+
easing: Easing.in(Easing.cubic),
|
|
81
|
+
}, (finished) => {
|
|
82
|
+
// Only fire onHide after animation actually completes
|
|
83
|
+
if (finished) {
|
|
84
|
+
runOnJS(safeOnHide)();
|
|
85
|
+
}
|
|
86
|
+
});
|
|
87
|
+
}, [position, safeOnHide]);
|
|
88
|
+
|
|
89
|
+
const animatedStyle = useAnimatedStyle(() => ({
|
|
90
|
+
opacity: opacity.value,
|
|
91
|
+
transform: [
|
|
92
|
+
{ translateY: translateY.value },
|
|
93
|
+
{ translateX: translateX.value },
|
|
94
|
+
{ scale: scale.value },
|
|
95
|
+
],
|
|
96
|
+
}));
|
|
97
|
+
|
|
98
|
+
const panResponder = useMemo(
|
|
99
|
+
() =>
|
|
100
|
+
PanResponder.create({
|
|
101
|
+
onMoveShouldSetPanResponder: (evt, gestureState) => {
|
|
102
|
+
// More responsive swipe detection
|
|
103
|
+
return Math.abs(gestureState.dx) > 15 || Math.abs(gestureState.dy) > 15;
|
|
104
|
+
},
|
|
105
|
+
onPanResponderMove: (evt, gestureState) => {
|
|
106
|
+
translateX.value = gestureState.dx;
|
|
107
|
+
// Add subtle vertical follow for natural feel
|
|
108
|
+
if (position === 'top' && gestureState.dy < 0) {
|
|
109
|
+
translateY.value = gestureState.dy * 0.3;
|
|
110
|
+
} else if (position === 'bottom' && gestureState.dy > 0) {
|
|
111
|
+
translateY.value = gestureState.dy * 0.3;
|
|
112
|
+
}
|
|
113
|
+
// Fade as user swipes further
|
|
114
|
+
opacity.value = interpolate(
|
|
115
|
+
Math.abs(gestureState.dx),
|
|
116
|
+
[0, 150],
|
|
117
|
+
[1, 0.3],
|
|
118
|
+
'clamp'
|
|
119
|
+
);
|
|
120
|
+
},
|
|
121
|
+
onPanResponderRelease: (evt, gestureState) => {
|
|
122
|
+
if (Math.abs(gestureState.dx) > 80) {
|
|
123
|
+
// Dismiss: fly out in swipe direction
|
|
124
|
+
const direction = gestureState.dx > 0 ? 1 : -1;
|
|
125
|
+
translateX.value = withTiming(direction * 400, { duration: 200 });
|
|
126
|
+
opacity.value = withTiming(0, { duration: 200 });
|
|
127
|
+
scale.value = withTiming(0.85, { duration: 200 }, (finished) => {
|
|
128
|
+
if (finished) {
|
|
129
|
+
runOnJS(safeOnHide)();
|
|
130
|
+
}
|
|
131
|
+
});
|
|
132
|
+
} else if (position === 'top' && gestureState.dy < -40) {
|
|
133
|
+
// Swipe up to dismiss (for top toasts)
|
|
134
|
+
translateY.value = withTiming(-100, { duration: 200 });
|
|
135
|
+
opacity.value = withTiming(0, { duration: 200 }, (finished) => {
|
|
136
|
+
if (finished) runOnJS(safeOnHide)();
|
|
137
|
+
});
|
|
138
|
+
} else if (position === 'bottom' && gestureState.dy > 40) {
|
|
139
|
+
// Swipe down to dismiss (for bottom toasts)
|
|
140
|
+
translateY.value = withTiming(100, { duration: 200 });
|
|
141
|
+
opacity.value = withTiming(0, { duration: 200 }, (finished) => {
|
|
142
|
+
if (finished) runOnJS(safeOnHide)();
|
|
143
|
+
});
|
|
144
|
+
} else {
|
|
145
|
+
// Snap back with spring
|
|
146
|
+
translateX.value = withSpring(0, { damping: 15, stiffness: 200 });
|
|
147
|
+
translateY.value = withSpring(0, { damping: 15, stiffness: 200 });
|
|
148
|
+
opacity.value = withSpring(1);
|
|
149
|
+
}
|
|
150
|
+
},
|
|
151
|
+
}),
|
|
152
|
+
[position, safeOnHide]
|
|
153
|
+
);
|
|
154
|
+
|
|
155
|
+
if (!visible) return null;
|
|
156
|
+
|
|
157
|
+
// If children is a React element, inject theme and duration props
|
|
158
|
+
const renderContent = () => {
|
|
159
|
+
if (React.isValidElement(children)) {
|
|
160
|
+
return React.cloneElement(children, { theme, duration });
|
|
161
|
+
}
|
|
162
|
+
return children;
|
|
163
|
+
};
|
|
164
|
+
|
|
165
|
+
return (
|
|
166
|
+
<Animated.View
|
|
167
|
+
{...panResponder.panHandlers}
|
|
168
|
+
style={[styles.container, animatedStyle, positionStyle, style]}
|
|
169
|
+
>
|
|
170
|
+
{renderContent()}
|
|
171
|
+
</Animated.View>
|
|
172
|
+
);
|
|
173
|
+
};
|
|
174
|
+
|
|
175
|
+
const styles = StyleSheet.create({
|
|
176
|
+
container: {
|
|
177
|
+
position: 'absolute',
|
|
178
|
+
left: 0,
|
|
179
|
+
right: 0,
|
|
180
|
+
alignItems: 'center',
|
|
181
|
+
zIndex: 9999,
|
|
182
|
+
},
|
|
183
|
+
top: {
|
|
184
|
+
top: 0,
|
|
185
|
+
},
|
|
186
|
+
center: {
|
|
187
|
+
// Center positioning handled by container
|
|
188
|
+
},
|
|
189
|
+
bottom: {
|
|
190
|
+
bottom: 20,
|
|
191
|
+
},
|
|
192
|
+
});
|
|
193
|
+
|
|
194
|
+
export default React.memo(Toast);
|