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 +117 -0
- package/dist/index.d.mts +50 -0
- package/dist/index.d.ts +50 -0
- package/dist/index.js +312 -0
- package/dist/index.mjs +288 -0
- package/package.json +35 -0
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.
|
package/dist/index.d.mts
ADDED
|
@@ -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.d.ts
ADDED
|
@@ -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
|
+
}
|