expo-toastification 1.1.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/README.md ADDED
@@ -0,0 +1,117 @@
1
+ # 🍞 expo-toastification
2
+
3
+ A simple, elegant and lightweight toast notification library for **Expo** and **React Native**.
4
+
5
+ Inspired by the developer experience of `vue-toastification`, built with **modern React Native and TypeScript best practices**.
6
+
7
+ # 🔧 Requirements
8
+
9
+ - Expo SDK >= 49
10
+ - React >= 18
11
+ - React Native >= 0.71
12
+ - uuid >= 8.3.2
13
+ - react-native-get-random-values
14
+ - react-native-safe-area-context
15
+
16
+ # 📦 Installation
17
+ - pnpm add expo-toastification
18
+
19
+ - npm install expo-toastification
20
+
21
+ - yarn add expo-toastification
22
+
23
+ # 🚀 Basic Setup (Minimum)
24
+
25
+ ### 1- Expo Router
26
+ ```
27
+ import { ToastProvider, configureToasts } from "expo-toastification";
28
+ import { Slot } from "expo-router";
29
+
30
+ configureToasts({
31
+ fontSize: 16,
32
+ textColor: "#0f172a",
33
+ enterDuration: 400,
34
+ exitDuration: 300,
35
+ });
36
+
37
+ export default function RootLayout() {
38
+ return (
39
+ <ToastProvider>
40
+ <Slot />
41
+ </ToastProvider>
42
+ );
43
+ }
44
+ ```
45
+
46
+ ### 2- Classic Expo / React Native
47
+ ```
48
+ import { ToastProvider, configureToasts } from "expo-toastification";
49
+
50
+ configureToasts({
51
+ fontSize: 15,
52
+ textColor: "#111",
53
+ });
54
+
55
+ export default function App() {
56
+ return (
57
+ <ToastProvider>
58
+ {/* your app */}
59
+ </ToastProvider>
60
+ );
61
+ }
62
+ ```
63
+
64
+ # 🧩 Per-Toast Customization
65
+
66
+ Every toast can override global settings.
67
+
68
+ ```
69
+ toast("Custom toast", {
70
+ duration: 5000,
71
+ fontSize: 18,
72
+ textColor: "#facc15",
73
+ });
74
+ ```
75
+
76
+ # ⚙️ Full Toast Options
77
+
78
+ ```
79
+ toast("Example", {
80
+ variant: "success",
81
+ position: "top",
82
+ duration: 4000,
83
+ fontSize: 16,
84
+ textColor: "#ffffff",
85
+ enterDuration: 400,
86
+ exitDuration: 300,
87
+ });
88
+ ```
89
+
90
+ # 🍞 Toasts
91
+ ```
92
+ import { toast } from "expo-toastification";
93
+
94
+ toast("Hello world!");
95
+
96
+ toast.success("Saved successfully");
97
+ toast.error("Something went wrong");
98
+ toast.info("New update available");
99
+ toast.warning("Be careful!");
100
+
101
+ ```
102
+
103
+ # 🛣 Roadmap
104
+
105
+ Planned features:
106
+
107
+ - Swipe to dismiss
108
+
109
+ - Custom renderers
110
+
111
+ - Theming support
112
+
113
+ - Toast groups / scopes
114
+
115
+ - Web support improvements
116
+
117
+ PRs and discussions are welcome.
@@ -0,0 +1,50 @@
1
+ import { PropsWithChildren, JSX } from 'react';
2
+
3
+ type ToastVariant = "default" | "success" | "error" | "info" | "warning";
4
+ type ToastPosition = "top" | "bottom";
5
+ /** Opciones públicas (input del usuario) */
6
+ interface ToastOptions {
7
+ variant?: ToastVariant;
8
+ duration?: number;
9
+ position?: ToastPosition;
10
+ /** Visual */
11
+ fontSize?: number;
12
+ textColor?: string;
13
+ /** Animation */
14
+ enterDuration?: number;
15
+ exitDuration?: number;
16
+ }
17
+ /** Opciones internas ya normalizadas */
18
+ interface ToastResolvedOptions {
19
+ readonly variant: ToastVariant;
20
+ readonly duration: number;
21
+ readonly position: ToastPosition;
22
+ readonly fontSize: number;
23
+ readonly textColor: string;
24
+ readonly enterDuration: number;
25
+ readonly exitDuration: number;
26
+ }
27
+ /** Entidad interna del sistema */
28
+ interface ToastInternal {
29
+ readonly id: string;
30
+ readonly message: string;
31
+ readonly options: ToastResolvedOptions;
32
+ }
33
+
34
+ type ToastFn = (message: string, options?: ToastOptions) => void;
35
+ declare const toast: ToastFn & {
36
+ success: (msg: string, opts?: ToastOptions) => void;
37
+ error: (msg: string, opts?: ToastOptions) => void;
38
+ info: (msg: string, opts?: ToastOptions) => void;
39
+ warning: (msg: string, opts?: ToastOptions) => void;
40
+ };
41
+
42
+ type ToastGlobalConfig = Omit<ToastResolvedOptions, "variant" | "position" | "duration">;
43
+ declare const toastGlobalConfig: ToastGlobalConfig;
44
+
45
+ type ToastUserConfig = Partial<typeof toastGlobalConfig>;
46
+ declare function configureToasts(config: ToastUserConfig): void;
47
+
48
+ declare function ToastProvider(props: PropsWithChildren): JSX.Element;
49
+
50
+ export { type ToastInternal, type ToastOptions, type ToastPosition, ToastProvider, type ToastResolvedOptions, type ToastVariant, configureToasts, toast };
@@ -0,0 +1,50 @@
1
+ import { PropsWithChildren, JSX } from 'react';
2
+
3
+ type ToastVariant = "default" | "success" | "error" | "info" | "warning";
4
+ type ToastPosition = "top" | "bottom";
5
+ /** Opciones públicas (input del usuario) */
6
+ interface ToastOptions {
7
+ variant?: ToastVariant;
8
+ duration?: number;
9
+ position?: ToastPosition;
10
+ /** Visual */
11
+ fontSize?: number;
12
+ textColor?: string;
13
+ /** Animation */
14
+ enterDuration?: number;
15
+ exitDuration?: number;
16
+ }
17
+ /** Opciones internas ya normalizadas */
18
+ interface ToastResolvedOptions {
19
+ readonly variant: ToastVariant;
20
+ readonly duration: number;
21
+ readonly position: ToastPosition;
22
+ readonly fontSize: number;
23
+ readonly textColor: string;
24
+ readonly enterDuration: number;
25
+ readonly exitDuration: number;
26
+ }
27
+ /** Entidad interna del sistema */
28
+ interface ToastInternal {
29
+ readonly id: string;
30
+ readonly message: string;
31
+ readonly options: ToastResolvedOptions;
32
+ }
33
+
34
+ type ToastFn = (message: string, options?: ToastOptions) => void;
35
+ declare const toast: ToastFn & {
36
+ success: (msg: string, opts?: ToastOptions) => void;
37
+ error: (msg: string, opts?: ToastOptions) => void;
38
+ info: (msg: string, opts?: ToastOptions) => void;
39
+ warning: (msg: string, opts?: ToastOptions) => void;
40
+ };
41
+
42
+ type ToastGlobalConfig = Omit<ToastResolvedOptions, "variant" | "position" | "duration">;
43
+ declare const toastGlobalConfig: ToastGlobalConfig;
44
+
45
+ type ToastUserConfig = Partial<typeof toastGlobalConfig>;
46
+ declare function configureToasts(config: ToastUserConfig): void;
47
+
48
+ declare function ToastProvider(props: PropsWithChildren): JSX.Element;
49
+
50
+ export { type ToastInternal, type ToastOptions, type ToastPosition, ToastProvider, type ToastResolvedOptions, type ToastVariant, configureToasts, toast };
package/dist/index.js ADDED
@@ -0,0 +1,312 @@
1
+ "use strict";
2
+ var __defProp = Object.defineProperty;
3
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
4
+ var __getOwnPropNames = Object.getOwnPropertyNames;
5
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
6
+ var __export = (target, all) => {
7
+ for (var name in all)
8
+ __defProp(target, name, { get: all[name], enumerable: true });
9
+ };
10
+ var __copyProps = (to, from, except, desc) => {
11
+ if (from && typeof from === "object" || typeof from === "function") {
12
+ for (let key of __getOwnPropNames(from))
13
+ if (!__hasOwnProp.call(to, key) && key !== except)
14
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
15
+ }
16
+ return to;
17
+ };
18
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
19
+
20
+ // src/index.ts
21
+ var index_exports = {};
22
+ __export(index_exports, {
23
+ ToastProvider: () => ToastProvider,
24
+ configureToasts: () => configureToasts,
25
+ toast: () => toast
26
+ });
27
+ module.exports = __toCommonJS(index_exports);
28
+
29
+ // src/core/configuration.ts
30
+ var toastGlobalConfig = {
31
+ fontSize: 14,
32
+ textColor: "#ffffff",
33
+ enterDuration: 250,
34
+ exitDuration: 200
35
+ };
36
+
37
+ // src/core/normalize-options.ts
38
+ var DEFAULTS = {
39
+ variant: "default",
40
+ position: "top",
41
+ duration: 3e3,
42
+ fontSize: 14,
43
+ textColor: "#ffffff",
44
+ enterDuration: 250,
45
+ exitDuration: 200
46
+ };
47
+ function normalizeOptions(options) {
48
+ var _a, _b, _c, _d, _e, _f, _g;
49
+ return {
50
+ variant: (_a = options == null ? void 0 : options.variant) != null ? _a : DEFAULTS.variant,
51
+ position: (_b = options == null ? void 0 : options.position) != null ? _b : DEFAULTS.position,
52
+ duration: (_c = options == null ? void 0 : options.duration) != null ? _c : DEFAULTS.duration,
53
+ fontSize: (_d = options == null ? void 0 : options.fontSize) != null ? _d : toastGlobalConfig.fontSize,
54
+ textColor: (_e = options == null ? void 0 : options.textColor) != null ? _e : toastGlobalConfig.textColor,
55
+ enterDuration: (_f = options == null ? void 0 : options.enterDuration) != null ? _f : toastGlobalConfig.enterDuration,
56
+ exitDuration: (_g = options == null ? void 0 : options.exitDuration) != null ? _g : toastGlobalConfig.exitDuration
57
+ };
58
+ }
59
+
60
+ // src/core/toast-manager.ts
61
+ var import_uuid = require("uuid");
62
+ var ToastManager = class {
63
+ constructor() {
64
+ this.subscribers = /* @__PURE__ */ new Set();
65
+ this.queue = [];
66
+ this.active = [];
67
+ this.maxToasts = 3;
68
+ }
69
+ subscribe(fn) {
70
+ this.subscribers.add(fn);
71
+ fn(this.active);
72
+ return () => this.subscribers.delete(fn);
73
+ }
74
+ publish(message, options) {
75
+ const toast2 = {
76
+ id: (0, import_uuid.v4)(),
77
+ message,
78
+ options: normalizeOptions(options)
79
+ };
80
+ this.queue.push(toast2);
81
+ this.flush();
82
+ }
83
+ remove(id) {
84
+ this.active = this.active.filter((t) => t.id !== id);
85
+ this.flush();
86
+ }
87
+ configure(config) {
88
+ if (typeof config.maxToasts === "number" && config.maxToasts > 0) {
89
+ this.maxToasts = config.maxToasts;
90
+ this.flush();
91
+ }
92
+ }
93
+ flush() {
94
+ const availableSlots = this.maxToasts - this.active.length;
95
+ if (availableSlots <= 0) return;
96
+ if (this.queue.length === 0) return;
97
+ const next = this.queue.splice(0, availableSlots);
98
+ this.active = [...this.active, ...next];
99
+ this.notify();
100
+ }
101
+ notify() {
102
+ const snapshot = [...this.active];
103
+ this.subscribers.forEach((fn) => fn(snapshot));
104
+ }
105
+ };
106
+ var toastManager = new ToastManager();
107
+
108
+ // src/api/toast.ts
109
+ var toast = Object.assign(
110
+ ((message, options) => toastManager.publish(message, options)),
111
+ {
112
+ success: (msg, opts) => toastManager.publish(msg, { ...opts, variant: "success" }),
113
+ error: (msg, opts) => toastManager.publish(msg, { ...opts, variant: "error" }),
114
+ info: (msg, opts) => toastManager.publish(msg, { ...opts, variant: "info" }),
115
+ warning: (msg, opts) => toastManager.publish(msg, { ...opts, variant: "warning" })
116
+ }
117
+ );
118
+
119
+ // src/api/configure.ts
120
+ function configureToasts(config) {
121
+ Object.assign(toastGlobalConfig, config);
122
+ }
123
+
124
+ // src/components/ToastHost.tsx
125
+ var import_react2 = require("react");
126
+ var import_react_native2 = require("react-native");
127
+ var import_react_native_safe_area_context = require("react-native-safe-area-context");
128
+
129
+ // src/components/ToastItem.tsx
130
+ var import_react = require("react");
131
+ var import_react_native = require("react-native");
132
+ var import_jsx_runtime = require("react/jsx-runtime");
133
+ var SCREEN_WIDTH = import_react_native.Dimensions.get("window").width;
134
+ function ToastItem(props) {
135
+ const { toast: toast2, onHide } = props;
136
+ const { options } = toast2;
137
+ const translateX = (0, import_react.useRef)(
138
+ new import_react_native.Animated.Value(SCREEN_WIDTH)
139
+ ).current;
140
+ const opacity = (0, import_react.useRef)(
141
+ new import_react_native.Animated.Value(0)
142
+ ).current;
143
+ (0, import_react.useEffect)(() => {
144
+ import_react_native.Animated.parallel([
145
+ import_react_native.Animated.timing(translateX, {
146
+ toValue: 0,
147
+ duration: options.enterDuration,
148
+ useNativeDriver: true
149
+ }),
150
+ import_react_native.Animated.timing(opacity, {
151
+ toValue: 1,
152
+ duration: options.enterDuration,
153
+ useNativeDriver: true
154
+ })
155
+ ]).start();
156
+ const timeout = setTimeout(() => {
157
+ import_react_native.Animated.parallel([
158
+ import_react_native.Animated.timing(translateX, {
159
+ toValue: SCREEN_WIDTH,
160
+ duration: options.exitDuration,
161
+ useNativeDriver: true
162
+ }),
163
+ import_react_native.Animated.timing(opacity, {
164
+ toValue: 0,
165
+ duration: options.exitDuration,
166
+ useNativeDriver: true
167
+ })
168
+ ]).start(({ finished }) => {
169
+ if (finished) {
170
+ onHide();
171
+ }
172
+ });
173
+ }, options.duration);
174
+ return () => {
175
+ clearTimeout(timeout);
176
+ };
177
+ }, [
178
+ translateX,
179
+ opacity,
180
+ options.duration,
181
+ options.enterDuration,
182
+ options.exitDuration,
183
+ onHide
184
+ ]);
185
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
186
+ import_react_native.Animated.View,
187
+ {
188
+ style: [
189
+ styles.toast,
190
+ styles[options.variant],
191
+ {
192
+ opacity,
193
+ transform: [{ translateX }]
194
+ }
195
+ ],
196
+ children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
197
+ import_react_native.Text,
198
+ {
199
+ style: [
200
+ styles.text,
201
+ {
202
+ fontSize: options.fontSize,
203
+ color: options.textColor
204
+ }
205
+ ],
206
+ children: toast2.message
207
+ }
208
+ )
209
+ }
210
+ );
211
+ }
212
+ var styles = import_react_native.StyleSheet.create({
213
+ toast: {
214
+ paddingHorizontal: 16,
215
+ paddingVertical: 12,
216
+ borderRadius: 10,
217
+ marginBottom: 8,
218
+ minWidth: "70%"
219
+ },
220
+ text: {
221
+ textAlign: "center",
222
+ fontWeight: "500"
223
+ },
224
+ default: {
225
+ backgroundColor: "#333"
226
+ },
227
+ success: {
228
+ backgroundColor: "#22c55e"
229
+ },
230
+ error: {
231
+ backgroundColor: "#ef4444"
232
+ },
233
+ info: {
234
+ backgroundColor: "#3b82f6"
235
+ },
236
+ warning: {
237
+ backgroundColor: "#f59e0b"
238
+ }
239
+ });
240
+
241
+ // src/components/ToastHost.tsx
242
+ var import_jsx_runtime2 = require("react/jsx-runtime");
243
+ function ToastHost() {
244
+ const insets = (0, import_react_native_safe_area_context.useSafeAreaInsets)();
245
+ const [toasts, setToasts] = (0, import_react2.useState)([]);
246
+ (0, import_react2.useEffect)(() => {
247
+ return toastManager.subscribe(setToasts);
248
+ }, []);
249
+ const topToasts = (0, import_react2.useMemo)(
250
+ () => toasts.filter((t) => t.options.position === "top"),
251
+ [toasts]
252
+ );
253
+ const bottomToasts = (0, import_react2.useMemo)(
254
+ () => toasts.filter((t) => t.options.position === "bottom"),
255
+ [toasts]
256
+ );
257
+ return /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(import_react_native2.View, { pointerEvents: "box-none", style: import_react_native2.StyleSheet.absoluteFill, children: [
258
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
259
+ import_react_native2.View,
260
+ {
261
+ pointerEvents: "box-none",
262
+ style: [styles2.stack, { top: insets.top + 12 }],
263
+ children: topToasts.map((toast2) => /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
264
+ ToastItem,
265
+ {
266
+ toast: toast2,
267
+ onHide: () => toastManager.remove(toast2.id)
268
+ },
269
+ toast2.id
270
+ ))
271
+ }
272
+ ),
273
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
274
+ import_react_native2.View,
275
+ {
276
+ pointerEvents: "box-none",
277
+ style: [styles2.stack, { bottom: insets.bottom + 12 }],
278
+ children: bottomToasts.map((toast2) => /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
279
+ ToastItem,
280
+ {
281
+ toast: toast2,
282
+ onHide: () => toastManager.remove(toast2.id)
283
+ },
284
+ toast2.id
285
+ ))
286
+ }
287
+ )
288
+ ] });
289
+ }
290
+ var styles2 = import_react_native2.StyleSheet.create({
291
+ stack: {
292
+ position: "absolute",
293
+ width: "100%",
294
+ alignItems: "center"
295
+ }
296
+ });
297
+
298
+ // src/components/ToastProvider.tsx
299
+ var import_jsx_runtime3 = require("react/jsx-runtime");
300
+ function ToastProvider(props) {
301
+ const { children } = props;
302
+ return /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(import_jsx_runtime3.Fragment, { children: [
303
+ children,
304
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(ToastHost, {})
305
+ ] });
306
+ }
307
+ // Annotate the CommonJS export names for ESM import in node:
308
+ 0 && (module.exports = {
309
+ ToastProvider,
310
+ configureToasts,
311
+ toast
312
+ });
package/dist/index.mjs ADDED
@@ -0,0 +1,288 @@
1
+ // src/core/configuration.ts
2
+ var toastGlobalConfig = {
3
+ fontSize: 14,
4
+ textColor: "#ffffff",
5
+ enterDuration: 250,
6
+ exitDuration: 200
7
+ };
8
+
9
+ // src/core/normalize-options.ts
10
+ var DEFAULTS = {
11
+ variant: "default",
12
+ position: "top",
13
+ duration: 3e3,
14
+ fontSize: 14,
15
+ textColor: "#ffffff",
16
+ enterDuration: 250,
17
+ exitDuration: 200
18
+ };
19
+ function normalizeOptions(options) {
20
+ var _a, _b, _c, _d, _e, _f, _g;
21
+ return {
22
+ variant: (_a = options == null ? void 0 : options.variant) != null ? _a : DEFAULTS.variant,
23
+ position: (_b = options == null ? void 0 : options.position) != null ? _b : DEFAULTS.position,
24
+ duration: (_c = options == null ? void 0 : options.duration) != null ? _c : DEFAULTS.duration,
25
+ fontSize: (_d = options == null ? void 0 : options.fontSize) != null ? _d : toastGlobalConfig.fontSize,
26
+ textColor: (_e = options == null ? void 0 : options.textColor) != null ? _e : toastGlobalConfig.textColor,
27
+ enterDuration: (_f = options == null ? void 0 : options.enterDuration) != null ? _f : toastGlobalConfig.enterDuration,
28
+ exitDuration: (_g = options == null ? void 0 : options.exitDuration) != null ? _g : toastGlobalConfig.exitDuration
29
+ };
30
+ }
31
+
32
+ // src/core/toast-manager.ts
33
+ import { v4 as uuidv4 } from "uuid";
34
+ var ToastManager = class {
35
+ constructor() {
36
+ this.subscribers = /* @__PURE__ */ new Set();
37
+ this.queue = [];
38
+ this.active = [];
39
+ this.maxToasts = 3;
40
+ }
41
+ subscribe(fn) {
42
+ this.subscribers.add(fn);
43
+ fn(this.active);
44
+ return () => this.subscribers.delete(fn);
45
+ }
46
+ publish(message, options) {
47
+ const toast2 = {
48
+ id: uuidv4(),
49
+ message,
50
+ options: normalizeOptions(options)
51
+ };
52
+ this.queue.push(toast2);
53
+ this.flush();
54
+ }
55
+ remove(id) {
56
+ this.active = this.active.filter((t) => t.id !== id);
57
+ this.flush();
58
+ }
59
+ configure(config) {
60
+ if (typeof config.maxToasts === "number" && config.maxToasts > 0) {
61
+ this.maxToasts = config.maxToasts;
62
+ this.flush();
63
+ }
64
+ }
65
+ flush() {
66
+ const availableSlots = this.maxToasts - this.active.length;
67
+ if (availableSlots <= 0) return;
68
+ if (this.queue.length === 0) return;
69
+ const next = this.queue.splice(0, availableSlots);
70
+ this.active = [...this.active, ...next];
71
+ this.notify();
72
+ }
73
+ notify() {
74
+ const snapshot = [...this.active];
75
+ this.subscribers.forEach((fn) => fn(snapshot));
76
+ }
77
+ };
78
+ var toastManager = new ToastManager();
79
+
80
+ // src/api/toast.ts
81
+ var toast = Object.assign(
82
+ ((message, options) => toastManager.publish(message, options)),
83
+ {
84
+ success: (msg, opts) => toastManager.publish(msg, { ...opts, variant: "success" }),
85
+ error: (msg, opts) => toastManager.publish(msg, { ...opts, variant: "error" }),
86
+ info: (msg, opts) => toastManager.publish(msg, { ...opts, variant: "info" }),
87
+ warning: (msg, opts) => toastManager.publish(msg, { ...opts, variant: "warning" })
88
+ }
89
+ );
90
+
91
+ // src/api/configure.ts
92
+ function configureToasts(config) {
93
+ Object.assign(toastGlobalConfig, config);
94
+ }
95
+
96
+ // src/components/ToastHost.tsx
97
+ import { useEffect as useEffect2, useMemo, useState } from "react";
98
+ import { StyleSheet as StyleSheet2, View } from "react-native";
99
+ import { useSafeAreaInsets } from "react-native-safe-area-context";
100
+
101
+ // src/components/ToastItem.tsx
102
+ import { useEffect, useRef } from "react";
103
+ import {
104
+ Animated,
105
+ StyleSheet,
106
+ Text,
107
+ Dimensions
108
+ } from "react-native";
109
+ import { jsx } from "react/jsx-runtime";
110
+ var SCREEN_WIDTH = Dimensions.get("window").width;
111
+ function ToastItem(props) {
112
+ const { toast: toast2, onHide } = props;
113
+ const { options } = toast2;
114
+ const translateX = useRef(
115
+ new Animated.Value(SCREEN_WIDTH)
116
+ ).current;
117
+ const opacity = useRef(
118
+ new Animated.Value(0)
119
+ ).current;
120
+ useEffect(() => {
121
+ Animated.parallel([
122
+ Animated.timing(translateX, {
123
+ toValue: 0,
124
+ duration: options.enterDuration,
125
+ useNativeDriver: true
126
+ }),
127
+ Animated.timing(opacity, {
128
+ toValue: 1,
129
+ duration: options.enterDuration,
130
+ useNativeDriver: true
131
+ })
132
+ ]).start();
133
+ const timeout = setTimeout(() => {
134
+ Animated.parallel([
135
+ Animated.timing(translateX, {
136
+ toValue: SCREEN_WIDTH,
137
+ duration: options.exitDuration,
138
+ useNativeDriver: true
139
+ }),
140
+ Animated.timing(opacity, {
141
+ toValue: 0,
142
+ duration: options.exitDuration,
143
+ useNativeDriver: true
144
+ })
145
+ ]).start(({ finished }) => {
146
+ if (finished) {
147
+ onHide();
148
+ }
149
+ });
150
+ }, options.duration);
151
+ return () => {
152
+ clearTimeout(timeout);
153
+ };
154
+ }, [
155
+ translateX,
156
+ opacity,
157
+ options.duration,
158
+ options.enterDuration,
159
+ options.exitDuration,
160
+ onHide
161
+ ]);
162
+ return /* @__PURE__ */ jsx(
163
+ Animated.View,
164
+ {
165
+ style: [
166
+ styles.toast,
167
+ styles[options.variant],
168
+ {
169
+ opacity,
170
+ transform: [{ translateX }]
171
+ }
172
+ ],
173
+ children: /* @__PURE__ */ jsx(
174
+ Text,
175
+ {
176
+ style: [
177
+ styles.text,
178
+ {
179
+ fontSize: options.fontSize,
180
+ color: options.textColor
181
+ }
182
+ ],
183
+ children: toast2.message
184
+ }
185
+ )
186
+ }
187
+ );
188
+ }
189
+ var styles = StyleSheet.create({
190
+ toast: {
191
+ paddingHorizontal: 16,
192
+ paddingVertical: 12,
193
+ borderRadius: 10,
194
+ marginBottom: 8,
195
+ minWidth: "70%"
196
+ },
197
+ text: {
198
+ textAlign: "center",
199
+ fontWeight: "500"
200
+ },
201
+ default: {
202
+ backgroundColor: "#333"
203
+ },
204
+ success: {
205
+ backgroundColor: "#22c55e"
206
+ },
207
+ error: {
208
+ backgroundColor: "#ef4444"
209
+ },
210
+ info: {
211
+ backgroundColor: "#3b82f6"
212
+ },
213
+ warning: {
214
+ backgroundColor: "#f59e0b"
215
+ }
216
+ });
217
+
218
+ // src/components/ToastHost.tsx
219
+ import { jsx as jsx2, jsxs } from "react/jsx-runtime";
220
+ function ToastHost() {
221
+ const insets = useSafeAreaInsets();
222
+ const [toasts, setToasts] = useState([]);
223
+ useEffect2(() => {
224
+ return toastManager.subscribe(setToasts);
225
+ }, []);
226
+ const topToasts = useMemo(
227
+ () => toasts.filter((t) => t.options.position === "top"),
228
+ [toasts]
229
+ );
230
+ const bottomToasts = useMemo(
231
+ () => toasts.filter((t) => t.options.position === "bottom"),
232
+ [toasts]
233
+ );
234
+ return /* @__PURE__ */ jsxs(View, { pointerEvents: "box-none", style: StyleSheet2.absoluteFill, children: [
235
+ /* @__PURE__ */ jsx2(
236
+ View,
237
+ {
238
+ pointerEvents: "box-none",
239
+ style: [styles2.stack, { top: insets.top + 12 }],
240
+ children: topToasts.map((toast2) => /* @__PURE__ */ jsx2(
241
+ ToastItem,
242
+ {
243
+ toast: toast2,
244
+ onHide: () => toastManager.remove(toast2.id)
245
+ },
246
+ toast2.id
247
+ ))
248
+ }
249
+ ),
250
+ /* @__PURE__ */ jsx2(
251
+ View,
252
+ {
253
+ pointerEvents: "box-none",
254
+ style: [styles2.stack, { bottom: insets.bottom + 12 }],
255
+ children: bottomToasts.map((toast2) => /* @__PURE__ */ jsx2(
256
+ ToastItem,
257
+ {
258
+ toast: toast2,
259
+ onHide: () => toastManager.remove(toast2.id)
260
+ },
261
+ toast2.id
262
+ ))
263
+ }
264
+ )
265
+ ] });
266
+ }
267
+ var styles2 = StyleSheet2.create({
268
+ stack: {
269
+ position: "absolute",
270
+ width: "100%",
271
+ alignItems: "center"
272
+ }
273
+ });
274
+
275
+ // src/components/ToastProvider.tsx
276
+ import { Fragment, jsx as jsx3, jsxs as jsxs2 } from "react/jsx-runtime";
277
+ function ToastProvider(props) {
278
+ const { children } = props;
279
+ return /* @__PURE__ */ jsxs2(Fragment, { children: [
280
+ children,
281
+ /* @__PURE__ */ jsx3(ToastHost, {})
282
+ ] });
283
+ }
284
+ export {
285
+ ToastProvider,
286
+ configureToasts,
287
+ toast
288
+ };
package/package.json ADDED
@@ -0,0 +1,35 @@
1
+ {
2
+ "name": "expo-toastification",
3
+ "version": "1.1.0",
4
+ "description": "Simple toast notifications for Expo & React Native",
5
+ "main": "dist/index.js",
6
+ "types": "dist/index.d.ts",
7
+ "files": [
8
+ "dist"
9
+ ],
10
+ "scripts": {
11
+ "build": "tsup src/index.ts --dts --format esm,cjs",
12
+ "prepublishOnly": "pnpm build"
13
+ },
14
+ "peerDependencies": {
15
+ "expo": "*",
16
+ "react": "*",
17
+ "react-native": "*",
18
+ "react-native-get-random-values": "*",
19
+ "react-native-safe-area-context": "*",
20
+ "uuid": "*"
21
+ },
22
+ "keywords": [
23
+ "expo",
24
+ "toast",
25
+ "react",
26
+ "react-native",
27
+ "notification"
28
+ ],
29
+ "license": "MIT",
30
+ "devDependencies": {
31
+ "@types/react": "^19.2.8",
32
+ "tsup": "^8.5.1",
33
+ "typescript": "^5.9.3"
34
+ }
35
+ }