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,123 @@
1
- "use strict";import{scheduleOnRN as t}from"react-native-worklets";import{toastStore as o}from"./toast-store.js";const s=t=>t?"string"==typeof t?{description:t}:{description:t.description,duration:t.duration,options:t}:{},i=t=>"string"==typeof t?{title:t}:t,r=(t,s)=>{const r=i(t.loading);s(o.show(r.title,r.description,"loading",r.duration??36e5))},n=(t,s)=>{const r=i(s.success);o.updateToast(t,{title:r.title,description:r.description,type:"success",duration:r.duration??4e3})},e=(t,s,r)=>{const n=((t,o)=>i("function"==typeof t?t(o):t))(s.error,r);o.updateToast(t,{title:n.title,description:n.description,type:"error",duration:n.duration??4e3})},c=(t,s,i,r)=>{o.show(t,s,i,r)};c.custom=(t,s)=>{const i=s?.type??"info";return o.show("",void 0,i,s?.duration,{...s,customContent:t})},c.success=(t,i,r)=>{const{description:n,duration:e,options:c}=s(i);o.show(t,n,"success",r??e,c)},c.error=(t,i,r)=>{const{description:n,duration:e,options:c}=s(i);o.show(t,n,"error",r??e,c)},c.info=(t,i,r)=>{const{description:n,duration:e,options:c}=s(i);o.show(t,n,"info",r??e,c)},c.promise=async(o,s)=>{let i="";await new Promise(o=>{t(r,s,t=>{i=t,o()})});try{const r=await o;return t(n,i,s),{data:r,success:!0}}catch(o){const r=o instanceof Error?o:new Error(String(o));return t(e,i,s,r),{error:r,success:!1}}},c.dismiss=t=>{o.hide(t)},c.dismissAll=()=>{o.hideAll()};export const toast=c;
1
+ "use strict";
2
+
3
+ import { toastStore } from "./toast-store.js";
4
+ const _toast = (title, description, type, duration) => {
5
+ toastStore.show(title, description, type, duration);
6
+ };
7
+ const parseDescriptionOrOptions = arg => {
8
+ if (!arg) return {};
9
+ if (typeof arg === "string") return {
10
+ description: arg
11
+ };
12
+ return {
13
+ description: arg.description,
14
+ duration: arg.duration,
15
+ options: arg
16
+ };
17
+ };
18
+ const parseMessage = input => typeof input === "string" ? {
19
+ title: input
20
+ } : input;
21
+ const parseErrorMessage = (input, error) => {
22
+ if (typeof input === "function") {
23
+ return parseMessage(input(error));
24
+ }
25
+ return parseMessage(input);
26
+ };
27
+ const promiseToast = async (promise, messages) => {
28
+ const loadingCfg = parseMessage(messages.loading);
29
+ const toastId = toastStore.show(loadingCfg.title, loadingCfg.description, "loading", loadingCfg.duration ?? 60 * 60 * 1000);
30
+ try {
31
+ const result = await promise;
32
+ const successCfg = parseMessage(messages.success);
33
+ toastStore.updateToast(toastId, {
34
+ title: successCfg.title,
35
+ description: successCfg.description,
36
+ type: "success",
37
+ duration: successCfg.duration ?? 4000
38
+ });
39
+ return {
40
+ data: result,
41
+ success: true
42
+ };
43
+ } catch (err) {
44
+ const error = err instanceof Error ? err : new Error(String(err));
45
+ const errorCfg = parseErrorMessage(messages.error, error);
46
+ toastStore.updateToast(toastId, {
47
+ title: errorCfg.title,
48
+ description: errorCfg.description,
49
+ type: "error",
50
+ duration: errorCfg.duration ?? 4000
51
+ });
52
+ return {
53
+ error,
54
+ success: false
55
+ };
56
+ }
57
+ };
58
+ const toastFn = _toast;
59
+ toastFn.custom = (content, options) => {
60
+ const type = options?.type ?? "info";
61
+ return toastStore.show("", undefined, type, options?.duration, {
62
+ ...options,
63
+ customContent: content
64
+ });
65
+ };
66
+ toastFn.success = (title, descriptionOrOptions, duration) => {
67
+ const {
68
+ description,
69
+ duration: optDuration,
70
+ options
71
+ } = parseDescriptionOrOptions(descriptionOrOptions);
72
+ toastStore.show(title, description, "success", duration ?? optDuration, options);
73
+ };
74
+ toastFn.error = (title, descriptionOrOptions, duration) => {
75
+ const {
76
+ description,
77
+ duration: optDuration,
78
+ options
79
+ } = parseDescriptionOrOptions(descriptionOrOptions);
80
+ toastStore.show(title, description, "error", duration ?? optDuration, options);
81
+ };
82
+ toastFn.info = (title, descriptionOrOptions, duration) => {
83
+ const {
84
+ description,
85
+ duration: optDuration,
86
+ options
87
+ } = parseDescriptionOrOptions(descriptionOrOptions);
88
+ toastStore.show(title, description, "info", duration ?? optDuration, options);
89
+ };
90
+ toastFn.promise = promiseToast;
91
+ toastFn.dismiss = id => {
92
+ toastStore.hide(id);
93
+ };
94
+ toastFn.dismissAll = () => {
95
+ toastStore.hideAll();
96
+ };
97
+
98
+ /**
99
+ * Toast API for showing notifications.
100
+ *
101
+ * @example
102
+ * ```ts
103
+ * import { toast } from 'react-native-bread';
104
+ *
105
+ * // Basic toasts
106
+ * toast.success("Saved!", "Your changes have been saved");
107
+ * toast.error("Error", "Something went wrong");
108
+ * toast.info("Tip", "Swipe up to dismiss");
109
+ *
110
+ * // Promise toast (loading → success/error)
111
+ * toast.promise(apiCall(), {
112
+ * loading: { title: "Loading..." },
113
+ * success: { title: "Done!" },
114
+ * error: (err) => ({ title: "Failed", description: err.message }),
115
+ * });
116
+ *
117
+ * // Dismiss toasts
118
+ * toast.dismiss(id);
119
+ * toast.dismissAll();
120
+ * ```
121
+ */
122
+ export const toast = toastFn;
123
+ //# sourceMappingURL=toast-api.js.map
@@ -1 +1,74 @@
1
- "use strict";import{memo as t,useEffect as e}from"react";import{ActivityIndicator as r}from"react-native";import o,{Easing as i,useAnimatedStyle as c,useSharedValue as n,withTiming as s}from"react-native-reanimated";import{ICON_ANIMATION_DURATION as a,ICON_SIZE as l}from"./constants.js";import{GreenCheck as u,InfoIcon as f,RedX as m}from"./icons/index.js";import{jsx as h}from"react/jsx-runtime";export const resolveIcon=(t,e,o,i)=>{if(o)return"function"==typeof o?o({color:e,size:l}):o;if(i)return i({color:e,size:l});switch(t){case"success":default:return h(u,{width:36,height:36,fill:e});case"error":return h(m,{width:l,height:l,fill:e});case"loading":return h(r,{size:l,color:e});case"info":return h(f,{width:l,height:l,fill:e})}};export const AnimatedIcon=t(({type:t,color:r,custom:l,config:u})=>{const f=n(0);e(()=>{f.value=s(1,{duration:a,easing:i.out(i.back(1.5))})},[f]);const m=c(()=>({opacity:f.value,transform:[{scale:.7+.3*f.value}]}));return h(o.View,{style:m,children:resolveIcon(t,r,l,u)})});
1
+ "use strict";
2
+
3
+ import { memo, useEffect } from "react";
4
+ import { ActivityIndicator } from "react-native";
5
+ import Animated, { Easing, useAnimatedStyle, useSharedValue, withTiming } from "react-native-reanimated";
6
+ import { ICON_ANIMATION_DURATION, ICON_SIZE } from "./constants.js";
7
+ import { GreenCheck, InfoIcon, RedX } from "./icons/index.js";
8
+ import { jsx as _jsx } from "react/jsx-runtime";
9
+ export const resolveIcon = (type, color, custom, config) => {
10
+ if (custom) return typeof custom === "function" ? custom({
11
+ color,
12
+ size: ICON_SIZE
13
+ }) : custom;
14
+ if (config) return config({
15
+ color,
16
+ size: ICON_SIZE
17
+ });
18
+ switch (type) {
19
+ case "success":
20
+ return /*#__PURE__*/_jsx(GreenCheck, {
21
+ width: 36,
22
+ height: 36,
23
+ fill: color
24
+ });
25
+ case "error":
26
+ return /*#__PURE__*/_jsx(RedX, {
27
+ width: ICON_SIZE,
28
+ height: ICON_SIZE,
29
+ fill: color
30
+ });
31
+ case "loading":
32
+ return /*#__PURE__*/_jsx(ActivityIndicator, {
33
+ size: ICON_SIZE,
34
+ color: color
35
+ });
36
+ case "info":
37
+ return /*#__PURE__*/_jsx(InfoIcon, {
38
+ width: ICON_SIZE,
39
+ height: ICON_SIZE,
40
+ fill: color
41
+ });
42
+ default:
43
+ return /*#__PURE__*/_jsx(GreenCheck, {
44
+ width: 36,
45
+ height: 36,
46
+ fill: color
47
+ });
48
+ }
49
+ };
50
+ export const AnimatedIcon = /*#__PURE__*/memo(({
51
+ type,
52
+ color,
53
+ custom,
54
+ config
55
+ }) => {
56
+ const progress = useSharedValue(0);
57
+ useEffect(() => {
58
+ progress.value = withTiming(1, {
59
+ duration: ICON_ANIMATION_DURATION,
60
+ easing: Easing.out(Easing.back(1.5))
61
+ });
62
+ }, [progress]);
63
+ const style = useAnimatedStyle(() => ({
64
+ opacity: progress.value,
65
+ transform: [{
66
+ scale: 0.7 + progress.value * 0.3
67
+ }]
68
+ }));
69
+ return /*#__PURE__*/_jsx(Animated.View, {
70
+ style: style,
71
+ children: resolveIcon(type, color, custom, config)
72
+ });
73
+ });
74
+ //# sourceMappingURL=toast-icons.js.map
@@ -1 +1,104 @@
1
- "use strict";import{useEffect as t}from"react";import{StyleSheet as o,View as r}from"react-native";import{ToastContainer as e}from"./toast.js";import{toastStore as n}from"./toast-store.js";import{jsx as i}from"react/jsx-runtime";export function BreadLoaf({config:o}){return t(()=>(n.setConfig(o),()=>{n.setConfig(void 0)}),[o]),i(r,{style:s.container,pointerEvents:"box-none",children:i(e,{})})}const s=o.create({container:{...o.absoluteFillObject,zIndex:9999}});
1
+ "use strict";
2
+
3
+ import { useEffect } from "react";
4
+ import { Platform, StyleSheet, View } from "react-native";
5
+ import { FullWindowOverlay } from "react-native-screens";
6
+ import { ToastContainer } from "./toast.js";
7
+ import { toastStore } from "./toast-store.js";
8
+ import { jsx as _jsx } from "react/jsx-runtime";
9
+ function ToastContent() {
10
+ return /*#__PURE__*/_jsx(View, {
11
+ style: styles.container,
12
+ pointerEvents: "box-none",
13
+ children: /*#__PURE__*/_jsx(ToastContainer, {})
14
+ });
15
+ }
16
+ /**
17
+ * Toast component that enables toast notifications in your app.
18
+ * Add `<BreadLoaf />` to your root layout to start showing toasts.
19
+ *
20
+ * @example
21
+ * ```tsx
22
+ * import { BreadLoaf } from 'react-native-bread';
23
+ *
24
+ * // Basic usage - add to your root layout
25
+ * export default function RootLayout() {
26
+ * return (
27
+ * <>
28
+ * <Stack />
29
+ * <BreadLoaf />
30
+ * </>
31
+ * );
32
+ * }
33
+ *
34
+ * // With configuration
35
+ * <BreadLoaf
36
+ * config={{
37
+ * position: 'bottom',
38
+ * stacking: false,
39
+ * defaultDuration: 5000,
40
+ * colors: {
41
+ * success: { accent: '#22c55e', background: '#f0fdf4' },
42
+ * error: { accent: '#ef4444', background: '#fef2f2' },
43
+ * },
44
+ * toastStyle: { borderRadius: 12 },
45
+ * }}
46
+ * />
47
+ * ```
48
+ */
49
+ export function BreadLoaf({
50
+ config
51
+ }) {
52
+ useEffect(() => {
53
+ toastStore.setConfig(config);
54
+ return () => {
55
+ toastStore.setConfig(undefined);
56
+ };
57
+ }, [config]);
58
+
59
+ // iOS: use FullWindowOverlay to render above native modals
60
+ if (Platform.OS === "ios") {
61
+ return /*#__PURE__*/_jsx(FullWindowOverlay, {
62
+ children: /*#__PURE__*/_jsx(ToastContent, {})
63
+ });
64
+ }
65
+ return /*#__PURE__*/_jsx(ToastContent, {});
66
+ }
67
+
68
+ /**
69
+ * Lightweight toast renderer for use inside modal screens.
70
+ *
71
+ * On Android, native modals render above the root React view, so toasts from
72
+ * the main `<BreadLoaf />` won't be visible. Add `<ToastPortal />` inside your
73
+ * modal layouts to show toasts above modal content.
74
+ *
75
+ * This component only renders toasts - it does not accept configuration.
76
+ * All styling/behavior is inherited from your root `<BreadLoaf />` config.
77
+ *
78
+ * @example
79
+ * ```tsx
80
+ * // app/(modal)/_layout.tsx
81
+ * import { Stack } from 'expo-router';
82
+ * import { ToastPortal } from 'react-native-bread';
83
+ * import { Platform } from 'react-native';
84
+ *
85
+ * export default function ModalLayout() {
86
+ * return (
87
+ * <>
88
+ * <Stack screenOptions={{ headerShown: false }} />
89
+ * {Platform.OS === 'android' && <ToastPortal />}
90
+ * </>
91
+ * );
92
+ * }
93
+ * ```
94
+ */
95
+ export function ToastPortal() {
96
+ return /*#__PURE__*/_jsx(ToastContent, {});
97
+ }
98
+ const styles = StyleSheet.create({
99
+ container: {
100
+ ...StyleSheet.absoluteFillObject,
101
+ zIndex: 9999
102
+ }
103
+ });
104
+ //# sourceMappingURL=toast-provider.js.map
@@ -1 +1,248 @@
1
- "use strict";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};export const 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,n=this.theme.stacking?this.theme.maxStack:1,l="toast-"+ ++this.toastIdCounter,c={id:l,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>=n){const t=h.slice(n-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),l;this.setState({visibleToasts:r.filter(t=>!s.has(t.id))})}return this.addToast(c,a),l};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
+ const EXIT_DURATION = 350;
4
+ const DEFAULT_THEME = {
5
+ position: "top",
6
+ offset: 0,
7
+ rtl: false,
8
+ stacking: true,
9
+ maxStack: 3,
10
+ dismissible: true,
11
+ showCloseButton: true,
12
+ colors: {
13
+ success: {
14
+ accent: "#28B770",
15
+ background: "#FFFFFF"
16
+ },
17
+ error: {
18
+ accent: "#F05964",
19
+ background: "#FFFFFF"
20
+ },
21
+ info: {
22
+ accent: "#EDBE43",
23
+ background: "#FFFFFF"
24
+ },
25
+ loading: {
26
+ accent: "#232323",
27
+ background: "#FFFFFF"
28
+ }
29
+ },
30
+ icons: {},
31
+ toastStyle: {},
32
+ titleStyle: {},
33
+ descriptionStyle: {},
34
+ defaultDuration: 4000
35
+ };
36
+ function mergeConfig(config) {
37
+ if (!config) return DEFAULT_THEME;
38
+ const mergedColors = {
39
+ ...DEFAULT_THEME.colors
40
+ };
41
+ if (config.colors) {
42
+ for (const type of Object.keys(config.colors)) {
43
+ const userColors = config.colors[type];
44
+ if (userColors) {
45
+ mergedColors[type] = {
46
+ ...DEFAULT_THEME.colors[type],
47
+ ...userColors
48
+ };
49
+ }
50
+ }
51
+ }
52
+ return {
53
+ position: config.position ?? DEFAULT_THEME.position,
54
+ offset: config.offset ?? DEFAULT_THEME.offset,
55
+ rtl: config.rtl ?? DEFAULT_THEME.rtl,
56
+ stacking: config.stacking ?? DEFAULT_THEME.stacking,
57
+ maxStack: config.maxStack ?? DEFAULT_THEME.maxStack,
58
+ dismissible: config.dismissible ?? DEFAULT_THEME.dismissible,
59
+ showCloseButton: config.showCloseButton ?? DEFAULT_THEME.showCloseButton,
60
+ colors: mergedColors,
61
+ icons: {
62
+ ...DEFAULT_THEME.icons,
63
+ ...config.icons
64
+ },
65
+ toastStyle: {
66
+ ...DEFAULT_THEME.toastStyle,
67
+ ...config.toastStyle
68
+ },
69
+ titleStyle: {
70
+ ...DEFAULT_THEME.titleStyle,
71
+ ...config.titleStyle
72
+ },
73
+ descriptionStyle: {
74
+ ...DEFAULT_THEME.descriptionStyle,
75
+ ...config.descriptionStyle
76
+ },
77
+ defaultDuration: config.defaultDuration ?? DEFAULT_THEME.defaultDuration
78
+ };
79
+ }
80
+ class ToastStore {
81
+ state = {
82
+ visibleToasts: []
83
+ };
84
+ theme = DEFAULT_THEME;
85
+ listeners = new Set();
86
+ toastIdCounter = 0;
87
+ timeouts = new Map();
88
+ subscribe = listener => {
89
+ this.listeners.add(listener);
90
+ return () => {
91
+ this.listeners.delete(listener);
92
+ };
93
+ };
94
+ emit() {
95
+ for (const listener of this.listeners) {
96
+ listener(this.state);
97
+ }
98
+ }
99
+ setState(partial) {
100
+ this.state = {
101
+ ...this.state,
102
+ ...partial
103
+ };
104
+ this.emit();
105
+ }
106
+ getState = () => this.state;
107
+ getTheme = () => this.theme;
108
+ setConfig = config => {
109
+ this.theme = mergeConfig(config);
110
+ };
111
+ show = (title, description, type = "success", duration, options) => {
112
+ const actualDuration = duration ?? options?.duration ?? this.theme.defaultDuration;
113
+ const maxToasts = this.theme.stacking ? this.theme.maxStack : 1;
114
+ const id = `toast-${++this.toastIdCounter}`;
115
+ const newToast = {
116
+ id,
117
+ title,
118
+ description: description ?? options?.description,
119
+ type,
120
+ duration: actualDuration,
121
+ createdAt: Date.now(),
122
+ isExiting: false,
123
+ options
124
+ };
125
+ const {
126
+ visibleToasts
127
+ } = this.state;
128
+ const activeToasts = visibleToasts.filter(t => !t.isExiting);
129
+ if (activeToasts.length >= maxToasts) {
130
+ const toastsToRemove = activeToasts.slice(maxToasts - 1);
131
+ for (const toast of toastsToRemove) {
132
+ const timeout = this.timeouts.get(toast.id);
133
+ if (timeout) {
134
+ clearTimeout(timeout);
135
+ this.timeouts.delete(toast.id);
136
+ }
137
+ }
138
+ const removeIds = new Set(toastsToRemove.map(t => t.id));
139
+ if (this.theme.stacking) {
140
+ this.setState({
141
+ visibleToasts: visibleToasts.filter(t => !removeIds.has(t.id))
142
+ });
143
+ } else {
144
+ this.setState({
145
+ visibleToasts: visibleToasts.map(t => removeIds.has(t.id) ? {
146
+ ...t,
147
+ isExiting: true
148
+ } : t)
149
+ });
150
+ setTimeout(() => {
151
+ for (const toast of toastsToRemove) {
152
+ this.removeToast(toast.id);
153
+ }
154
+ this.addToast(newToast, actualDuration);
155
+ }, EXIT_DURATION - 220);
156
+ return id;
157
+ }
158
+ }
159
+ this.addToast(newToast, actualDuration);
160
+ return id;
161
+ };
162
+ addToast(toast, duration) {
163
+ this.setState({
164
+ visibleToasts: [toast, ...this.state.visibleToasts.filter(t => !t.isExiting)]
165
+ });
166
+ this.scheduleTimeout(toast.id, duration, 0);
167
+ this.rescheduleAllTimeouts();
168
+ }
169
+ scheduleTimeout(id, baseDuration, index) {
170
+ const existingTimeout = this.timeouts.get(id);
171
+ if (existingTimeout) {
172
+ clearTimeout(existingTimeout);
173
+ }
174
+ const duration = baseDuration * (index + 1);
175
+ const timeout = setTimeout(() => {
176
+ this.hide(id);
177
+ }, duration);
178
+ this.timeouts.set(id, timeout);
179
+ }
180
+ rescheduleAllTimeouts() {
181
+ const {
182
+ visibleToasts
183
+ } = this.state;
184
+ visibleToasts.forEach((toast, index) => {
185
+ if (toast.isExiting || index === 0) return;
186
+ this.scheduleTimeout(toast.id, toast.duration, index);
187
+ });
188
+ }
189
+ hide = id => {
190
+ const {
191
+ visibleToasts
192
+ } = this.state;
193
+ const toast = visibleToasts.find(t => t.id === id);
194
+ if (!toast || toast.isExiting) return;
195
+ const timeout = this.timeouts.get(id);
196
+ if (timeout) {
197
+ clearTimeout(timeout);
198
+ this.timeouts.delete(id);
199
+ }
200
+ this.setState({
201
+ visibleToasts: visibleToasts.map(t => t.id === id ? {
202
+ ...t,
203
+ isExiting: true
204
+ } : t)
205
+ });
206
+ setTimeout(() => {
207
+ this.removeToast(id);
208
+ }, EXIT_DURATION);
209
+ };
210
+ removeToast(id) {
211
+ const timeout = this.timeouts.get(id);
212
+ if (timeout) {
213
+ clearTimeout(timeout);
214
+ this.timeouts.delete(id);
215
+ }
216
+ this.setState({
217
+ visibleToasts: this.state.visibleToasts.filter(t => t.id !== id)
218
+ });
219
+ this.rescheduleAllTimeouts();
220
+ }
221
+ updateToast = (id, data) => {
222
+ const {
223
+ visibleToasts
224
+ } = this.state;
225
+ const index = visibleToasts.findIndex(t => t.id === id);
226
+ if (index === -1) return;
227
+ this.setState({
228
+ visibleToasts: visibleToasts.map(t => t.id === id ? {
229
+ ...t,
230
+ ...data
231
+ } : t)
232
+ });
233
+ if (data.duration !== undefined) {
234
+ this.scheduleTimeout(id, data.duration, index);
235
+ }
236
+ };
237
+ hideAll = () => {
238
+ for (const timeout of this.timeouts.values()) {
239
+ clearTimeout(timeout);
240
+ }
241
+ this.timeouts.clear();
242
+ this.setState({
243
+ visibleToasts: []
244
+ });
245
+ };
246
+ }
247
+ export const toastStore = new ToastStore();
248
+ //# sourceMappingURL=toast-store.js.map