@selimh/react-native-toast 0.2.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 ADDED
@@ -0,0 +1,20 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Selim Hamzaoğulları
4
+ Permission is hereby granted, free of charge, to any person obtaining a copy
5
+ of this software and associated documentation files (the "Software"), to deal
6
+ in the Software without restriction, including without limitation the rights
7
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
8
+ copies of the Software, and to permit persons to whom the Software is
9
+ furnished to do so, subject to the following conditions:
10
+
11
+ The above copyright notice and this permission notice shall be included in all
12
+ copies or substantial portions of the Software.
13
+
14
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
19
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
20
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,98 @@
1
+ # react-native-toast
2
+
3
+ A beautifully animated, highly customizable, and imperative Toast library for React Native based on `react-native-reanimated`.
4
+
5
+ ![Image](https://github.com/user-attachments/assets/3e9910ab-0b8a-468e-a778-79ea0d53c525)
6
+
7
+ ## Installation
8
+
9
+ ```sh
10
+ npm install react-native-toast
11
+ # or
12
+ yarn add react-native-toast
13
+ ```
14
+
15
+ You also need to install the peer dependencies:
16
+
17
+ ```sh
18
+ yarn add react-native-reanimated react-native-safe-area-context
19
+ ```
20
+
21
+ > **Note:** Make sure to follow the installation instructions for `react-native-reanimated` (e.g., adding the babel plugin) and `react-native-safe-area-context`.
22
+
23
+ ## Usage
24
+
25
+ 1. Wrap your root directory with `ToastProvider`:
26
+
27
+ ```tsx
28
+ import { ToastProvider } from 'react-native-toast';
29
+ import { SafeAreaProvider } from 'react-native-safe-area-context';
30
+
31
+ export default function App() {
32
+ return (
33
+ <SafeAreaProvider>
34
+ {/*
35
+ theme can be 'light', 'dark', or 'system' (default: 'system')
36
+ It will automatically detect user's device preferences.
37
+ */}
38
+ <ToastProvider theme="system">
39
+ {/* Your app components */}
40
+ </ToastProvider>
41
+ </SafeAreaProvider>
42
+ );
43
+ }
44
+ ```
45
+
46
+ 2. Call `Toast.show` from anywhere in your app:
47
+
48
+ ```tsx
49
+ import { Toast } from 'react-native-toast';
50
+
51
+ Toast.show({
52
+ type: 'success',
53
+ text1: 'Hello',
54
+ text2: 'This is an awesome toast',
55
+ position: 'top',
56
+ visibilityTime: 4000,
57
+ });
58
+ ```
59
+
60
+ ### Dark Mode (Theme) Support
61
+ `react-native-toast` perfectly supports light and dark modes out of the box.
62
+
63
+ 1. **System Dependant (Default):** If you pass `theme="system"` to `ToastProvider` or omit it, the toast will automatically adapt to the user's iOS/Android theme.
64
+ 2. **Forced Theme:** You can override the theme dynamically on a per-toast basis:
65
+ ```tsx
66
+ Toast.show({
67
+ type: 'info',
68
+ theme: 'dark', // 'light' | 'dark' | 'system'
69
+ text1: 'Always Dark',
70
+ });
71
+ ```
72
+
73
+ ## APIs
74
+
75
+ ### `Toast.show(options: ToastOptions)`
76
+
77
+ | Option | Type | Default | Description |
78
+ | --- | --- | --- | --- |
79
+ | `type` | `'success' \| 'error' \| 'info' \| 'warning'` | `'info'` | Type of the toast |
80
+ | `text1` | `string` | `undefined` | Primary text |
81
+ | `text2` | `string` | `undefined` | Secondary description |
82
+ | `position` | `'top' \| 'bottom'` | `'top'` | Position of the toast |
83
+ | `theme` | `'light' \| 'dark' \| 'system'` | `'system'` | Toast visual theme |
84
+ | `visibilityTime` | `number` | `3000` | Duration in ms |
85
+ | `topOffset` | `number` | `40` | Offset from top edge |
86
+ | `bottomOffset` | `number` | `40` | Offset from bottom edge |
87
+ | `autoHide` | `boolean` | `true` | If false, toast will not hide automatically |
88
+ | `customView` | `React.ReactNode` | `undefined` | Render a completely custom component |
89
+ | `onPress` | `() => void` | `undefined` | Called when toast is tapped |
90
+ | `onShow` | `() => void` | `undefined` | Called when animation finishes showing |
91
+ | `onHide` | `() => void` | `undefined` | Called when animation finishes hiding |
92
+
93
+ ### `Toast.hide()`
94
+ Hides the currently visible toast.
95
+
96
+ ## License
97
+
98
+ MIT
@@ -0,0 +1,181 @@
1
+ "use strict";
2
+
3
+ import React, { useEffect } from 'react';
4
+ import { StyleSheet, Text, View, TouchableOpacity, useColorScheme } from 'react-native';
5
+ import Animated, { useSharedValue, useAnimatedStyle, withSpring, withTiming, runOnJS } from 'react-native-reanimated';
6
+ import { useSafeAreaInsets } from 'react-native-safe-area-context';
7
+ import { SuccessIcon, ErrorIcon, InfoIcon, WarningIcon } from "./icons.js";
8
+ import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
9
+ export const Toast = ({
10
+ isVisible,
11
+ type = 'info',
12
+ text1,
13
+ text2,
14
+ position = 'top',
15
+ topOffset = 10,
16
+ bottomOffset = 10,
17
+ theme,
18
+ providerTheme = 'system',
19
+ onPress,
20
+ onAnimationEnd,
21
+ customView
22
+ }) => {
23
+ const insets = useSafeAreaInsets();
24
+ const systemTheme = useColorScheme();
25
+ const activeTheme = theme && theme !== 'system' ? theme : providerTheme !== 'system' ? providerTheme : systemTheme;
26
+ const isDark = activeTheme === 'dark';
27
+ const translateY = useSharedValue(position === 'top' ? -150 : 150);
28
+ const opacity = useSharedValue(0);
29
+ useEffect(() => {
30
+ if (isVisible) {
31
+ // Reset position instantly before starting the animation
32
+ translateY.value = position === 'top' ? -150 : 150;
33
+ opacity.value = withTiming(1, {
34
+ duration: 300
35
+ });
36
+ translateY.value = withSpring(0, {
37
+ damping: 40,
38
+ stiffness: 250
39
+ }, finished => {
40
+ if (finished && onAnimationEnd) {
41
+ runOnJS(onAnimationEnd)(true);
42
+ }
43
+ });
44
+ } else {
45
+ opacity.value = withTiming(0, {
46
+ duration: 300
47
+ });
48
+ translateY.value = withTiming(position === 'top' ? -150 : 150, {
49
+ duration: 300
50
+ }, finished => {
51
+ if (finished && onAnimationEnd) {
52
+ runOnJS(onAnimationEnd)(false);
53
+ }
54
+ });
55
+ }
56
+ }, [isVisible, opacity, translateY, position, onAnimationEnd]);
57
+ const animatedStyle = useAnimatedStyle(() => {
58
+ return {
59
+ opacity: opacity.value,
60
+ transform: [{
61
+ translateY: translateY.value
62
+ }]
63
+ };
64
+ });
65
+ const getIcon = () => {
66
+ switch (type) {
67
+ case 'success':
68
+ return /*#__PURE__*/_jsx(SuccessIcon, {});
69
+ case 'error':
70
+ return /*#__PURE__*/_jsx(ErrorIcon, {});
71
+ case 'warning':
72
+ return /*#__PURE__*/_jsx(WarningIcon, {});
73
+ case 'info':
74
+ default:
75
+ return /*#__PURE__*/_jsx(InfoIcon, {});
76
+ }
77
+ };
78
+ const getContainerStyle = () => {
79
+ const isTop = position === 'top';
80
+ return [styles.container, isTop ? {
81
+ top: insets.top + topOffset
82
+ } : {
83
+ bottom: insets.bottom + bottomOffset
84
+ }, animatedStyle];
85
+ };
86
+ const contentStyle = [styles.content, isDark ? styles.contentDark : styles.contentLight];
87
+ const text1Style = [styles.text1, isDark ? styles.text1Dark : styles.text1Light];
88
+ const text2Style = [styles.text2, isDark ? styles.text2Dark : styles.text2Light];
89
+ return /*#__PURE__*/_jsx(Animated.View, {
90
+ // @ts-ignore - TS2589 bypass
91
+ style: getContainerStyle(),
92
+ pointerEvents: isVisible ? 'box-none' : 'none',
93
+ children: /*#__PURE__*/_jsx(TouchableOpacity, {
94
+ activeOpacity: 0.9,
95
+ onPress: onPress,
96
+ disabled: !onPress,
97
+ style: customView ? styles.customContent : contentStyle,
98
+ children: customView ? customView : /*#__PURE__*/_jsxs(_Fragment, {
99
+ children: [/*#__PURE__*/_jsx(View, {
100
+ style: styles.iconContainer,
101
+ children: getIcon()
102
+ }), /*#__PURE__*/_jsxs(View, {
103
+ style: styles.textContainer,
104
+ children: [text1 && /*#__PURE__*/_jsx(Text, {
105
+ style: text1Style,
106
+ children: text1
107
+ }), text2 && /*#__PURE__*/_jsx(Text, {
108
+ style: text2Style,
109
+ children: text2
110
+ })]
111
+ })]
112
+ })
113
+ })
114
+ });
115
+ };
116
+ const styles = StyleSheet.create({
117
+ container: {
118
+ position: 'absolute',
119
+ left: 16,
120
+ right: 16,
121
+ zIndex: 9999,
122
+ alignItems: 'center'
123
+ },
124
+ customContent: {
125
+ width: '100%',
126
+ maxWidth: 400
127
+ },
128
+ content: {
129
+ flexDirection: 'row',
130
+ borderRadius: 12,
131
+ padding: 16,
132
+ width: '100%',
133
+ shadowOffset: {
134
+ width: 0,
135
+ height: 4
136
+ },
137
+ shadowOpacity: 0.1,
138
+ shadowRadius: 12,
139
+ elevation: 5,
140
+ alignItems: 'center',
141
+ maxWidth: 400
142
+ },
143
+ contentLight: {
144
+ backgroundColor: '#ffffff',
145
+ shadowColor: '#000'
146
+ },
147
+ contentDark: {
148
+ backgroundColor: '#1f2937',
149
+ shadowColor: '#000',
150
+ borderWidth: 1,
151
+ borderColor: '#374151'
152
+ },
153
+ iconContainer: {
154
+ marginRight: 12
155
+ },
156
+ textContainer: {
157
+ flex: 1,
158
+ justifyContent: 'center'
159
+ },
160
+ text1: {
161
+ fontSize: 15,
162
+ fontWeight: '600',
163
+ marginBottom: 4
164
+ },
165
+ text1Light: {
166
+ color: '#1f2937'
167
+ },
168
+ text1Dark: {
169
+ color: '#f9fafb'
170
+ },
171
+ text2: {
172
+ fontSize: 13
173
+ },
174
+ text2Light: {
175
+ color: '#6b7280'
176
+ },
177
+ text2Dark: {
178
+ color: '#9ca3af'
179
+ }
180
+ });
181
+ //# sourceMappingURL=Toast.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"names":["React","useEffect","StyleSheet","Text","View","TouchableOpacity","useColorScheme","Animated","useSharedValue","useAnimatedStyle","withSpring","withTiming","runOnJS","useSafeAreaInsets","SuccessIcon","ErrorIcon","InfoIcon","WarningIcon","jsx","_jsx","jsxs","_jsxs","Fragment","_Fragment","Toast","isVisible","type","text1","text2","position","topOffset","bottomOffset","theme","providerTheme","onPress","onAnimationEnd","customView","insets","systemTheme","activeTheme","isDark","translateY","opacity","value","duration","damping","stiffness","finished","animatedStyle","transform","getIcon","getContainerStyle","isTop","styles","container","top","bottom","contentStyle","content","contentDark","contentLight","text1Style","text1Dark","text1Light","text2Style","text2Dark","text2Light","style","pointerEvents","children","activeOpacity","disabled","customContent","iconContainer","textContainer","create","left","right","zIndex","alignItems","width","maxWidth","flexDirection","borderRadius","padding","shadowOffset","height","shadowOpacity","shadowRadius","elevation","backgroundColor","shadowColor","borderWidth","borderColor","marginRight","flex","justifyContent","fontSize","fontWeight","marginBottom","color"],"sourceRoot":"../../src","sources":["Toast.tsx"],"mappings":"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","ignoreList":[]}
@@ -0,0 +1,56 @@
1
+ "use strict";
2
+
3
+ import React, { useState, useCallback, useRef, useEffect } from 'react';
4
+ import { Toast } from "./Toast.js";
5
+ import { jsx as _jsx, Fragment as _Fragment, jsxs as _jsxs } from "react/jsx-runtime";
6
+ export const ToastRefWrapper = /*#__PURE__*/React.createRef();
7
+ export const ToastProvider = ({
8
+ children,
9
+ theme = 'system'
10
+ }) => {
11
+ const [options, setOptions] = useState({});
12
+ const [isVisible, setIsVisible] = useState(false);
13
+ const hideTimeoutRef = useRef(null);
14
+ const show = useCallback(opts => {
15
+ setOptions(opts);
16
+ setIsVisible(true);
17
+ if (hideTimeoutRef.current) clearTimeout(hideTimeoutRef.current);
18
+ const autoHide = opts.autoHide ?? true;
19
+ const visibilityTime = opts.visibilityTime ?? 3000;
20
+ if (autoHide) {
21
+ hideTimeoutRef.current = setTimeout(() => {
22
+ setIsVisible(false);
23
+ }, visibilityTime);
24
+ }
25
+ }, []);
26
+ const hide = useCallback(() => {
27
+ setIsVisible(false);
28
+ if (hideTimeoutRef.current) clearTimeout(hideTimeoutRef.current);
29
+ }, []);
30
+ useEffect(() => {
31
+ ToastRefWrapper.current = {
32
+ show,
33
+ hide
34
+ };
35
+ }, [show, hide]);
36
+ const onAnimationEnd = useCallback(visible => {
37
+ if (!visible) {
38
+ options.onHide?.();
39
+ } else {
40
+ options.onShow?.();
41
+ }
42
+ }, [options]);
43
+ return /*#__PURE__*/_jsxs(_Fragment, {
44
+ children: [children, /*#__PURE__*/_jsx(Toast, {
45
+ ...options,
46
+ isVisible: isVisible,
47
+ onAnimationEnd: onAnimationEnd,
48
+ providerTheme: theme
49
+ })]
50
+ });
51
+ };
52
+ export const ToastService = {
53
+ show: options => ToastRefWrapper.current?.show(options),
54
+ hide: () => ToastRefWrapper.current?.hide()
55
+ };
56
+ //# sourceMappingURL=ToastProvider.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"names":["React","useState","useCallback","useRef","useEffect","Toast","jsx","_jsx","Fragment","_Fragment","jsxs","_jsxs","ToastRefWrapper","createRef","ToastProvider","children","theme","options","setOptions","isVisible","setIsVisible","hideTimeoutRef","show","opts","current","clearTimeout","autoHide","visibilityTime","setTimeout","hide","onAnimationEnd","visible","onHide","onShow","providerTheme","ToastService"],"sourceRoot":"../../src","sources":["ToastProvider.tsx"],"mappings":";;AAAA,OAAOA,KAAK,IAAIC,QAAQ,EAAEC,WAAW,EAAEC,MAAM,EAAEC,SAAS,QAAQ,OAAO;AACvE,SAASC,KAAK,QAAQ,YAAS;AAAC,SAAAC,GAAA,IAAAC,IAAA,EAAAC,QAAA,IAAAC,SAAA,EAAAC,IAAA,IAAAC,KAAA;AAGhC,OAAO,MAAMC,eAAe,gBAAGZ,KAAK,CAACa,SAAS,CAAW,CAAC;AAE1D,OAAO,MAAMC,aAAa,GAAGA,CAAC;EAC5BC,QAAQ;EACRC,KAAK,GAAG;AACU,CAAC,KAAK;EACxB,MAAM,CAACC,OAAO,EAAEC,UAAU,CAAC,GAAGjB,QAAQ,CAAe,CAAC,CAAC,CAAC;EACxD,MAAM,CAACkB,SAAS,EAAEC,YAAY,CAAC,GAAGnB,QAAQ,CAAC,KAAK,CAAC;EACjD,MAAMoB,cAAc,GAAGlB,MAAM,CAAwB,IAAI,CAAC;EAE1D,MAAMmB,IAAI,GAAGpB,WAAW,CAAEqB,IAAkB,IAAK;IAC/CL,UAAU,CAACK,IAAI,CAAC;IAChBH,YAAY,CAAC,IAAI,CAAC;IAElB,IAAIC,cAAc,CAACG,OAAO,EAAEC,YAAY,CAACJ,cAAc,CAACG,OAAO,CAAC;IAEhE,MAAME,QAAQ,GAAGH,IAAI,CAACG,QAAQ,IAAI,IAAI;IACtC,MAAMC,cAAc,GAAGJ,IAAI,CAACI,cAAc,IAAI,IAAI;IAElD,IAAID,QAAQ,EAAE;MACZL,cAAc,CAACG,OAAO,GAAGI,UAAU,CAAC,MAAM;QACxCR,YAAY,CAAC,KAAK,CAAC;MACrB,CAAC,EAAEO,cAAc,CAAC;IACpB;EACF,CAAC,EAAE,EAAE,CAAC;EAEN,MAAME,IAAI,GAAG3B,WAAW,CAAC,MAAM;IAC7BkB,YAAY,CAAC,KAAK,CAAC;IACnB,IAAIC,cAAc,CAACG,OAAO,EAAEC,YAAY,CAACJ,cAAc,CAACG,OAAO,CAAC;EAClE,CAAC,EAAE,EAAE,CAAC;EAENpB,SAAS,CAAC,MAAM;IACbQ,eAAe,CAASY,OAAO,GAAG;MAAEF,IAAI;MAAEO;IAAK,CAAC;EACnD,CAAC,EAAE,CAACP,IAAI,EAAEO,IAAI,CAAC,CAAC;EAEhB,MAAMC,cAAc,GAAG5B,WAAW,CAC/B6B,OAAgB,IAAK;IACpB,IAAI,CAACA,OAAO,EAAE;MACZd,OAAO,CAACe,MAAM,GAAG,CAAC;IACpB,CAAC,MAAM;MACLf,OAAO,CAACgB,MAAM,GAAG,CAAC;IACpB;EACF,CAAC,EACD,CAAChB,OAAO,CACV,CAAC;EAED,oBACEN,KAAA,CAAAF,SAAA;IAAAM,QAAA,GACGA,QAAQ,eACTR,IAAA,CAACF,KAAK;MAAA,GACAY,OAAO;MACXE,SAAS,EAAEA,SAAU;MACrBW,cAAc,EAAEA,cAAe;MAC/BI,aAAa,EAAElB;IAAM,CACtB,CAAC;EAAA,CACF,CAAC;AAEP,CAAC;AAED,OAAO,MAAMmB,YAAY,GAAG;EAC1Bb,IAAI,EAAGL,OAAqB,IAAKL,eAAe,CAACY,OAAO,EAAEF,IAAI,CAACL,OAAO,CAAC;EACvEY,IAAI,EAAEA,CAAA,KAAMjB,eAAe,CAACY,OAAO,EAAEK,IAAI,CAAC;AAC5C,CAAC","ignoreList":[]}
@@ -0,0 +1,59 @@
1
+ "use strict";
2
+
3
+ import { Text, View, StyleSheet } from 'react-native';
4
+ import { jsx as _jsx } from "react/jsx-runtime";
5
+ export const SuccessIcon = () => /*#__PURE__*/_jsx(View, {
6
+ style: [styles.container, styles.success],
7
+ children: /*#__PURE__*/_jsx(Text, {
8
+ style: styles.icon,
9
+ children: "\u2713"
10
+ })
11
+ });
12
+ export const ErrorIcon = () => /*#__PURE__*/_jsx(View, {
13
+ style: [styles.container, styles.error],
14
+ children: /*#__PURE__*/_jsx(Text, {
15
+ style: styles.icon,
16
+ children: "\u2715"
17
+ })
18
+ });
19
+ export const InfoIcon = () => /*#__PURE__*/_jsx(View, {
20
+ style: [styles.container, styles.info],
21
+ children: /*#__PURE__*/_jsx(Text, {
22
+ style: styles.icon,
23
+ children: "i"
24
+ })
25
+ });
26
+ export const WarningIcon = () => /*#__PURE__*/_jsx(View, {
27
+ style: [styles.container, styles.warning],
28
+ children: /*#__PURE__*/_jsx(Text, {
29
+ style: styles.icon,
30
+ children: "!"
31
+ })
32
+ });
33
+ const styles = StyleSheet.create({
34
+ container: {
35
+ width: 24,
36
+ height: 24,
37
+ borderRadius: 12,
38
+ justifyContent: 'center',
39
+ alignItems: 'center'
40
+ },
41
+ icon: {
42
+ color: '#fff',
43
+ fontSize: 14,
44
+ fontWeight: 'bold'
45
+ },
46
+ success: {
47
+ backgroundColor: '#4ade80'
48
+ },
49
+ error: {
50
+ backgroundColor: '#f87171'
51
+ },
52
+ info: {
53
+ backgroundColor: '#60a5fa'
54
+ },
55
+ warning: {
56
+ backgroundColor: '#fbbf24'
57
+ }
58
+ });
59
+ //# sourceMappingURL=icons.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"names":["Text","View","StyleSheet","jsx","_jsx","SuccessIcon","style","styles","container","success","children","icon","ErrorIcon","error","InfoIcon","info","WarningIcon","warning","create","width","height","borderRadius","justifyContent","alignItems","color","fontSize","fontWeight","backgroundColor"],"sourceRoot":"../../src","sources":["icons.tsx"],"mappings":";;AAAA,SAASA,IAAI,EAAEC,IAAI,EAAEC,UAAU,QAAQ,cAAc;AAAC,SAAAC,GAAA,IAAAC,IAAA;AAEtD,OAAO,MAAMC,WAAW,GAAGA,CAAA,kBACzBD,IAAA,CAACH,IAAI;EAACK,KAAK,EAAE,CAACC,MAAM,CAACC,SAAS,EAAED,MAAM,CAACE,OAAO,CAAE;EAAAC,QAAA,eAC9CN,IAAA,CAACJ,IAAI;IAACM,KAAK,EAAEC,MAAM,CAACI,IAAK;IAAAD,QAAA,EAAC;EAAC,CAAM;AAAC,CAC9B,CACP;AAED,OAAO,MAAME,SAAS,GAAGA,CAAA,kBACvBR,IAAA,CAACH,IAAI;EAACK,KAAK,EAAE,CAACC,MAAM,CAACC,SAAS,EAAED,MAAM,CAACM,KAAK,CAAE;EAAAH,QAAA,eAC5CN,IAAA,CAACJ,IAAI;IAACM,KAAK,EAAEC,MAAM,CAACI,IAAK;IAAAD,QAAA,EAAC;EAAC,CAAM;AAAC,CAC9B,CACP;AAED,OAAO,MAAMI,QAAQ,GAAGA,CAAA,kBACtBV,IAAA,CAACH,IAAI;EAACK,KAAK,EAAE,CAACC,MAAM,CAACC,SAAS,EAAED,MAAM,CAACQ,IAAI,CAAE;EAAAL,QAAA,eAC3CN,IAAA,CAACJ,IAAI;IAACM,KAAK,EAAEC,MAAM,CAACI,IAAK;IAAAD,QAAA,EAAC;EAAC,CAAM;AAAC,CAC9B,CACP;AAED,OAAO,MAAMM,WAAW,GAAGA,CAAA,kBACzBZ,IAAA,CAACH,IAAI;EAACK,KAAK,EAAE,CAACC,MAAM,CAACC,SAAS,EAAED,MAAM,CAACU,OAAO,CAAE;EAAAP,QAAA,eAC9CN,IAAA,CAACJ,IAAI;IAACM,KAAK,EAAEC,MAAM,CAACI,IAAK;IAAAD,QAAA,EAAC;EAAC,CAAM;AAAC,CAC9B,CACP;AAED,MAAMH,MAAM,GAAGL,UAAU,CAACgB,MAAM,CAAC;EAC/BV,SAAS,EAAE;IACTW,KAAK,EAAE,EAAE;IACTC,MAAM,EAAE,EAAE;IACVC,YAAY,EAAE,EAAE;IAChBC,cAAc,EAAE,QAAQ;IACxBC,UAAU,EAAE;EACd,CAAC;EACDZ,IAAI,EAAE;IACJa,KAAK,EAAE,MAAM;IACbC,QAAQ,EAAE,EAAE;IACZC,UAAU,EAAE;EACd,CAAC;EACDjB,OAAO,EAAE;IACPkB,eAAe,EAAE;EACnB,CAAC;EACDd,KAAK,EAAE;IACLc,eAAe,EAAE;EACnB,CAAC;EACDZ,IAAI,EAAE;IACJY,eAAe,EAAE;EACnB,CAAC;EACDV,OAAO,EAAE;IACPU,eAAe,EAAE;EACnB;AACF,CAAC,CAAC","ignoreList":[]}
@@ -0,0 +1,7 @@
1
+ "use strict";
2
+
3
+ export * from "./types.js";
4
+ export * from "./ToastProvider.js";
5
+ export { ToastService as Toast } from "./ToastProvider.js";
6
+ export { Toast as CustomToast } from "./Toast.js";
7
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"names":["ToastService","Toast","CustomToast"],"sourceRoot":"../../src","sources":["index.tsx"],"mappings":";;AAAA,cAAc,YAAS;AACvB,cAAc,oBAAiB;AAC/B,SAASA,YAAY,IAAIC,KAAK,QAAQ,oBAAiB;AACvD,SAASA,KAAK,IAAIC,WAAW,QAAQ,YAAS","ignoreList":[]}
@@ -0,0 +1 @@
1
+ {"type":"module"}
@@ -0,0 +1,2 @@
1
+ "use strict";
2
+ //# sourceMappingURL=types.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"names":[],"sourceRoot":"../../src","sources":["types.ts"],"mappings":"","ignoreList":[]}
@@ -0,0 +1 @@
1
+ {"type":"module"}
@@ -0,0 +1,4 @@
1
+ import React from 'react';
2
+ import type { ToastProps } from './types';
3
+ export declare const Toast: React.FC<ToastProps>;
4
+ //# sourceMappingURL=Toast.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"Toast.d.ts","sourceRoot":"","sources":["../../../src/Toast.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAoB,MAAM,OAAO,CAAC;AAgBzC,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,SAAS,CAAC;AAG1C,eAAO,MAAM,KAAK,EAAE,KAAK,CAAC,EAAE,CAAC,UAAU,CAoItC,CAAC"}
@@ -0,0 +1,9 @@
1
+ import React from 'react';
2
+ import type { ToastOptions, ToastRef, ToastProviderProps } from './types';
3
+ export declare const ToastRefWrapper: React.RefObject<ToastRef | null>;
4
+ export declare const ToastProvider: ({ children, theme, }: ToastProviderProps) => import("react/jsx-runtime").JSX.Element;
5
+ export declare const ToastService: {
6
+ show: (options: ToastOptions) => void | undefined;
7
+ hide: () => void | undefined;
8
+ };
9
+ //# sourceMappingURL=ToastProvider.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ToastProvider.d.ts","sourceRoot":"","sources":["../../../src/ToastProvider.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAmD,MAAM,OAAO,CAAC;AAExE,OAAO,KAAK,EAAE,YAAY,EAAE,QAAQ,EAAE,kBAAkB,EAAE,MAAM,SAAS,CAAC;AAE1E,eAAO,MAAM,eAAe,kCAA8B,CAAC;AAE3D,eAAO,MAAM,aAAa,GAAI,sBAG3B,kBAAkB,4CAoDpB,CAAC;AAEF,eAAO,MAAM,YAAY;oBACP,YAAY;;CAE7B,CAAC"}
@@ -0,0 +1,5 @@
1
+ export declare const SuccessIcon: () => import("react/jsx-runtime").JSX.Element;
2
+ export declare const ErrorIcon: () => import("react/jsx-runtime").JSX.Element;
3
+ export declare const InfoIcon: () => import("react/jsx-runtime").JSX.Element;
4
+ export declare const WarningIcon: () => import("react/jsx-runtime").JSX.Element;
5
+ //# sourceMappingURL=icons.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"icons.d.ts","sourceRoot":"","sources":["../../../src/icons.tsx"],"names":[],"mappings":"AAEA,eAAO,MAAM,WAAW,+CAIvB,CAAC;AAEF,eAAO,MAAM,SAAS,+CAIrB,CAAC;AAEF,eAAO,MAAM,QAAQ,+CAIpB,CAAC;AAEF,eAAO,MAAM,WAAW,+CAIvB,CAAC"}
@@ -0,0 +1,5 @@
1
+ export * from './types';
2
+ export * from './ToastProvider';
3
+ export { ToastService as Toast } from './ToastProvider';
4
+ export { Toast as CustomToast } from './Toast';
5
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/index.tsx"],"names":[],"mappings":"AAAA,cAAc,SAAS,CAAC;AACxB,cAAc,iBAAiB,CAAC;AAChC,OAAO,EAAE,YAAY,IAAI,KAAK,EAAE,MAAM,iBAAiB,CAAC;AACxD,OAAO,EAAE,KAAK,IAAI,WAAW,EAAE,MAAM,SAAS,CAAC"}
@@ -0,0 +1,33 @@
1
+ export type ToastType = 'success' | 'error' | 'info' | 'warning';
2
+ export type ToastPosition = 'top' | 'bottom';
3
+ export type ToastTheme = 'light' | 'dark' | 'system';
4
+ export interface ToastOptions {
5
+ type?: ToastType;
6
+ text1?: string;
7
+ text2?: string;
8
+ duration?: number;
9
+ position?: ToastPosition;
10
+ topOffset?: number;
11
+ bottomOffset?: number;
12
+ visibilityTime?: number;
13
+ autoHide?: boolean;
14
+ theme?: ToastTheme;
15
+ onPress?: () => void;
16
+ onShow?: () => void;
17
+ onHide?: () => void;
18
+ customView?: React.ReactNode;
19
+ }
20
+ export interface ToastProps extends ToastOptions {
21
+ isVisible: boolean;
22
+ providerTheme?: ToastTheme;
23
+ onAnimationEnd: (isVisible: boolean) => void;
24
+ }
25
+ export interface ToastProviderProps {
26
+ children: React.ReactNode;
27
+ theme?: ToastTheme;
28
+ }
29
+ export interface ToastRef {
30
+ show: (options: ToastOptions) => void;
31
+ hide: () => void;
32
+ }
33
+ //# sourceMappingURL=types.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../../src/types.ts"],"names":[],"mappings":"AAAA,MAAM,MAAM,SAAS,GAAG,SAAS,GAAG,OAAO,GAAG,MAAM,GAAG,SAAS,CAAC;AAEjE,MAAM,MAAM,aAAa,GAAG,KAAK,GAAG,QAAQ,CAAC;AAE7C,MAAM,MAAM,UAAU,GAAG,OAAO,GAAG,MAAM,GAAG,QAAQ,CAAC;AAErD,MAAM,WAAW,YAAY;IAC3B,IAAI,CAAC,EAAE,SAAS,CAAC;IACjB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,QAAQ,CAAC,EAAE,aAAa,CAAC;IACzB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,KAAK,CAAC,EAAE,UAAU,CAAC;IACnB,OAAO,CAAC,EAAE,MAAM,IAAI,CAAC;IACrB,MAAM,CAAC,EAAE,MAAM,IAAI,CAAC;IACpB,MAAM,CAAC,EAAE,MAAM,IAAI,CAAC;IACpB,UAAU,CAAC,EAAE,KAAK,CAAC,SAAS,CAAC;CAC9B;AAED,MAAM,WAAW,UAAW,SAAQ,YAAY;IAC9C,SAAS,EAAE,OAAO,CAAC;IACnB,aAAa,CAAC,EAAE,UAAU,CAAC;IAC3B,cAAc,EAAE,CAAC,SAAS,EAAE,OAAO,KAAK,IAAI,CAAC;CAC9C;AAED,MAAM,WAAW,kBAAkB;IACjC,QAAQ,EAAE,KAAK,CAAC,SAAS,CAAC;IAC1B,KAAK,CAAC,EAAE,UAAU,CAAC;CACpB;AAED,MAAM,WAAW,QAAQ;IACvB,IAAI,EAAE,CAAC,OAAO,EAAE,YAAY,KAAK,IAAI,CAAC;IACtC,IAAI,EAAE,MAAM,IAAI,CAAC;CAClB"}
package/package.json ADDED
@@ -0,0 +1,169 @@
1
+ {
2
+ "name": "@selimh/react-native-toast",
3
+ "version": "0.2.0",
4
+ "description": "A beautifully animated, highly customizable, imperative Toast library for React Native.",
5
+ "keywords": [
6
+ "react-native",
7
+ "react-native-toast",
8
+ "toast",
9
+ "reanimated",
10
+ "alert",
11
+ "notification",
12
+ "snackbar",
13
+ "toast-message"
14
+ ],
15
+ "main": "./lib/module/index.js",
16
+ "types": "./lib/typescript/src/index.d.ts",
17
+ "exports": {
18
+ ".": {
19
+ "source": "./src/index.tsx",
20
+ "types": "./lib/typescript/src/index.d.ts",
21
+ "default": "./lib/module/index.js"
22
+ },
23
+ "./package.json": "./package.json"
24
+ },
25
+ "files": [
26
+ "src",
27
+ "lib",
28
+ "android",
29
+ "ios",
30
+ "cpp",
31
+ "*.podspec",
32
+ "react-native.config.js",
33
+ "!ios/build",
34
+ "!android/build",
35
+ "!android/gradle",
36
+ "!android/gradlew",
37
+ "!android/gradlew.bat",
38
+ "!android/local.properties",
39
+ "!**/__tests__",
40
+ "!**/__fixtures__",
41
+ "!**/__mocks__",
42
+ "!**/.*"
43
+ ],
44
+ "scripts": {
45
+ "example": "yarn workspace react-native-toast-example",
46
+ "clean": "del-cli lib",
47
+ "prepare": "bob build",
48
+ "typecheck": "tsc",
49
+ "lint": "eslint \"**/*.{js,ts,tsx}\"",
50
+ "test": "jest",
51
+ "release": "release-it --only-version"
52
+ },
53
+ "repository": {
54
+ "type": "git",
55
+ "url": "git+https://github.com/selimhamzaogullari/react-native-toast.git"
56
+ },
57
+ "author": "Selim Hamzaoğulları <selimhamzaogullari@hotmail.com> (https://github.com/selimhamzaogullari)",
58
+ "license": "MIT",
59
+ "bugs": {
60
+ "url": "https://github.com/selimhamzaogullari/react-native-toast/issues"
61
+ },
62
+ "homepage": "https://github.com/selimhamzaogullari/react-native-toast#readme",
63
+ "publishConfig": {
64
+ "registry": "https://registry.npmjs.org/",
65
+ "access": "public"
66
+ },
67
+ "devDependencies": {
68
+ "@commitlint/config-conventional": "^19.8.1",
69
+ "@eslint/compat": "^1.3.2",
70
+ "@eslint/eslintrc": "^3.3.1",
71
+ "@eslint/js": "^9.35.0",
72
+ "@react-native/babel-preset": "0.83.0",
73
+ "@react-native/eslint-config": "0.83.0",
74
+ "@release-it/conventional-changelog": "^10.0.1",
75
+ "@types/jest": "^29.5.14",
76
+ "@types/react": "^19.1.12",
77
+ "commitlint": "^19.8.1",
78
+ "del-cli": "^6.0.0",
79
+ "eslint": "^9.35.0",
80
+ "eslint-config-prettier": "^10.1.8",
81
+ "eslint-plugin-prettier": "^5.5.4",
82
+ "jest": "^29.7.0",
83
+ "lefthook": "^2.0.3",
84
+ "prettier": "^2.8.8",
85
+ "react": "19.2.0",
86
+ "react-native": "0.83.2",
87
+ "react-native-builder-bob": "^0.40.18",
88
+ "react-native-reanimated": "3.16.7",
89
+ "react-native-safe-area-context": "^5.7.0",
90
+ "release-it": "^19.0.4",
91
+ "turbo": "^2.5.6",
92
+ "typescript": "^5.9.2"
93
+ },
94
+ "peerDependencies": {
95
+ "react": "*",
96
+ "react-native": "*"
97
+ },
98
+ "workspaces": [
99
+ "example"
100
+ ],
101
+ "packageManager": "yarn@4.11.0",
102
+ "react-native-builder-bob": {
103
+ "source": "src",
104
+ "output": "lib",
105
+ "targets": [
106
+ [
107
+ "module",
108
+ {
109
+ "esm": true
110
+ }
111
+ ],
112
+ [
113
+ "typescript",
114
+ {
115
+ "project": "tsconfig.build.json"
116
+ }
117
+ ]
118
+ ]
119
+ },
120
+ "prettier": {
121
+ "quoteProps": "consistent",
122
+ "singleQuote": true,
123
+ "tabWidth": 2,
124
+ "trailingComma": "es5",
125
+ "useTabs": false
126
+ },
127
+ "jest": {
128
+ "preset": "react-native",
129
+ "modulePathIgnorePatterns": [
130
+ "<rootDir>/example/node_modules",
131
+ "<rootDir>/lib/"
132
+ ]
133
+ },
134
+ "commitlint": {
135
+ "extends": [
136
+ "@commitlint/config-conventional"
137
+ ]
138
+ },
139
+ "release-it": {
140
+ "git": {
141
+ "commitMessage": "chore: release ${version}",
142
+ "tagName": "v${version}"
143
+ },
144
+ "npm": {
145
+ "publish": true
146
+ },
147
+ "github": {
148
+ "release": true
149
+ },
150
+ "plugins": {
151
+ "@release-it/conventional-changelog": {
152
+ "preset": {
153
+ "name": "angular"
154
+ }
155
+ }
156
+ }
157
+ },
158
+ "create-react-native-library": {
159
+ "type": "library",
160
+ "languages": "js",
161
+ "tools": [
162
+ "eslint",
163
+ "jest",
164
+ "lefthook",
165
+ "release-it"
166
+ ],
167
+ "version": "0.57.2"
168
+ }
169
+ }
package/src/Toast.tsx ADDED
@@ -0,0 +1,215 @@
1
+ import React, { useEffect } from 'react';
2
+ import {
3
+ StyleSheet,
4
+ Text,
5
+ View,
6
+ TouchableOpacity,
7
+ useColorScheme,
8
+ } from 'react-native';
9
+ import Animated, {
10
+ useSharedValue,
11
+ useAnimatedStyle,
12
+ withSpring,
13
+ withTiming,
14
+ runOnJS,
15
+ } from 'react-native-reanimated';
16
+ import { useSafeAreaInsets } from 'react-native-safe-area-context';
17
+ import type { ToastProps } from './types';
18
+ import { SuccessIcon, ErrorIcon, InfoIcon, WarningIcon } from './icons';
19
+
20
+ export const Toast: React.FC<ToastProps> = ({
21
+ isVisible,
22
+ type = 'info',
23
+ text1,
24
+ text2,
25
+ position = 'top',
26
+ topOffset = 10,
27
+ bottomOffset = 10,
28
+ theme,
29
+ providerTheme = 'system',
30
+ onPress,
31
+ onAnimationEnd,
32
+ customView,
33
+ }) => {
34
+ const insets = useSafeAreaInsets();
35
+ const systemTheme = useColorScheme();
36
+
37
+ const activeTheme =
38
+ theme && theme !== 'system'
39
+ ? theme
40
+ : providerTheme !== 'system'
41
+ ? providerTheme
42
+ : systemTheme;
43
+
44
+ const isDark = activeTheme === 'dark';
45
+
46
+ const translateY = useSharedValue(position === 'top' ? -150 : 150);
47
+ const opacity = useSharedValue(0);
48
+
49
+ useEffect(() => {
50
+ if (isVisible) {
51
+ // Reset position instantly before starting the animation
52
+ translateY.value = position === 'top' ? -150 : 150;
53
+
54
+ opacity.value = withTiming(1, { duration: 300 });
55
+ translateY.value = withSpring(
56
+ 0,
57
+ {
58
+ damping: 40,
59
+ stiffness: 250,
60
+ },
61
+ (finished) => {
62
+ if (finished && onAnimationEnd) {
63
+ runOnJS(onAnimationEnd)(true);
64
+ }
65
+ }
66
+ );
67
+ } else {
68
+ opacity.value = withTiming(0, { duration: 300 });
69
+ translateY.value = withTiming(
70
+ position === 'top' ? -150 : 150,
71
+ { duration: 300 },
72
+ (finished) => {
73
+ if (finished && onAnimationEnd) {
74
+ runOnJS(onAnimationEnd)(false);
75
+ }
76
+ }
77
+ );
78
+ }
79
+ }, [isVisible, opacity, translateY, position, onAnimationEnd]);
80
+
81
+ const animatedStyle = useAnimatedStyle(() => {
82
+ return {
83
+ opacity: opacity.value,
84
+ transform: [{ translateY: translateY.value }],
85
+ };
86
+ });
87
+
88
+ const getIcon = () => {
89
+ switch (type) {
90
+ case 'success':
91
+ return <SuccessIcon />;
92
+ case 'error':
93
+ return <ErrorIcon />;
94
+ case 'warning':
95
+ return <WarningIcon />;
96
+ case 'info':
97
+ default:
98
+ return <InfoIcon />;
99
+ }
100
+ };
101
+
102
+ const getContainerStyle = () => {
103
+ const isTop = position === 'top';
104
+ return [
105
+ styles.container,
106
+ isTop
107
+ ? { top: insets.top + topOffset }
108
+ : { bottom: insets.bottom + bottomOffset },
109
+ animatedStyle,
110
+ ] as any;
111
+ };
112
+
113
+ const contentStyle = [
114
+ styles.content,
115
+ isDark ? styles.contentDark : styles.contentLight,
116
+ ];
117
+ const text1Style = [
118
+ styles.text1,
119
+ isDark ? styles.text1Dark : styles.text1Light,
120
+ ];
121
+ const text2Style = [
122
+ styles.text2,
123
+ isDark ? styles.text2Dark : styles.text2Light,
124
+ ];
125
+
126
+ return (
127
+ <Animated.View
128
+ // @ts-ignore - TS2589 bypass
129
+ style={getContainerStyle()}
130
+ pointerEvents={isVisible ? 'box-none' : 'none'}
131
+ >
132
+ <TouchableOpacity
133
+ activeOpacity={0.9}
134
+ onPress={onPress}
135
+ disabled={!onPress}
136
+ style={customView ? styles.customContent : contentStyle}
137
+ >
138
+ {customView ? (
139
+ customView
140
+ ) : (
141
+ <>
142
+ <View style={styles.iconContainer}>{getIcon()}</View>
143
+ <View style={styles.textContainer}>
144
+ {text1 && <Text style={text1Style}>{text1}</Text>}
145
+ {text2 && <Text style={text2Style}>{text2}</Text>}
146
+ </View>
147
+ </>
148
+ )}
149
+ </TouchableOpacity>
150
+ </Animated.View>
151
+ );
152
+ };
153
+
154
+ const styles = StyleSheet.create({
155
+ container: {
156
+ position: 'absolute',
157
+ left: 16,
158
+ right: 16,
159
+ zIndex: 9999,
160
+ alignItems: 'center',
161
+ },
162
+ customContent: {
163
+ width: '100%',
164
+ maxWidth: 400,
165
+ },
166
+ content: {
167
+ flexDirection: 'row',
168
+ borderRadius: 12,
169
+ padding: 16,
170
+ width: '100%',
171
+ shadowOffset: { width: 0, height: 4 },
172
+ shadowOpacity: 0.1,
173
+ shadowRadius: 12,
174
+ elevation: 5,
175
+ alignItems: 'center',
176
+ maxWidth: 400,
177
+ },
178
+ contentLight: {
179
+ backgroundColor: '#ffffff',
180
+ shadowColor: '#000',
181
+ },
182
+ contentDark: {
183
+ backgroundColor: '#1f2937',
184
+ shadowColor: '#000',
185
+ borderWidth: 1,
186
+ borderColor: '#374151',
187
+ },
188
+ iconContainer: {
189
+ marginRight: 12,
190
+ },
191
+ textContainer: {
192
+ flex: 1,
193
+ justifyContent: 'center',
194
+ },
195
+ text1: {
196
+ fontSize: 15,
197
+ fontWeight: '600',
198
+ marginBottom: 4,
199
+ },
200
+ text1Light: {
201
+ color: '#1f2937',
202
+ },
203
+ text1Dark: {
204
+ color: '#f9fafb',
205
+ },
206
+ text2: {
207
+ fontSize: 13,
208
+ },
209
+ text2Light: {
210
+ color: '#6b7280',
211
+ },
212
+ text2Dark: {
213
+ color: '#9ca3af',
214
+ },
215
+ });
@@ -0,0 +1,67 @@
1
+ import React, { useState, useCallback, useRef, useEffect } from 'react';
2
+ import { Toast } from './Toast';
3
+ import type { ToastOptions, ToastRef, ToastProviderProps } from './types';
4
+
5
+ export const ToastRefWrapper = React.createRef<ToastRef>();
6
+
7
+ export const ToastProvider = ({
8
+ children,
9
+ theme = 'system',
10
+ }: ToastProviderProps) => {
11
+ const [options, setOptions] = useState<ToastOptions>({});
12
+ const [isVisible, setIsVisible] = useState(false);
13
+ const hideTimeoutRef = useRef<NodeJS.Timeout | null>(null);
14
+
15
+ const show = useCallback((opts: ToastOptions) => {
16
+ setOptions(opts);
17
+ setIsVisible(true);
18
+
19
+ if (hideTimeoutRef.current) clearTimeout(hideTimeoutRef.current);
20
+
21
+ const autoHide = opts.autoHide ?? true;
22
+ const visibilityTime = opts.visibilityTime ?? 3000;
23
+
24
+ if (autoHide) {
25
+ hideTimeoutRef.current = setTimeout(() => {
26
+ setIsVisible(false);
27
+ }, visibilityTime);
28
+ }
29
+ }, []);
30
+
31
+ const hide = useCallback(() => {
32
+ setIsVisible(false);
33
+ if (hideTimeoutRef.current) clearTimeout(hideTimeoutRef.current);
34
+ }, []);
35
+
36
+ useEffect(() => {
37
+ (ToastRefWrapper as any).current = { show, hide };
38
+ }, [show, hide]);
39
+
40
+ const onAnimationEnd = useCallback(
41
+ (visible: boolean) => {
42
+ if (!visible) {
43
+ options.onHide?.();
44
+ } else {
45
+ options.onShow?.();
46
+ }
47
+ },
48
+ [options]
49
+ );
50
+
51
+ return (
52
+ <>
53
+ {children}
54
+ <Toast
55
+ {...options}
56
+ isVisible={isVisible}
57
+ onAnimationEnd={onAnimationEnd}
58
+ providerTheme={theme}
59
+ />
60
+ </>
61
+ );
62
+ };
63
+
64
+ export const ToastService = {
65
+ show: (options: ToastOptions) => ToastRefWrapper.current?.show(options),
66
+ hide: () => ToastRefWrapper.current?.hide(),
67
+ };
package/src/icons.tsx ADDED
@@ -0,0 +1,52 @@
1
+ import { Text, View, StyleSheet } from 'react-native';
2
+
3
+ export const SuccessIcon = () => (
4
+ <View style={[styles.container, styles.success]}>
5
+ <Text style={styles.icon}>✓</Text>
6
+ </View>
7
+ );
8
+
9
+ export const ErrorIcon = () => (
10
+ <View style={[styles.container, styles.error]}>
11
+ <Text style={styles.icon}>✕</Text>
12
+ </View>
13
+ );
14
+
15
+ export const InfoIcon = () => (
16
+ <View style={[styles.container, styles.info]}>
17
+ <Text style={styles.icon}>i</Text>
18
+ </View>
19
+ );
20
+
21
+ export const WarningIcon = () => (
22
+ <View style={[styles.container, styles.warning]}>
23
+ <Text style={styles.icon}>!</Text>
24
+ </View>
25
+ );
26
+
27
+ const styles = StyleSheet.create({
28
+ container: {
29
+ width: 24,
30
+ height: 24,
31
+ borderRadius: 12,
32
+ justifyContent: 'center',
33
+ alignItems: 'center',
34
+ },
35
+ icon: {
36
+ color: '#fff',
37
+ fontSize: 14,
38
+ fontWeight: 'bold',
39
+ },
40
+ success: {
41
+ backgroundColor: '#4ade80',
42
+ },
43
+ error: {
44
+ backgroundColor: '#f87171',
45
+ },
46
+ info: {
47
+ backgroundColor: '#60a5fa',
48
+ },
49
+ warning: {
50
+ backgroundColor: '#fbbf24',
51
+ },
52
+ });
package/src/index.tsx ADDED
@@ -0,0 +1,4 @@
1
+ export * from './types';
2
+ export * from './ToastProvider';
3
+ export { ToastService as Toast } from './ToastProvider';
4
+ export { Toast as CustomToast } from './Toast';
package/src/types.ts ADDED
@@ -0,0 +1,38 @@
1
+ export type ToastType = 'success' | 'error' | 'info' | 'warning';
2
+
3
+ export type ToastPosition = 'top' | 'bottom';
4
+
5
+ export type ToastTheme = 'light' | 'dark' | 'system';
6
+
7
+ export interface ToastOptions {
8
+ type?: ToastType;
9
+ text1?: string;
10
+ text2?: string;
11
+ duration?: number;
12
+ position?: ToastPosition;
13
+ topOffset?: number;
14
+ bottomOffset?: number;
15
+ visibilityTime?: number;
16
+ autoHide?: boolean;
17
+ theme?: ToastTheme;
18
+ onPress?: () => void;
19
+ onShow?: () => void;
20
+ onHide?: () => void;
21
+ customView?: React.ReactNode;
22
+ }
23
+
24
+ export interface ToastProps extends ToastOptions {
25
+ isVisible: boolean;
26
+ providerTheme?: ToastTheme;
27
+ onAnimationEnd: (isVisible: boolean) => void;
28
+ }
29
+
30
+ export interface ToastProviderProps {
31
+ children: React.ReactNode;
32
+ theme?: ToastTheme;
33
+ }
34
+
35
+ export interface ToastRef {
36
+ show: (options: ToastOptions) => void;
37
+ hide: () => void;
38
+ }