@metamask/design-system-react-native 0.38.0 → 0.39.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.
@@ -28,6 +28,7 @@ exports.toast = exports.Toaster = void 0;
28
28
  const design_system_twrnc_preset_1 = require("@metamask/design-system-twrnc-preset");
29
29
  const react_1 = __importStar(require("react"));
30
30
  const react_native_1 = require("react-native");
31
+ const react_native_gesture_handler_1 = require("react-native-gesture-handler");
31
32
  const react_native_reanimated_1 = __importStar(require("react-native-reanimated"));
32
33
  const react_native_safe_area_context_1 = require("react-native-safe-area-context");
33
34
  const react_native_worklets_1 = require("react-native-worklets");
@@ -49,10 +50,23 @@ const ToasterComponent = (0, react_1.forwardRef)(({ twClassName, ...props }, ref
49
50
  const tw = (0, design_system_twrnc_preset_1.useTailwind)();
50
51
  const [toastOptions, setToastOptions] = (0, react_1.useState)(undefined);
51
52
  const replacementTimerRef = (0, react_1.useRef)(null);
53
+ const autoDismissTimeoutRef = (0, react_1.useRef)(null);
54
+ const animationStartedRef = (0, react_1.useRef)(false);
55
+ const visibleAtRef = (0, react_1.useRef)(null);
56
+ const hasNoTimeoutRef = (0, react_1.useRef)(false);
57
+ // Invalidates queued spring-back resumes after replace/dismiss. A completed
58
+ // spring cannot be cancelled, so scheduleOnRN(resume) can still run later.
59
+ const toastGenerationRef = (0, react_1.useRef)(0);
52
60
  const { top: topInset } = (0, react_native_safe_area_context_1.useSafeAreaInsets)();
53
61
  const toastHeight = (0, react_native_reanimated_1.useSharedValue)(screenHeight);
62
+ const hiddenTranslateY = (0, react_native_reanimated_1.useSharedValue)(-screenHeight);
63
+ const visibleTranslateY = (0, react_native_reanimated_1.useSharedValue)(0);
64
+ const gestureStartY = (0, react_native_reanimated_1.useSharedValue)(0);
54
65
  const translateYProgress = (0, react_native_reanimated_1.useSharedValue)(-screenHeight);
66
+ const isDismissing = (0, react_native_reanimated_1.useSharedValue)(false);
67
+ const toastGeneration = (0, react_native_reanimated_1.useSharedValue)(0);
55
68
  const topOffset = toastOptions?.topOffset ?? 0;
69
+ hasNoTimeoutRef.current = Boolean(toastOptions?.hasNoTimeout);
56
70
  const animatedStyle = (0, react_native_reanimated_1.useAnimatedStyle)(() => ({
57
71
  transform: [{ translateY: translateYProgress.value + topOffset }],
58
72
  }));
@@ -61,16 +75,78 @@ const ToasterComponent = (0, react_1.forwardRef)(({ twClassName, ...props }, ref
61
75
  animatedStyle,
62
76
  ], [tw, animatedStyle]);
63
77
  const innerRef = (0, react_1.useRef)(null);
64
- const resetState = () => setToastOptions(undefined);
78
+ const clearScheduledAutoDismiss = () => {
79
+ if (autoDismissTimeoutRef.current !== null) {
80
+ clearTimeout(autoDismissTimeoutRef.current);
81
+ autoDismissTimeoutRef.current = null;
82
+ }
83
+ };
84
+ const bumpToastGeneration = () => {
85
+ toastGenerationRef.current += 1;
86
+ toastGeneration.value = toastGenerationRef.current;
87
+ };
88
+ const resetState = () => {
89
+ animationStartedRef.current = false;
90
+ visibleAtRef.current = null;
91
+ isDismissing.value = false;
92
+ clearScheduledAutoDismiss();
93
+ setToastOptions(undefined);
94
+ };
95
+ const startDismissAnimation = () => {
96
+ bumpToastGeneration();
97
+ clearScheduledAutoDismiss();
98
+ isDismissing.value = true;
99
+ visibleAtRef.current = null;
100
+ translateYProgress.value = (0, react_native_reanimated_1.withSpring)(hiddenTranslateY.value, Toast_constants_1.TOAST_SPRING_CONFIG, (finished) => {
101
+ if (finished) {
102
+ (0, react_native_worklets_1.scheduleOnRN)(resetState);
103
+ }
104
+ });
105
+ };
106
+ const scheduleAutoDismiss = (delayMs) => {
107
+ clearScheduledAutoDismiss();
108
+ autoDismissTimeoutRef.current = setTimeout(() => {
109
+ autoDismissTimeoutRef.current = null;
110
+ startDismissAnimation();
111
+ }, delayMs);
112
+ };
113
+ const beginAutoDismiss = () => {
114
+ visibleAtRef.current = Date.now();
115
+ scheduleAutoDismiss(Toast_constants_1.TOAST_VISIBILITY_DURATION);
116
+ };
117
+ const resumeAutoDismissAfterSwipe = () => {
118
+ if (hasNoTimeoutRef.current || isDismissing.value) {
119
+ return;
120
+ }
121
+ // Entrance was interrupted before auto-dismiss started — start a full timer.
122
+ if (visibleAtRef.current === null) {
123
+ beginAutoDismiss();
124
+ return;
125
+ }
126
+ const elapsed = Date.now() - visibleAtRef.current;
127
+ if (elapsed >= Toast_constants_1.TOAST_VISIBILITY_DURATION) {
128
+ startDismissAnimation();
129
+ return;
130
+ }
131
+ scheduleAutoDismiss(Toast_constants_1.TOAST_VISIBILITY_DURATION - elapsed);
132
+ };
65
133
  const showToast = (options) => {
134
+ bumpToastGeneration();
66
135
  let timeoutDuration = 0;
67
136
  const normalizedOptions = {
68
137
  hasNoTimeout: false,
69
138
  ...options,
70
139
  };
71
140
  if (toastOptions) {
141
+ // Always clear any pending auto-dismiss from the previous toast, including
142
+ // when the replacement is persistent (hasNoTimeout). Otherwise the old
143
+ // timer can fire and dismiss the new toast.
144
+ clearScheduledAutoDismiss();
72
145
  (0, react_native_reanimated_1.cancelAnimation)(translateYProgress);
73
146
  timeoutDuration = 100;
147
+ animationStartedRef.current = false;
148
+ visibleAtRef.current = null;
149
+ isDismissing.value = false;
74
150
  setToastOptions(undefined);
75
151
  }
76
152
  if (replacementTimerRef.current !== null) {
@@ -86,10 +162,83 @@ const ToasterComponent = (0, react_1.forwardRef)(({ twClassName, ...props }, ref
86
162
  clearTimeout(replacementTimerRef.current);
87
163
  replacementTimerRef.current = null;
88
164
  }
89
- translateYProgress.value = (0, react_native_reanimated_1.withSpring)(getHiddenTranslateY(toastHeight.value, topOffset), Toast_constants_1.TOAST_SPRING_CONFIG, () => {
90
- (0, react_native_worklets_1.scheduleOnRN)(resetState);
91
- });
165
+ startDismissAnimation();
166
+ };
167
+ const closeToastRef = (0, react_1.useRef)(closeToast);
168
+ const resumeAutoDismissAfterSwipeRef = (0, react_1.useRef)(resumeAutoDismissAfterSwipe);
169
+ const clearScheduledAutoDismissRef = (0, react_1.useRef)(clearScheduledAutoDismiss);
170
+ closeToastRef.current = closeToast;
171
+ resumeAutoDismissAfterSwipeRef.current = resumeAutoDismissAfterSwipe;
172
+ clearScheduledAutoDismissRef.current = clearScheduledAutoDismiss;
173
+ const dismissToastFromSwipe = () => {
174
+ closeToastRef.current();
175
+ };
176
+ const resumeAutoDismissFromSwipe = (generation) => {
177
+ // Ignore resumes from a toast that was replaced or dismissed after the
178
+ // spring-back started (completed springs cannot be cancelled).
179
+ if (generation !== toastGenerationRef.current) {
180
+ return;
181
+ }
182
+ resumeAutoDismissAfterSwipeRef.current();
183
+ };
184
+ const clearScheduledAutoDismissFromSwipe = () => {
185
+ clearScheduledAutoDismissRef.current();
92
186
  };
187
+ const swipeGesture = (0, react_1.useMemo)(() =>
188
+ // These gesture callbacks need explicit 'worklet' directives because this
189
+ // package ships a pre-built dist compiled by ts-bridge (tsc), which emits the
190
+ // gesture chain as a namespaced call (react_native_gesture_handler_1.Gesture).
191
+ // The consumer's Reanimated/Worklets Babel plugin does run over dist, but its
192
+ // gesture auto-detection doesn't recognize that compiled namespaced form.
193
+ react_native_gesture_handler_1.Gesture.Pan()
194
+ .activeOffsetY(Toast_constants_1.TOAST_SWIPE_ACTIVE_OFFSET_Y)
195
+ .failOffsetX([-Toast_constants_1.TOAST_SWIPE_FAIL_OFFSET_X, Toast_constants_1.TOAST_SWIPE_FAIL_OFFSET_X])
196
+ .onStart(() => {
197
+ 'worklet';
198
+ // Don't interrupt an in-progress dismiss animation.
199
+ if (isDismissing.value) {
200
+ return;
201
+ }
202
+ (0, react_native_worklets_1.scheduleOnRN)(clearScheduledAutoDismissFromSwipe);
203
+ (0, react_native_reanimated_1.cancelAnimation)(translateYProgress);
204
+ gestureStartY.value = translateYProgress.value;
205
+ })
206
+ .onUpdate((event) => {
207
+ 'worklet';
208
+ if (isDismissing.value) {
209
+ return;
210
+ }
211
+ const nextTranslateY = gestureStartY.value + event.translationY;
212
+ // Toast sits at the top; only allow dragging upward (more negative).
213
+ translateYProgress.value = Math.min(nextTranslateY, visibleTranslateY.value);
214
+ })
215
+ .onEnd((event) => {
216
+ 'worklet';
217
+ if (isDismissing.value) {
218
+ return;
219
+ }
220
+ const { translationY, velocityY } = event;
221
+ const dismissDistance = Math.max(toastHeight.value * Toast_constants_1.TOAST_DISMISS_DISTANCE_THRESHOLD, 24);
222
+ const hasReachedDismissOffset = translationY <= -dismissDistance;
223
+ const hasReachedSwipeThreshold = Math.abs(velocityY) > Toast_constants_1.TOAST_DISMISS_VELOCITY_THRESHOLD;
224
+ const isQuickDismissing = velocityY < 0;
225
+ const shouldDismiss = hasReachedDismissOffset ||
226
+ (hasReachedSwipeThreshold && isQuickDismissing);
227
+ if (shouldDismiss) {
228
+ (0, react_native_worklets_1.scheduleOnRN)(dismissToastFromSwipe);
229
+ return;
230
+ }
231
+ const generationAtSpringStart = toastGeneration.value;
232
+ translateYProgress.value = (0, react_native_reanimated_1.withSpring)(visibleTranslateY.value, Toast_constants_1.TOAST_SPRING_CONFIG, (finished) => {
233
+ // A new pan cancels this spring via cancelAnimation; only resume
234
+ // auto-dismiss when the spring-back completed naturally.
235
+ if (finished) {
236
+ (0, react_native_worklets_1.scheduleOnRN)(resumeAutoDismissFromSwipe, generationAtSpringStart);
237
+ }
238
+ });
239
+ }),
240
+ // Shared values and swipe JS wrappers are stable for the component lifetime.
241
+ []);
93
242
  innerRef.current = {
94
243
  closeToast,
95
244
  showToast,
@@ -114,30 +263,38 @@ const ToasterComponent = (0, react_1.forwardRef)(({ twClassName, ...props }, ref
114
263
  .join(' ');
115
264
  const onAnimatedViewLayout = (e) => {
116
265
  const { height } = e.nativeEvent.layout;
117
- const hiddenTranslateY = getHiddenTranslateY(height, topOffset);
118
- const visibleTranslateY = topInset + Toast_constants_1.TOAST_TOP_PADDING;
266
+ const nextHiddenTranslateY = getHiddenTranslateY(height, topOffset);
267
+ const nextVisibleTranslateY = topInset + Toast_constants_1.TOAST_TOP_PADDING;
268
+ hiddenTranslateY.value = nextHiddenTranslateY;
269
+ visibleTranslateY.value = nextVisibleTranslateY;
119
270
  toastHeight.value = height;
120
- translateYProgress.value = hiddenTranslateY;
271
+ if (animationStartedRef.current) {
272
+ return;
273
+ }
274
+ animationStartedRef.current = true;
275
+ translateYProgress.value = nextHiddenTranslateY;
121
276
  if (toastOptions.hasNoTimeout) {
122
- translateYProgress.value = (0, react_native_reanimated_1.withSpring)(visibleTranslateY, Toast_constants_1.TOAST_SPRING_CONFIG);
277
+ translateYProgress.value = (0, react_native_reanimated_1.withSpring)(nextVisibleTranslateY, Toast_constants_1.TOAST_SPRING_CONFIG);
123
278
  }
124
279
  else {
125
- translateYProgress.value = (0, react_native_reanimated_1.withSpring)(visibleTranslateY, Toast_constants_1.TOAST_SPRING_CONFIG, () => {
126
- translateYProgress.value = (0, react_native_reanimated_1.withDelay)(Toast_constants_1.TOAST_VISIBILITY_DURATION, (0, react_native_reanimated_1.withSpring)(hiddenTranslateY, Toast_constants_1.TOAST_SPRING_CONFIG, () => {
127
- (0, react_native_worklets_1.scheduleOnRN)(resetState);
128
- }));
280
+ translateYProgress.value = (0, react_native_reanimated_1.withSpring)(nextVisibleTranslateY, Toast_constants_1.TOAST_SPRING_CONFIG, (finished) => {
281
+ if (finished) {
282
+ (0, react_native_worklets_1.scheduleOnRN)(beginAutoDismiss);
283
+ }
129
284
  });
130
285
  }
131
286
  };
132
- return (<react_native_reanimated_1.default.View onLayout={onAnimatedViewLayout} style={baseStyle} {...props}>
133
- {actionButtonLabel && actionButtonOnPress ? (<Toast_1.Toast {...toastProps} actionButtonLabel={actionButtonLabel} actionButtonOnPress={actionButtonOnPress} onClose={() => {
287
+ return (<react_native_gesture_handler_1.GestureDetector gesture={swipeGesture}>
288
+ <react_native_reanimated_1.default.View onLayout={onAnimatedViewLayout} style={baseStyle} {...props}>
289
+ {actionButtonLabel && actionButtonOnPress ? (<Toast_1.Toast {...toastProps} actionButtonLabel={actionButtonLabel} actionButtonOnPress={actionButtonOnPress} onClose={() => {
134
290
  closeToast();
135
291
  toastOnClose?.();
136
292
  }} twClassName={toastTwClassNames}/>) : (<Toast_1.Toast {...restToastProps} onClose={() => {
137
293
  closeToast();
138
294
  toastOnClose?.();
139
295
  }} twClassName={toastTwClassNames}/>)}
140
- </react_native_reanimated_1.default.View>);
296
+ </react_native_reanimated_1.default.View>
297
+ </react_native_gesture_handler_1.GestureDetector>);
141
298
  });
142
299
  ToasterComponent.displayName = 'Toaster';
143
300
  exports.Toaster = ToasterComponent;
@@ -1 +1 @@
1
- {"version":3,"file":"Toaster.cjs","sourceRoot":"","sources":["../../../src/components/Toast/Toaster.tsx"],"names":[],"mappings":"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","sourcesContent":["// Third party dependencies.\nimport { useTailwind } from '@metamask/design-system-twrnc-preset';\nimport React, {\n forwardRef,\n useImperativeHandle,\n useLayoutEffect,\n useMemo,\n useRef,\n useState,\n} from 'react';\nimport type { RefObject } from 'react';\nimport { Dimensions } from 'react-native';\nimport type { LayoutChangeEvent, StyleProp, ViewStyle } from 'react-native';\nimport Animated, {\n cancelAnimation,\n useAnimatedStyle,\n useSharedValue,\n withDelay,\n withSpring,\n} from 'react-native-reanimated';\nimport { useSafeAreaInsets } from 'react-native-safe-area-context';\nimport { scheduleOnRN } from 'react-native-worklets';\n\n// Internal dependencies.\nimport { Toast } from './Toast';\nimport {\n TOAST_SPRING_CONFIG,\n TOAST_TOP_PADDING,\n TOAST_VISIBILITY_DURATION,\n} from './Toast.constants';\nimport type {\n ToastOptions,\n ToastProps,\n ToasterProps,\n ToasterRef,\n} from './Toast.types';\n\nconst screenHeight = Dimensions.get('window').height;\n\nlet registeredRef: RefObject<ToasterRef | null> | null = null;\n\nconst assertRegisteredRef = (method: 'dismiss' | 'toast'): ToasterRef => {\n if (!registeredRef?.current) {\n const invocation = method === 'toast' ? 'toast()' : `toast.${method}()`;\n throw new Error(\n `${invocation} called before <Toaster /> mounted. Render <Toaster /> once at the root of your app.`,\n );\n }\n return registeredRef.current;\n};\n\nconst getToastProps = ({\n hasNoTimeout: _hasNoTimeout,\n onClose: _onClose,\n topOffset: _topOffset,\n twClassName: _twClassName,\n ...toastProps\n}: ToastOptions): Omit<ToastProps, 'twClassName'> => toastProps;\n\nconst getHiddenTranslateY = (height: number, offset: number) =>\n -(height + offset);\n\nconst ToasterComponent = forwardRef<ToasterRef, ToasterProps>(\n ({ twClassName, ...props }, ref) => {\n const tw = useTailwind();\n const [toastOptions, setToastOptions] = useState<ToastOptions | undefined>(\n undefined,\n );\n const replacementTimerRef = useRef<ReturnType<typeof setTimeout> | null>(\n null,\n );\n const { top: topInset } = useSafeAreaInsets();\n const toastHeight = useSharedValue(screenHeight);\n const translateYProgress = useSharedValue(-screenHeight);\n const topOffset = toastOptions?.topOffset ?? 0;\n const animatedStyle = useAnimatedStyle(() => ({\n transform: [{ translateY: translateYProgress.value + topOffset }],\n }));\n const baseStyle = useMemo(\n () =>\n [\n tw.style('absolute left-4 right-4 top-0'),\n animatedStyle,\n ] as StyleProp<ViewStyle>,\n [tw, animatedStyle],\n );\n const innerRef = useRef<ToasterRef | null>(null);\n\n const resetState = () => setToastOptions(undefined);\n\n const showToast = (options: ToastOptions) => {\n let timeoutDuration = 0;\n const normalizedOptions = {\n hasNoTimeout: false,\n ...options,\n };\n if (toastOptions) {\n cancelAnimation(translateYProgress);\n timeoutDuration = 100;\n setToastOptions(undefined);\n }\n if (replacementTimerRef.current !== null) {\n clearTimeout(replacementTimerRef.current);\n }\n replacementTimerRef.current = setTimeout(() => {\n replacementTimerRef.current = null;\n setToastOptions(normalizedOptions);\n }, timeoutDuration);\n };\n\n const closeToast = () => {\n if (replacementTimerRef.current !== null) {\n clearTimeout(replacementTimerRef.current);\n replacementTimerRef.current = null;\n }\n translateYProgress.value = withSpring(\n getHiddenTranslateY(toastHeight.value, topOffset),\n TOAST_SPRING_CONFIG,\n () => {\n scheduleOnRN(resetState);\n },\n );\n };\n\n innerRef.current = {\n closeToast,\n showToast,\n };\n\n useImperativeHandle(ref, () => innerRef.current as ToasterRef);\n\n useLayoutEffect(() => {\n registeredRef = innerRef;\n return () => {\n if (registeredRef === innerRef) {\n registeredRef = null;\n }\n };\n }, []);\n\n if (!toastOptions) {\n return null;\n }\n\n const { onClose: toastOnClose, twClassName: toastTwClassName } =\n toastOptions;\n const toastProps = getToastProps(toastOptions);\n const { actionButtonLabel, actionButtonOnPress, ...restToastProps } =\n toastProps;\n const toastTwClassNames = [twClassName, toastTwClassName]\n .filter(Boolean)\n .join(' ');\n\n const onAnimatedViewLayout = (e: LayoutChangeEvent) => {\n const { height } = e.nativeEvent.layout;\n const hiddenTranslateY = getHiddenTranslateY(height, topOffset);\n const visibleTranslateY = topInset + TOAST_TOP_PADDING;\n\n toastHeight.value = height;\n translateYProgress.value = hiddenTranslateY;\n\n if (toastOptions.hasNoTimeout) {\n translateYProgress.value = withSpring(\n visibleTranslateY,\n TOAST_SPRING_CONFIG,\n );\n } else {\n translateYProgress.value = withSpring(\n visibleTranslateY,\n TOAST_SPRING_CONFIG,\n () => {\n translateYProgress.value = withDelay(\n TOAST_VISIBILITY_DURATION,\n withSpring(hiddenTranslateY, TOAST_SPRING_CONFIG, () => {\n scheduleOnRN(resetState);\n }),\n );\n },\n );\n }\n };\n\n return (\n <Animated.View\n onLayout={onAnimatedViewLayout}\n style={baseStyle}\n {...props}\n >\n {actionButtonLabel && actionButtonOnPress ? (\n <Toast\n {...toastProps}\n actionButtonLabel={actionButtonLabel}\n actionButtonOnPress={actionButtonOnPress}\n onClose={() => {\n closeToast();\n toastOnClose?.();\n }}\n twClassName={toastTwClassNames}\n />\n ) : (\n <Toast\n {...restToastProps}\n onClose={() => {\n closeToast();\n toastOnClose?.();\n }}\n twClassName={toastTwClassNames}\n />\n )}\n </Animated.View>\n );\n },\n);\n\nToasterComponent.displayName = 'Toaster';\n\ntype ToastFunction = ((options: ToastOptions) => void) & {\n dismiss: () => void;\n};\n\nexport const Toaster = ToasterComponent;\n\nexport const toast = ((options: ToastOptions) => {\n assertRegisteredRef('toast').showToast(options);\n}) as ToastFunction;\n\ntoast.dismiss = () => {\n assertRegisteredRef('dismiss').closeToast();\n};\n"]}
1
+ {"version":3,"file":"Toaster.cjs","sourceRoot":"","sources":["../../../src/components/Toast/Toaster.tsx"],"names":[],"mappings":"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","sourcesContent":["// Third party dependencies.\nimport { useTailwind } from '@metamask/design-system-twrnc-preset';\nimport React, {\n forwardRef,\n useImperativeHandle,\n useLayoutEffect,\n useMemo,\n useRef,\n useState,\n} from 'react';\nimport type { RefObject } from 'react';\nimport { Dimensions } from 'react-native';\nimport type { LayoutChangeEvent, StyleProp, ViewStyle } from 'react-native';\nimport { Gesture, GestureDetector } from 'react-native-gesture-handler';\nimport Animated, {\n cancelAnimation,\n useAnimatedStyle,\n useSharedValue,\n withSpring,\n} from 'react-native-reanimated';\nimport { useSafeAreaInsets } from 'react-native-safe-area-context';\nimport { scheduleOnRN } from 'react-native-worklets';\n\n// Internal dependencies.\nimport { Toast } from './Toast';\nimport {\n TOAST_DISMISS_DISTANCE_THRESHOLD,\n TOAST_DISMISS_VELOCITY_THRESHOLD,\n TOAST_SPRING_CONFIG,\n TOAST_SWIPE_ACTIVE_OFFSET_Y,\n TOAST_SWIPE_FAIL_OFFSET_X,\n TOAST_TOP_PADDING,\n TOAST_VISIBILITY_DURATION,\n} from './Toast.constants';\nimport type {\n ToastOptions,\n ToastProps,\n ToasterProps,\n ToasterRef,\n} from './Toast.types';\n\nconst screenHeight = Dimensions.get('window').height;\n\nlet registeredRef: RefObject<ToasterRef | null> | null = null;\n\nconst assertRegisteredRef = (method: 'dismiss' | 'toast'): ToasterRef => {\n if (!registeredRef?.current) {\n const invocation = method === 'toast' ? 'toast()' : `toast.${method}()`;\n throw new Error(\n `${invocation} called before <Toaster /> mounted. Render <Toaster /> once at the root of your app.`,\n );\n }\n return registeredRef.current;\n};\n\nconst getToastProps = ({\n hasNoTimeout: _hasNoTimeout,\n onClose: _onClose,\n topOffset: _topOffset,\n twClassName: _twClassName,\n ...toastProps\n}: ToastOptions): Omit<ToastProps, 'twClassName'> => toastProps;\n\nconst getHiddenTranslateY = (height: number, offset: number) =>\n -(height + offset);\n\nconst ToasterComponent = forwardRef<ToasterRef, ToasterProps>(\n ({ twClassName, ...props }, ref) => {\n const tw = useTailwind();\n const [toastOptions, setToastOptions] = useState<ToastOptions | undefined>(\n undefined,\n );\n const replacementTimerRef = useRef<ReturnType<typeof setTimeout> | null>(\n null,\n );\n const autoDismissTimeoutRef = useRef<ReturnType<typeof setTimeout> | null>(\n null,\n );\n const animationStartedRef = useRef(false);\n const visibleAtRef = useRef<number | null>(null);\n const hasNoTimeoutRef = useRef(false);\n // Invalidates queued spring-back resumes after replace/dismiss. A completed\n // spring cannot be cancelled, so scheduleOnRN(resume) can still run later.\n const toastGenerationRef = useRef(0);\n const { top: topInset } = useSafeAreaInsets();\n const toastHeight = useSharedValue(screenHeight);\n const hiddenTranslateY = useSharedValue(-screenHeight);\n const visibleTranslateY = useSharedValue(0);\n const gestureStartY = useSharedValue(0);\n const translateYProgress = useSharedValue(-screenHeight);\n const isDismissing = useSharedValue(false);\n const toastGeneration = useSharedValue(0);\n const topOffset = toastOptions?.topOffset ?? 0;\n hasNoTimeoutRef.current = Boolean(toastOptions?.hasNoTimeout);\n const animatedStyle = useAnimatedStyle(() => ({\n transform: [{ translateY: translateYProgress.value + topOffset }],\n }));\n const baseStyle = useMemo(\n () =>\n [\n tw.style('absolute left-4 right-4 top-0'),\n animatedStyle,\n ] as StyleProp<ViewStyle>,\n [tw, animatedStyle],\n );\n const innerRef = useRef<ToasterRef | null>(null);\n\n const clearScheduledAutoDismiss = () => {\n if (autoDismissTimeoutRef.current !== null) {\n clearTimeout(autoDismissTimeoutRef.current);\n autoDismissTimeoutRef.current = null;\n }\n };\n\n const bumpToastGeneration = () => {\n toastGenerationRef.current += 1;\n toastGeneration.value = toastGenerationRef.current;\n };\n\n const resetState = () => {\n animationStartedRef.current = false;\n visibleAtRef.current = null;\n isDismissing.value = false;\n clearScheduledAutoDismiss();\n setToastOptions(undefined);\n };\n\n const startDismissAnimation = () => {\n bumpToastGeneration();\n clearScheduledAutoDismiss();\n isDismissing.value = true;\n visibleAtRef.current = null;\n translateYProgress.value = withSpring(\n hiddenTranslateY.value,\n TOAST_SPRING_CONFIG,\n (finished) => {\n if (finished) {\n scheduleOnRN(resetState);\n }\n },\n );\n };\n\n const scheduleAutoDismiss = (delayMs: number) => {\n clearScheduledAutoDismiss();\n autoDismissTimeoutRef.current = setTimeout(() => {\n autoDismissTimeoutRef.current = null;\n startDismissAnimation();\n }, delayMs);\n };\n\n const beginAutoDismiss = () => {\n visibleAtRef.current = Date.now();\n scheduleAutoDismiss(TOAST_VISIBILITY_DURATION);\n };\n\n const resumeAutoDismissAfterSwipe = () => {\n if (hasNoTimeoutRef.current || isDismissing.value) {\n return;\n }\n\n // Entrance was interrupted before auto-dismiss started — start a full timer.\n if (visibleAtRef.current === null) {\n beginAutoDismiss();\n return;\n }\n\n const elapsed = Date.now() - visibleAtRef.current;\n if (elapsed >= TOAST_VISIBILITY_DURATION) {\n startDismissAnimation();\n return;\n }\n\n scheduleAutoDismiss(TOAST_VISIBILITY_DURATION - elapsed);\n };\n\n const showToast = (options: ToastOptions) => {\n bumpToastGeneration();\n let timeoutDuration = 0;\n const normalizedOptions = {\n hasNoTimeout: false,\n ...options,\n };\n if (toastOptions) {\n // Always clear any pending auto-dismiss from the previous toast, including\n // when the replacement is persistent (hasNoTimeout). Otherwise the old\n // timer can fire and dismiss the new toast.\n clearScheduledAutoDismiss();\n cancelAnimation(translateYProgress);\n timeoutDuration = 100;\n animationStartedRef.current = false;\n visibleAtRef.current = null;\n isDismissing.value = false;\n setToastOptions(undefined);\n }\n if (replacementTimerRef.current !== null) {\n clearTimeout(replacementTimerRef.current);\n }\n replacementTimerRef.current = setTimeout(() => {\n replacementTimerRef.current = null;\n setToastOptions(normalizedOptions);\n }, timeoutDuration);\n };\n\n const closeToast = () => {\n if (replacementTimerRef.current !== null) {\n clearTimeout(replacementTimerRef.current);\n replacementTimerRef.current = null;\n }\n startDismissAnimation();\n };\n\n const closeToastRef = useRef(closeToast);\n const resumeAutoDismissAfterSwipeRef = useRef(resumeAutoDismissAfterSwipe);\n const clearScheduledAutoDismissRef = useRef(clearScheduledAutoDismiss);\n closeToastRef.current = closeToast;\n resumeAutoDismissAfterSwipeRef.current = resumeAutoDismissAfterSwipe;\n clearScheduledAutoDismissRef.current = clearScheduledAutoDismiss;\n\n const dismissToastFromSwipe = () => {\n closeToastRef.current();\n };\n\n const resumeAutoDismissFromSwipe = (generation: number) => {\n // Ignore resumes from a toast that was replaced or dismissed after the\n // spring-back started (completed springs cannot be cancelled).\n if (generation !== toastGenerationRef.current) {\n return;\n }\n resumeAutoDismissAfterSwipeRef.current();\n };\n\n const clearScheduledAutoDismissFromSwipe = () => {\n clearScheduledAutoDismissRef.current();\n };\n\n const swipeGesture = useMemo(\n () =>\n // These gesture callbacks need explicit 'worklet' directives because this\n // package ships a pre-built dist compiled by ts-bridge (tsc), which emits the\n // gesture chain as a namespaced call (react_native_gesture_handler_1.Gesture).\n // The consumer's Reanimated/Worklets Babel plugin does run over dist, but its\n // gesture auto-detection doesn't recognize that compiled namespaced form.\n Gesture.Pan()\n .activeOffsetY(TOAST_SWIPE_ACTIVE_OFFSET_Y)\n .failOffsetX([-TOAST_SWIPE_FAIL_OFFSET_X, TOAST_SWIPE_FAIL_OFFSET_X])\n .onStart(() => {\n 'worklet';\n\n // Don't interrupt an in-progress dismiss animation.\n if (isDismissing.value) {\n return;\n }\n scheduleOnRN(clearScheduledAutoDismissFromSwipe);\n cancelAnimation(translateYProgress);\n gestureStartY.value = translateYProgress.value;\n })\n .onUpdate((event) => {\n 'worklet';\n\n if (isDismissing.value) {\n return;\n }\n const nextTranslateY = gestureStartY.value + event.translationY;\n // Toast sits at the top; only allow dragging upward (more negative).\n translateYProgress.value = Math.min(\n nextTranslateY,\n visibleTranslateY.value,\n );\n })\n .onEnd((event) => {\n 'worklet';\n\n if (isDismissing.value) {\n return;\n }\n const { translationY, velocityY } = event;\n const dismissDistance = Math.max(\n toastHeight.value * TOAST_DISMISS_DISTANCE_THRESHOLD,\n 24,\n );\n const hasReachedDismissOffset = translationY <= -dismissDistance;\n const hasReachedSwipeThreshold =\n Math.abs(velocityY) > TOAST_DISMISS_VELOCITY_THRESHOLD;\n const isQuickDismissing = velocityY < 0;\n\n const shouldDismiss =\n hasReachedDismissOffset ||\n (hasReachedSwipeThreshold && isQuickDismissing);\n\n if (shouldDismiss) {\n scheduleOnRN(dismissToastFromSwipe);\n return;\n }\n\n const generationAtSpringStart = toastGeneration.value;\n translateYProgress.value = withSpring(\n visibleTranslateY.value,\n TOAST_SPRING_CONFIG,\n (finished) => {\n // A new pan cancels this spring via cancelAnimation; only resume\n // auto-dismiss when the spring-back completed naturally.\n if (finished) {\n scheduleOnRN(\n resumeAutoDismissFromSwipe,\n generationAtSpringStart,\n );\n }\n },\n );\n }),\n // Shared values and swipe JS wrappers are stable for the component lifetime.\n [],\n );\n\n innerRef.current = {\n closeToast,\n showToast,\n };\n\n useImperativeHandle(ref, () => innerRef.current as ToasterRef);\n\n useLayoutEffect(() => {\n registeredRef = innerRef;\n return () => {\n if (registeredRef === innerRef) {\n registeredRef = null;\n }\n };\n }, []);\n\n if (!toastOptions) {\n return null;\n }\n\n const { onClose: toastOnClose, twClassName: toastTwClassName } =\n toastOptions;\n const toastProps = getToastProps(toastOptions);\n const { actionButtonLabel, actionButtonOnPress, ...restToastProps } =\n toastProps;\n const toastTwClassNames = [twClassName, toastTwClassName]\n .filter(Boolean)\n .join(' ');\n\n const onAnimatedViewLayout = (e: LayoutChangeEvent) => {\n const { height } = e.nativeEvent.layout;\n const nextHiddenTranslateY = getHiddenTranslateY(height, topOffset);\n const nextVisibleTranslateY = topInset + TOAST_TOP_PADDING;\n\n hiddenTranslateY.value = nextHiddenTranslateY;\n visibleTranslateY.value = nextVisibleTranslateY;\n toastHeight.value = height;\n\n if (animationStartedRef.current) {\n return;\n }\n\n animationStartedRef.current = true;\n translateYProgress.value = nextHiddenTranslateY;\n\n if (toastOptions.hasNoTimeout) {\n translateYProgress.value = withSpring(\n nextVisibleTranslateY,\n TOAST_SPRING_CONFIG,\n );\n } else {\n translateYProgress.value = withSpring(\n nextVisibleTranslateY,\n TOAST_SPRING_CONFIG,\n (finished) => {\n if (finished) {\n scheduleOnRN(beginAutoDismiss);\n }\n },\n );\n }\n };\n\n return (\n <GestureDetector gesture={swipeGesture}>\n <Animated.View\n onLayout={onAnimatedViewLayout}\n style={baseStyle}\n {...props}\n >\n {actionButtonLabel && actionButtonOnPress ? (\n <Toast\n {...toastProps}\n actionButtonLabel={actionButtonLabel}\n actionButtonOnPress={actionButtonOnPress}\n onClose={() => {\n closeToast();\n toastOnClose?.();\n }}\n twClassName={toastTwClassNames}\n />\n ) : (\n <Toast\n {...restToastProps}\n onClose={() => {\n closeToast();\n toastOnClose?.();\n }}\n twClassName={toastTwClassNames}\n />\n )}\n </Animated.View>\n </GestureDetector>\n );\n },\n);\n\nToasterComponent.displayName = 'Toaster';\n\ntype ToastFunction = ((options: ToastOptions) => void) & {\n dismiss: () => void;\n};\n\nexport const Toaster = ToasterComponent;\n\nexport const toast = ((options: ToastOptions) => {\n assertRegisteredRef('toast').showToast(options);\n}) as ToastFunction;\n\ntoast.dismiss = () => {\n assertRegisteredRef('dismiss').closeToast();\n};\n"]}
@@ -1 +1 @@
1
- {"version":3,"file":"Toaster.d.cts","sourceRoot":"","sources":["../../../src/components/Toast/Toaster.tsx"],"names":[],"mappings":"AAEA,OAAO,KAON,cAAc;AAqBf,OAAO,KAAK,EACV,YAAY,EAGZ,UAAU,EACX,0BAAsB;AAqLvB,KAAK,aAAa,GAAG,CAAC,CAAC,OAAO,EAAE,YAAY,KAAK,IAAI,CAAC,GAAG;IACvD,OAAO,EAAE,MAAM,IAAI,CAAC;CACrB,CAAC;AAEF,eAAO,MAAM,OAAO;;sFAAmB,CAAC;AAExC,eAAO,MAAM,KAAK,eAEC,CAAC"}
1
+ {"version":3,"file":"Toaster.d.cts","sourceRoot":"","sources":["../../../src/components/Toast/Toaster.tsx"],"names":[],"mappings":"AAEA,OAAO,KAON,cAAc;AAyBf,OAAO,KAAK,EACV,YAAY,EAGZ,UAAU,EACX,0BAAsB;AAuXvB,KAAK,aAAa,GAAG,CAAC,CAAC,OAAO,EAAE,YAAY,KAAK,IAAI,CAAC,GAAG;IACvD,OAAO,EAAE,MAAM,IAAI,CAAC;CACrB,CAAC;AAEF,eAAO,MAAM,OAAO;;sFAAmB,CAAC;AAExC,eAAO,MAAM,KAAK,eAEC,CAAC"}
@@ -1 +1 @@
1
- {"version":3,"file":"Toaster.d.mts","sourceRoot":"","sources":["../../../src/components/Toast/Toaster.tsx"],"names":[],"mappings":"AAEA,OAAO,KAON,cAAc;AAqBf,OAAO,KAAK,EACV,YAAY,EAGZ,UAAU,EACX,0BAAsB;AAqLvB,KAAK,aAAa,GAAG,CAAC,CAAC,OAAO,EAAE,YAAY,KAAK,IAAI,CAAC,GAAG;IACvD,OAAO,EAAE,MAAM,IAAI,CAAC;CACrB,CAAC;AAEF,eAAO,MAAM,OAAO;;sFAAmB,CAAC;AAExC,eAAO,MAAM,KAAK,eAEC,CAAC"}
1
+ {"version":3,"file":"Toaster.d.mts","sourceRoot":"","sources":["../../../src/components/Toast/Toaster.tsx"],"names":[],"mappings":"AAEA,OAAO,KAON,cAAc;AAyBf,OAAO,KAAK,EACV,YAAY,EAGZ,UAAU,EACX,0BAAsB;AAuXvB,KAAK,aAAa,GAAG,CAAC,CAAC,OAAO,EAAE,YAAY,KAAK,IAAI,CAAC,GAAG;IACvD,OAAO,EAAE,MAAM,IAAI,CAAC;CACrB,CAAC;AAEF,eAAO,MAAM,OAAO;;sFAAmB,CAAC;AAExC,eAAO,MAAM,KAAK,eAEC,CAAC"}
@@ -9,12 +9,13 @@ import { useTailwind } from "@metamask/design-system-twrnc-preset";
9
9
  import $React, { forwardRef, useImperativeHandle, useLayoutEffect, useMemo, useRef, useState } from "react";
10
10
  const React = $importDefault($React);
11
11
  import { Dimensions } from "react-native";
12
- import Animated, { cancelAnimation, useAnimatedStyle, useSharedValue, withDelay, withSpring } from "react-native-reanimated";
12
+ import { Gesture, GestureDetector } from "react-native-gesture-handler";
13
+ import Animated, { cancelAnimation, useAnimatedStyle, useSharedValue, withSpring } from "react-native-reanimated";
13
14
  import { useSafeAreaInsets } from "react-native-safe-area-context";
14
15
  import { scheduleOnRN } from "react-native-worklets";
15
16
  // Internal dependencies.
16
17
  import { Toast } from "./Toast.mjs";
17
- import { TOAST_SPRING_CONFIG, TOAST_TOP_PADDING, TOAST_VISIBILITY_DURATION } from "./Toast.constants.mjs";
18
+ import { TOAST_DISMISS_DISTANCE_THRESHOLD, TOAST_DISMISS_VELOCITY_THRESHOLD, TOAST_SPRING_CONFIG, TOAST_SWIPE_ACTIVE_OFFSET_Y, TOAST_SWIPE_FAIL_OFFSET_X, TOAST_TOP_PADDING, TOAST_VISIBILITY_DURATION } from "./Toast.constants.mjs";
18
19
  const screenHeight = Dimensions.get('window').height;
19
20
  let registeredRef = null;
20
21
  const assertRegisteredRef = (method) => {
@@ -30,10 +31,23 @@ const ToasterComponent = forwardRef(({ twClassName, ...props }, ref) => {
30
31
  const tw = useTailwind();
31
32
  const [toastOptions, setToastOptions] = useState(undefined);
32
33
  const replacementTimerRef = useRef(null);
34
+ const autoDismissTimeoutRef = useRef(null);
35
+ const animationStartedRef = useRef(false);
36
+ const visibleAtRef = useRef(null);
37
+ const hasNoTimeoutRef = useRef(false);
38
+ // Invalidates queued spring-back resumes after replace/dismiss. A completed
39
+ // spring cannot be cancelled, so scheduleOnRN(resume) can still run later.
40
+ const toastGenerationRef = useRef(0);
33
41
  const { top: topInset } = useSafeAreaInsets();
34
42
  const toastHeight = useSharedValue(screenHeight);
43
+ const hiddenTranslateY = useSharedValue(-screenHeight);
44
+ const visibleTranslateY = useSharedValue(0);
45
+ const gestureStartY = useSharedValue(0);
35
46
  const translateYProgress = useSharedValue(-screenHeight);
47
+ const isDismissing = useSharedValue(false);
48
+ const toastGeneration = useSharedValue(0);
36
49
  const topOffset = toastOptions?.topOffset ?? 0;
50
+ hasNoTimeoutRef.current = Boolean(toastOptions?.hasNoTimeout);
37
51
  const animatedStyle = useAnimatedStyle(() => ({
38
52
  transform: [{ translateY: translateYProgress.value + topOffset }],
39
53
  }));
@@ -42,16 +56,78 @@ const ToasterComponent = forwardRef(({ twClassName, ...props }, ref) => {
42
56
  animatedStyle,
43
57
  ], [tw, animatedStyle]);
44
58
  const innerRef = useRef(null);
45
- const resetState = () => setToastOptions(undefined);
59
+ const clearScheduledAutoDismiss = () => {
60
+ if (autoDismissTimeoutRef.current !== null) {
61
+ clearTimeout(autoDismissTimeoutRef.current);
62
+ autoDismissTimeoutRef.current = null;
63
+ }
64
+ };
65
+ const bumpToastGeneration = () => {
66
+ toastGenerationRef.current += 1;
67
+ toastGeneration.value = toastGenerationRef.current;
68
+ };
69
+ const resetState = () => {
70
+ animationStartedRef.current = false;
71
+ visibleAtRef.current = null;
72
+ isDismissing.value = false;
73
+ clearScheduledAutoDismiss();
74
+ setToastOptions(undefined);
75
+ };
76
+ const startDismissAnimation = () => {
77
+ bumpToastGeneration();
78
+ clearScheduledAutoDismiss();
79
+ isDismissing.value = true;
80
+ visibleAtRef.current = null;
81
+ translateYProgress.value = withSpring(hiddenTranslateY.value, TOAST_SPRING_CONFIG, (finished) => {
82
+ if (finished) {
83
+ scheduleOnRN(resetState);
84
+ }
85
+ });
86
+ };
87
+ const scheduleAutoDismiss = (delayMs) => {
88
+ clearScheduledAutoDismiss();
89
+ autoDismissTimeoutRef.current = setTimeout(() => {
90
+ autoDismissTimeoutRef.current = null;
91
+ startDismissAnimation();
92
+ }, delayMs);
93
+ };
94
+ const beginAutoDismiss = () => {
95
+ visibleAtRef.current = Date.now();
96
+ scheduleAutoDismiss(TOAST_VISIBILITY_DURATION);
97
+ };
98
+ const resumeAutoDismissAfterSwipe = () => {
99
+ if (hasNoTimeoutRef.current || isDismissing.value) {
100
+ return;
101
+ }
102
+ // Entrance was interrupted before auto-dismiss started — start a full timer.
103
+ if (visibleAtRef.current === null) {
104
+ beginAutoDismiss();
105
+ return;
106
+ }
107
+ const elapsed = Date.now() - visibleAtRef.current;
108
+ if (elapsed >= TOAST_VISIBILITY_DURATION) {
109
+ startDismissAnimation();
110
+ return;
111
+ }
112
+ scheduleAutoDismiss(TOAST_VISIBILITY_DURATION - elapsed);
113
+ };
46
114
  const showToast = (options) => {
115
+ bumpToastGeneration();
47
116
  let timeoutDuration = 0;
48
117
  const normalizedOptions = {
49
118
  hasNoTimeout: false,
50
119
  ...options,
51
120
  };
52
121
  if (toastOptions) {
122
+ // Always clear any pending auto-dismiss from the previous toast, including
123
+ // when the replacement is persistent (hasNoTimeout). Otherwise the old
124
+ // timer can fire and dismiss the new toast.
125
+ clearScheduledAutoDismiss();
53
126
  cancelAnimation(translateYProgress);
54
127
  timeoutDuration = 100;
128
+ animationStartedRef.current = false;
129
+ visibleAtRef.current = null;
130
+ isDismissing.value = false;
55
131
  setToastOptions(undefined);
56
132
  }
57
133
  if (replacementTimerRef.current !== null) {
@@ -67,10 +143,83 @@ const ToasterComponent = forwardRef(({ twClassName, ...props }, ref) => {
67
143
  clearTimeout(replacementTimerRef.current);
68
144
  replacementTimerRef.current = null;
69
145
  }
70
- translateYProgress.value = withSpring(getHiddenTranslateY(toastHeight.value, topOffset), TOAST_SPRING_CONFIG, () => {
71
- scheduleOnRN(resetState);
72
- });
146
+ startDismissAnimation();
147
+ };
148
+ const closeToastRef = useRef(closeToast);
149
+ const resumeAutoDismissAfterSwipeRef = useRef(resumeAutoDismissAfterSwipe);
150
+ const clearScheduledAutoDismissRef = useRef(clearScheduledAutoDismiss);
151
+ closeToastRef.current = closeToast;
152
+ resumeAutoDismissAfterSwipeRef.current = resumeAutoDismissAfterSwipe;
153
+ clearScheduledAutoDismissRef.current = clearScheduledAutoDismiss;
154
+ const dismissToastFromSwipe = () => {
155
+ closeToastRef.current();
156
+ };
157
+ const resumeAutoDismissFromSwipe = (generation) => {
158
+ // Ignore resumes from a toast that was replaced or dismissed after the
159
+ // spring-back started (completed springs cannot be cancelled).
160
+ if (generation !== toastGenerationRef.current) {
161
+ return;
162
+ }
163
+ resumeAutoDismissAfterSwipeRef.current();
164
+ };
165
+ const clearScheduledAutoDismissFromSwipe = () => {
166
+ clearScheduledAutoDismissRef.current();
73
167
  };
168
+ const swipeGesture = useMemo(() =>
169
+ // These gesture callbacks need explicit 'worklet' directives because this
170
+ // package ships a pre-built dist compiled by ts-bridge (tsc), which emits the
171
+ // gesture chain as a namespaced call (react_native_gesture_handler_1.Gesture).
172
+ // The consumer's Reanimated/Worklets Babel plugin does run over dist, but its
173
+ // gesture auto-detection doesn't recognize that compiled namespaced form.
174
+ Gesture.Pan()
175
+ .activeOffsetY(TOAST_SWIPE_ACTIVE_OFFSET_Y)
176
+ .failOffsetX([-TOAST_SWIPE_FAIL_OFFSET_X, TOAST_SWIPE_FAIL_OFFSET_X])
177
+ .onStart(() => {
178
+ 'worklet';
179
+ // Don't interrupt an in-progress dismiss animation.
180
+ if (isDismissing.value) {
181
+ return;
182
+ }
183
+ scheduleOnRN(clearScheduledAutoDismissFromSwipe);
184
+ cancelAnimation(translateYProgress);
185
+ gestureStartY.value = translateYProgress.value;
186
+ })
187
+ .onUpdate((event) => {
188
+ 'worklet';
189
+ if (isDismissing.value) {
190
+ return;
191
+ }
192
+ const nextTranslateY = gestureStartY.value + event.translationY;
193
+ // Toast sits at the top; only allow dragging upward (more negative).
194
+ translateYProgress.value = Math.min(nextTranslateY, visibleTranslateY.value);
195
+ })
196
+ .onEnd((event) => {
197
+ 'worklet';
198
+ if (isDismissing.value) {
199
+ return;
200
+ }
201
+ const { translationY, velocityY } = event;
202
+ const dismissDistance = Math.max(toastHeight.value * TOAST_DISMISS_DISTANCE_THRESHOLD, 24);
203
+ const hasReachedDismissOffset = translationY <= -dismissDistance;
204
+ const hasReachedSwipeThreshold = Math.abs(velocityY) > TOAST_DISMISS_VELOCITY_THRESHOLD;
205
+ const isQuickDismissing = velocityY < 0;
206
+ const shouldDismiss = hasReachedDismissOffset ||
207
+ (hasReachedSwipeThreshold && isQuickDismissing);
208
+ if (shouldDismiss) {
209
+ scheduleOnRN(dismissToastFromSwipe);
210
+ return;
211
+ }
212
+ const generationAtSpringStart = toastGeneration.value;
213
+ translateYProgress.value = withSpring(visibleTranslateY.value, TOAST_SPRING_CONFIG, (finished) => {
214
+ // A new pan cancels this spring via cancelAnimation; only resume
215
+ // auto-dismiss when the spring-back completed naturally.
216
+ if (finished) {
217
+ scheduleOnRN(resumeAutoDismissFromSwipe, generationAtSpringStart);
218
+ }
219
+ });
220
+ }),
221
+ // Shared values and swipe JS wrappers are stable for the component lifetime.
222
+ []);
74
223
  innerRef.current = {
75
224
  closeToast,
76
225
  showToast,
@@ -95,30 +244,38 @@ const ToasterComponent = forwardRef(({ twClassName, ...props }, ref) => {
95
244
  .join(' ');
96
245
  const onAnimatedViewLayout = (e) => {
97
246
  const { height } = e.nativeEvent.layout;
98
- const hiddenTranslateY = getHiddenTranslateY(height, topOffset);
99
- const visibleTranslateY = topInset + TOAST_TOP_PADDING;
247
+ const nextHiddenTranslateY = getHiddenTranslateY(height, topOffset);
248
+ const nextVisibleTranslateY = topInset + TOAST_TOP_PADDING;
249
+ hiddenTranslateY.value = nextHiddenTranslateY;
250
+ visibleTranslateY.value = nextVisibleTranslateY;
100
251
  toastHeight.value = height;
101
- translateYProgress.value = hiddenTranslateY;
252
+ if (animationStartedRef.current) {
253
+ return;
254
+ }
255
+ animationStartedRef.current = true;
256
+ translateYProgress.value = nextHiddenTranslateY;
102
257
  if (toastOptions.hasNoTimeout) {
103
- translateYProgress.value = withSpring(visibleTranslateY, TOAST_SPRING_CONFIG);
258
+ translateYProgress.value = withSpring(nextVisibleTranslateY, TOAST_SPRING_CONFIG);
104
259
  }
105
260
  else {
106
- translateYProgress.value = withSpring(visibleTranslateY, TOAST_SPRING_CONFIG, () => {
107
- translateYProgress.value = withDelay(TOAST_VISIBILITY_DURATION, withSpring(hiddenTranslateY, TOAST_SPRING_CONFIG, () => {
108
- scheduleOnRN(resetState);
109
- }));
261
+ translateYProgress.value = withSpring(nextVisibleTranslateY, TOAST_SPRING_CONFIG, (finished) => {
262
+ if (finished) {
263
+ scheduleOnRN(beginAutoDismiss);
264
+ }
110
265
  });
111
266
  }
112
267
  };
113
- return (<Animated.View onLayout={onAnimatedViewLayout} style={baseStyle} {...props}>
114
- {actionButtonLabel && actionButtonOnPress ? (<Toast {...toastProps} actionButtonLabel={actionButtonLabel} actionButtonOnPress={actionButtonOnPress} onClose={() => {
268
+ return (<GestureDetector gesture={swipeGesture}>
269
+ <Animated.View onLayout={onAnimatedViewLayout} style={baseStyle} {...props}>
270
+ {actionButtonLabel && actionButtonOnPress ? (<Toast {...toastProps} actionButtonLabel={actionButtonLabel} actionButtonOnPress={actionButtonOnPress} onClose={() => {
115
271
  closeToast();
116
272
  toastOnClose?.();
117
273
  }} twClassName={toastTwClassNames}/>) : (<Toast {...restToastProps} onClose={() => {
118
274
  closeToast();
119
275
  toastOnClose?.();
120
276
  }} twClassName={toastTwClassNames}/>)}
121
- </Animated.View>);
277
+ </Animated.View>
278
+ </GestureDetector>);
122
279
  });
123
280
  ToasterComponent.displayName = 'Toaster';
124
281
  export const Toaster = ToasterComponent;