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.
@@ -1 +1,109 @@
1
- "use strict";Object.defineProperty(exports,"__esModule",{value:!0}),exports.BreadLoaf=function({config:i}){return(0,e.useEffect)(()=>(o.toastStore.setConfig(i),()=>{o.toastStore.setConfig(void 0)}),[i]),(0,s.jsx)(t.View,{style:n.container,pointerEvents:"box-none",children:(0,s.jsx)(r.ToastContainer,{})})};var e=require("react"),t=require("react-native"),r=require("./toast.js"),o=require("./toast-store.js"),s=require("react/jsx-runtime");const n=t.StyleSheet.create({container:{...t.StyleSheet.absoluteFillObject,zIndex:9999}});
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";Object.defineProperty(exports,"__esModule",{value:!0}),exports.toastStore=void 0;const t={position:"top",offset:0,rtl:!1,stacking:!0,maxStack:3,dismissible:!0,showCloseButton:!0,colors:{success:{accent:"#28B770",background:"#FFFFFF"},error:{accent:"#F05964",background:"#FFFFFF"},info:{accent:"#EDBE43",background:"#FFFFFF"},loading:{accent:"#232323",background:"#FFFFFF"}},icons:{},toastStyle:{},titleStyle:{},descriptionStyle:{},defaultDuration:4e3};exports.toastStore=new class{state={visibleToasts:[]};theme=t;listeners=new Set;toastIdCounter=0;timeouts=new Map;subscribe=t=>(this.listeners.add(t),()=>{this.listeners.delete(t)});emit(){for(const t of this.listeners)t(this.state)}setState(t){this.state={...this.state,...t},this.emit()}getState=()=>this.state;getTheme=()=>this.theme;setConfig=s=>{this.theme=function(s){if(!s)return t;const e={...t.colors};if(s.colors)for(const i of Object.keys(s.colors)){const o=s.colors[i];o&&(e[i]={...t.colors[i],...o})}return{position:s.position??t.position,offset:s.offset??t.offset,rtl:s.rtl??t.rtl,stacking:s.stacking??t.stacking,maxStack:s.maxStack??t.maxStack,dismissible:s.dismissible??t.dismissible,showCloseButton:s.showCloseButton??t.showCloseButton,colors:e,icons:{...t.icons,...s.icons},toastStyle:{...t.toastStyle,...s.toastStyle},titleStyle:{...t.titleStyle,...s.titleStyle},descriptionStyle:{...t.descriptionStyle,...s.descriptionStyle},defaultDuration:s.defaultDuration??t.defaultDuration}}(s)};show=(t,s,e="success",i,o)=>{const a=i??o?.duration??this.theme.defaultDuration,l=this.theme.stacking?this.theme.maxStack:1,n="toast-"+ ++this.toastIdCounter,c={id:n,title:t,description:s??o?.description,type:e,duration:a,createdAt:Date.now(),isExiting:!1,options:o},{visibleToasts:r}=this.state,h=r.filter(t=>!t.isExiting);if(h.length>=l){const t=h.slice(l-1);for(const s of t){const t=this.timeouts.get(s.id);t&&(clearTimeout(t),this.timeouts.delete(s.id))}const s=new Set(t.map(t=>t.id));if(!this.theme.stacking)return this.setState({visibleToasts:r.map(t=>s.has(t.id)?{...t,isExiting:!0}:t)}),setTimeout(()=>{for(const s of t)this.removeToast(s.id);this.addToast(c,a)},130),n;this.setState({visibleToasts:r.filter(t=>!s.has(t.id))})}return this.addToast(c,a),n};addToast(t,s){this.setState({visibleToasts:[t,...this.state.visibleToasts.filter(t=>!t.isExiting)]}),this.scheduleTimeout(t.id,s,0),this.rescheduleAllTimeouts()}scheduleTimeout(t,s,e){const i=this.timeouts.get(t);i&&clearTimeout(i);const o=setTimeout(()=>{this.hide(t)},s*(e+1));this.timeouts.set(t,o)}rescheduleAllTimeouts(){const{visibleToasts:t}=this.state;t.forEach((t,s)=>{t.isExiting||0===s||this.scheduleTimeout(t.id,t.duration,s)})}hide=t=>{const{visibleToasts:s}=this.state,e=s.find(s=>s.id===t);if(!e||e.isExiting)return;const i=this.timeouts.get(t);i&&(clearTimeout(i),this.timeouts.delete(t)),this.setState({visibleToasts:s.map(s=>s.id===t?{...s,isExiting:!0}:s)}),setTimeout(()=>{this.removeToast(t)},350)};removeToast(t){const s=this.timeouts.get(t);s&&(clearTimeout(s),this.timeouts.delete(t)),this.setState({visibleToasts:this.state.visibleToasts.filter(s=>s.id!==t)}),this.rescheduleAllTimeouts()}updateToast=(t,s)=>{const{visibleToasts:e}=this.state,i=e.findIndex(s=>s.id===t);-1!==i&&(this.setState({visibleToasts:e.map(e=>e.id===t?{...e,...s}:e)}),void 0!==s.duration&&this.scheduleTimeout(t,s.duration,i))};hideAll=()=>{for(const t of this.timeouts.values())clearTimeout(t);this.timeouts.clear(),this.setState({visibleToasts:[]})}};
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