rn-toastify 1.0.8 → 1.0.9
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.MD +135 -124
- package/docs/Toast.gif +0 -0
- package/index.js +31 -8
- package/package.json +43 -43
- package/src/Toast.js +10 -2
- package/src/components/CustomeToast.js +4 -2
- package/src/components/EmojiToast.js +7 -3
- package/src/components/ErrorToast.js +7 -3
- package/src/components/LoadingToast.js +7 -3
- package/src/components/SuccessToast.js +9 -5
- package/src/context/ToastContainer.js +26 -14
- package/src/context/ToastManager.js +7 -4
package/README.MD
CHANGED
|
@@ -5,188 +5,194 @@
|
|
|
5
5
|
## Demo
|
|
6
6
|
|
|
7
7
|
Animated toast message component for React Native.
|
|
8
|
-
|
|
8
|
+
# rn-toastify (react-native-toast)
|
|
9
9
|
|
|
10
|
+
Animated, customizable toast notifications for React Native with a simple imperative API, responsive animations, and built-in light/dark theme support.
|
|
10
11
|
|
|
12
|
+
Demo GIF: https://github.com/muku534/react-native-toast/blob/master/docs/Toast.gif
|
|
11
13
|
|
|
12
|
-
##
|
|
13
|
-
|
|
14
|
-
- 🚀 Imperative API
|
|
15
|
-
- 🎨 Customizable layouts
|
|
16
|
-
- 🔧 Flexible config
|
|
17
|
-
- 📅 Promise Handling
|
|
18
|
-
- 📍 Position Control
|
|
19
|
-
|
|
14
|
+
## Key features
|
|
20
15
|
|
|
16
|
+
- Single-root container (mount once at App entry)
|
|
17
|
+
- Imperative hook API (useToast) — call from anywhere in your component tree
|
|
18
|
+
- System-aware light/dark theme with optional forced theme
|
|
19
|
+
- Promise helper (loading → success/error)
|
|
20
|
+
- Custom content and emoji toasts
|
|
21
21
|
|
|
22
22
|
## Installation
|
|
23
23
|
|
|
24
24
|
```bash
|
|
25
25
|
npm install rn-toastify
|
|
26
26
|
```
|
|
27
|
-
|
|
28
|
-
## Usage
|
|
29
27
|
|
|
30
|
-
|
|
28
|
+
Or with yarn:
|
|
29
|
+
|
|
30
|
+
```bash
|
|
31
|
+
yarn add rn-toastify
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
## Quick start (recommended)
|
|
35
|
+
|
|
36
|
+
1. Mount a single `ToastContainer` at the top of your app (App.js). This container listens to the package singleton manager and renders toasts from anywhere in the app.
|
|
37
|
+
|
|
38
|
+
App.js
|
|
39
|
+
|
|
40
|
+
```javascript
|
|
41
|
+
import React from 'react';
|
|
42
|
+
import { NavigationContainer } from '@react-navigation/native';
|
|
43
|
+
import MainNavigator from './src/navigation';
|
|
44
|
+
import useToast, { ToastContainer } from 'rn-toastify';
|
|
45
|
+
|
|
46
|
+
export default function App() {
|
|
47
|
+
return (
|
|
48
|
+
<>
|
|
49
|
+
<NavigationContainer>
|
|
50
|
+
<MainNavigator />
|
|
51
|
+
</NavigationContainer>
|
|
31
52
|
|
|
32
|
-
|
|
53
|
+
{/* Mount once at root. Optionally force theme: <ToastContainer theme="dark" /> */}
|
|
54
|
+
<ToastContainer />
|
|
55
|
+
</>
|
|
56
|
+
);
|
|
57
|
+
}
|
|
58
|
+
```
|
|
33
59
|
|
|
34
|
-
|
|
60
|
+
2. Use the `useToast()` hook anywhere in your app (inside React function components):
|
|
35
61
|
|
|
36
62
|
```javascript
|
|
63
|
+
import React from 'react';
|
|
64
|
+
import { View, Button } from 'react-native';
|
|
37
65
|
import useToast from 'rn-toastify';
|
|
38
|
-
import ToastContainer from 'rn-toastify';
|
|
39
66
|
|
|
40
|
-
|
|
41
|
-
|
|
67
|
+
export default function HomeScreen() {
|
|
68
|
+
const { success, error, promise, custom, emoji } = useToast();
|
|
42
69
|
|
|
43
70
|
return (
|
|
44
|
-
<View
|
|
45
|
-
|
|
46
|
-
<
|
|
71
|
+
<View>
|
|
72
|
+
<Button title="Success" onPress={() => success('Saved successfully')} />
|
|
73
|
+
<Button title="Error" onPress={() => error('Failed to save', { position: 'top' })} />
|
|
47
74
|
</View>
|
|
48
75
|
);
|
|
49
|
-
}
|
|
76
|
+
}
|
|
77
|
+
```
|
|
50
78
|
|
|
51
|
-
|
|
79
|
+
## API (summary)
|
|
52
80
|
|
|
53
|
-
|
|
81
|
+
`useToast()` returns an object with convenience methods:
|
|
54
82
|
|
|
55
|
-
-
|
|
83
|
+
- `show(content, options)` — show a raw element or text (content can be a React element)
|
|
84
|
+
- `success(messageOrElement, options)`
|
|
85
|
+
- `error(messageOrElement, options)`
|
|
86
|
+
- `custom(element, options)`
|
|
87
|
+
- `emoji(message, emojiChar, options)`
|
|
88
|
+
- `promise(promise, { loading, success, error }, options)`
|
|
56
89
|
|
|
57
|
-
|
|
90
|
+
### Options (common)
|
|
58
91
|
|
|
59
|
-
-
|
|
60
|
-
|
|
61
|
-
|
|
92
|
+
- `duration`: number (milliseconds). Use `Infinity` for persistent toasts.
|
|
93
|
+
- `position`: `'top' | 'bottom' | 'center'`
|
|
94
|
+
- `style`: object — custom style to apply to the toast wrapper
|
|
62
95
|
|
|
96
|
+
## Examples
|
|
97
|
+
|
|
98
|
+
Success toast
|
|
99
|
+
|
|
100
|
+
```javascript
|
|
63
101
|
const { success } = useToast();
|
|
102
|
+
success('Operation successful', { duration: 1500, position: 'top' });
|
|
103
|
+
```
|
|
64
104
|
|
|
65
|
-
|
|
66
|
-
success('Operation was successful!', { duration: 1500, position: 'top' });
|
|
67
|
-
};
|
|
105
|
+
Promise helper
|
|
68
106
|
|
|
107
|
+
```javascript
|
|
108
|
+
const { promise } = useToast();
|
|
109
|
+
|
|
110
|
+
const myPromise = fetch('/api/save');
|
|
69
111
|
|
|
112
|
+
promise(myPromise, {
|
|
113
|
+
loading: 'Saving…',
|
|
114
|
+
success: 'Saved!',
|
|
115
|
+
error: 'Save failed',
|
|
116
|
+
});
|
|
70
117
|
```
|
|
71
|
-
|
|
118
|
+
|
|
119
|
+
Custom content
|
|
120
|
+
|
|
72
121
|
```javascript
|
|
73
|
-
|
|
122
|
+
const { custom } = useToast();
|
|
123
|
+
custom(<MyCustomView />, { duration: 2000 });
|
|
124
|
+
```
|
|
74
125
|
|
|
75
|
-
|
|
126
|
+
Emoji toast
|
|
76
127
|
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
};
|
|
128
|
+
```javascript
|
|
129
|
+
const { emoji } = useToast();
|
|
130
|
+
emoji('Great job!', '👍', { duration: 1300 });
|
|
131
|
+
```
|
|
132
|
+
|
|
133
|
+
## Theme
|
|
80
134
|
|
|
135
|
+
- By default `ToastContainer` detects the system color scheme (via `Appearance`) and passes a `theme` prop to visual toast components.
|
|
136
|
+
- To force a theme, mount the container with `theme`:
|
|
81
137
|
|
|
138
|
+
```jsx
|
|
139
|
+
<ToastContainer theme="dark" />
|
|
82
140
|
```
|
|
83
141
|
|
|
84
|
-
|
|
85
|
-
```javascript
|
|
86
|
-
import useToast from 'rn-toastify';
|
|
142
|
+
If the system color scheme is unavailable, the toast theme defaults to `light`.
|
|
87
143
|
|
|
88
|
-
|
|
144
|
+
## Compatibility & installation notes
|
|
89
145
|
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
resolve();
|
|
94
|
-
// reject();
|
|
95
|
-
}, 1500);
|
|
96
|
-
});
|
|
97
|
-
|
|
98
|
-
promise(myPromise, {
|
|
99
|
-
loading: 'Loading...',
|
|
100
|
-
success: 'Promise resolved!',
|
|
101
|
-
error: 'Promise rejected!',
|
|
102
|
-
});
|
|
103
|
-
};
|
|
146
|
+
- Peer dependencies: `react`, `react-native`, `react-native-reanimated` (v2+), `lottie-react-native` (if using animations). Install and link those in your app. See `package.json`.
|
|
147
|
+
- Reanimated setup: add `react-native-reanimated/plugin` to `babel.config.js` as the last plugin.
|
|
148
|
+
- iOS: run `cd ios && pod install` on macOS after installing native dependencies.
|
|
104
149
|
|
|
150
|
+
## Import styles
|
|
105
151
|
|
|
106
|
-
|
|
152
|
+
This package exports a default `useToast` hook and named exports. Both of the following work:
|
|
107
153
|
|
|
108
|
-
- Custom Toast
|
|
109
154
|
```javascript
|
|
110
|
-
|
|
111
|
-
import
|
|
112
|
-
import useToast from 'rn-toastify';
|
|
155
|
+
// ES default + named
|
|
156
|
+
import useToast, { ToastContainer } from 'rn-toastify';
|
|
113
157
|
|
|
114
|
-
|
|
158
|
+
// CommonJS
|
|
159
|
+
const rnToastify = require('rn-toastify');
|
|
160
|
+
const useToast = rnToastify.useToast || rnToastify.default;
|
|
161
|
+
const { ToastContainer } = rnToastify;
|
|
162
|
+
```
|
|
115
163
|
|
|
116
|
-
|
|
117
|
-
custom(
|
|
118
|
-
<View style={styles.customContent}>
|
|
119
|
-
<Image
|
|
120
|
-
style={styles.image}
|
|
121
|
-
source={{
|
|
122
|
-
uri: 'https://images.unsplash.com/photo-1494790108377-be9c29b29330?ixlib=rb-1.2.1&ixqx=6GHAjsWpt9&ixid=eyJhcHBfaWQiOjEyMDd9&auto=format&fit=facearea&facepad=2.2&w=160&h=160&q=80',
|
|
123
|
-
}}
|
|
124
|
-
/>
|
|
125
|
-
<View style={styles.textContainer}>
|
|
126
|
-
<Text style={styles.customText}>Emilia Gates</Text>
|
|
127
|
-
<Text style={styles.customSubText}>Sure! 8:30pm works great!</Text>
|
|
128
|
-
</View>
|
|
129
|
-
</View>,
|
|
130
|
-
{ duration: 1500, position: 'top' }
|
|
131
|
-
);
|
|
132
|
-
};
|
|
164
|
+
## Troubleshooting
|
|
133
165
|
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
alignItems: 'center',
|
|
138
|
-
},
|
|
139
|
-
image: {
|
|
140
|
-
width: 50,
|
|
141
|
-
height: 50,
|
|
142
|
-
borderRadius: 25,
|
|
143
|
-
},
|
|
144
|
-
textContainer: {
|
|
145
|
-
marginLeft: 10,
|
|
146
|
-
},
|
|
147
|
-
customText: {
|
|
148
|
-
fontWeight: 'bold',
|
|
149
|
-
},
|
|
150
|
-
customSubText: {
|
|
151
|
-
color: 'gray',
|
|
152
|
-
},
|
|
153
|
-
});
|
|
166
|
+
- Crash on app start / `_rnToastify.default is not a function`:
|
|
167
|
+
- Clear Metro cache and rebuild: `npx react-native start --reset-cache` and rebuild the app.
|
|
168
|
+
- Ensure your app resolves the package's `index.js` correctly (this package exports a CommonJS-friendly entry to avoid interop issues).
|
|
154
169
|
|
|
155
|
-
|
|
170
|
+
- Reanimated-related errors:
|
|
171
|
+
- Add the Reanimated babel plugin and rebuild the app. See Reanimated installation docs.
|
|
156
172
|
|
|
157
|
-
-
|
|
158
|
-
|
|
159
|
-
import useToast from 'rn-toastify';
|
|
173
|
+
- Lottie errors on iOS:
|
|
174
|
+
- Run `cd ios && pod install` and rebuild from Xcode.
|
|
160
175
|
|
|
161
|
-
|
|
176
|
+
- Toasts not showing:
|
|
177
|
+
- Make sure `ToastContainer` is mounted once at the app root.
|
|
178
|
+
- Ensure native peer dependencies are installed and app rebuilt.
|
|
162
179
|
|
|
163
|
-
|
|
164
|
-
emoji('Great job!', '👍', { duration: 1500, position: 'top' });
|
|
165
|
-
};
|
|
180
|
+
## Publishing & versioning
|
|
166
181
|
|
|
167
|
-
|
|
182
|
+
When you're ready to release this change to npm:
|
|
168
183
|
|
|
169
|
-
|
|
184
|
+
1. Update the package version in `package.json` (or run `npm version patch|minor|major`).
|
|
185
|
+
2. Run tests and build (if any): `npm test`.
|
|
186
|
+
3. Pack and inspect: `npm pack`.
|
|
187
|
+
4. Publish: `npm publish --access public` (make sure you're logged in via `npm login`).
|
|
170
188
|
|
|
171
|
-
|
|
189
|
+
## Contributing
|
|
172
190
|
|
|
173
|
-
|
|
174
|
-
import React from 'react';
|
|
175
|
-
import { StyleSheet, View, Button } from 'react-native';
|
|
176
|
-
import useToast from 'rn-toastify';
|
|
177
|
-
import ToastContainer from 'rn-toastify';
|
|
191
|
+
PRs welcome. Please open an issue if you find bugs or want features like scoped containers, configurable color tokens, or example apps.
|
|
178
192
|
|
|
179
|
-
|
|
180
|
-
const { success, error, promise, custom, emoji } = useToast();
|
|
193
|
+
## License
|
|
181
194
|
|
|
182
|
-
|
|
183
|
-
<View style={styles.container}>
|
|
184
|
-
<Button title="Show Success Toast" onPress={() => success('Operation was successful!', { duration: 1500, position: 'top' })} />
|
|
185
|
-
<Button title="Show Error Toast" onPress={() => error('Something went wrong!', { duration: 1500, position: 'top' })} />
|
|
186
|
-
<Button title="Show Promise Toast" onPress={() => {
|
|
187
|
-
const myPromise = new Promise((resolve) => setTimeout(resolve, 1500));
|
|
188
|
-
promise(myPromise, { loading: 'Loading...', success: 'Promise resolved!', error: 'Promise rejected!' });
|
|
189
|
-
}} />
|
|
195
|
+
MIT
|
|
190
196
|
<Button title="Show Custom Toast" onPress={() => custom(
|
|
191
197
|
<View style={styles.customContent}>
|
|
192
198
|
<Text>Custom Toast Content</Text>
|
|
@@ -216,6 +222,11 @@ export default AppContent;
|
|
|
216
222
|
```
|
|
217
223
|
This example integrates multiple toast types and demonstrates how to trigger each one. It also includes the necessary ToastContainer to display the toasts.
|
|
218
224
|
|
|
225
|
+
Theme and single-root notes
|
|
226
|
+
- Mount `ToastContainer` once at the app root (recommended). The container detects the system color scheme by default and passes a `theme` prop to all toast components.
|
|
227
|
+
- To force a theme, pass `theme="dark"` or `theme="light"` to `<ToastContainer />`.
|
|
228
|
+
- If the system color scheme is unavailable, the toast theme defaults to `light`.
|
|
229
|
+
|
|
219
230
|
|
|
220
231
|
## API Reference
|
|
221
232
|
|
package/docs/Toast.gif
CHANGED
|
Binary file
|
package/index.js
CHANGED
|
@@ -1,8 +1,31 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
1
|
+
// CommonJS-friendly entry to avoid default/import interop issues with Metro in some RN versions
|
|
2
|
+
// We attempt to require modules and use .default if present (handles transpiled ESM)
|
|
3
|
+
const _useToastMod = require('./src/hooks/useToast');
|
|
4
|
+
const _toastMod = require('./src/Toast');
|
|
5
|
+
const _containerMod = require('./src/context/ToastContainer');
|
|
6
|
+
const _successMod = require('./src/components/SuccessToast');
|
|
7
|
+
const _customeMod = require('./src/components/CustomeToast');
|
|
8
|
+
const _emojiMod = require('./src/components/EmojiToast');
|
|
9
|
+
const _errorMod = require('./src/components/ErrorToast');
|
|
10
|
+
const _loadingMod = require('./src/components/LoadingToast');
|
|
11
|
+
|
|
12
|
+
const useToast = (_useToastMod && _useToastMod.__esModule) ? _useToastMod.default : _useToastMod;
|
|
13
|
+
const Toast = (_toastMod && _toastMod.__esModule) ? _toastMod.default : _toastMod;
|
|
14
|
+
const ToastContainer = (_containerMod && _containerMod.__esModule) ? _containerMod.default : _containerMod;
|
|
15
|
+
const SuccessToast = (_successMod && _successMod.__esModule) ? _successMod.default : _successMod;
|
|
16
|
+
const CustomeToast = (_customeMod && _customeMod.__esModule) ? _customeMod.default : _customeMod;
|
|
17
|
+
const EmojiToast = (_emojiMod && _emojiMod.__esModule) ? _emojiMod.default : _emojiMod;
|
|
18
|
+
const ErrorToast = (_errorMod && _errorMod.__esModule) ? _errorMod.default : _errorMod;
|
|
19
|
+
const LoadingToast = (_loadingMod && _loadingMod.__esModule) ? _loadingMod.default : _loadingMod;
|
|
20
|
+
|
|
21
|
+
// Export for both require() and import syntax
|
|
22
|
+
module.exports = useToast;
|
|
23
|
+
module.exports.default = useToast;
|
|
24
|
+
module.exports.useToast = useToast;
|
|
25
|
+
module.exports.Toast = Toast;
|
|
26
|
+
module.exports.ToastContainer = ToastContainer;
|
|
27
|
+
module.exports.SuccessToast = SuccessToast;
|
|
28
|
+
module.exports.CustomeToast = CustomeToast;
|
|
29
|
+
module.exports.EmojiToast = EmojiToast;
|
|
30
|
+
module.exports.ErrorToast = ErrorToast;
|
|
31
|
+
module.exports.LoadingToast = LoadingToast;
|
package/package.json
CHANGED
|
@@ -1,44 +1,44 @@
|
|
|
1
|
-
{
|
|
2
|
-
"name": "rn-toastify",
|
|
3
|
-
"version": "1.0.
|
|
4
|
-
"description": "A customizable and performant toast notification library for React Native, featuring smooth animations, swipe gestures, and flexible styling options.",
|
|
5
|
-
"main": "index.js",
|
|
6
|
-
"repository": {
|
|
7
|
-
"type": "git",
|
|
8
|
-
"url": "https://github.com/muku534/react-native-toast"
|
|
9
|
-
},
|
|
10
|
-
"homepage": "https://github.com/muku534/react-native-toast#readme",
|
|
11
|
-
"scripts": {
|
|
12
|
-
"test": "jest"
|
|
13
|
-
},
|
|
14
|
-
"keywords": [
|
|
15
|
-
"react-native",
|
|
16
|
-
"toast",
|
|
17
|
-
"react-native-toastify",
|
|
18
|
-
"react-native-toast",
|
|
19
|
-
"react-native-toast-message",
|
|
20
|
-
"rn-toastify",
|
|
21
|
-
"alert",
|
|
22
|
-
"notification",
|
|
23
|
-
"library"
|
|
24
|
-
],
|
|
25
|
-
"author": "Mukesh Prajapati",
|
|
26
|
-
"license": "MIT",
|
|
27
|
-
"peerDependencies": {
|
|
28
|
-
"react": ">=16.8.0 <19.0.0",
|
|
29
|
-
"react-native": ">=0.60.0 <1.0.0",
|
|
30
|
-
"react-native-reanimated": ">=2.0.0 <
|
|
31
|
-
"lottie-react-native": ">=3.0.0 <
|
|
32
|
-
},
|
|
33
|
-
"dependencies": {
|
|
34
|
-
"events": "^3.3.0"
|
|
35
|
-
},
|
|
36
|
-
"devDependencies": {
|
|
37
|
-
"@testing-library/jest-native": "^5.4.3",
|
|
38
|
-
"@testing-library/react-native": "^12.5.2",
|
|
39
|
-
"@types/jest": "^28.1.0",
|
|
40
|
-
"@types/react-native": "^0.67.3",
|
|
41
|
-
"jest": "^28.1.0",
|
|
42
|
-
"react-test-renderer": "^17.0.2"
|
|
43
|
-
}
|
|
1
|
+
{
|
|
2
|
+
"name": "rn-toastify",
|
|
3
|
+
"version": "1.0.9",
|
|
4
|
+
"description": "A customizable and performant toast notification library for React Native, featuring smooth animations, swipe gestures, and flexible styling options.",
|
|
5
|
+
"main": "index.js",
|
|
6
|
+
"repository": {
|
|
7
|
+
"type": "git",
|
|
8
|
+
"url": "https://github.com/muku534/react-native-toast"
|
|
9
|
+
},
|
|
10
|
+
"homepage": "https://github.com/muku534/react-native-toast#readme",
|
|
11
|
+
"scripts": {
|
|
12
|
+
"test": "jest"
|
|
13
|
+
},
|
|
14
|
+
"keywords": [
|
|
15
|
+
"react-native",
|
|
16
|
+
"toast",
|
|
17
|
+
"react-native-toastify",
|
|
18
|
+
"react-native-toast",
|
|
19
|
+
"react-native-toast-message",
|
|
20
|
+
"rn-toastify",
|
|
21
|
+
"alert",
|
|
22
|
+
"notification",
|
|
23
|
+
"library"
|
|
24
|
+
],
|
|
25
|
+
"author": "Mukesh Prajapati",
|
|
26
|
+
"license": "MIT",
|
|
27
|
+
"peerDependencies": {
|
|
28
|
+
"react": ">=16.8.0 <19.0.0",
|
|
29
|
+
"react-native": ">=0.60.0 <1.0.0",
|
|
30
|
+
"react-native-reanimated": ">=2.0.0 <5.0.0",
|
|
31
|
+
"lottie-react-native": ">=3.0.0 <9.0.0"
|
|
32
|
+
},
|
|
33
|
+
"dependencies": {
|
|
34
|
+
"events": "^3.3.0"
|
|
35
|
+
},
|
|
36
|
+
"devDependencies": {
|
|
37
|
+
"@testing-library/jest-native": "^5.4.3",
|
|
38
|
+
"@testing-library/react-native": "^12.5.2",
|
|
39
|
+
"@types/jest": "^28.1.0",
|
|
40
|
+
"@types/react-native": "^0.67.3",
|
|
41
|
+
"jest": "^28.1.0",
|
|
42
|
+
"react-test-renderer": "^17.0.2"
|
|
43
|
+
}
|
|
44
44
|
}
|
package/src/Toast.js
CHANGED
|
@@ -9,7 +9,7 @@ import Animated, {
|
|
|
9
9
|
Easing,
|
|
10
10
|
} from 'react-native-reanimated';
|
|
11
11
|
|
|
12
|
-
const Toast = ({ visible, duration, position, children, onHide, style }) => {
|
|
12
|
+
const Toast = ({ visible, duration, position, children, onHide, style, theme }) => {
|
|
13
13
|
const opacity = useSharedValue(0);
|
|
14
14
|
const translateY = useSharedValue(position === 'top' ? -50 : 50);
|
|
15
15
|
const translateX = useSharedValue(0);
|
|
@@ -77,12 +77,20 @@ const Toast = ({ visible, duration, position, children, onHide, style }) => {
|
|
|
77
77
|
|
|
78
78
|
if (!visible) return null;
|
|
79
79
|
|
|
80
|
+
// If children is a React element, inject theme prop; otherwise render as-is.
|
|
81
|
+
const renderContent = () => {
|
|
82
|
+
if (React.isValidElement(children)) {
|
|
83
|
+
return React.cloneElement(children, { theme });
|
|
84
|
+
}
|
|
85
|
+
return children;
|
|
86
|
+
};
|
|
87
|
+
|
|
80
88
|
return (
|
|
81
89
|
<Animated.View
|
|
82
90
|
{...panResponder.panHandlers}
|
|
83
91
|
style={[styles.container, animatedStyle, positionStyle, style]}
|
|
84
92
|
>
|
|
85
|
-
{
|
|
93
|
+
{renderContent()}
|
|
86
94
|
</Animated.View>
|
|
87
95
|
);
|
|
88
96
|
};
|
|
@@ -5,9 +5,11 @@ import {
|
|
|
5
5
|
widthPercentageToDP as wp,
|
|
6
6
|
} from '../utils/Pixel/Index';
|
|
7
7
|
|
|
8
|
-
const CustomToast = ({ content }) => {
|
|
8
|
+
const CustomToast = ({ content, theme = 'light' }) => {
|
|
9
|
+
const isDark = theme === 'dark';
|
|
10
|
+
const bg = isDark ? '#111827' : '#FFFFFF';
|
|
9
11
|
return (
|
|
10
|
-
<View style={styles.customToast}>
|
|
12
|
+
<View style={[styles.customToast, { backgroundColor: bg }]}>
|
|
11
13
|
{content}
|
|
12
14
|
</View>
|
|
13
15
|
);
|
|
@@ -5,10 +5,14 @@ import {
|
|
|
5
5
|
widthPercentageToDP as wp,
|
|
6
6
|
} from '../utils/Pixel/Index';
|
|
7
7
|
|
|
8
|
-
const EmojiToast = ({ message, emoji }) => {
|
|
8
|
+
const EmojiToast = ({ message, emoji, theme = 'light' }) => {
|
|
9
|
+
const isDark = theme === 'dark';
|
|
10
|
+
const bg = isDark ? '#111827' : '#F7F7FC';
|
|
11
|
+
const textColor = isDark ? '#F3F4F6' : 'black';
|
|
12
|
+
|
|
9
13
|
return (
|
|
10
|
-
<View style={styles.emojiToast}>
|
|
11
|
-
<Text style={styles.text}>{emoji} {message}</Text>
|
|
14
|
+
<View style={[styles.emojiToast, { backgroundColor: bg }]}>
|
|
15
|
+
<Text style={[styles.text, { color: textColor }]}>{emoji} {message}</Text>
|
|
12
16
|
</View>
|
|
13
17
|
);
|
|
14
18
|
}
|
|
@@ -7,9 +7,13 @@ import {
|
|
|
7
7
|
widthPercentageToDP as wp,
|
|
8
8
|
} from '../utils/Pixel/Index';
|
|
9
9
|
|
|
10
|
-
const ErrorToast = ({ message }) => {
|
|
10
|
+
const ErrorToast = ({ message, theme = 'light' }) => {
|
|
11
|
+
const isDark = theme === 'dark';
|
|
12
|
+
const containerBg = isDark ? '#111827' : '#F7F7FC';
|
|
13
|
+
const textColor = isDark ? '#FEE2E2' : '#991B1B';
|
|
14
|
+
|
|
11
15
|
return (
|
|
12
|
-
<View style={styles.container}>
|
|
16
|
+
<View style={[styles.container, { backgroundColor: containerBg }]}>
|
|
13
17
|
<LottieView
|
|
14
18
|
source={require('../../assets/animated_Icon/ErrorAnimation.json')} // Replace with your success Lottie animation path
|
|
15
19
|
autoPlay
|
|
@@ -17,7 +21,7 @@ const ErrorToast = ({ message }) => {
|
|
|
17
21
|
speed={1.5}
|
|
18
22
|
style={styles.lottie}
|
|
19
23
|
/>
|
|
20
|
-
<Text style={styles.text}>{message}</Text>
|
|
24
|
+
<Text style={[styles.text, { color: textColor }]}>{message}</Text>
|
|
21
25
|
</View>
|
|
22
26
|
);
|
|
23
27
|
}
|
|
@@ -6,10 +6,14 @@ import {
|
|
|
6
6
|
widthPercentageToDP as wp,
|
|
7
7
|
} from '../utils/Pixel/Index';
|
|
8
8
|
|
|
9
|
-
const LoadingToast = ({ message }) => {
|
|
9
|
+
const LoadingToast = ({ message, theme = 'light' }) => {
|
|
10
|
+
const isDark = theme === 'dark';
|
|
11
|
+
const bg = isDark ? '#111827' : '#FFFFFF';
|
|
12
|
+
const indicatorColor = isDark ? '#9CA3AF' : '#6B7280';
|
|
13
|
+
|
|
10
14
|
return (
|
|
11
|
-
<View style={styles.toast}>
|
|
12
|
-
<ActivityIndicator size="
|
|
15
|
+
<View style={[styles.toast, { backgroundColor: bg }]}>
|
|
16
|
+
<ActivityIndicator size="small" color={indicatorColor} />
|
|
13
17
|
{/** <Text style={styles.text}>{message}</Text> */}
|
|
14
18
|
</View>
|
|
15
19
|
);
|
|
@@ -6,9 +6,13 @@ import {
|
|
|
6
6
|
widthPercentageToDP as wp,
|
|
7
7
|
} from '../utils/Pixel/Index';
|
|
8
8
|
|
|
9
|
-
const SuccessToast = ({ message }) => {
|
|
9
|
+
const SuccessToast = ({ message, theme = 'light' }) => {
|
|
10
|
+
const isDark = theme === 'dark';
|
|
11
|
+
const containerBg = isDark ? '#111827' : '#F7F7FC';
|
|
12
|
+
const textColor = isDark ? '#F3F4F6' : '#064E3B';
|
|
13
|
+
|
|
10
14
|
return (
|
|
11
|
-
<View style={styles.container}>
|
|
15
|
+
<View style={[styles.container, { backgroundColor: containerBg }]}>
|
|
12
16
|
<LottieView
|
|
13
17
|
source={require('../../assets/animated_Icon/SuccessAnimation.json')} // Replace with your success Lottie animation path
|
|
14
18
|
autoPlay
|
|
@@ -16,7 +20,7 @@ const SuccessToast = ({ message }) => {
|
|
|
16
20
|
style={styles.lottie}
|
|
17
21
|
speed={1.2}
|
|
18
22
|
/>
|
|
19
|
-
<Text style={styles.text}>{message}</Text>
|
|
23
|
+
<Text style={[styles.text, { color: textColor }]}>{message}</Text>
|
|
20
24
|
</View>
|
|
21
25
|
);
|
|
22
26
|
};
|
|
@@ -27,8 +31,8 @@ const styles = StyleSheet.create({
|
|
|
27
31
|
height: hp(6.8),
|
|
28
32
|
paddingHorizontal: wp(4),
|
|
29
33
|
borderRadius: wp(4),
|
|
30
|
-
|
|
31
|
-
|
|
34
|
+
// backgroundColor: '#d2f7d2',
|
|
35
|
+
backgroundColor: '#F7F7FC',
|
|
32
36
|
alignItems: 'center',
|
|
33
37
|
flexDirection: 'row',
|
|
34
38
|
},
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import React, { useEffect, useState } from 'react';
|
|
2
|
-
import { View, StyleSheet } from 'react-native';
|
|
2
|
+
import { View, StyleSheet, Appearance } from 'react-native';
|
|
3
3
|
import Toast from '../Toast';
|
|
4
4
|
import toastManagerInstance from './ToastManager';
|
|
5
5
|
import {
|
|
@@ -7,8 +7,9 @@ import {
|
|
|
7
7
|
widthPercentageToDP as wp,
|
|
8
8
|
} from '../utils/Pixel/Index';
|
|
9
9
|
|
|
10
|
-
const ToastContainer = () => {
|
|
10
|
+
const ToastContainer = ({ theme: forcedTheme } = {}) => {
|
|
11
11
|
const [toasts, setToasts] = useState([]);
|
|
12
|
+
const [theme, setTheme] = useState(forcedTheme ?? (Appearance.getColorScheme() || 'light'));
|
|
12
13
|
|
|
13
14
|
useEffect(() => {
|
|
14
15
|
const handleShow = (toast) => {
|
|
@@ -22,28 +23,37 @@ const ToastContainer = () => {
|
|
|
22
23
|
toastManagerInstance.on('show', handleShow);
|
|
23
24
|
toastManagerInstance.on('remove', handleRemove);
|
|
24
25
|
|
|
26
|
+
let appearanceSub;
|
|
27
|
+
if (!forcedTheme && Appearance && Appearance.addChangeListener) {
|
|
28
|
+
appearanceSub = Appearance.addChangeListener(({ colorScheme }) => {
|
|
29
|
+
setTheme(colorScheme ?? 'light');
|
|
30
|
+
});
|
|
31
|
+
}
|
|
32
|
+
|
|
25
33
|
return () => {
|
|
26
34
|
toastManagerInstance.off('show', handleShow);
|
|
27
35
|
toastManagerInstance.off('remove', handleRemove);
|
|
36
|
+
if (appearanceSub && appearanceSub.remove) appearanceSub.remove();
|
|
28
37
|
};
|
|
29
38
|
}, []);
|
|
30
39
|
|
|
31
40
|
// Separate toasts by position
|
|
32
|
-
const topToasts = toasts.filter(toast => toast
|
|
33
|
-
const centerToasts = toasts.filter(toast => toast
|
|
34
|
-
const bottomToasts = toasts.filter(toast => toast
|
|
41
|
+
const topToasts = toasts.filter(toast => toast?.options?.position === 'top');
|
|
42
|
+
const centerToasts = toasts.filter(toast => toast?.options?.position === 'center');
|
|
43
|
+
const bottomToasts = toasts.filter(toast => toast?.options?.position === 'bottom');
|
|
35
44
|
|
|
36
45
|
return (
|
|
37
46
|
<>
|
|
38
47
|
{/* Top positioned toasts */}
|
|
39
|
-
<View style={styles.topContainer}>
|
|
48
|
+
<View style={styles.topContainer} pointerEvents="box-none">
|
|
40
49
|
{topToasts.map((toast, index) => (
|
|
41
50
|
<Toast
|
|
42
51
|
key={toast.id}
|
|
43
52
|
visible={true}
|
|
44
|
-
duration={toast
|
|
53
|
+
duration={toast?.options?.duration}
|
|
45
54
|
position="top"
|
|
46
|
-
|
|
55
|
+
theme={theme}
|
|
56
|
+
style={[toast?.options?.style || {}, { top: hp(0.1) + index * 60 }]} // Adjust spacing between top toasts
|
|
47
57
|
onHide={() => toastManagerInstance.remove(toast.id)}
|
|
48
58
|
>
|
|
49
59
|
{toast.content}
|
|
@@ -52,14 +62,15 @@ const ToastContainer = () => {
|
|
|
52
62
|
</View>
|
|
53
63
|
|
|
54
64
|
{/* Center positioned toasts */}
|
|
55
|
-
<View style={styles.centerContainer}>
|
|
65
|
+
<View style={styles.centerContainer} pointerEvents="box-none">
|
|
56
66
|
{centerToasts.map((toast, index) => (
|
|
57
67
|
<Toast
|
|
58
68
|
key={toast.id}
|
|
59
69
|
visible={true}
|
|
60
|
-
duration={toast
|
|
70
|
+
duration={toast?.options?.duration}
|
|
61
71
|
position="center"
|
|
62
|
-
|
|
72
|
+
theme={theme}
|
|
73
|
+
style={[toast?.options?.style || {}, { marginTop: index * 60 }]} // Adjust spacing between center toasts
|
|
63
74
|
onHide={() => toastManagerInstance.remove(toast.id)}
|
|
64
75
|
>
|
|
65
76
|
{toast.content}
|
|
@@ -68,14 +79,15 @@ const ToastContainer = () => {
|
|
|
68
79
|
</View>
|
|
69
80
|
|
|
70
81
|
{/* Bottom positioned toasts */}
|
|
71
|
-
<View style={styles.bottomContainer}>
|
|
82
|
+
<View style={styles.bottomContainer} pointerEvents="box-none">
|
|
72
83
|
{bottomToasts.map((toast, index) => (
|
|
73
84
|
<Toast
|
|
74
85
|
key={toast.id}
|
|
75
86
|
visible={true}
|
|
76
|
-
duration={toast
|
|
87
|
+
duration={toast?.options?.duration}
|
|
77
88
|
position="bottom"
|
|
78
|
-
|
|
89
|
+
theme={theme}
|
|
90
|
+
style={[toast?.options?.style || {}, { bottom: hp(2) + index * 60 }]} // Adjust spacing between bottom toasts
|
|
79
91
|
onHide={() => toastManagerInstance.remove(toast.id)}
|
|
80
92
|
>
|
|
81
93
|
{toast.content}
|
|
@@ -1,14 +1,16 @@
|
|
|
1
1
|
import { EventEmitter } from 'events';
|
|
2
2
|
|
|
3
3
|
class ToastManager extends EventEmitter {
|
|
4
|
-
show
|
|
5
|
-
|
|
4
|
+
// show accepts an optional id so callers (like promise) can control the id lifecycle
|
|
5
|
+
show(content, options = {}, id = null) {
|
|
6
|
+
const _id = id ?? Date.now().toString();
|
|
6
7
|
const defaultOptions = {
|
|
7
8
|
duration: 1000,
|
|
8
9
|
position: 'bottom',
|
|
9
10
|
style: {},
|
|
10
11
|
};
|
|
11
|
-
this.emit('show', { id, content, options: { ...defaultOptions, ...options } });
|
|
12
|
+
this.emit('show', { id: _id, content, options: { ...defaultOptions, ...options } });
|
|
13
|
+
return _id;
|
|
12
14
|
}
|
|
13
15
|
|
|
14
16
|
remove(id) {
|
|
@@ -17,7 +19,8 @@ class ToastManager extends EventEmitter {
|
|
|
17
19
|
|
|
18
20
|
async promise(promise, { loading, success, error }) {
|
|
19
21
|
const id = Date.now().toString();
|
|
20
|
-
|
|
22
|
+
// show the loading toast using the same id so we can remove it later
|
|
23
|
+
this.show(loading, { duration: Infinity, position: 'top' }, id);
|
|
21
24
|
try {
|
|
22
25
|
await promise;
|
|
23
26
|
this.remove(id);
|