@react-navigation/stack 8.0.0-alpha.6 → 8.0.0-alpha.60

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 (147) hide show
  1. package/lib/module/TransitionConfigs/CardStyleInterpolators.js +280 -84
  2. package/lib/module/TransitionConfigs/CardStyleInterpolators.js.map +1 -1
  3. package/lib/module/TransitionConfigs/HeaderStyleInterpolators.js +11 -44
  4. package/lib/module/TransitionConfigs/HeaderStyleInterpolators.js.map +1 -1
  5. package/lib/module/TransitionConfigs/TransitionEasings.js +71 -0
  6. package/lib/module/TransitionConfigs/TransitionEasings.js.map +1 -0
  7. package/lib/module/TransitionConfigs/TransitionPresets.js +50 -10
  8. package/lib/module/TransitionConfigs/TransitionPresets.js.map +1 -1
  9. package/lib/module/TransitionConfigs/TransitionSpecs.js +69 -34
  10. package/lib/module/TransitionConfigs/TransitionSpecs.js.map +1 -1
  11. package/lib/module/index.js +2 -2
  12. package/lib/module/index.js.map +1 -1
  13. package/lib/module/navigators/createStackNavigator.js +37 -33
  14. package/lib/module/navigators/createStackNavigator.js.map +1 -1
  15. package/lib/module/utils/GestureHandlerContext.js +5 -0
  16. package/lib/module/utils/GestureHandlerContext.js.map +1 -0
  17. package/lib/module/utils/gestureActivationCriteria.js +10 -8
  18. package/lib/module/utils/gestureActivationCriteria.js.map +1 -1
  19. package/lib/module/utils/useCardAnimation.js +1 -1
  20. package/lib/module/utils/useCardAnimation.js.map +1 -1
  21. package/lib/module/utils/useGestureHandler.js +9 -0
  22. package/lib/module/utils/useGestureHandler.js.map +1 -0
  23. package/lib/module/utils/useKeyboardManager.js +71 -26
  24. package/lib/module/utils/useKeyboardManager.js.map +1 -1
  25. package/lib/module/views/GestureHandler.js +15 -14
  26. package/lib/module/views/GestureHandler.js.map +1 -1
  27. package/lib/module/views/GestureHandlerNative.js +16 -10
  28. package/lib/module/views/GestureHandlerNative.js.map +1 -1
  29. package/lib/module/views/Header/Header.js +4 -2
  30. package/lib/module/views/Header/Header.js.map +1 -1
  31. package/lib/module/views/Header/HeaderContainer.js +60 -21
  32. package/lib/module/views/Header/HeaderContainer.js.map +1 -1
  33. package/lib/module/views/Header/HeaderSegment.js +6 -9
  34. package/lib/module/views/Header/HeaderSegment.js.map +1 -1
  35. package/lib/module/views/Stack/Card.js +170 -123
  36. package/lib/module/views/Stack/Card.js.map +1 -1
  37. package/lib/module/views/Stack/CardContainer.js +96 -98
  38. package/lib/module/views/Stack/CardContainer.js.map +1 -1
  39. package/lib/module/views/Stack/CardContent.js +10 -37
  40. package/lib/module/views/Stack/CardContent.js.map +1 -1
  41. package/lib/module/views/Stack/CardStack.js +211 -147
  42. package/lib/module/views/Stack/CardStack.js.map +1 -1
  43. package/lib/module/views/Stack/StackView.js +106 -57
  44. package/lib/module/views/Stack/StackView.js.map +1 -1
  45. package/lib/typescript/src/TransitionConfigs/CardStyleInterpolators.d.ts +20 -12
  46. package/lib/typescript/src/TransitionConfigs/CardStyleInterpolators.d.ts.map +1 -1
  47. package/lib/typescript/src/TransitionConfigs/HeaderStyleInterpolators.d.ts +4 -4
  48. package/lib/typescript/src/TransitionConfigs/HeaderStyleInterpolators.d.ts.map +1 -1
  49. package/lib/typescript/src/TransitionConfigs/TransitionEasings.d.ts +38 -0
  50. package/lib/typescript/src/TransitionConfigs/TransitionEasings.d.ts.map +1 -0
  51. package/lib/typescript/src/TransitionConfigs/TransitionPresets.d.ts +15 -3
  52. package/lib/typescript/src/TransitionConfigs/TransitionPresets.d.ts.map +1 -1
  53. package/lib/typescript/src/TransitionConfigs/TransitionSpecs.d.ts +44 -15
  54. package/lib/typescript/src/TransitionConfigs/TransitionSpecs.d.ts.map +1 -1
  55. package/lib/typescript/src/index.d.ts +12 -12
  56. package/lib/typescript/src/index.d.ts.map +1 -1
  57. package/lib/typescript/src/navigators/createStackNavigator.d.ts +10 -15
  58. package/lib/typescript/src/navigators/createStackNavigator.d.ts.map +1 -1
  59. package/lib/typescript/src/types.d.ts +120 -108
  60. package/lib/typescript/src/types.d.ts.map +1 -1
  61. package/lib/typescript/src/utils/CardAnimationContext.d.ts +1 -1
  62. package/lib/typescript/src/utils/CardAnimationContext.d.ts.map +1 -1
  63. package/lib/typescript/src/utils/GestureHandlerContext.d.ts +3 -0
  64. package/lib/typescript/src/utils/GestureHandlerContext.d.ts.map +1 -0
  65. package/lib/typescript/src/utils/gestureActivationCriteria.d.ts +4 -51
  66. package/lib/typescript/src/utils/gestureActivationCriteria.d.ts.map +1 -1
  67. package/lib/typescript/src/utils/getDistanceForDirection.d.ts +1 -1
  68. package/lib/typescript/src/utils/getDistanceForDirection.d.ts.map +1 -1
  69. package/lib/typescript/src/utils/getInvertedMultiplier.d.ts +1 -1
  70. package/lib/typescript/src/utils/getInvertedMultiplier.d.ts.map +1 -1
  71. package/lib/typescript/src/utils/getModalRoutesKeys.d.ts +1 -1
  72. package/lib/typescript/src/utils/getModalRoutesKeys.d.ts.map +1 -1
  73. package/lib/typescript/src/utils/getShadowStyle.d.ts +5 -5
  74. package/lib/typescript/src/utils/useCardAnimation.d.ts +1 -1
  75. package/lib/typescript/src/utils/useCardAnimation.d.ts.map +1 -1
  76. package/lib/typescript/src/utils/useGestureHandler.d.ts +2 -0
  77. package/lib/typescript/src/utils/useGestureHandler.d.ts.map +1 -0
  78. package/lib/typescript/src/utils/useKeyboardManager.d.ts +12 -2
  79. package/lib/typescript/src/utils/useKeyboardManager.d.ts.map +1 -1
  80. package/lib/typescript/src/views/GestureHandler.android.d.ts +1 -1
  81. package/lib/typescript/src/views/GestureHandler.android.d.ts.map +1 -1
  82. package/lib/typescript/src/views/GestureHandler.d.ts +7 -11
  83. package/lib/typescript/src/views/GestureHandler.d.ts.map +1 -1
  84. package/lib/typescript/src/views/GestureHandler.ios.d.ts +1 -1
  85. package/lib/typescript/src/views/GestureHandler.ios.d.ts.map +1 -1
  86. package/lib/typescript/src/views/GestureHandlerNative.d.ts +9 -4
  87. package/lib/typescript/src/views/GestureHandlerNative.d.ts.map +1 -1
  88. package/lib/typescript/src/views/Header/Header.d.ts +2 -2
  89. package/lib/typescript/src/views/Header/Header.d.ts.map +1 -1
  90. package/lib/typescript/src/views/Header/HeaderContainer.d.ts +9 -3
  91. package/lib/typescript/src/views/Header/HeaderContainer.d.ts.map +1 -1
  92. package/lib/typescript/src/views/Header/HeaderSegment.d.ts +6 -4
  93. package/lib/typescript/src/views/Header/HeaderSegment.d.ts.map +1 -1
  94. package/lib/typescript/src/views/Stack/Card.d.ts +14 -8
  95. package/lib/typescript/src/views/Stack/Card.d.ts.map +1 -1
  96. package/lib/typescript/src/views/Stack/CardContainer.d.ts +7 -7
  97. package/lib/typescript/src/views/Stack/CardContainer.d.ts.map +1 -1
  98. package/lib/typescript/src/views/Stack/CardContent.d.ts +1 -1
  99. package/lib/typescript/src/views/Stack/CardContent.d.ts.map +1 -1
  100. package/lib/typescript/src/views/Stack/CardStack.d.ts +10 -6
  101. package/lib/typescript/src/views/Stack/CardStack.d.ts.map +1 -1
  102. package/lib/typescript/src/views/Stack/StackView.d.ts +14 -17
  103. package/lib/typescript/src/views/Stack/StackView.d.ts.map +1 -1
  104. package/package.json +23 -24
  105. package/src/TransitionConfigs/CardStyleInterpolators.tsx +399 -100
  106. package/src/TransitionConfigs/HeaderStyleInterpolators.tsx +15 -46
  107. package/src/TransitionConfigs/TransitionEasings.tsx +90 -0
  108. package/src/TransitionConfigs/TransitionPresets.tsx +56 -10
  109. package/src/TransitionConfigs/TransitionSpecs.tsx +68 -34
  110. package/src/index.tsx +3 -2
  111. package/src/navigators/createStackNavigator.tsx +74 -91
  112. package/src/types.tsx +149 -123
  113. package/src/utils/GestureHandlerContext.tsx +5 -0
  114. package/src/utils/gestureActivationCriteria.tsx +14 -10
  115. package/src/utils/useCardAnimation.tsx +1 -1
  116. package/src/utils/useGestureHandler.tsx +9 -0
  117. package/src/utils/useKeyboardManager.tsx +96 -33
  118. package/src/views/GestureHandler.tsx +24 -16
  119. package/src/views/GestureHandlerNative.tsx +24 -13
  120. package/src/views/Header/Header.tsx +4 -2
  121. package/src/views/Header/HeaderContainer.tsx +79 -37
  122. package/src/views/Header/HeaderSegment.tsx +10 -11
  123. package/src/views/Stack/Card.tsx +241 -154
  124. package/src/views/Stack/CardContainer.tsx +111 -127
  125. package/src/views/Stack/CardContent.tsx +13 -51
  126. package/src/views/Stack/CardStack.tsx +478 -335
  127. package/src/views/Stack/StackView.tsx +181 -82
  128. package/lib/module/utils/GestureHandlerRefContext.js +0 -5
  129. package/lib/module/utils/GestureHandlerRefContext.js.map +0 -1
  130. package/lib/module/utils/findLastIndex.js +0 -11
  131. package/lib/module/utils/findLastIndex.js.map +0 -1
  132. package/lib/module/utils/useGestureHandlerRef.js +0 -12
  133. package/lib/module/utils/useGestureHandlerRef.js.map +0 -1
  134. package/lib/module/views/Stack/CardA11yWrapper.js +0 -43
  135. package/lib/module/views/Stack/CardA11yWrapper.js.map +0 -1
  136. package/lib/typescript/src/utils/GestureHandlerRefContext.d.ts +0 -3
  137. package/lib/typescript/src/utils/GestureHandlerRefContext.d.ts.map +0 -1
  138. package/lib/typescript/src/utils/findLastIndex.d.ts +0 -2
  139. package/lib/typescript/src/utils/findLastIndex.d.ts.map +0 -1
  140. package/lib/typescript/src/utils/useGestureHandlerRef.d.ts +0 -3
  141. package/lib/typescript/src/utils/useGestureHandlerRef.d.ts.map +0 -1
  142. package/lib/typescript/src/views/Stack/CardA11yWrapper.d.ts +0 -15
  143. package/lib/typescript/src/views/Stack/CardA11yWrapper.d.ts.map +0 -1
  144. package/src/utils/GestureHandlerRefContext.tsx +0 -5
  145. package/src/utils/findLastIndex.tsx +0 -9
  146. package/src/utils/useGestureHandlerRef.tsx +0 -15
  147. package/src/views/Stack/CardA11yWrapper.tsx +0 -67
@@ -5,6 +5,16 @@ import type {
5
5
  StackCardInterpolationProps,
6
6
  } from '../types';
7
7
  import { conditional } from '../utils/conditional';
8
+ import {
9
+ accelerate,
10
+ ACTIVITY_CLOSE_DIM,
11
+ clamp,
12
+ decelerate,
13
+ FAST_OUT_EXTRA_SLOW_IN,
14
+ FAST_OUT_SLOW_IN,
15
+ LINEAR_OUT_SLOW_IN,
16
+ timeline,
17
+ } from './TransitionEasings';
8
18
 
9
19
  const { add, multiply } = Animated;
10
20
 
@@ -39,13 +49,7 @@ export function forHorizontalIOS({
39
49
 
40
50
  const overlayOpacity = current.progress.interpolate({
41
51
  inputRange: [0, 1],
42
- outputRange: [0, 0.07],
43
- extrapolate: 'clamp',
44
- });
45
-
46
- const shadowOpacity = current.progress.interpolate({
47
- inputRange: [0, 1],
48
- outputRange: [0, 0.3],
52
+ outputRange: [0, 0.1],
49
53
  extrapolate: 'clamp',
50
54
  });
51
55
 
@@ -59,23 +63,14 @@ export function forHorizontalIOS({
59
63
  ],
60
64
  },
61
65
  overlayStyle: { opacity: overlayOpacity },
62
- shadowStyle: { shadowOpacity },
66
+ shadowStyle:
67
+ Platform.OS === 'web'
68
+ ? // React Native Web prints deprecation warnings for `shadowX` properties
69
+ {}
70
+ : { shadowOpacity: 0.04 },
63
71
  };
64
72
  }
65
73
 
66
- /**
67
- * iOS-style slide in from the left.
68
- */
69
- export function forHorizontalIOSInverted({
70
- inverted,
71
- ...rest
72
- }: StackCardInterpolationProps): StackCardInterpolatedStyle {
73
- return forHorizontalIOS({
74
- ...rest,
75
- inverted: Animated.multiply(inverted, -1),
76
- });
77
- }
78
-
79
74
  /**
80
75
  * Standard iOS-style slide in from the bottom (used for modals).
81
76
  */
@@ -138,6 +133,50 @@ export function forModalPresentationIOS({
138
133
 
139
134
  const isFirst = index === 0;
140
135
 
136
+ if (Platform.OS !== 'ios' || parseInt(Platform.Version, 10) >= 26) {
137
+ const scaleValue = screen.width ? 1 - (topOffset * 3) / screen.width : 1;
138
+ const recede = ((screen.height - statusBarHeight) * (1 - scaleValue)) / 2;
139
+
140
+ const translateY = multiply(
141
+ progress.interpolate({
142
+ inputRange: [0, 1, 2],
143
+ outputRange: [
144
+ screen.height,
145
+ // The front modal rests below the one behind it to reveal a peek.
146
+ index > 1 ? topOffset : 0,
147
+ isFirst ? 0 : -recede,
148
+ ],
149
+ }),
150
+ inverted
151
+ );
152
+
153
+ const scale = isFirst
154
+ ? 1
155
+ : progress.interpolate({
156
+ inputRange: [0, 1, 2],
157
+ outputRange: [1, 1, scaleValue],
158
+ });
159
+
160
+ const dimOpacity = progress.interpolate({
161
+ inputRange: [0, 1, 2],
162
+ outputRange: [0, 0, isFirst ? 0.2 : 0.12],
163
+ });
164
+
165
+ const borderRadius = isFirst ? 0 : 38;
166
+
167
+ return {
168
+ cardStyle: {
169
+ overflow: 'hidden',
170
+ borderCurve: 'continuous',
171
+ borderTopLeftRadius: borderRadius,
172
+ borderTopRightRadius: borderRadius,
173
+ marginTop: isFirst ? 0 : statusBarHeight,
174
+ transform: [{ translateY }, { scale }],
175
+ },
176
+ dimStyle: { opacity: dimOpacity },
177
+ };
178
+ }
179
+
141
180
  const translateY = multiply(
142
181
  progress.interpolate({
143
182
  inputRange: [0, 1, 2],
@@ -198,9 +237,9 @@ export function forModalPresentationIOS({
198
237
  */
199
238
  export function forFadeFromBottomAndroid({
200
239
  current,
240
+ next,
201
241
  inverted,
202
242
  layouts: { screen },
203
- closing,
204
243
  }: StackCardInterpolationProps): StackCardInterpolatedStyle {
205
244
  const translateY = multiply(
206
245
  current.progress.interpolate({
@@ -212,13 +251,24 @@ export function forFadeFromBottomAndroid({
212
251
  );
213
252
 
214
253
  const opacity = conditional(
215
- closing,
216
- current.progress,
217
- current.progress.interpolate({
218
- inputRange: [0, 0.5, 0.9, 1],
219
- outputRange: [0, 0.25, 0.7, 1],
220
- extrapolate: 'clamp',
221
- })
254
+ current.closing,
255
+ current.progress.interpolate(
256
+ timeline({
257
+ duration: 250,
258
+ easing: (value) => accelerate(4, value),
259
+ closing: true,
260
+ stops: [100],
261
+ output: (time) => 1 - clamp((time - 100) / 150),
262
+ })
263
+ ),
264
+ current.progress.interpolate(
265
+ timeline({
266
+ duration: 350,
267
+ easing: (value) => decelerate(5, value),
268
+ stops: [200],
269
+ output: (time) => decelerate(4, clamp(time / 200)),
270
+ })
271
+ )
222
272
  );
223
273
 
224
274
  return {
@@ -226,6 +276,29 @@ export function forFadeFromBottomAndroid({
226
276
  opacity,
227
277
  transform: [{ translateY }],
228
278
  },
279
+ dimStyle: next
280
+ ? {
281
+ opacity: conditional(
282
+ next.closing,
283
+ next.progress.interpolate(
284
+ timeline({
285
+ duration: 250,
286
+ easing: (value) => accelerate(4, value),
287
+ closing: true,
288
+ output: (time) => 0.3 * (1 - LINEAR_OUT_SLOW_IN(time / 250)),
289
+ })
290
+ ),
291
+ next.progress.interpolate(
292
+ timeline({
293
+ duration: 350,
294
+ easing: (value) => decelerate(5, value),
295
+ stops: [217],
296
+ output: (time) => 0.3 * FAST_OUT_SLOW_IN(clamp(time / 217)),
297
+ })
298
+ )
299
+ ),
300
+ }
301
+ : undefined,
229
302
  };
230
303
  }
231
304
 
@@ -238,40 +311,80 @@ export function forRevealFromBottomAndroid({
238
311
  inverted,
239
312
  layouts: { screen },
240
313
  }: StackCardInterpolationProps): StackCardInterpolatedStyle {
314
+ const translationOpenRanges = timeline({
315
+ duration: 425,
316
+ easing: FAST_OUT_EXTRA_SLOW_IN,
317
+ output: (time) => FAST_OUT_SLOW_IN(time / 425),
318
+ });
319
+
320
+ const translationCloseRanges = timeline({
321
+ duration: 425,
322
+ easing: FAST_OUT_EXTRA_SLOW_IN,
323
+ closing: true,
324
+ output: (time) => 1 - FAST_OUT_SLOW_IN(time / 425),
325
+ });
326
+
327
+ const clipProgress = current.progress.interpolate({
328
+ inputRange: [0, 1],
329
+ outputRange: [0, 1],
330
+ extrapolate: 'clamp',
331
+ });
332
+
333
+ const translationProgress = conditional(
334
+ current.closing,
335
+ current.progress.interpolate(translationCloseRanges),
336
+ current.progress.interpolate(translationOpenRanges)
337
+ );
338
+
241
339
  const containerTranslateY = multiply(
242
- current.progress.interpolate({
243
- inputRange: [0, 1],
244
- outputRange: [screen.height, 0],
245
- extrapolate: 'clamp',
246
- }),
340
+ multiply(
341
+ add(
342
+ add(1, multiply(clipProgress, -0.959)),
343
+ multiply(translationProgress, -0.041)
344
+ ),
345
+ screen.height
346
+ ),
247
347
  inverted
248
348
  );
249
349
 
250
350
  const cardTranslateYFocused = multiply(
251
- current.progress.interpolate({
252
- inputRange: [0, 1],
253
- outputRange: [screen.height * (95.9 / 100) * -1, 0],
254
- extrapolate: 'clamp',
255
- }),
351
+ multiply(add(clipProgress, -1), screen.height * 0.959),
256
352
  inverted
257
353
  );
258
354
 
259
355
  const cardTranslateYUnfocused = next
260
356
  ? multiply(
261
- next.progress.interpolate({
262
- inputRange: [0, 1],
263
- outputRange: [0, screen.height * (2 / 100) * -1],
264
- extrapolate: 'clamp',
265
- }),
357
+ multiply(
358
+ conditional(
359
+ next.closing,
360
+ next.progress.interpolate(translationCloseRanges),
361
+ next.progress.interpolate(translationOpenRanges)
362
+ ),
363
+ screen.height * -0.02
364
+ ),
266
365
  inverted
267
366
  )
268
367
  : 0;
269
368
 
270
- const overlayOpacity = current.progress.interpolate({
271
- inputRange: [0, 0.36, 1],
272
- outputRange: [0, 0.1, 0.1],
273
- extrapolate: 'clamp',
274
- });
369
+ const overlayOpacity = conditional(
370
+ current.closing,
371
+ current.progress.interpolate(
372
+ timeline({
373
+ duration: 425,
374
+ easing: FAST_OUT_EXTRA_SLOW_IN,
375
+ closing: true,
376
+ output: (time) => 0.1 * (1 - ACTIVITY_CLOSE_DIM(time / 425)),
377
+ })
378
+ ),
379
+ current.progress.interpolate(
380
+ timeline({
381
+ duration: 425,
382
+ easing: FAST_OUT_EXTRA_SLOW_IN,
383
+ stops: [117],
384
+ output: (time) => 0.1 * clamp(time / 117),
385
+ })
386
+ )
387
+ );
275
388
 
276
389
  return {
277
390
  containerStyle: {
@@ -289,62 +402,94 @@ export function forRevealFromBottomAndroid({
289
402
  }
290
403
 
291
404
  /**
292
- * Standard Android-style zoom for Android 10.
405
+ * Standard Android-style scale from center for Android 10.
293
406
  */
294
407
  export function forScaleFromCenterAndroid({
295
408
  current,
296
409
  next,
297
- closing,
298
410
  }: StackCardInterpolationProps): StackCardInterpolatedStyle {
299
- const progress = add(
300
- current.progress.interpolate({
301
- inputRange: [0, 1],
302
- outputRange: [0, 1],
303
- extrapolate: 'clamp',
304
- }),
305
- next
306
- ? next.progress.interpolate({
411
+ const opacity = conditional(
412
+ current.closing,
413
+ current.progress.interpolate(
414
+ timeline({
415
+ duration: 400,
416
+ easing: FAST_OUT_EXTRA_SLOW_IN,
417
+ closing: true,
418
+ stops: [33, 83],
419
+ output: (time) => 1 - clamp((time - 33) / 50),
420
+ })
421
+ ),
422
+ current.progress.interpolate(
423
+ timeline({
424
+ duration: 400,
425
+ easing: FAST_OUT_EXTRA_SLOW_IN,
426
+ stops: [50, 100],
427
+ output: (time) => clamp((time - 50) / 50),
428
+ })
429
+ )
430
+ );
431
+
432
+ const scale = next
433
+ ? conditional(
434
+ next.closing,
435
+ next.progress.interpolate({
307
436
  inputRange: [0, 1],
308
- outputRange: [0, 1],
437
+ outputRange: [1, 1.1],
438
+ extrapolate: 'clamp',
439
+ }),
440
+ next.progress.interpolate({
441
+ inputRange: [0, 1],
442
+ outputRange: [1, 1.05],
309
443
  extrapolate: 'clamp',
310
444
  })
311
- : 0
312
- );
313
-
314
- const opacity = progress.interpolate({
315
- inputRange: [0, 0.75, 0.875, 1, 1.0825, 1.2075, 2],
316
- outputRange: [0, 0, 1, 1, 1, 1, 0],
317
- });
318
-
319
- const scale = conditional(
320
- closing,
321
- current.progress.interpolate({
322
- inputRange: [0, 1],
323
- outputRange: [0.925, 1],
324
- extrapolate: 'clamp',
325
- }),
326
- progress.interpolate({
327
- inputRange: [0, 1, 2],
328
- outputRange: [0.85, 1, 1.075],
329
- })
330
- );
445
+ )
446
+ : conditional(
447
+ current.closing,
448
+ current.progress.interpolate({
449
+ inputRange: [0, 1],
450
+ outputRange: [0.9, 1],
451
+ extrapolate: 'clamp',
452
+ }),
453
+ current.progress.interpolate({
454
+ inputRange: [0, 1],
455
+ outputRange: [0.85, 1],
456
+ extrapolate: 'clamp',
457
+ })
458
+ );
331
459
 
332
460
  return {
333
461
  cardStyle: {
334
462
  opacity,
335
463
  transform: [{ scale }],
336
464
  },
465
+ dimStyle: next
466
+ ? {
467
+ opacity: multiply(
468
+ next.closing.interpolate({
469
+ inputRange: [0, 1],
470
+ outputRange: [1, 0],
471
+ }),
472
+ next.progress.interpolate(
473
+ timeline({
474
+ duration: 400,
475
+ easing: FAST_OUT_EXTRA_SLOW_IN,
476
+ stops: [83, 250],
477
+ output: (time) => 0.6 * clamp((time - 83) / 167),
478
+ })
479
+ )
480
+ ),
481
+ }
482
+ : undefined,
337
483
  };
338
484
  }
339
485
 
340
486
  /**
341
- * Standard Android-style fade from right for Android 14.
487
+ * Standard Android-style fade from right for Android 14 and later.
342
488
  */
343
489
  export function forFadeFromRightAndroid({
344
490
  current,
345
491
  next,
346
492
  inverted,
347
- closing,
348
493
  }: StackCardInterpolationProps): StackCardInterpolatedStyle {
349
494
  const translateFocused = multiply(
350
495
  current.progress.interpolate({
@@ -367,13 +512,24 @@ export function forFadeFromRightAndroid({
367
512
  : 0;
368
513
 
369
514
  const opacity = conditional(
370
- closing,
371
- current.progress.interpolate({
372
- inputRange: [0, 1],
373
- outputRange: [0, 1],
374
- extrapolate: 'clamp',
375
- }),
376
- current.progress
515
+ current.closing,
516
+ current.progress.interpolate(
517
+ timeline({
518
+ duration: 450,
519
+ easing: FAST_OUT_EXTRA_SLOW_IN,
520
+ closing: true,
521
+ stops: [35, 118],
522
+ output: (time) => 1 - clamp((time - 35) / 83),
523
+ })
524
+ ),
525
+ current.progress.interpolate(
526
+ timeline({
527
+ duration: 450,
528
+ easing: FAST_OUT_EXTRA_SLOW_IN,
529
+ stops: [50, 133],
530
+ output: (time) => clamp((time - 50) / 83),
531
+ })
532
+ )
377
533
  );
378
534
 
379
535
  return {
@@ -396,20 +552,27 @@ export function forBottomSheetAndroid({
396
552
  current,
397
553
  inverted,
398
554
  layouts: { screen },
399
- closing,
400
555
  }: StackCardInterpolationProps): StackCardInterpolatedStyle {
401
556
  const translateY = multiply(
402
557
  current.progress.interpolate({
403
558
  inputRange: [0, 1],
404
- outputRange: [screen.height * 0.8, 0],
559
+ outputRange: [screen.height * 0.2, 0],
405
560
  extrapolate: 'clamp',
406
561
  }),
407
562
  inverted
408
563
  );
409
564
 
410
565
  const opacity = conditional(
411
- closing,
412
- current.progress,
566
+ current.closing,
567
+ current.progress.interpolate(
568
+ timeline({
569
+ duration: 350,
570
+ easing: FAST_OUT_EXTRA_SLOW_IN,
571
+ closing: true,
572
+ stops: [300],
573
+ output: (time) => 1 - FAST_OUT_EXTRA_SLOW_IN(clamp(time / 300)),
574
+ })
575
+ ),
413
576
  current.progress.interpolate({
414
577
  inputRange: [0, 1],
415
578
  outputRange: [0, 1],
@@ -417,23 +580,107 @@ export function forBottomSheetAndroid({
417
580
  })
418
581
  );
419
582
 
420
- const overlayOpacity = current.progress.interpolate({
583
+ // We follow Android's window dim timing for the 0.32 scrim.
584
+ // This makes it reach full opacity after 128 ms and clear after 112 ms.
585
+ // See https://android.googlesource.com/platform/frameworks/base/+/refs/tags/android-17.0.0_r1/services/core/java/com/android/server/wm/DimmerAnimationHelper.java
586
+ const overlayOpacity = conditional(
587
+ current.closing,
588
+ current.progress.interpolate(
589
+ timeline({
590
+ duration: 350,
591
+ easing: FAST_OUT_EXTRA_SLOW_IN,
592
+ closing: true,
593
+ stops: [112],
594
+ output: (time) => 0.32 * (1 - clamp(time / 112)),
595
+ })
596
+ ),
597
+ current.progress.interpolate(
598
+ timeline({
599
+ duration: 400,
600
+ easing: FAST_OUT_EXTRA_SLOW_IN,
601
+ stops: [128],
602
+ output: (time) => 0.32 * clamp(time / 128),
603
+ })
604
+ )
605
+ );
606
+
607
+ return {
608
+ cardStyle: {
609
+ opacity,
610
+ transform: [{ translateY }],
611
+ },
612
+ overlayStyle: { opacity: overlayOpacity },
613
+ };
614
+ }
615
+
616
+ /**
617
+ * Standard Android dialog transition.
618
+ */
619
+ export function forDialogAndroid({
620
+ current: { progress, closing },
621
+ }: StackCardInterpolationProps): StackCardInterpolatedStyle {
622
+ const opacity = conditional(
623
+ closing,
624
+ progress.interpolate(
625
+ timeline({
626
+ duration: 220,
627
+ easing: (value) => decelerate(5, value),
628
+ closing: true,
629
+ stops: [150],
630
+ output: (time) => 1 - decelerate(3, clamp(time / 150)),
631
+ })
632
+ ),
633
+ progress.interpolate(
634
+ timeline({
635
+ duration: 220,
636
+ easing: (value) => decelerate(5, value),
637
+ stops: [150],
638
+ output: (time) => decelerate(3, clamp(time / 150)),
639
+ })
640
+ )
641
+ );
642
+
643
+ const scale = progress.interpolate({
421
644
  inputRange: [0, 1],
422
- outputRange: [0, 0.3],
645
+ outputRange: [0.9, 1],
423
646
  extrapolate: 'clamp',
424
647
  });
425
648
 
649
+ // We follow Android's window dim timing for the 0.6 dialog dim.
650
+ // This makes it reach or clear full opacity after 132 ms.
651
+ // See https://android.googlesource.com/platform/frameworks/base/+/refs/tags/android-17.0.0_r1/services/core/java/com/android/server/wm/DimmerAnimationHelper.java
652
+ const overlayOpacity = conditional(
653
+ closing,
654
+ progress.interpolate(
655
+ timeline({
656
+ duration: 220,
657
+ easing: (value) => decelerate(5, value),
658
+ closing: true,
659
+ stops: [132],
660
+ output: (time) => 0.6 * (1 - clamp(time / 132)),
661
+ })
662
+ ),
663
+ progress.interpolate(
664
+ timeline({
665
+ duration: 220,
666
+ easing: (value) => decelerate(5, value),
667
+ stops: [132],
668
+ output: (time) => 0.6 * clamp(time / 132),
669
+ })
670
+ )
671
+ );
672
+
426
673
  return {
427
674
  cardStyle: {
428
675
  opacity,
429
- transform: [{ translateY }],
676
+ transform: [{ scale }],
430
677
  },
431
678
  overlayStyle: { opacity: overlayOpacity },
432
679
  };
433
680
  }
434
681
 
435
682
  /**
436
- * Simple fade animation for dialogs
683
+ * Simple fade animation.
437
684
  */
438
685
  export function forFadeFromCenter({
439
686
  current: { progress },
@@ -443,15 +690,67 @@ export function forFadeFromCenter({
443
690
  opacity: progress.interpolate({
444
691
  inputRange: [0, 0.5, 0.9, 1],
445
692
  outputRange: [0, 0.25, 0.7, 1],
693
+ extrapolate: 'clamp',
446
694
  }),
447
695
  },
448
- overlayStyle: {
449
- opacity: progress.interpolate({
696
+ };
697
+ }
698
+
699
+ /**
700
+ * Standard iOS cross-dissolve transition.
701
+ */
702
+ export function forCrossDissolveIOS({
703
+ current,
704
+ next,
705
+ }: StackCardInterpolationProps): StackCardInterpolatedStyle {
706
+ // UIKit cross-dissolve darkens identical light screens at the midpoint.
707
+ // Dimming the outgoing card reproduces this without exposing earlier screens.
708
+ return {
709
+ cardStyle: {
710
+ opacity: current.progress.interpolate({
450
711
  inputRange: [0, 1],
451
- outputRange: [0, 0.5],
712
+ outputRange: [0, 1],
452
713
  extrapolate: 'clamp',
453
714
  }),
454
715
  },
716
+ dimStyle: next
717
+ ? {
718
+ opacity: next.progress.interpolate({
719
+ inputRange: [0, 1],
720
+ outputRange: [0, 1],
721
+ extrapolate: 'clamp',
722
+ }),
723
+ }
724
+ : undefined,
725
+ };
726
+ }
727
+
728
+ /**
729
+ * Standard iOS horizontal flip transition.
730
+ */
731
+ export function forFlipIOS({
732
+ current,
733
+ next,
734
+ inverted,
735
+ }: StackCardInterpolationProps): StackCardInterpolatedStyle {
736
+ const progress = add(current.progress, next ? next.progress : 0);
737
+
738
+ const rotateY = multiply(
739
+ progress.interpolate({
740
+ inputRange: [0, 1, 2],
741
+ outputRange: [180, 0, -180],
742
+ }),
743
+ inverted
744
+ ).interpolate({
745
+ inputRange: [-180, 0, 180],
746
+ outputRange: ['-180deg', '0deg', '180deg'],
747
+ });
748
+
749
+ return {
750
+ cardStyle: {
751
+ backfaceVisibility: 'hidden',
752
+ transform: [{ perspective: 1000 }, { rotateY }],
753
+ },
455
754
  };
456
755
  }
457
756