@cdx-ui/components 0.0.1-beta.79 → 0.0.1-beta.80

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.
Files changed (214) hide show
  1. package/lib/commonjs/animation/hooks/index.js +17 -0
  2. package/lib/commonjs/animation/hooks/index.js.map +1 -0
  3. package/lib/commonjs/animation/hooks/useCombinedAnimationDisabledState.js +50 -0
  4. package/lib/commonjs/animation/hooks/useCombinedAnimationDisabledState.js.map +1 -0
  5. package/lib/commonjs/animation/index.js +39 -0
  6. package/lib/commonjs/animation/index.js.map +1 -0
  7. package/lib/commonjs/animation/types.js +6 -0
  8. package/lib/commonjs/animation/types.js.map +1 -0
  9. package/lib/commonjs/animation/utils.js +223 -0
  10. package/lib/commonjs/animation/utils.js.map +1 -0
  11. package/lib/commonjs/components/CLAUDE.md +1 -1
  12. package/lib/commonjs/components/Card/index.js +45 -17
  13. package/lib/commonjs/components/Card/index.js.map +1 -1
  14. package/lib/commonjs/components/Toast/animation.js +329 -0
  15. package/lib/commonjs/components/Toast/animation.js.map +1 -0
  16. package/lib/commonjs/components/Toast/hooks.js +66 -0
  17. package/lib/commonjs/components/Toast/hooks.js.map +1 -0
  18. package/lib/commonjs/components/Toast/index.js +493 -0
  19. package/lib/commonjs/components/Toast/index.js.map +1 -0
  20. package/lib/commonjs/components/Toast/styles.js +141 -0
  21. package/lib/commonjs/components/Toast/styles.js.map +1 -0
  22. package/lib/commonjs/components/Toast/types.js +6 -0
  23. package/lib/commonjs/components/Toast/types.js.map +1 -0
  24. package/lib/commonjs/components/index.js +12 -0
  25. package/lib/commonjs/components/index.js.map +1 -1
  26. package/lib/commonjs/index.js +15 -0
  27. package/lib/commonjs/index.js.map +1 -1
  28. package/lib/commonjs/providers/animation-settings/index.js +31 -0
  29. package/lib/commonjs/providers/animation-settings/index.js.map +1 -0
  30. package/lib/commonjs/providers/animation-settings/provider.js +59 -0
  31. package/lib/commonjs/providers/animation-settings/provider.js.map +1 -0
  32. package/lib/commonjs/providers/animation-settings/types.js +6 -0
  33. package/lib/commonjs/providers/animation-settings/types.js.map +1 -0
  34. package/lib/commonjs/providers/forge-design/index.js +29 -0
  35. package/lib/commonjs/providers/forge-design/index.js.map +1 -0
  36. package/lib/commonjs/providers/forge-design/provider.js +71 -0
  37. package/lib/commonjs/providers/forge-design/provider.js.map +1 -0
  38. package/lib/commonjs/providers/forge-design/types.js +6 -0
  39. package/lib/commonjs/providers/forge-design/types.js.map +1 -0
  40. package/lib/commonjs/providers/text-component/index.js +28 -0
  41. package/lib/commonjs/providers/text-component/index.js.map +1 -0
  42. package/lib/commonjs/providers/text-component/provider.js +11 -0
  43. package/lib/commonjs/providers/text-component/provider.js.map +1 -0
  44. package/lib/commonjs/providers/text-component/types.js +6 -0
  45. package/lib/commonjs/providers/text-component/types.js.map +1 -0
  46. package/lib/commonjs/providers/toast/index.js +33 -0
  47. package/lib/commonjs/providers/toast/index.js.map +1 -0
  48. package/lib/commonjs/providers/toast/insets-container.js +160 -0
  49. package/lib/commonjs/providers/toast/insets-container.js.map +1 -0
  50. package/lib/commonjs/providers/toast/lifecycle.context.js +36 -0
  51. package/lib/commonjs/providers/toast/lifecycle.context.js.map +1 -0
  52. package/lib/commonjs/providers/toast/provider.js +508 -0
  53. package/lib/commonjs/providers/toast/provider.js.map +1 -0
  54. package/lib/commonjs/providers/toast/reducer.js +46 -0
  55. package/lib/commonjs/providers/toast/reducer.js.map +1 -0
  56. package/lib/commonjs/providers/toast/toast-config.context.js +29 -0
  57. package/lib/commonjs/providers/toast/toast-config.context.js.map +1 -0
  58. package/lib/commonjs/providers/toast/toast-item-renderer.js +43 -0
  59. package/lib/commonjs/providers/toast/toast-item-renderer.js.map +1 -0
  60. package/lib/commonjs/providers/toast/types.js +6 -0
  61. package/lib/commonjs/providers/toast/types.js.map +1 -0
  62. package/lib/module/animation/hooks/index.js +4 -0
  63. package/lib/module/animation/hooks/index.js.map +1 -0
  64. package/lib/module/animation/hooks/useCombinedAnimationDisabledState.js +47 -0
  65. package/lib/module/animation/hooks/useCombinedAnimationDisabledState.js.map +1 -0
  66. package/lib/module/animation/index.js +6 -0
  67. package/lib/module/animation/index.js.map +1 -0
  68. package/lib/module/animation/types.js +4 -0
  69. package/lib/module/animation/types.js.map +1 -0
  70. package/lib/module/animation/utils.js +212 -0
  71. package/lib/module/animation/utils.js.map +1 -0
  72. package/lib/module/components/CLAUDE.md +1 -1
  73. package/lib/module/components/Card/index.js +47 -19
  74. package/lib/module/components/Card/index.js.map +1 -1
  75. package/lib/module/components/Toast/animation.js +324 -0
  76. package/lib/module/components/Toast/animation.js.map +1 -0
  77. package/lib/module/components/Toast/hooks.js +61 -0
  78. package/lib/module/components/Toast/hooks.js.map +1 -0
  79. package/lib/module/components/Toast/index.js +487 -0
  80. package/lib/module/components/Toast/index.js.map +1 -0
  81. package/lib/module/components/Toast/styles.js +138 -0
  82. package/lib/module/components/Toast/styles.js.map +1 -0
  83. package/lib/module/components/Toast/types.js +4 -0
  84. package/lib/module/components/Toast/types.js.map +1 -0
  85. package/lib/module/components/index.js +1 -0
  86. package/lib/module/components/index.js.map +1 -1
  87. package/lib/module/index.js +1 -0
  88. package/lib/module/index.js.map +1 -1
  89. package/lib/module/providers/animation-settings/index.js +4 -0
  90. package/lib/module/providers/animation-settings/index.js.map +1 -0
  91. package/lib/module/providers/animation-settings/provider.js +52 -0
  92. package/lib/module/providers/animation-settings/provider.js.map +1 -0
  93. package/lib/module/providers/animation-settings/types.js +4 -0
  94. package/lib/module/providers/animation-settings/types.js.map +1 -0
  95. package/lib/module/providers/forge-design/index.js +5 -0
  96. package/lib/module/providers/forge-design/index.js.map +1 -0
  97. package/lib/module/providers/forge-design/provider.js +66 -0
  98. package/lib/module/providers/forge-design/provider.js.map +1 -0
  99. package/lib/module/providers/forge-design/types.js +4 -0
  100. package/lib/module/providers/forge-design/types.js.map +1 -0
  101. package/lib/module/providers/text-component/index.js +5 -0
  102. package/lib/module/providers/text-component/index.js.map +1 -0
  103. package/lib/module/providers/text-component/provider.js +6 -0
  104. package/lib/module/providers/text-component/provider.js.map +1 -0
  105. package/lib/module/providers/text-component/types.js +4 -0
  106. package/lib/module/providers/text-component/types.js.map +1 -0
  107. package/lib/module/providers/toast/index.js +6 -0
  108. package/lib/module/providers/toast/index.js.map +1 -0
  109. package/lib/module/providers/toast/insets-container.js +156 -0
  110. package/lib/module/providers/toast/insets-container.js.map +1 -0
  111. package/lib/module/providers/toast/lifecycle.context.js +32 -0
  112. package/lib/module/providers/toast/lifecycle.context.js.map +1 -0
  113. package/lib/module/providers/toast/provider.js +503 -0
  114. package/lib/module/providers/toast/provider.js.map +1 -0
  115. package/lib/module/providers/toast/reducer.js +42 -0
  116. package/lib/module/providers/toast/reducer.js.map +1 -0
  117. package/lib/module/providers/toast/toast-config.context.js +25 -0
  118. package/lib/module/providers/toast/toast-config.context.js.map +1 -0
  119. package/lib/module/providers/toast/toast-item-renderer.js +39 -0
  120. package/lib/module/providers/toast/toast-item-renderer.js.map +1 -0
  121. package/lib/module/providers/toast/types.js +4 -0
  122. package/lib/module/providers/toast/types.js.map +1 -0
  123. package/lib/typescript/animation/hooks/index.d.ts +2 -0
  124. package/lib/typescript/animation/hooks/index.d.ts.map +1 -0
  125. package/lib/typescript/animation/hooks/useCombinedAnimationDisabledState.d.ts +22 -0
  126. package/lib/typescript/animation/hooks/useCombinedAnimationDisabledState.d.ts.map +1 -0
  127. package/lib/typescript/animation/index.d.ts +4 -0
  128. package/lib/typescript/animation/index.d.ts.map +1 -0
  129. package/lib/typescript/animation/types.d.ts +105 -0
  130. package/lib/typescript/animation/types.d.ts.map +1 -0
  131. package/lib/typescript/animation/utils.d.ts +120 -0
  132. package/lib/typescript/animation/utils.d.ts.map +1 -0
  133. package/lib/typescript/components/Card/index.d.ts +4 -0
  134. package/lib/typescript/components/Card/index.d.ts.map +1 -1
  135. package/lib/typescript/components/Toast/animation.d.ts +21 -0
  136. package/lib/typescript/components/Toast/animation.d.ts.map +1 -0
  137. package/lib/typescript/components/Toast/hooks.d.ts +17 -0
  138. package/lib/typescript/components/Toast/hooks.d.ts.map +1 -0
  139. package/lib/typescript/components/Toast/index.d.ts +28 -0
  140. package/lib/typescript/components/Toast/index.d.ts.map +1 -0
  141. package/lib/typescript/components/Toast/styles.d.ts +73 -0
  142. package/lib/typescript/components/Toast/styles.d.ts.map +1 -0
  143. package/lib/typescript/components/Toast/types.d.ts +303 -0
  144. package/lib/typescript/components/Toast/types.d.ts.map +1 -0
  145. package/lib/typescript/components/index.d.ts +1 -0
  146. package/lib/typescript/components/index.d.ts.map +1 -1
  147. package/lib/typescript/index.d.ts +2 -0
  148. package/lib/typescript/index.d.ts.map +1 -1
  149. package/lib/typescript/providers/animation-settings/index.d.ts +3 -0
  150. package/lib/typescript/providers/animation-settings/index.d.ts.map +1 -0
  151. package/lib/typescript/providers/animation-settings/provider.d.ts +28 -0
  152. package/lib/typescript/providers/animation-settings/provider.d.ts.map +1 -0
  153. package/lib/typescript/providers/animation-settings/types.d.ts +35 -0
  154. package/lib/typescript/providers/animation-settings/types.d.ts.map +1 -0
  155. package/lib/typescript/providers/forge-design/index.d.ts +3 -0
  156. package/lib/typescript/providers/forge-design/index.d.ts.map +1 -0
  157. package/lib/typescript/providers/forge-design/provider.d.ts +24 -0
  158. package/lib/typescript/providers/forge-design/provider.d.ts.map +1 -0
  159. package/lib/typescript/providers/forge-design/types.d.ts +82 -0
  160. package/lib/typescript/providers/forge-design/types.d.ts.map +1 -0
  161. package/lib/typescript/providers/text-component/index.d.ts +3 -0
  162. package/lib/typescript/providers/text-component/index.d.ts.map +1 -0
  163. package/lib/typescript/providers/text-component/provider.d.ts +7 -0
  164. package/lib/typescript/providers/text-component/provider.d.ts.map +1 -0
  165. package/lib/typescript/providers/text-component/types.d.ts +51 -0
  166. package/lib/typescript/providers/text-component/types.d.ts.map +1 -0
  167. package/lib/typescript/providers/toast/index.d.ts +5 -0
  168. package/lib/typescript/providers/toast/index.d.ts.map +1 -0
  169. package/lib/typescript/providers/toast/insets-container.d.ts +72 -0
  170. package/lib/typescript/providers/toast/insets-container.d.ts.map +1 -0
  171. package/lib/typescript/providers/toast/lifecycle.context.d.ts +32 -0
  172. package/lib/typescript/providers/toast/lifecycle.context.d.ts.map +1 -0
  173. package/lib/typescript/providers/toast/provider.d.ts +32 -0
  174. package/lib/typescript/providers/toast/provider.d.ts.map +1 -0
  175. package/lib/typescript/providers/toast/reducer.d.ts +14 -0
  176. package/lib/typescript/providers/toast/reducer.d.ts.map +1 -0
  177. package/lib/typescript/providers/toast/toast-config.context.d.ts +20 -0
  178. package/lib/typescript/providers/toast/toast-config.context.d.ts.map +1 -0
  179. package/lib/typescript/providers/toast/toast-item-renderer.d.ts +7 -0
  180. package/lib/typescript/providers/toast/toast-item-renderer.d.ts.map +1 -0
  181. package/lib/typescript/providers/toast/types.d.ts +422 -0
  182. package/lib/typescript/providers/toast/types.d.ts.map +1 -0
  183. package/package.json +4 -4
  184. package/src/animation/hooks/index.ts +1 -0
  185. package/src/animation/hooks/useCombinedAnimationDisabledState.ts +46 -0
  186. package/src/animation/index.ts +3 -0
  187. package/src/animation/types.ts +128 -0
  188. package/src/animation/utils.ts +249 -0
  189. package/src/components/CLAUDE.md +1 -1
  190. package/src/components/Card/index.tsx +58 -28
  191. package/src/components/Toast/animation.ts +387 -0
  192. package/src/components/Toast/hooks.ts +92 -0
  193. package/src/components/Toast/index.tsx +520 -0
  194. package/src/components/Toast/styles.ts +186 -0
  195. package/src/components/Toast/types.ts +332 -0
  196. package/src/components/index.ts +1 -0
  197. package/src/index.ts +2 -0
  198. package/src/providers/animation-settings/index.ts +11 -0
  199. package/src/providers/animation-settings/provider.tsx +54 -0
  200. package/src/providers/animation-settings/types.ts +37 -0
  201. package/src/providers/forge-design/index.ts +2 -0
  202. package/src/providers/forge-design/provider.tsx +61 -0
  203. package/src/providers/forge-design/types.ts +84 -0
  204. package/src/providers/text-component/index.ts +2 -0
  205. package/src/providers/text-component/provider.tsx +7 -0
  206. package/src/providers/text-component/types.ts +52 -0
  207. package/src/providers/toast/index.ts +4 -0
  208. package/src/providers/toast/insets-container.tsx +230 -0
  209. package/src/providers/toast/lifecycle.context.tsx +37 -0
  210. package/src/providers/toast/provider.tsx +567 -0
  211. package/src/providers/toast/reducer.ts +40 -0
  212. package/src/providers/toast/toast-config.context.ts +25 -0
  213. package/src/providers/toast/toast-item-renderer.tsx +40 -0
  214. package/src/providers/toast/types.ts +431 -0
@@ -0,0 +1,387 @@
1
+ import { Platform, useWindowDimensions } from 'react-native';
2
+ import { Gesture } from 'react-native-gesture-handler';
3
+ import {
4
+ Easing,
5
+ EntryOrExitLayoutType,
6
+ Extrapolation,
7
+ FadeInDown,
8
+ FadeInUp,
9
+ interpolate,
10
+ Keyframe,
11
+ useAnimatedStyle,
12
+ useSharedValue,
13
+ withDecay,
14
+ withSpring,
15
+ withTiming,
16
+ } from 'react-native-reanimated';
17
+ import { scheduleOnRN } from 'react-native-worklets';
18
+ import {
19
+ useCombinedAnimationDisabledState,
20
+ getAnimationValueMergedConfig,
21
+ getAnimationValueProperty,
22
+ getIsAnimationDisabledValue,
23
+ getRootAnimationState,
24
+ } from '../../animation';
25
+ import type { UseToastRootAnimationOptions } from './types';
26
+
27
+ // --------------------------------------------------
28
+
29
+ export const enteringTop = FadeInUp.springify()
30
+ .withInitialValues({
31
+ opacity: 1,
32
+ transform: [{ translateY: -100 }],
33
+ })
34
+ .mass(3);
35
+
36
+ export const exitingTop = new Keyframe({
37
+ 0: {
38
+ opacity: 1,
39
+ transform: [{ translateY: 0 }, { scale: 1 }],
40
+ },
41
+ 100: {
42
+ opacity: 0.5,
43
+ transform: [{ translateY: -100 }, { scale: 0.97 }],
44
+ easing: Easing.bezier(0.4, 0, 1, 1),
45
+ },
46
+ }).duration(150);
47
+
48
+ export const enteringBottom = FadeInDown.springify()
49
+ .withInitialValues({
50
+ opacity: 1,
51
+ transform: [{ translateY: 100 }],
52
+ })
53
+ .mass(3);
54
+
55
+ export const exitingBottom = new Keyframe({
56
+ 0: {
57
+ opacity: 1,
58
+ transform: [{ translateY: 0 }, { scale: 1 }],
59
+ },
60
+ 100: {
61
+ opacity: 0.5,
62
+ transform: [{ translateY: 100 }, { scale: 0.97 }],
63
+ easing: Easing.bezier(0.4, 0, 1, 1),
64
+ },
65
+ }).duration(150);
66
+
67
+ export interface UseToastRootAnimationResult {
68
+ rContainerStyle: ReturnType<typeof useAnimatedStyle>;
69
+ isAllAnimationsDisabled: boolean;
70
+ entering: EntryOrExitLayoutType | undefined;
71
+ exiting: EntryOrExitLayoutType | undefined;
72
+ panGesture: ReturnType<typeof Gesture.Pan>;
73
+ }
74
+
75
+ // --------------------------------------------------
76
+
77
+ /**
78
+ * Animation hook for Toast root component
79
+ * Handles opacity, translateY, and scale animations based on toast index and placement
80
+ * Also handles gesture-based swipe to dismiss and rubber-band drag effects
81
+ */
82
+ export function useToastRootAnimation(
83
+ options: UseToastRootAnimationOptions,
84
+ ): UseToastRootAnimationResult {
85
+ const {
86
+ animation,
87
+ index,
88
+ total,
89
+ heights,
90
+ placement = 'bottom',
91
+ hide,
92
+ id,
93
+ isSwipeable = true,
94
+ maxVisibleToasts = 3,
95
+ } = options;
96
+
97
+ const { height: screenHeight } = useWindowDimensions();
98
+
99
+ const { animationConfig, isAnimationDisabled } = getRootAnimationState(animation);
100
+
101
+ const isAllAnimationsDisabled = useCombinedAnimationDisabledState(animation);
102
+
103
+ const isAnimationDisabledValue = getIsAnimationDisabledValue({
104
+ isAnimationDisabled,
105
+ isAllAnimationsDisabled,
106
+ });
107
+
108
+ // Entering animation
109
+ const enteringTopValue = getAnimationValueProperty({
110
+ animationValue: animationConfig?.entering,
111
+ property: 'top',
112
+ defaultValue: enteringTop,
113
+ });
114
+
115
+ const enteringBottomValue = getAnimationValueProperty({
116
+ animationValue: animationConfig?.entering,
117
+ property: 'bottom',
118
+ defaultValue: enteringBottom,
119
+ });
120
+
121
+ // Exiting animation
122
+ const exitingTopValue = getAnimationValueProperty({
123
+ animationValue: animationConfig?.exiting,
124
+ property: 'top',
125
+ defaultValue: exitingTop,
126
+ });
127
+
128
+ const exitingBottomValue = getAnimationValueProperty({
129
+ animationValue: animationConfig?.exiting,
130
+ property: 'bottom',
131
+ defaultValue: exitingBottom,
132
+ });
133
+
134
+ // Opacity animation
135
+ const opacityValue = getAnimationValueProperty({
136
+ animationValue: animationConfig?.opacity,
137
+ property: 'value',
138
+ defaultValue: [1, 0] as [number, number],
139
+ });
140
+
141
+ const opacityTimingConfig = getAnimationValueMergedConfig({
142
+ animationValue: animationConfig?.opacity,
143
+ property: 'timingConfig',
144
+ defaultValue: { duration: 300 },
145
+ });
146
+
147
+ // TranslateY animation
148
+ const translateYValue = getAnimationValueProperty({
149
+ animationValue: animationConfig?.translateY,
150
+ property: 'value',
151
+ defaultValue: [0, 10] as [number, number],
152
+ });
153
+
154
+ const translateYTimingConfig = getAnimationValueMergedConfig({
155
+ animationValue: animationConfig?.translateY,
156
+ property: 'timingConfig',
157
+ defaultValue: { duration: 300 },
158
+ });
159
+
160
+ // Scale animation
161
+ const scaleValue = getAnimationValueProperty({
162
+ animationValue: animationConfig?.scale,
163
+ property: 'value',
164
+ defaultValue: [1, 0.97] as [number, number],
165
+ });
166
+
167
+ const scaleTimingConfig = getAnimationValueMergedConfig({
168
+ animationValue: animationConfig?.scale,
169
+ property: 'timingConfig',
170
+ defaultValue: { duration: 300 },
171
+ });
172
+
173
+ // Gesture state shared values
174
+ const isDragging = useSharedValue(false);
175
+ const gestureTranslateY = useSharedValue(0);
176
+ const gestureScale = useSharedValue(1);
177
+
178
+ // Helper function to delay hide call based on velocity
179
+ const delayedHide = (toastId: string | undefined, velocity: number) => {
180
+ const velocityBasedTimeout = Math.min(200, Math.abs(velocity));
181
+ setTimeout(() => {
182
+ if (hide && toastId) {
183
+ hide(toastId);
184
+ }
185
+ }, velocityBasedTimeout);
186
+ };
187
+
188
+ // Create pan gesture handler
189
+ const panGesture = Gesture.Pan()
190
+ .enabled(!isAnimationDisabledValue && isSwipeable)
191
+ .onBegin(() => {
192
+ isDragging.set(true);
193
+ gestureTranslateY.set(0);
194
+ gestureScale.set(0.995);
195
+ })
196
+ .onChange((event) => {
197
+ if (!isDragging.get()) return;
198
+
199
+ const translationY = event.translationY;
200
+ const maxDragDistance = screenHeight;
201
+ const rubberEffectDistance = 40;
202
+
203
+ if (placement.startsWith('top')) {
204
+ // Top placement: swipe up (negative Y) to dismiss, drag down (positive Y) with rubber effect
205
+ if (translationY < 0) {
206
+ // Swiping up - direct translation for dismissal
207
+ gestureTranslateY.set(translationY);
208
+ } else if (translationY > 0) {
209
+ // Dragging down - apply rubber effect
210
+ const rubberTranslateY = interpolate(
211
+ translationY,
212
+ [0, maxDragDistance],
213
+ [0, rubberEffectDistance],
214
+ Extrapolation.CLAMP,
215
+ );
216
+ gestureTranslateY.set(rubberTranslateY);
217
+ }
218
+ } else {
219
+ // Bottom placement: swipe down (positive Y) to dismiss, drag up (negative Y) with rubber effect
220
+ if (translationY > 0) {
221
+ // Swiping down - direct translation for dismissal
222
+ gestureTranslateY.set(translationY);
223
+ } else if (translationY < 0) {
224
+ // Dragging up - apply rubber effect
225
+ const rubberTranslateY = interpolate(
226
+ Math.abs(translationY),
227
+ [0, maxDragDistance],
228
+ [0, rubberEffectDistance],
229
+ Extrapolation.CLAMP,
230
+ );
231
+ gestureTranslateY.set(-rubberTranslateY);
232
+ }
233
+ }
234
+ })
235
+ .onFinalize((event) => {
236
+ gestureScale.set(1);
237
+
238
+ const translationY = event.translationY;
239
+ const velocityY = event.velocityY;
240
+ const dismissThreshold = 50;
241
+ const velocityThreshold = 500;
242
+
243
+ // Check if dismissal threshold is met (distance > 50px OR velocity > 500)
244
+ const shouldDismiss =
245
+ Math.abs(translationY) > dismissThreshold || Math.abs(velocityY) > velocityThreshold;
246
+
247
+ if (placement.startsWith('top')) {
248
+ // Top placement: dismiss if swiped up (negative Y)
249
+ if (shouldDismiss && translationY < 0 && id) {
250
+ // Use withDecay to continue motion with velocity
251
+ gestureTranslateY.set(
252
+ withDecay(
253
+ {
254
+ velocity: velocityY * 1.5,
255
+ clamp: [Number.NEGATIVE_INFINITY, 0],
256
+ },
257
+ () => {
258
+ isDragging.set(false);
259
+ },
260
+ ),
261
+ );
262
+ // Delay hide call to allow decay animation to play
263
+ scheduleOnRN(delayedHide, id, velocityY);
264
+ } else {
265
+ // Animate back to 0
266
+ gestureTranslateY.set(withSpring(0));
267
+ isDragging.set(false);
268
+ }
269
+ } else {
270
+ // Bottom placement: dismiss if swiped down (positive Y)
271
+ if (shouldDismiss && translationY > 0 && id) {
272
+ // Use withDecay to continue motion with velocity
273
+ gestureTranslateY.set(
274
+ withDecay(
275
+ {
276
+ velocity: velocityY * 1.5,
277
+ clamp: [0, Number.POSITIVE_INFINITY],
278
+ },
279
+ () => {
280
+ isDragging.set(false);
281
+ },
282
+ ),
283
+ );
284
+ // Delay hide call to allow decay animation to play
285
+ scheduleOnRN(delayedHide, id, velocityY);
286
+ } else {
287
+ // Animate back to 0
288
+ gestureTranslateY.set(withSpring(0));
289
+ isDragging.set(false);
290
+ }
291
+ }
292
+ });
293
+
294
+ // Web drives enter/exit via CSS transitions on `data-state` (see Toast/styles.ts
295
+ // and Toast/index.tsx). Its `height` is left unset so the toast owns its own
296
+ // natural height — the Reanimated `height: withSpring(...)` channel collides
297
+ // with the CSS engine during handoff and produces the "translate down/up + right"
298
+ // blink that the review flagged. Native keeps the spring so stack-height changes
299
+ // animate smoothly.
300
+ const isWeb = Platform.OS === 'web';
301
+
302
+ const rContainerStyle = useAnimatedStyle(() => {
303
+ const lastToastId = Object.keys(heights.get())[Object.keys(heights.get()).length - 1];
304
+ const lastToastHeight = lastToastId ? heights.get()[lastToastId] : undefined;
305
+
306
+ const sign = placement.startsWith('top') ? 1 : -1;
307
+
308
+ const totalValue = total.get();
309
+
310
+ const inputRange = [totalValue - 1, totalValue - 2];
311
+ const opacityInputRange = [totalValue - maxVisibleToasts, totalValue - maxVisibleToasts - 1];
312
+
313
+ const opacity = interpolate(index, opacityInputRange, opacityValue);
314
+
315
+ // Handle translateY based on dragging state
316
+ let translateY: number;
317
+ let scale: number;
318
+ if (isDragging.get()) {
319
+ // During gesture: use gesture-based translateY
320
+ translateY = gestureTranslateY.get();
321
+ scale = gestureScale.get();
322
+ } else {
323
+ // Normal state: use stack-based interpolation
324
+ translateY = interpolate(index, inputRange, [translateYValue[0], translateYValue[1] * sign], {
325
+ extrapolateLeft: Extrapolation.CLAMP,
326
+ });
327
+ scale = interpolate(index, inputRange, scaleValue, {
328
+ extrapolateLeft: Extrapolation.CLAMP,
329
+ });
330
+ }
331
+
332
+ if (isAnimationDisabledValue) {
333
+ return {
334
+ height: isWeb ? undefined : lastToastHeight,
335
+ pointerEvents: opacity === 0 ? 'none' : 'auto',
336
+ opacity,
337
+ transform: [
338
+ {
339
+ translateY,
340
+ },
341
+ {
342
+ scale,
343
+ },
344
+ ],
345
+ };
346
+ }
347
+
348
+ return {
349
+ height: isWeb
350
+ ? undefined
351
+ : lastToastHeight
352
+ ? withSpring(lastToastHeight, {
353
+ damping: 100,
354
+ stiffness: 1200,
355
+ mass: 3,
356
+ })
357
+ : undefined,
358
+ pointerEvents: opacity === 0 ? 'none' : 'auto',
359
+ opacity: withTiming(opacity, opacityTimingConfig),
360
+ transform: [
361
+ {
362
+ translateY: isDragging.get()
363
+ ? translateY
364
+ : withTiming(translateY, translateYTimingConfig),
365
+ },
366
+ {
367
+ scale: isDragging.get() ? withSpring(scale) : withTiming(scale, scaleTimingConfig),
368
+ },
369
+ ],
370
+ };
371
+ });
372
+
373
+ // Determine entering and exiting animations based on placement
374
+ const enteringAnimation = placement.startsWith('top') ? enteringTopValue : enteringBottomValue;
375
+ const exitingAnimation = placement.startsWith('top') ? exitingTopValue : exitingBottomValue;
376
+
377
+ return {
378
+ rContainerStyle,
379
+ isAllAnimationsDisabled,
380
+ // Web replaces Reanimated layout animations with CSS transitions driven
381
+ // by `data-state`; returning `undefined` here prevents the two channels
382
+ // from colliding on the wrapper `Animated.View`.
383
+ entering: isAnimationDisabledValue || isWeb ? undefined : enteringAnimation,
384
+ exiting: isAnimationDisabledValue || isWeb ? undefined : exitingAnimation,
385
+ panGesture,
386
+ };
387
+ }
@@ -0,0 +1,92 @@
1
+ import { useMemo } from 'react';
2
+ import type { StyleProp, ViewStyle } from 'react-native';
3
+ import { StyleSheet } from 'react-native';
4
+ import { useResolveClassNames } from 'uniwind';
5
+ import type { ButtonProps } from '../Button';
6
+ import { ToastColor } from './types';
7
+
8
+ export function useButtonColor({
9
+ color,
10
+ toastColor,
11
+ }: {
12
+ color: ButtonProps['color'];
13
+ toastColor: ToastColor;
14
+ }) {
15
+ // TODO: Add toastActionVariants to styles.ts to restore color-aware action styling
16
+ return useMemo(() => {
17
+ if (color) return color;
18
+
19
+ switch (toastColor) {
20
+ case 'info':
21
+ return 'info';
22
+ case 'success':
23
+ return 'success';
24
+ case 'warning':
25
+ return 'warning';
26
+ case 'danger':
27
+ return 'danger';
28
+ default:
29
+ return 'action';
30
+ }
31
+ }, [toastColor, color]);
32
+ }
33
+
34
+ interface UseVerticalPlaceholderStylesParams {
35
+ rootClassName?: string;
36
+ style?: StyleProp<ViewStyle>;
37
+ }
38
+
39
+ export function useVerticalPlaceholderStyles({
40
+ rootClassName,
41
+ style,
42
+ }: UseVerticalPlaceholderStylesParams) {
43
+ const resolvedRootClassName = useResolveClassNames(rootClassName ?? '');
44
+
45
+ return useMemo(() => {
46
+ const resolvedStyle = style ? StyleSheet.flatten(style) : undefined;
47
+
48
+ const stylePaddingTop =
49
+ resolvedStyle?.paddingTop ?? resolvedStyle?.paddingVertical ?? resolvedStyle?.padding;
50
+ const stylePaddingBottom =
51
+ resolvedStyle?.paddingBottom ?? resolvedStyle?.paddingVertical ?? resolvedStyle?.padding;
52
+
53
+ const classNamePaddingTop =
54
+ resolvedRootClassName?.paddingTop ??
55
+ resolvedRootClassName?.paddingVertical ??
56
+ resolvedRootClassName?.padding;
57
+ const classNamePaddingBottom =
58
+ resolvedRootClassName?.paddingBottom ??
59
+ resolvedRootClassName?.paddingVertical ??
60
+ resolvedRootClassName?.padding;
61
+
62
+ const paddingTopValue = stylePaddingTop ?? classNamePaddingTop;
63
+ const paddingBottomValue = stylePaddingBottom ?? classNamePaddingBottom;
64
+
65
+ const topHeight =
66
+ typeof paddingTopValue === 'number' && paddingTopValue >= 0 ? paddingTopValue : 0;
67
+ const bottomHeight =
68
+ typeof paddingBottomValue === 'number' && paddingBottomValue >= 0 ? paddingBottomValue : 0;
69
+
70
+ const styleBackgroundColor = resolvedStyle?.backgroundColor;
71
+ const classNameBackgroundColor = resolvedRootClassName?.backgroundColor;
72
+ const backgroundColor = styleBackgroundColor ?? classNameBackgroundColor ?? undefined;
73
+
74
+ const topStyle: ViewStyle = {
75
+ height: topHeight,
76
+ };
77
+
78
+ const bottomStyle: ViewStyle = {
79
+ height: bottomHeight,
80
+ };
81
+
82
+ if (backgroundColor !== undefined) {
83
+ topStyle.backgroundColor = backgroundColor;
84
+ bottomStyle.backgroundColor = backgroundColor;
85
+ }
86
+
87
+ return {
88
+ topStyle,
89
+ bottomStyle,
90
+ };
91
+ }, [resolvedRootClassName, style]);
92
+ }