react-native-bread 0.5.2 → 0.6.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 +27 -11
- package/lib/commonjs/constants.js +25 -1
- package/lib/commonjs/icons/CloseIcon.js +22 -1
- package/lib/commonjs/icons/GreenCheck.js +27 -1
- package/lib/commonjs/icons/InfoIcon.js +24 -1
- package/lib/commonjs/icons/RedX.js +27 -1
- package/lib/commonjs/icons/index.js +34 -1
- package/lib/commonjs/index.js +65 -1
- package/lib/commonjs/pool.js +53 -1
- package/lib/commonjs/toast-api.js +127 -1
- package/lib/commonjs/toast-icons.js +80 -1
- package/lib/commonjs/toast-provider.js +109 -1
- package/lib/commonjs/toast-store.js +252 -1
- package/lib/commonjs/toast.js +351 -1
- package/lib/commonjs/types.js +6 -1
- package/lib/commonjs/use-toast-state.js +72 -1
- package/lib/module/constants.js +21 -1
- package/lib/module/icons/CloseIcon.js +16 -1
- package/lib/module/icons/GreenCheck.js +21 -1
- package/lib/module/icons/InfoIcon.js +18 -1
- package/lib/module/icons/RedX.js +21 -1
- package/lib/module/icons/index.js +7 -1
- package/lib/module/index.js +14 -1
- package/lib/module/pool.js +47 -1
- package/lib/module/toast-api.js +123 -1
- package/lib/module/toast-icons.js +74 -1
- package/lib/module/toast-provider.js +104 -1
- package/lib/module/toast-store.js +248 -1
- package/lib/module/toast.js +345 -1
- package/lib/module/types.js +4 -1
- package/lib/module/use-toast-state.js +67 -1
- package/lib/typescript/index.d.ts +1 -1
- package/lib/typescript/toast-provider.d.ts +28 -0
- package/package.json +7 -3
|
@@ -1 +1,109 @@
|
|
|
1
|
-
"use strict";
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
Object.defineProperty(exports, "__esModule", {
|
|
4
|
+
value: true
|
|
5
|
+
});
|
|
6
|
+
exports.BreadLoaf = BreadLoaf;
|
|
7
|
+
exports.ToastPortal = ToastPortal;
|
|
8
|
+
var _react = require("react");
|
|
9
|
+
var _reactNative = require("react-native");
|
|
10
|
+
var _reactNativeScreens = require("react-native-screens");
|
|
11
|
+
var _toast = require("./toast.js");
|
|
12
|
+
var _toastStore = require("./toast-store.js");
|
|
13
|
+
var _jsxRuntime = require("react/jsx-runtime");
|
|
14
|
+
function ToastContent() {
|
|
15
|
+
return /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.View, {
|
|
16
|
+
style: styles.container,
|
|
17
|
+
pointerEvents: "box-none",
|
|
18
|
+
children: /*#__PURE__*/(0, _jsxRuntime.jsx)(_toast.ToastContainer, {})
|
|
19
|
+
});
|
|
20
|
+
}
|
|
21
|
+
/**
|
|
22
|
+
* Toast component that enables toast notifications in your app.
|
|
23
|
+
* Add `<BreadLoaf />` to your root layout to start showing toasts.
|
|
24
|
+
*
|
|
25
|
+
* @example
|
|
26
|
+
* ```tsx
|
|
27
|
+
* import { BreadLoaf } from 'react-native-bread';
|
|
28
|
+
*
|
|
29
|
+
* // Basic usage - add to your root layout
|
|
30
|
+
* export default function RootLayout() {
|
|
31
|
+
* return (
|
|
32
|
+
* <>
|
|
33
|
+
* <Stack />
|
|
34
|
+
* <BreadLoaf />
|
|
35
|
+
* </>
|
|
36
|
+
* );
|
|
37
|
+
* }
|
|
38
|
+
*
|
|
39
|
+
* // With configuration
|
|
40
|
+
* <BreadLoaf
|
|
41
|
+
* config={{
|
|
42
|
+
* position: 'bottom',
|
|
43
|
+
* stacking: false,
|
|
44
|
+
* defaultDuration: 5000,
|
|
45
|
+
* colors: {
|
|
46
|
+
* success: { accent: '#22c55e', background: '#f0fdf4' },
|
|
47
|
+
* error: { accent: '#ef4444', background: '#fef2f2' },
|
|
48
|
+
* },
|
|
49
|
+
* toastStyle: { borderRadius: 12 },
|
|
50
|
+
* }}
|
|
51
|
+
* />
|
|
52
|
+
* ```
|
|
53
|
+
*/
|
|
54
|
+
function BreadLoaf({
|
|
55
|
+
config
|
|
56
|
+
}) {
|
|
57
|
+
(0, _react.useEffect)(() => {
|
|
58
|
+
_toastStore.toastStore.setConfig(config);
|
|
59
|
+
return () => {
|
|
60
|
+
_toastStore.toastStore.setConfig(undefined);
|
|
61
|
+
};
|
|
62
|
+
}, [config]);
|
|
63
|
+
|
|
64
|
+
// iOS: use FullWindowOverlay to render above native modals
|
|
65
|
+
if (_reactNative.Platform.OS === "ios") {
|
|
66
|
+
return /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNativeScreens.FullWindowOverlay, {
|
|
67
|
+
children: /*#__PURE__*/(0, _jsxRuntime.jsx)(ToastContent, {})
|
|
68
|
+
});
|
|
69
|
+
}
|
|
70
|
+
return /*#__PURE__*/(0, _jsxRuntime.jsx)(ToastContent, {});
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
/**
|
|
74
|
+
* Lightweight toast renderer for use inside modal screens.
|
|
75
|
+
*
|
|
76
|
+
* On Android, native modals render above the root React view, so toasts from
|
|
77
|
+
* the main `<BreadLoaf />` won't be visible. Add `<ToastPortal />` inside your
|
|
78
|
+
* modal layouts to show toasts above modal content.
|
|
79
|
+
*
|
|
80
|
+
* This component only renders toasts - it does not accept configuration.
|
|
81
|
+
* All styling/behavior is inherited from your root `<BreadLoaf />` config.
|
|
82
|
+
*
|
|
83
|
+
* @example
|
|
84
|
+
* ```tsx
|
|
85
|
+
* // app/(modal)/_layout.tsx
|
|
86
|
+
* import { Stack } from 'expo-router';
|
|
87
|
+
* import { ToastPortal } from 'react-native-bread';
|
|
88
|
+
* import { Platform } from 'react-native';
|
|
89
|
+
*
|
|
90
|
+
* export default function ModalLayout() {
|
|
91
|
+
* return (
|
|
92
|
+
* <>
|
|
93
|
+
* <Stack screenOptions={{ headerShown: false }} />
|
|
94
|
+
* {Platform.OS === 'android' && <ToastPortal />}
|
|
95
|
+
* </>
|
|
96
|
+
* );
|
|
97
|
+
* }
|
|
98
|
+
* ```
|
|
99
|
+
*/
|
|
100
|
+
function ToastPortal() {
|
|
101
|
+
return /*#__PURE__*/(0, _jsxRuntime.jsx)(ToastContent, {});
|
|
102
|
+
}
|
|
103
|
+
const styles = _reactNative.StyleSheet.create({
|
|
104
|
+
container: {
|
|
105
|
+
..._reactNative.StyleSheet.absoluteFillObject,
|
|
106
|
+
zIndex: 9999
|
|
107
|
+
}
|
|
108
|
+
});
|
|
109
|
+
//# sourceMappingURL=toast-provider.js.map
|
|
@@ -1 +1,252 @@
|
|
|
1
|
-
"use strict";
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
Object.defineProperty(exports, "__esModule", {
|
|
4
|
+
value: true
|
|
5
|
+
});
|
|
6
|
+
exports.toastStore = void 0;
|
|
7
|
+
const EXIT_DURATION = 350;
|
|
8
|
+
const DEFAULT_THEME = {
|
|
9
|
+
position: "top",
|
|
10
|
+
offset: 0,
|
|
11
|
+
rtl: false,
|
|
12
|
+
stacking: true,
|
|
13
|
+
maxStack: 3,
|
|
14
|
+
dismissible: true,
|
|
15
|
+
showCloseButton: true,
|
|
16
|
+
colors: {
|
|
17
|
+
success: {
|
|
18
|
+
accent: "#28B770",
|
|
19
|
+
background: "#FFFFFF"
|
|
20
|
+
},
|
|
21
|
+
error: {
|
|
22
|
+
accent: "#F05964",
|
|
23
|
+
background: "#FFFFFF"
|
|
24
|
+
},
|
|
25
|
+
info: {
|
|
26
|
+
accent: "#EDBE43",
|
|
27
|
+
background: "#FFFFFF"
|
|
28
|
+
},
|
|
29
|
+
loading: {
|
|
30
|
+
accent: "#232323",
|
|
31
|
+
background: "#FFFFFF"
|
|
32
|
+
}
|
|
33
|
+
},
|
|
34
|
+
icons: {},
|
|
35
|
+
toastStyle: {},
|
|
36
|
+
titleStyle: {},
|
|
37
|
+
descriptionStyle: {},
|
|
38
|
+
defaultDuration: 4000
|
|
39
|
+
};
|
|
40
|
+
function mergeConfig(config) {
|
|
41
|
+
if (!config) return DEFAULT_THEME;
|
|
42
|
+
const mergedColors = {
|
|
43
|
+
...DEFAULT_THEME.colors
|
|
44
|
+
};
|
|
45
|
+
if (config.colors) {
|
|
46
|
+
for (const type of Object.keys(config.colors)) {
|
|
47
|
+
const userColors = config.colors[type];
|
|
48
|
+
if (userColors) {
|
|
49
|
+
mergedColors[type] = {
|
|
50
|
+
...DEFAULT_THEME.colors[type],
|
|
51
|
+
...userColors
|
|
52
|
+
};
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
return {
|
|
57
|
+
position: config.position ?? DEFAULT_THEME.position,
|
|
58
|
+
offset: config.offset ?? DEFAULT_THEME.offset,
|
|
59
|
+
rtl: config.rtl ?? DEFAULT_THEME.rtl,
|
|
60
|
+
stacking: config.stacking ?? DEFAULT_THEME.stacking,
|
|
61
|
+
maxStack: config.maxStack ?? DEFAULT_THEME.maxStack,
|
|
62
|
+
dismissible: config.dismissible ?? DEFAULT_THEME.dismissible,
|
|
63
|
+
showCloseButton: config.showCloseButton ?? DEFAULT_THEME.showCloseButton,
|
|
64
|
+
colors: mergedColors,
|
|
65
|
+
icons: {
|
|
66
|
+
...DEFAULT_THEME.icons,
|
|
67
|
+
...config.icons
|
|
68
|
+
},
|
|
69
|
+
toastStyle: {
|
|
70
|
+
...DEFAULT_THEME.toastStyle,
|
|
71
|
+
...config.toastStyle
|
|
72
|
+
},
|
|
73
|
+
titleStyle: {
|
|
74
|
+
...DEFAULT_THEME.titleStyle,
|
|
75
|
+
...config.titleStyle
|
|
76
|
+
},
|
|
77
|
+
descriptionStyle: {
|
|
78
|
+
...DEFAULT_THEME.descriptionStyle,
|
|
79
|
+
...config.descriptionStyle
|
|
80
|
+
},
|
|
81
|
+
defaultDuration: config.defaultDuration ?? DEFAULT_THEME.defaultDuration
|
|
82
|
+
};
|
|
83
|
+
}
|
|
84
|
+
class ToastStore {
|
|
85
|
+
state = {
|
|
86
|
+
visibleToasts: []
|
|
87
|
+
};
|
|
88
|
+
theme = DEFAULT_THEME;
|
|
89
|
+
listeners = new Set();
|
|
90
|
+
toastIdCounter = 0;
|
|
91
|
+
timeouts = new Map();
|
|
92
|
+
subscribe = listener => {
|
|
93
|
+
this.listeners.add(listener);
|
|
94
|
+
return () => {
|
|
95
|
+
this.listeners.delete(listener);
|
|
96
|
+
};
|
|
97
|
+
};
|
|
98
|
+
emit() {
|
|
99
|
+
for (const listener of this.listeners) {
|
|
100
|
+
listener(this.state);
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
setState(partial) {
|
|
104
|
+
this.state = {
|
|
105
|
+
...this.state,
|
|
106
|
+
...partial
|
|
107
|
+
};
|
|
108
|
+
this.emit();
|
|
109
|
+
}
|
|
110
|
+
getState = () => this.state;
|
|
111
|
+
getTheme = () => this.theme;
|
|
112
|
+
setConfig = config => {
|
|
113
|
+
this.theme = mergeConfig(config);
|
|
114
|
+
};
|
|
115
|
+
show = (title, description, type = "success", duration, options) => {
|
|
116
|
+
const actualDuration = duration ?? options?.duration ?? this.theme.defaultDuration;
|
|
117
|
+
const maxToasts = this.theme.stacking ? this.theme.maxStack : 1;
|
|
118
|
+
const id = `toast-${++this.toastIdCounter}`;
|
|
119
|
+
const newToast = {
|
|
120
|
+
id,
|
|
121
|
+
title,
|
|
122
|
+
description: description ?? options?.description,
|
|
123
|
+
type,
|
|
124
|
+
duration: actualDuration,
|
|
125
|
+
createdAt: Date.now(),
|
|
126
|
+
isExiting: false,
|
|
127
|
+
options
|
|
128
|
+
};
|
|
129
|
+
const {
|
|
130
|
+
visibleToasts
|
|
131
|
+
} = this.state;
|
|
132
|
+
const activeToasts = visibleToasts.filter(t => !t.isExiting);
|
|
133
|
+
if (activeToasts.length >= maxToasts) {
|
|
134
|
+
const toastsToRemove = activeToasts.slice(maxToasts - 1);
|
|
135
|
+
for (const toast of toastsToRemove) {
|
|
136
|
+
const timeout = this.timeouts.get(toast.id);
|
|
137
|
+
if (timeout) {
|
|
138
|
+
clearTimeout(timeout);
|
|
139
|
+
this.timeouts.delete(toast.id);
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
const removeIds = new Set(toastsToRemove.map(t => t.id));
|
|
143
|
+
if (this.theme.stacking) {
|
|
144
|
+
this.setState({
|
|
145
|
+
visibleToasts: visibleToasts.filter(t => !removeIds.has(t.id))
|
|
146
|
+
});
|
|
147
|
+
} else {
|
|
148
|
+
this.setState({
|
|
149
|
+
visibleToasts: visibleToasts.map(t => removeIds.has(t.id) ? {
|
|
150
|
+
...t,
|
|
151
|
+
isExiting: true
|
|
152
|
+
} : t)
|
|
153
|
+
});
|
|
154
|
+
setTimeout(() => {
|
|
155
|
+
for (const toast of toastsToRemove) {
|
|
156
|
+
this.removeToast(toast.id);
|
|
157
|
+
}
|
|
158
|
+
this.addToast(newToast, actualDuration);
|
|
159
|
+
}, EXIT_DURATION - 220);
|
|
160
|
+
return id;
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
this.addToast(newToast, actualDuration);
|
|
164
|
+
return id;
|
|
165
|
+
};
|
|
166
|
+
addToast(toast, duration) {
|
|
167
|
+
this.setState({
|
|
168
|
+
visibleToasts: [toast, ...this.state.visibleToasts.filter(t => !t.isExiting)]
|
|
169
|
+
});
|
|
170
|
+
this.scheduleTimeout(toast.id, duration, 0);
|
|
171
|
+
this.rescheduleAllTimeouts();
|
|
172
|
+
}
|
|
173
|
+
scheduleTimeout(id, baseDuration, index) {
|
|
174
|
+
const existingTimeout = this.timeouts.get(id);
|
|
175
|
+
if (existingTimeout) {
|
|
176
|
+
clearTimeout(existingTimeout);
|
|
177
|
+
}
|
|
178
|
+
const duration = baseDuration * (index + 1);
|
|
179
|
+
const timeout = setTimeout(() => {
|
|
180
|
+
this.hide(id);
|
|
181
|
+
}, duration);
|
|
182
|
+
this.timeouts.set(id, timeout);
|
|
183
|
+
}
|
|
184
|
+
rescheduleAllTimeouts() {
|
|
185
|
+
const {
|
|
186
|
+
visibleToasts
|
|
187
|
+
} = this.state;
|
|
188
|
+
visibleToasts.forEach((toast, index) => {
|
|
189
|
+
if (toast.isExiting || index === 0) return;
|
|
190
|
+
this.scheduleTimeout(toast.id, toast.duration, index);
|
|
191
|
+
});
|
|
192
|
+
}
|
|
193
|
+
hide = id => {
|
|
194
|
+
const {
|
|
195
|
+
visibleToasts
|
|
196
|
+
} = this.state;
|
|
197
|
+
const toast = visibleToasts.find(t => t.id === id);
|
|
198
|
+
if (!toast || toast.isExiting) return;
|
|
199
|
+
const timeout = this.timeouts.get(id);
|
|
200
|
+
if (timeout) {
|
|
201
|
+
clearTimeout(timeout);
|
|
202
|
+
this.timeouts.delete(id);
|
|
203
|
+
}
|
|
204
|
+
this.setState({
|
|
205
|
+
visibleToasts: visibleToasts.map(t => t.id === id ? {
|
|
206
|
+
...t,
|
|
207
|
+
isExiting: true
|
|
208
|
+
} : t)
|
|
209
|
+
});
|
|
210
|
+
setTimeout(() => {
|
|
211
|
+
this.removeToast(id);
|
|
212
|
+
}, EXIT_DURATION);
|
|
213
|
+
};
|
|
214
|
+
removeToast(id) {
|
|
215
|
+
const timeout = this.timeouts.get(id);
|
|
216
|
+
if (timeout) {
|
|
217
|
+
clearTimeout(timeout);
|
|
218
|
+
this.timeouts.delete(id);
|
|
219
|
+
}
|
|
220
|
+
this.setState({
|
|
221
|
+
visibleToasts: this.state.visibleToasts.filter(t => t.id !== id)
|
|
222
|
+
});
|
|
223
|
+
this.rescheduleAllTimeouts();
|
|
224
|
+
}
|
|
225
|
+
updateToast = (id, data) => {
|
|
226
|
+
const {
|
|
227
|
+
visibleToasts
|
|
228
|
+
} = this.state;
|
|
229
|
+
const index = visibleToasts.findIndex(t => t.id === id);
|
|
230
|
+
if (index === -1) return;
|
|
231
|
+
this.setState({
|
|
232
|
+
visibleToasts: visibleToasts.map(t => t.id === id ? {
|
|
233
|
+
...t,
|
|
234
|
+
...data
|
|
235
|
+
} : t)
|
|
236
|
+
});
|
|
237
|
+
if (data.duration !== undefined) {
|
|
238
|
+
this.scheduleTimeout(id, data.duration, index);
|
|
239
|
+
}
|
|
240
|
+
};
|
|
241
|
+
hideAll = () => {
|
|
242
|
+
for (const timeout of this.timeouts.values()) {
|
|
243
|
+
clearTimeout(timeout);
|
|
244
|
+
}
|
|
245
|
+
this.timeouts.clear();
|
|
246
|
+
this.setState({
|
|
247
|
+
visibleToasts: []
|
|
248
|
+
});
|
|
249
|
+
};
|
|
250
|
+
}
|
|
251
|
+
const toastStore = exports.toastStore = new ToastStore();
|
|
252
|
+
//# sourceMappingURL=toast-store.js.map
|