react-native-bread 0.5.2 → 0.5.3

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,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
@@ -1 +1,345 @@
1
- "use strict";import{memo as t,useCallback as e,useEffect as o,useMemo as i,useState as n}from"react";import{Pressable as s,StyleSheet as a,Text as r,View as l}from"react-native";import{Gesture as c,GestureDetector as u}from"react-native-gesture-handler";import d,{interpolate as p,useAnimatedStyle as m,useSharedValue as g,withTiming as h}from"react-native-reanimated";import{useSafeAreaInsets as f}from"react-native-safe-area-context";import{scheduleOnRN as v}from"react-native-worklets";import{DISMISS_THRESHOLD as x,DISMISS_VELOCITY_THRESHOLD as y,EASING as T,ENTRY_DURATION as w,ENTRY_OFFSET as C,EXIT_DURATION as b,EXIT_OFFSET as Y,ICON_ANIMATION_DURATION as k,MAX_DRAG_CLAMP as I,MAX_DRAG_RESISTANCE as S,SPRING_BACK_DURATION as j,STACK_OFFSET_PER_ITEM as z,STACK_SCALE_PER_ITEM as B,STACK_TRANSITION_DURATION as E,SWIPE_EXIT_OFFSET as M}from"./constants.js";import{CloseIcon as H}from"./icons/index.js";import{animationPool as R,getSlotIndex as F,releaseSlot as V,slotTrackers as D}from"./pool.js";import{AnimatedIcon as W,resolveIcon as A}from"./toast-icons.js";import{toastStore as O}from"./toast-store.js";import{useToastState as P}from"./use-toast-state.js";import{jsx as L,jsxs as U}from"react/jsx-runtime";export const ToastContainer=()=>{const{top:t,bottom:o}=f(),{visibleToasts:n,theme:s,toastsWithIndex:a,isBottom:r,topToastRef:d,isBottomRef:p,isDismissibleRef:m}=P(),w=g(!1),C=i(()=>c.Pan().onStart(()=>{"worklet";w.set(!1)}).onUpdate(t=>{"worklet";if(!m.current.value)return;const e=d.current.value;if(!e)return;const{slot:o}=e,i=p.current.value,n=t.translationY,s=i?n:-n,a=i?-n:n;if(s>0){const e=i?Math.min(n,I):Math.max(n,-I);o.translationY.value=e;const a=s>x||(i?t.velocityY>y:t.velocityY<-y);w.set(a)}else{const t=S*(1-Math.exp(-a/250));o.translationY.value=i?-Math.min(t,S):Math.min(t,S),w.value=!1}}).onEnd(()=>{"worklet";if(!m.current.value)return;const t=d.current.value;if(!t)return;const{slot:e}=t,o=p.current.value;if(w.value){e.progress.value=h(0,{duration:b,easing:T});const i=o?M:-M;e.translationY.value=h(e.translationY.value+i,{duration:b,easing:T}),v(t.dismiss)}else e.translationY.value=h(0,{duration:j,easing:T})}),[w,m,d,p]),Y=e(t=>{d.current.value=t},[d]);if(0===n.length)return null;const k=r?o:t,z=r?{bottom:k+s.offset+2}:{top:k+s.offset+2};return L(u,{gesture:C,children:L(l,{style:[G.container,z],pointerEvents:"box-none",children:a.map(({toast:t,index:e})=>L(q,{toast:t,index:e,theme:s,position:s.position,isTopToast:0===e,registerTopToast:Y},t.id))})})};const q=t(({toast:t,index:i,theme:a,position:c,isTopToast:u,registerTopToast:g})=>{const[f]=n(()=>F(t.id)),v=R[f],x=D[f],y="bottom"===c,I=y?C:-C,S=y?Y:-Y,[j,M]=n("loading"===t.type),[P,q]=n(!1);o(()=>{v.progress.value=0,v.translationY.value=0,v.stackIndex.value=i,v.progress.value=h(1,{duration:w,easing:T});const e=setTimeout(()=>q(!0),50);return()=>{clearTimeout(e),V(t.id)}},[]);const J=e(()=>{O.hide(t.id)},[t.id]);o(()=>{let e=null;return t.isExiting&&!x.wasExiting&&(x.wasExiting=!0,v.progress.value=h(0,{duration:b,easing:T}),v.translationY.value=h(S,{duration:b,easing:T})),x.initialized&&i!==x.prevIndex&&(v.stackIndex.value=h(i,{duration:E,easing:T})),x.prevIndex=i,x.initialized=!0,"loading"===t.type?M(!0):j&&(e=setTimeout(()=>M(!1),k+50)),u&&g({slot:v,dismiss:J}),()=>{e&&clearTimeout(e),u&&g(null)}},[t.isExiting,i,v,x,S,t.type,j,u,g,J]);const K=j&&"loading"!==t.type,N=m(()=>{const t=p(v.progress.value,[0,1],[I,0]),e=y?v.stackIndex.value*z:v.stackIndex.value*-z,o=1-v.stackIndex.value*B,i=t+v.translationY.value+e,n=p(v.progress.value,[0,1],[0,1]),s=y?v.translationY.value:-v.translationY.value,a=n*(s>0?p(s,[0,130],[1,0],"clamp"):1);return{transform:[{translateY:i},{scale:o*p(Math.abs(v.translationY.value),[0,50],[1,.98],"clamp")}],opacity:a,zIndex:1e3-Math.round(v.stackIndex.value)}}),{options:Q}=t,X=a.colors[t.type];if(void 0!==Q?.customContent){const e=Q.customContent;return L(d.View,{style:[G.toast,G.customContentToast,y?G.toastBottom:G.toastTop,{backgroundColor:X.background},a.toastStyle,Q.style,N],children:"function"==typeof e?e({id:t.id,dismiss:J,type:t.type,isExiting:!!t.isExiting}):e})}const Z="loading"!==t.type&&(Q?.showCloseButton??a.showCloseButton);return U(d.View,{style:[G.toast,y?G.toastBottom:G.toastTop,{backgroundColor:X.background},a.rtl&&G.rtl,a.toastStyle,Q?.style,N],children:[L(l,{style:G.iconContainer,children:P&&(K?L(W,{type:t.type,color:X.accent,custom:Q?.icon,config:a.icons[t.type]},t.type):A(t.type,X.accent,Q?.icon,a.icons[t.type]))}),U(l,{style:G.textContainer,children:[L(r,{maxFontSizeMultiplier:1.35,allowFontScaling:!1,style:[G.title,{color:X.accent},a.rtl&&{textAlign:"right"},a.titleStyle,Q?.titleStyle],children:t.title}),t.description&&L(r,{allowFontScaling:!1,maxFontSizeMultiplier:1.35,style:[G.description,a.rtl&&{textAlign:"right"},a.descriptionStyle,Q?.descriptionStyle],children:t.description})]}),Z&&L(s,{style:G.closeButton,onPress:J,hitSlop:12,children:L(H,{width:20,height:20})})]})},(t,e)=>t.toast.id===e.toast.id&&t.toast.type===e.toast.type&&t.toast.title===e.toast.title&&t.toast.description===e.toast.description&&t.toast.isExiting===e.toast.isExiting&&t.index===e.index&&t.position===e.position&&t.theme===e.theme&&t.isTopToast===e.isTopToast),G=a.create({container:{position:"absolute",left:16,right:16,zIndex:1e3},toast:{flexDirection:"row",alignItems:"center",gap:12,minHeight:36,borderRadius:20,borderCurve:"continuous",position:"absolute",left:0,right:0,paddingHorizontal:12,paddingVertical:10,shadowColor:"#000",shadowOffset:{width:0,height:8},shadowOpacity:.05,shadowRadius:24,elevation:8},customContentToast:{padding:0,paddingHorizontal:0,paddingVertical:0,overflow:"hidden"},rtl:{flexDirection:"row-reverse"},toastTop:{top:0},toastBottom:{bottom:0},iconContainer:{width:48,height:48,alignItems:"center",justifyContent:"center",marginLeft:8},textContainer:{flex:1,gap:1,justifyContent:"center"},title:{fontSize:14,fontWeight:"700",lineHeight:20},description:{color:"#6B7280",fontSize:12,fontWeight:"500",lineHeight:16},closeButton:{padding:4,alignItems:"center",justifyContent:"center"}});
1
+ "use strict";
2
+
3
+ import { memo, useCallback, useEffect, useMemo, useState } from "react";
4
+ import { Pressable, StyleSheet, Text, View } from "react-native";
5
+ import { Gesture, GestureDetector } from "react-native-gesture-handler";
6
+ import Animated, { interpolate, useAnimatedStyle, useSharedValue, withTiming } from "react-native-reanimated";
7
+ import { useSafeAreaInsets } from "react-native-safe-area-context";
8
+ import { scheduleOnRN } from "react-native-worklets";
9
+ import { DISMISS_THRESHOLD, DISMISS_VELOCITY_THRESHOLD, EASING, ENTRY_DURATION, ENTRY_OFFSET, EXIT_DURATION, EXIT_OFFSET, ICON_ANIMATION_DURATION, MAX_DRAG_CLAMP, MAX_DRAG_RESISTANCE, SPRING_BACK_DURATION, STACK_OFFSET_PER_ITEM, STACK_SCALE_PER_ITEM, STACK_TRANSITION_DURATION, SWIPE_EXIT_OFFSET } from "./constants.js";
10
+ import { CloseIcon } from "./icons/index.js";
11
+ import { animationPool, getSlotIndex, releaseSlot, slotTrackers } from "./pool.js";
12
+ import { AnimatedIcon, resolveIcon } from "./toast-icons.js";
13
+ import { toastStore } from "./toast-store.js";
14
+ import { useToastState } from "./use-toast-state.js";
15
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
16
+ export const ToastContainer = () => {
17
+ const {
18
+ top,
19
+ bottom
20
+ } = useSafeAreaInsets();
21
+ const {
22
+ visibleToasts,
23
+ theme,
24
+ toastsWithIndex,
25
+ isBottom,
26
+ topToastRef,
27
+ isBottomRef,
28
+ isDismissibleRef
29
+ } = useToastState();
30
+ const shouldDismiss = useSharedValue(false);
31
+ const panGesture = useMemo(() => Gesture.Pan().onStart(() => {
32
+ "worklet";
33
+
34
+ shouldDismiss.set(false);
35
+ }).onUpdate(event => {
36
+ "worklet";
37
+
38
+ if (!isDismissibleRef.current.value) return;
39
+ const ref = topToastRef.current.value;
40
+ if (!ref) return;
41
+ const {
42
+ slot
43
+ } = ref;
44
+ const bottom = isBottomRef.current.value;
45
+ const rawY = event.translationY;
46
+ const dismissDrag = bottom ? rawY : -rawY;
47
+ const resistDrag = bottom ? -rawY : rawY;
48
+ if (dismissDrag > 0) {
49
+ const clampedY = bottom ? Math.min(rawY, MAX_DRAG_CLAMP) : Math.max(rawY, -MAX_DRAG_CLAMP);
50
+ slot.translationY.value = clampedY;
51
+ const shouldTriggerDismiss = dismissDrag > DISMISS_THRESHOLD || (bottom ? event.velocityY > DISMISS_VELOCITY_THRESHOLD : event.velocityY < -DISMISS_VELOCITY_THRESHOLD);
52
+ shouldDismiss.set(shouldTriggerDismiss);
53
+ } else {
54
+ const exponentialDrag = MAX_DRAG_RESISTANCE * (1 - Math.exp(-resistDrag / 250));
55
+ slot.translationY.value = bottom ? -Math.min(exponentialDrag, MAX_DRAG_RESISTANCE) : Math.min(exponentialDrag, MAX_DRAG_RESISTANCE);
56
+ shouldDismiss.value = false;
57
+ }
58
+ }).onEnd(() => {
59
+ "worklet";
60
+
61
+ if (!isDismissibleRef.current.value) return;
62
+ const ref = topToastRef.current.value;
63
+ if (!ref) return;
64
+ const {
65
+ slot
66
+ } = ref;
67
+ const bottom = isBottomRef.current.value;
68
+ if (shouldDismiss.value) {
69
+ slot.progress.value = withTiming(0, {
70
+ duration: EXIT_DURATION,
71
+ easing: EASING
72
+ });
73
+ const exitOffset = bottom ? SWIPE_EXIT_OFFSET : -SWIPE_EXIT_OFFSET;
74
+ slot.translationY.value = withTiming(slot.translationY.value + exitOffset, {
75
+ duration: EXIT_DURATION,
76
+ easing: EASING
77
+ });
78
+ scheduleOnRN(ref.dismiss);
79
+ } else {
80
+ slot.translationY.value = withTiming(0, {
81
+ duration: SPRING_BACK_DURATION,
82
+ easing: EASING
83
+ });
84
+ }
85
+ }), [shouldDismiss, isDismissibleRef, topToastRef, isBottomRef]);
86
+ const registerTopToast = useCallback(values => {
87
+ topToastRef.current.value = values;
88
+ }, [topToastRef]);
89
+ if (visibleToasts.length === 0) return null;
90
+ const inset = isBottom ? bottom : top;
91
+ const positionStyle = isBottom ? {
92
+ bottom: inset + theme.offset + 2
93
+ } : {
94
+ top: inset + theme.offset + 2
95
+ };
96
+ return /*#__PURE__*/_jsx(GestureDetector, {
97
+ gesture: panGesture,
98
+ children: /*#__PURE__*/_jsx(View, {
99
+ style: [styles.container, positionStyle],
100
+ pointerEvents: "box-none",
101
+ children: toastsWithIndex.map(({
102
+ toast,
103
+ index
104
+ }) => /*#__PURE__*/_jsx(MemoizedToastItem, {
105
+ toast: toast,
106
+ index: index,
107
+ theme: theme,
108
+ position: theme.position,
109
+ isTopToast: index === 0,
110
+ registerTopToast: registerTopToast
111
+ }, toast.id))
112
+ })
113
+ });
114
+ };
115
+ const ToastItem = ({
116
+ toast,
117
+ index,
118
+ theme,
119
+ position,
120
+ isTopToast,
121
+ registerTopToast
122
+ }) => {
123
+ const [slotIdx] = useState(() => getSlotIndex(toast.id));
124
+ const slot = animationPool[slotIdx];
125
+ const tracker = slotTrackers[slotIdx];
126
+ const isBottom = position === "bottom";
127
+ const entryFromY = isBottom ? ENTRY_OFFSET : -ENTRY_OFFSET;
128
+ const exitToY = isBottom ? EXIT_OFFSET : -EXIT_OFFSET;
129
+ const [wasLoading, setWasLoading] = useState(toast.type === "loading");
130
+ const [showIcon, setShowIcon] = useState(false);
131
+
132
+ // biome-ignore lint/correctness/useExhaustiveDependencies: mount-only effect
133
+ useEffect(() => {
134
+ slot.progress.value = 0;
135
+ slot.translationY.value = 0;
136
+ slot.stackIndex.value = index;
137
+ slot.progress.value = withTiming(1, {
138
+ duration: ENTRY_DURATION,
139
+ easing: EASING
140
+ });
141
+ const iconTimeout = setTimeout(() => setShowIcon(true), 50);
142
+ return () => {
143
+ clearTimeout(iconTimeout);
144
+ releaseSlot(toast.id);
145
+ };
146
+ }, []);
147
+ const dismissToast = useCallback(() => {
148
+ toastStore.hide(toast.id);
149
+ }, [toast.id]);
150
+ useEffect(() => {
151
+ let loadingTimeout = null;
152
+ if (toast.isExiting && !tracker.wasExiting) {
153
+ tracker.wasExiting = true;
154
+ slot.progress.value = withTiming(0, {
155
+ duration: EXIT_DURATION,
156
+ easing: EASING
157
+ });
158
+ slot.translationY.value = withTiming(exitToY, {
159
+ duration: EXIT_DURATION,
160
+ easing: EASING
161
+ });
162
+ }
163
+ if (tracker.initialized && index !== tracker.prevIndex) {
164
+ slot.stackIndex.value = withTiming(index, {
165
+ duration: STACK_TRANSITION_DURATION,
166
+ easing: EASING
167
+ });
168
+ }
169
+ tracker.prevIndex = index;
170
+ tracker.initialized = true;
171
+ if (toast.type === "loading") {
172
+ setWasLoading(true);
173
+ } else if (wasLoading) {
174
+ loadingTimeout = setTimeout(() => setWasLoading(false), ICON_ANIMATION_DURATION + 50);
175
+ }
176
+ if (isTopToast) {
177
+ registerTopToast({
178
+ slot: slot,
179
+ dismiss: dismissToast
180
+ });
181
+ }
182
+ return () => {
183
+ if (loadingTimeout) clearTimeout(loadingTimeout);
184
+ if (isTopToast) registerTopToast(null);
185
+ };
186
+ }, [toast.isExiting, index, slot, tracker, exitToY, toast.type, wasLoading, isTopToast, registerTopToast, dismissToast]);
187
+ const shouldAnimateIcon = wasLoading && toast.type !== "loading";
188
+ const animatedStyle = useAnimatedStyle(() => {
189
+ const baseTranslateY = interpolate(slot.progress.value, [0, 1], [entryFromY, 0]);
190
+ const stackOffsetY = isBottom ? slot.stackIndex.value * STACK_OFFSET_PER_ITEM : slot.stackIndex.value * -STACK_OFFSET_PER_ITEM;
191
+ const stackScale = 1 - slot.stackIndex.value * STACK_SCALE_PER_ITEM;
192
+ const finalTranslateY = baseTranslateY + slot.translationY.value + stackOffsetY;
193
+ const progressOpacity = interpolate(slot.progress.value, [0, 1], [0, 1]);
194
+ const dismissDirection = isBottom ? slot.translationY.value : -slot.translationY.value;
195
+ const dragOpacity = dismissDirection > 0 ? interpolate(dismissDirection, [0, 130], [1, 0], "clamp") : 1;
196
+ const opacity = progressOpacity * dragOpacity;
197
+ const dragScale = interpolate(Math.abs(slot.translationY.value), [0, 50], [1, 0.98], "clamp");
198
+ const scale = stackScale * dragScale;
199
+ return {
200
+ transform: [{
201
+ translateY: finalTranslateY
202
+ }, {
203
+ scale
204
+ }],
205
+ opacity,
206
+ zIndex: 1000 - Math.round(slot.stackIndex.value)
207
+ };
208
+ });
209
+ const {
210
+ options
211
+ } = toast;
212
+ const colors = theme.colors[toast.type];
213
+ if (options?.customContent !== undefined) {
214
+ const content = options.customContent;
215
+ return /*#__PURE__*/_jsx(Animated.View, {
216
+ style: [styles.toast, styles.customContentToast, isBottom ? styles.toastBottom : styles.toastTop, {
217
+ backgroundColor: colors.background
218
+ }, theme.toastStyle, options.style, animatedStyle],
219
+ children: typeof content === "function" ? content({
220
+ id: toast.id,
221
+ dismiss: dismissToast,
222
+ type: toast.type,
223
+ isExiting: !!toast.isExiting
224
+ }) : content
225
+ });
226
+ }
227
+ const shouldShowCloseButton = toast.type !== "loading" && (options?.showCloseButton ?? theme.showCloseButton);
228
+ return /*#__PURE__*/_jsxs(Animated.View, {
229
+ style: [styles.toast, isBottom ? styles.toastBottom : styles.toastTop, {
230
+ backgroundColor: colors.background
231
+ }, theme.rtl && styles.rtl, theme.toastStyle, options?.style, animatedStyle],
232
+ children: [/*#__PURE__*/_jsx(View, {
233
+ style: styles.iconContainer,
234
+ children: showIcon && (shouldAnimateIcon ? /*#__PURE__*/_jsx(AnimatedIcon, {
235
+ type: toast.type,
236
+ color: colors.accent,
237
+ custom: options?.icon,
238
+ config: theme.icons[toast.type]
239
+ }, toast.type) : resolveIcon(toast.type, colors.accent, options?.icon, theme.icons[toast.type]))
240
+ }), /*#__PURE__*/_jsxs(View, {
241
+ style: styles.textContainer,
242
+ children: [/*#__PURE__*/_jsx(Text, {
243
+ maxFontSizeMultiplier: 1.35,
244
+ allowFontScaling: false,
245
+ style: [styles.title, {
246
+ color: colors.accent
247
+ }, theme.rtl && {
248
+ textAlign: "right"
249
+ }, theme.titleStyle, options?.titleStyle],
250
+ children: toast.title
251
+ }), toast.description && /*#__PURE__*/_jsx(Text, {
252
+ allowFontScaling: false,
253
+ maxFontSizeMultiplier: 1.35,
254
+ style: [styles.description, theme.rtl && {
255
+ textAlign: "right"
256
+ }, theme.descriptionStyle, options?.descriptionStyle],
257
+ children: toast.description
258
+ })]
259
+ }), shouldShowCloseButton && /*#__PURE__*/_jsx(Pressable, {
260
+ style: styles.closeButton,
261
+ onPress: dismissToast,
262
+ hitSlop: 12,
263
+ children: /*#__PURE__*/_jsx(CloseIcon, {
264
+ width: 20,
265
+ height: 20
266
+ })
267
+ })]
268
+ });
269
+ };
270
+ const MemoizedToastItem = /*#__PURE__*/memo(ToastItem, (prev, next) => {
271
+ return prev.toast.id === next.toast.id && prev.toast.type === next.toast.type && prev.toast.title === next.toast.title && prev.toast.description === next.toast.description && prev.toast.isExiting === next.toast.isExiting && prev.index === next.index && prev.position === next.position && prev.theme === next.theme && prev.isTopToast === next.isTopToast;
272
+ });
273
+ const styles = StyleSheet.create({
274
+ container: {
275
+ position: "absolute",
276
+ left: 16,
277
+ right: 16,
278
+ zIndex: 1000
279
+ },
280
+ toast: {
281
+ flexDirection: "row",
282
+ alignItems: "center",
283
+ gap: 12,
284
+ minHeight: 36,
285
+ borderRadius: 20,
286
+ borderCurve: "continuous",
287
+ position: "absolute",
288
+ left: 0,
289
+ right: 0,
290
+ paddingHorizontal: 12,
291
+ paddingVertical: 10,
292
+ shadowColor: "#000",
293
+ shadowOffset: {
294
+ width: 0,
295
+ height: 8
296
+ },
297
+ shadowOpacity: 0.05,
298
+ shadowRadius: 24,
299
+ elevation: 8
300
+ },
301
+ customContentToast: {
302
+ padding: 0,
303
+ paddingHorizontal: 0,
304
+ paddingVertical: 0,
305
+ overflow: "hidden"
306
+ },
307
+ rtl: {
308
+ flexDirection: "row-reverse"
309
+ },
310
+ toastTop: {
311
+ top: 0
312
+ },
313
+ toastBottom: {
314
+ bottom: 0
315
+ },
316
+ iconContainer: {
317
+ width: 48,
318
+ height: 48,
319
+ alignItems: "center",
320
+ justifyContent: "center",
321
+ marginLeft: 8
322
+ },
323
+ textContainer: {
324
+ flex: 1,
325
+ gap: 1,
326
+ justifyContent: "center"
327
+ },
328
+ title: {
329
+ fontSize: 14,
330
+ fontWeight: "700",
331
+ lineHeight: 20
332
+ },
333
+ description: {
334
+ color: "#6B7280",
335
+ fontSize: 12,
336
+ fontWeight: "500",
337
+ lineHeight: 16
338
+ },
339
+ closeButton: {
340
+ padding: 4,
341
+ alignItems: "center",
342
+ justifyContent: "center"
343
+ }
344
+ });
345
+ //# sourceMappingURL=toast.js.map
@@ -1 +1,4 @@
1
- "use strict";export{};
1
+ "use strict";
2
+
3
+ export {};
4
+ //# sourceMappingURL=types.js.map
@@ -1 +1,67 @@
1
- "use strict";import{useEffect as t,useMemo as i,useRef as s,useState as e}from"react";import{makeMutable as o}from"react-native-reanimated";import{toastStore as n}from"./toast-store.js";export const useToastState=()=>{const[r,l]=e([]),[m,a]=e(()=>n.getTheme()),u=s(o(null)),b=s(o("bottom"===m.position)),c=s(o(!0)),p="bottom"===m.position,d=r.find(t=>!t.isExiting),f=d?.options?.dismissible??m.dismissible;t(()=>{const t=n.getState().visibleToasts,i=n.getTheme();l(t);const s=t.find(t=>!t.isExiting);b.current.value="bottom"===i.position,c.current.value=s?.options?.dismissible??i.dismissible;let e=null,o=null;const r=n.subscribe(t=>{e=t.visibleToasts,null===o&&(o=requestAnimationFrame(()=>{const t=e??n.getState().visibleToasts,i=n.getTheme();e&&(l(e),e=null),o=null,a(t=>t===i?t:i);const s=t.find(t=>!t.isExiting);b.current.value="bottom"===i.position,c.current.value=s?.options?.dismissible??i.dismissible}))});return r},[]);const g=i(()=>{const t=new Map;let i=0;for(const s of r)t.set(s.id,s.isExiting?-1:i),s.isExiting||i++;return[...r].reverse().map(i=>({toast:i,index:t.get(i.id)??0}))},[r]);return{visibleToasts:r,theme:m,toastsWithIndex:g,isBottom:p,isTopDismissible:f,topToastRef:u,isBottomRef:b,isDismissibleRef:c}};
1
+ "use strict";
2
+
3
+ import { useEffect, useMemo, useRef, useState } from "react";
4
+ import { makeMutable } from "react-native-reanimated";
5
+ import { toastStore } from "./toast-store.js";
6
+ export const useToastState = () => {
7
+ const [visibleToasts, setVisibleToasts] = useState([]);
8
+ const [theme, setTheme] = useState(() => toastStore.getTheme());
9
+ const topToastRef = useRef(makeMutable(null));
10
+ const isBottomRef = useRef(makeMutable(theme.position === "bottom"));
11
+ const isDismissibleRef = useRef(makeMutable(true));
12
+ const isBottom = theme.position === "bottom";
13
+ const topToast = visibleToasts.find(t => !t.isExiting);
14
+ const isTopDismissible = topToast?.options?.dismissible ?? theme.dismissible;
15
+ useEffect(() => {
16
+ const initialToasts = toastStore.getState().visibleToasts;
17
+ const initialTheme = toastStore.getTheme();
18
+ setVisibleToasts(initialToasts);
19
+ const initialTopToast = initialToasts.find(t => !t.isExiting);
20
+ isBottomRef.current.value = initialTheme.position === "bottom";
21
+ isDismissibleRef.current.value = initialTopToast?.options?.dismissible ?? initialTheme.dismissible;
22
+ let pendingToasts = null;
23
+ let rafId = null;
24
+ const unsubscribe = toastStore.subscribe(state => {
25
+ pendingToasts = state.visibleToasts;
26
+ if (rafId === null) {
27
+ rafId = requestAnimationFrame(() => {
28
+ const currentToasts = pendingToasts ?? toastStore.getState().visibleToasts;
29
+ const currentTheme = toastStore.getTheme();
30
+ if (pendingToasts) {
31
+ setVisibleToasts(pendingToasts);
32
+ pendingToasts = null;
33
+ }
34
+ rafId = null;
35
+ setTheme(prev => prev === currentTheme ? prev : currentTheme);
36
+ const topToast = currentToasts.find(t => !t.isExiting);
37
+ isBottomRef.current.value = currentTheme.position === "bottom";
38
+ isDismissibleRef.current.value = topToast?.options?.dismissible ?? currentTheme.dismissible;
39
+ });
40
+ }
41
+ });
42
+ return unsubscribe;
43
+ }, []);
44
+ const toastsWithIndex = useMemo(() => {
45
+ const indices = new Map();
46
+ let visualIndex = 0;
47
+ for (const t of visibleToasts) {
48
+ indices.set(t.id, t.isExiting ? -1 : visualIndex);
49
+ if (!t.isExiting) visualIndex++;
50
+ }
51
+ return [...visibleToasts].reverse().map(t => ({
52
+ toast: t,
53
+ index: indices.get(t.id) ?? 0
54
+ }));
55
+ }, [visibleToasts]);
56
+ return {
57
+ visibleToasts,
58
+ theme,
59
+ toastsWithIndex,
60
+ isBottom,
61
+ isTopDismissible,
62
+ topToastRef,
63
+ isBottomRef,
64
+ isDismissibleRef
65
+ };
66
+ };
67
+ //# sourceMappingURL=use-toast-state.js.map