rn-toastify 1.0.8 → 1.0.10
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 +120 -203
- 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 +35 -15
- package/src/context/ToastManager.js +7 -4
package/README.MD
CHANGED
|
@@ -1,273 +1,190 @@
|
|
|
1
|
+
# React Native Toastify
|
|
1
2
|
|
|
2
|
-
|
|
3
|
+
A highly customizable and performant toast notification library for React Native. Featuring smooth animations, swipe gestures, and flexible styling options.
|
|
3
4
|
|
|
4
|
-
|
|
5
|
-
## Demo
|
|
6
|
-
|
|
7
|
-
Animated toast message component for React Native.
|
|
8
5
|

|
|
9
6
|
|
|
7
|
+
---
|
|
10
8
|
|
|
9
|
+
## 🚀 Features
|
|
11
10
|
|
|
12
|
-
|
|
11
|
+
- **Single-root container**: Mount once at the app root.
|
|
12
|
+
- **Imperative API**: Use `useToast` hook anywhere in your app.
|
|
13
|
+
- **Light/Dark Theme**: System-aware with optional forced theme.
|
|
14
|
+
- **Promise Helper**: Show loading, success, and error states.
|
|
15
|
+
- **Customizable**: Add custom content, styles, and emojis.
|
|
13
16
|
|
|
14
|
-
|
|
15
|
-
- 🎨 Customizable layouts
|
|
16
|
-
- 🔧 Flexible config
|
|
17
|
-
- 📅 Promise Handling
|
|
18
|
-
- 📍 Position Control
|
|
17
|
+
---
|
|
19
18
|
|
|
19
|
+
## 📦 Installation
|
|
20
20
|
|
|
21
|
-
|
|
22
|
-
## Installation
|
|
21
|
+
Install the package using npm or yarn:
|
|
23
22
|
|
|
24
23
|
```bash
|
|
25
24
|
npm install rn-toastify
|
|
26
25
|
```
|
|
27
|
-
|
|
28
|
-
## Usage
|
|
29
|
-
|
|
30
|
-
To integrate the toast notifications into your application, follow these steps:
|
|
31
|
-
|
|
32
|
-
- Import and Setup
|
|
33
|
-
|
|
34
|
-
Start by importing the necessary components and hooks from the library:
|
|
35
|
-
|
|
36
|
-
```javascript
|
|
37
|
-
import useToast from 'rn-toastify';
|
|
38
|
-
import ToastContainer from 'rn-toastify';
|
|
39
|
-
|
|
40
|
-
const AppContent = () => {
|
|
41
|
-
// Toast functions here
|
|
42
|
-
|
|
43
|
-
return (
|
|
44
|
-
<View style={styles.container}>
|
|
45
|
-
{/* Buttons to trigger toasts */}
|
|
46
|
-
<ToastContainer />
|
|
47
|
-
</View>
|
|
48
|
-
);
|
|
49
|
-
};
|
|
50
26
|
|
|
51
|
-
|
|
27
|
+
Or:
|
|
52
28
|
|
|
29
|
+
```bash
|
|
30
|
+
yarn add rn-toastify
|
|
53
31
|
```
|
|
54
32
|
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
The useToast hook provides access to different types of toasts. Below are examples of how to implement each toast type:
|
|
33
|
+
### Peer Dependencies
|
|
58
34
|
|
|
59
|
-
|
|
60
|
-
```javascript
|
|
61
|
-
import useToast from 'rn-toastify';
|
|
62
|
-
|
|
63
|
-
const { success } = useToast();
|
|
35
|
+
Ensure the following peer dependencies are installed:
|
|
64
36
|
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
37
|
+
- `react`
|
|
38
|
+
- `react-native`
|
|
39
|
+
- `react-native-reanimated` (v2+)
|
|
40
|
+
- `lottie-react-native`
|
|
68
41
|
|
|
42
|
+
For iOS, run:
|
|
69
43
|
|
|
44
|
+
```bash
|
|
45
|
+
cd ios && pod install
|
|
70
46
|
```
|
|
71
|
-
- Error Toast
|
|
72
|
-
```javascript
|
|
73
|
-
import useToast from 'rn-toastify';
|
|
74
47
|
|
|
75
|
-
|
|
48
|
+
---
|
|
76
49
|
|
|
77
|
-
|
|
78
|
-
error('Something went wrong!', { duration: 1500, position: 'top' });
|
|
79
|
-
};
|
|
50
|
+
## 🛠️ Quick Start
|
|
80
51
|
|
|
52
|
+
### Step 1: Mount the Toast Container
|
|
81
53
|
|
|
82
|
-
|
|
54
|
+
Add the `ToastContainer` to your app's root component (e.g., `App.js`):
|
|
83
55
|
|
|
84
|
-
- Promise Toast
|
|
85
56
|
```javascript
|
|
86
|
-
import
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
const handlePromiseToast = () => {
|
|
91
|
-
const myPromise = new Promise((resolve, reject) => {
|
|
92
|
-
setTimeout(() => {
|
|
93
|
-
resolve();
|
|
94
|
-
// reject();
|
|
95
|
-
}, 1500);
|
|
96
|
-
});
|
|
57
|
+
import React from 'react';
|
|
58
|
+
import { NavigationContainer } from '@react-navigation/native';
|
|
59
|
+
import MainNavigator from './src/navigation';
|
|
60
|
+
import { ToastContainer } from 'rn-toastify';
|
|
97
61
|
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
62
|
+
export default function App() {
|
|
63
|
+
return (
|
|
64
|
+
<>
|
|
65
|
+
<NavigationContainer>
|
|
66
|
+
<MainNavigator />
|
|
67
|
+
</NavigationContainer>
|
|
68
|
+
<ToastContainer theme="dark" /> {/* Optional: Force theme */}
|
|
69
|
+
</>
|
|
70
|
+
);
|
|
71
|
+
}
|
|
72
|
+
```
|
|
104
73
|
|
|
74
|
+
### Step 2: Use the `useToast` Hook
|
|
105
75
|
|
|
106
|
-
|
|
76
|
+
Call toast methods anywhere in your app:
|
|
107
77
|
|
|
108
|
-
- Custom Toast
|
|
109
78
|
```javascript
|
|
110
79
|
import React from 'react';
|
|
111
|
-
import { View,
|
|
80
|
+
import { View, Button } from 'react-native';
|
|
112
81
|
import useToast from 'rn-toastify';
|
|
113
82
|
|
|
114
|
-
|
|
83
|
+
export default function HomeScreen() {
|
|
84
|
+
const { success, error } = useToast();
|
|
115
85
|
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
<
|
|
120
|
-
|
|
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' }
|
|
86
|
+
return (
|
|
87
|
+
<View>
|
|
88
|
+
<Button title="Show Success" onPress={() => success('Operation successful!')} />
|
|
89
|
+
<Button title="Show Error" onPress={() => error('Something went wrong.', { position: 'top' })} />
|
|
90
|
+
</View>
|
|
131
91
|
);
|
|
132
|
-
}
|
|
133
|
-
|
|
134
|
-
const styles = StyleSheet.create({
|
|
135
|
-
customContent: {
|
|
136
|
-
flexDirection: 'row',
|
|
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
|
-
});
|
|
154
|
-
|
|
92
|
+
}
|
|
155
93
|
```
|
|
156
94
|
|
|
157
|
-
|
|
158
|
-
```javascript
|
|
159
|
-
import useToast from 'rn-toastify';
|
|
95
|
+
---
|
|
160
96
|
|
|
161
|
-
|
|
97
|
+
## 📖 API Reference
|
|
162
98
|
|
|
163
|
-
|
|
164
|
-
emoji('Great job!', '👍', { duration: 1500, position: 'top' });
|
|
165
|
-
};
|
|
99
|
+
### `useToast` Hook
|
|
166
100
|
|
|
167
|
-
|
|
101
|
+
The `useToast` hook provides the following methods:
|
|
168
102
|
|
|
169
|
-
|
|
103
|
+
| Method | Description |
|
|
104
|
+
|----------|--------------------------------------|
|
|
105
|
+
| `show` | Display a custom toast. |
|
|
106
|
+
| `success`| Show a success toast. |
|
|
107
|
+
| `error` | Show an error toast. |
|
|
108
|
+
| `custom` | Render a custom React element. |
|
|
109
|
+
| `emoji` | Display a toast with an emoji. |
|
|
110
|
+
| `promise`| Handle promise states with toasts. |
|
|
170
111
|
|
|
171
|
-
|
|
112
|
+
### Common Options
|
|
172
113
|
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
114
|
+
| Option | Type | Description |
|
|
115
|
+
|------------|----------|--------------------------------------|
|
|
116
|
+
| `duration` | `number` | Duration in milliseconds. |
|
|
117
|
+
| `position` | `string` | `'top' | 'bottom' | 'center'`. |
|
|
118
|
+
| `style` | `object` | Custom styles for the toast wrapper. |
|
|
178
119
|
|
|
179
|
-
|
|
180
|
-
const { success, error, promise, custom, emoji } = useToast();
|
|
120
|
+
### `promise` Method Options
|
|
181
121
|
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
const myPromise = new Promise((resolve) => setTimeout(resolve, 1500));
|
|
188
|
-
promise(myPromise, { loading: 'Loading...', success: 'Promise resolved!', error: 'Promise rejected!' });
|
|
189
|
-
}} />
|
|
190
|
-
<Button title="Show Custom Toast" onPress={() => custom(
|
|
191
|
-
<View style={styles.customContent}>
|
|
192
|
-
<Text>Custom Toast Content</Text>
|
|
193
|
-
</View>,
|
|
194
|
-
{ duration: 1500, position: 'top' }
|
|
195
|
-
)} />
|
|
196
|
-
<Button title="Show Emoji Toast" onPress={() => emoji('Great job!', '👍', { duration: 1500, position: 'top' })} />
|
|
197
|
-
<ToastContainer />
|
|
198
|
-
</View>
|
|
199
|
-
);
|
|
200
|
-
};
|
|
201
|
-
|
|
202
|
-
const styles = StyleSheet.create({
|
|
203
|
-
container: {
|
|
204
|
-
flex: 1,
|
|
205
|
-
justifyContent: 'center',
|
|
206
|
-
alignItems: 'center',
|
|
207
|
-
},
|
|
208
|
-
customContent: {
|
|
209
|
-
padding: 10,
|
|
210
|
-
backgroundColor: '#fff',
|
|
211
|
-
borderRadius: 5,
|
|
212
|
-
},
|
|
213
|
-
});
|
|
122
|
+
| Option | Type | Description |
|
|
123
|
+
|------------|----------|--------------------------------------|
|
|
124
|
+
| `loading` | `string` | Message during loading. |
|
|
125
|
+
| `success` | `string` | Message on success. |
|
|
126
|
+
| `error` | `string` | Message on error. |
|
|
214
127
|
|
|
215
|
-
|
|
216
|
-
```
|
|
217
|
-
This example integrates multiple toast types and demonstrates how to trigger each one. It also includes the necessary ToastContainer to display the toasts.
|
|
128
|
+
---
|
|
218
129
|
|
|
130
|
+
## 🌈 Examples
|
|
219
131
|
|
|
220
|
-
|
|
132
|
+
### Success Toast
|
|
133
|
+
|
|
134
|
+
```javascript
|
|
135
|
+
const { success } = useToast();
|
|
136
|
+
success('Operation successful!', { duration: 1500, position: 'top' });
|
|
137
|
+
```
|
|
221
138
|
|
|
222
|
-
|
|
223
|
-
All toast methods accept the following parameters:
|
|
139
|
+
### Promise Helper
|
|
224
140
|
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
| `message` | `string` | **Required**. The message to display in the toast. |
|
|
228
|
-
| `options` | `object` | **Optional**. Configuration options for the toast (e.g., duration, position). |
|
|
141
|
+
```javascript
|
|
142
|
+
const { promise } = useToast();
|
|
229
143
|
|
|
144
|
+
const myPromise = fetch('/api/save');
|
|
230
145
|
|
|
231
|
-
|
|
146
|
+
promise(myPromise, {
|
|
147
|
+
loading: 'Saving…',
|
|
148
|
+
success: 'Saved successfully!',
|
|
149
|
+
error: 'Save failed.',
|
|
150
|
+
});
|
|
151
|
+
```
|
|
232
152
|
|
|
233
|
-
|
|
153
|
+
### Custom Content
|
|
234
154
|
|
|
235
|
-
|
|
155
|
+
```javascript
|
|
156
|
+
const { custom } = useToast();
|
|
157
|
+
custom(<MyCustomComponent />, { duration: 2000 });
|
|
158
|
+
```
|
|
236
159
|
|
|
237
|
-
|
|
160
|
+
### Emoji Toast
|
|
238
161
|
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
| `success` | `string` | Message to display if the promise resolves. |
|
|
244
|
-
| `error` | `string` | Message to display if the promise is rejected. |
|
|
162
|
+
```javascript
|
|
163
|
+
const { emoji } = useToast();
|
|
164
|
+
emoji('Great job!', '🎉', { duration: 1300 });
|
|
165
|
+
```
|
|
245
166
|
|
|
246
|
-
|
|
167
|
+
---
|
|
247
168
|
|
|
248
|
-
|
|
169
|
+
## 🎨 Theme Support
|
|
249
170
|
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
| `content` | `element` | **Required**. React element to render in the toast. |
|
|
171
|
+
- **Automatic**: Detects system theme (light/dark).
|
|
172
|
+
- **Forced**: Pass `theme="light"` or `theme="dark"` to `ToastContainer`.
|
|
253
173
|
|
|
254
|
-
|
|
174
|
+
If the system theme is unavailable, the default is `light`.
|
|
255
175
|
|
|
256
|
-
|
|
176
|
+
---
|
|
257
177
|
|
|
258
|
-
|
|
259
|
-
| :-------- | :------- | :------------------------- |
|
|
260
|
-
| `emoji` | `string` | **Required**. The emoji to display alongside the message. |
|
|
178
|
+
## 🐛 Troubleshooting
|
|
261
179
|
|
|
262
|
-
|
|
180
|
+
### Common Issues
|
|
263
181
|
|
|
264
|
-
|
|
182
|
+
- **Toasts not showing**: Ensure `ToastContainer` is mounted at the app root.
|
|
183
|
+
- **Reanimated errors**: Add the Reanimated Babel plugin and rebuild.
|
|
184
|
+
- **Lottie issues on iOS**: Run `cd ios && pod install`.
|
|
265
185
|
|
|
266
|
-
|
|
267
|
-
| :-------- | :------- | :------------------------- |
|
|
268
|
-
| `duration` | `number` |**Optional**. Duration in milliseconds for which the toast is visible. |
|
|
269
|
-
| `position` | `string` | **Optional**. Position of the toast on the screen (top, bottom, center). |
|
|
186
|
+
---
|
|
270
187
|
|
|
271
|
-
## License
|
|
188
|
+
## 📜 License
|
|
272
189
|
|
|
273
|
-
[
|
|
190
|
+
MIT License. See [LICENSE](./LICENSE) for details.
|
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.10",
|
|
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, StatusBar, Platform, SafeAreaView } 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,44 +23,62 @@ 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
|
-
<
|
|
48
|
+
<SafeAreaView
|
|
49
|
+
style={[
|
|
50
|
+
styles.topContainer,
|
|
51
|
+
{
|
|
52
|
+
top: Platform.OS === 'android' ? StatusBar.currentHeight || 0 : 0,
|
|
53
|
+
},
|
|
54
|
+
]}
|
|
55
|
+
pointerEvents="box-none"
|
|
56
|
+
>
|
|
40
57
|
{topToasts.map((toast, index) => (
|
|
41
58
|
<Toast
|
|
42
59
|
key={toast.id}
|
|
43
60
|
visible={true}
|
|
44
|
-
duration={toast
|
|
61
|
+
duration={toast?.options?.duration}
|
|
45
62
|
position="top"
|
|
46
|
-
|
|
63
|
+
theme={theme}
|
|
64
|
+
style={[toast?.options?.style || {}, { marginTop: index * 60 }]} // Adjust spacing between top toasts
|
|
47
65
|
onHide={() => toastManagerInstance.remove(toast.id)}
|
|
48
66
|
>
|
|
49
67
|
{toast.content}
|
|
50
68
|
</Toast>
|
|
51
69
|
))}
|
|
52
|
-
</
|
|
70
|
+
</SafeAreaView>
|
|
53
71
|
|
|
54
72
|
{/* Center positioned toasts */}
|
|
55
|
-
<View style={styles.centerContainer}>
|
|
73
|
+
<View style={styles.centerContainer} pointerEvents="box-none">
|
|
56
74
|
{centerToasts.map((toast, index) => (
|
|
57
75
|
<Toast
|
|
58
76
|
key={toast.id}
|
|
59
77
|
visible={true}
|
|
60
|
-
duration={toast
|
|
78
|
+
duration={toast?.options?.duration}
|
|
61
79
|
position="center"
|
|
62
|
-
|
|
80
|
+
theme={theme}
|
|
81
|
+
style={[toast?.options?.style || {}, { marginTop: index * 60 }]} // Adjust spacing between center toasts
|
|
63
82
|
onHide={() => toastManagerInstance.remove(toast.id)}
|
|
64
83
|
>
|
|
65
84
|
{toast.content}
|
|
@@ -68,14 +87,15 @@ const ToastContainer = () => {
|
|
|
68
87
|
</View>
|
|
69
88
|
|
|
70
89
|
{/* Bottom positioned toasts */}
|
|
71
|
-
<View style={styles.bottomContainer}>
|
|
90
|
+
<View style={styles.bottomContainer} pointerEvents="box-none">
|
|
72
91
|
{bottomToasts.map((toast, index) => (
|
|
73
92
|
<Toast
|
|
74
93
|
key={toast.id}
|
|
75
94
|
visible={true}
|
|
76
|
-
duration={toast
|
|
95
|
+
duration={toast?.options?.duration}
|
|
77
96
|
position="bottom"
|
|
78
|
-
|
|
97
|
+
theme={theme}
|
|
98
|
+
style={[toast?.options?.style || {}, { bottom: hp(2) + index * 60 }]} // Adjust spacing between bottom toasts
|
|
79
99
|
onHide={() => toastManagerInstance.remove(toast.id)}
|
|
80
100
|
>
|
|
81
101
|
{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);
|