@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
@@ -1,12 +1,23 @@
1
1
  import * as React from 'react';
2
2
  import { type HostInstance, Keyboard, TextInput } from 'react-native';
3
3
 
4
- export function useKeyboardManager(isEnabled: () => boolean) {
4
+ export function useKeyboardManager({
5
+ enabled,
6
+ focused,
7
+ contentRef,
8
+ }: {
9
+ enabled: boolean;
10
+ focused: boolean;
11
+ contentRef: React.RefObject<HostInstance | null>;
12
+ }) {
5
13
  // Numeric id of the previously focused text input
6
14
  // When a gesture didn't change the tab, we can restore the focused input with this
7
15
  const previouslyFocusedTextInputRef = React.useRef<HostInstance>(undefined);
8
16
  const startTimestampRef = React.useRef<number>(0);
9
- const keyboardTimeoutRef = React.useRef<NodeJS.Timeout>(undefined);
17
+ const keyboardTimeoutRef =
18
+ React.useRef<ReturnType<typeof setTimeout>>(undefined);
19
+ const enabledRef = React.useRef(enabled);
20
+ const focusedRef = React.useRef(focused);
10
21
 
11
22
  const clearKeyboardTimeout = React.useCallback(() => {
12
23
  if (keyboardTimeoutRef.current !== undefined) {
@@ -16,7 +27,7 @@ export function useKeyboardManager(isEnabled: () => boolean) {
16
27
  }, []);
17
28
 
18
29
  const onPageChangeStart = React.useCallback(() => {
19
- if (!isEnabled()) {
30
+ if (!enabledRef.current || !focusedRef.current) {
20
31
  return;
21
32
  }
22
33
 
@@ -32,37 +43,10 @@ export function useKeyboardManager(isEnabled: () => boolean) {
32
43
 
33
44
  // Store timestamp for touch start
34
45
  startTimestampRef.current = Date.now();
35
- }, [clearKeyboardTimeout, isEnabled]);
36
-
37
- const onPageChangeConfirm = React.useCallback(
38
- (force: boolean) => {
39
- if (!isEnabled()) {
40
- return;
41
- }
42
-
43
- clearKeyboardTimeout();
44
-
45
- if (force) {
46
- // Always dismiss input, even if we don't have a ref to it
47
- // We might not have the ref if onPageChangeStart was never called
48
- // This can happen if page change was not from a gesture
49
- Keyboard.dismiss();
50
- } else {
51
- const input = previouslyFocusedTextInputRef.current;
52
-
53
- // Dismiss the keyboard only if an input was a focused before
54
- // This makes sure we don't dismiss input on going back and focusing an input
55
- input?.blur();
56
- }
57
-
58
- // Cleanup the ID on successful page change
59
- previouslyFocusedTextInputRef.current = undefined;
60
- },
61
- [clearKeyboardTimeout, isEnabled]
62
- );
46
+ }, [clearKeyboardTimeout]);
63
47
 
64
48
  const onPageChangeCancel = React.useCallback(() => {
65
- if (!isEnabled()) {
49
+ if (!enabledRef.current || !focusedRef.current) {
66
50
  return;
67
51
  }
68
52
 
@@ -89,7 +73,86 @@ export function useKeyboardManager(isEnabled: () => boolean) {
89
73
  previouslyFocusedTextInputRef.current = undefined;
90
74
  }
91
75
  }
92
- }, [clearKeyboardTimeout, isEnabled]);
76
+ }, [clearKeyboardTimeout]);
77
+
78
+ const onPageChangeConfirm = React.useCallback(
79
+ ({
80
+ gesture,
81
+ active,
82
+ closing,
83
+ }: {
84
+ gesture: boolean;
85
+ active: boolean;
86
+ closing: boolean;
87
+ }) => {
88
+ if (!enabledRef.current || !focusedRef.current) {
89
+ return;
90
+ }
91
+
92
+ if (!closing) {
93
+ onPageChangeCancel();
94
+ return;
95
+ }
96
+
97
+ clearKeyboardTimeout();
98
+
99
+ if (!gesture) {
100
+ // Always dismiss input, even if we don't have a ref to it
101
+ // We might not have the ref if onPageChangeStart was never called
102
+ // This can happen if page change was not from a gesture
103
+ Keyboard.dismiss();
104
+ } else if (active) {
105
+ const input = previouslyFocusedTextInputRef.current;
106
+
107
+ // Dismiss the keyboard only if an input was a focused before
108
+ // This makes sure we don't dismiss input on going back and focusing an input
109
+ input?.blur();
110
+ }
111
+
112
+ // Cleanup the ID on successful page change
113
+ previouslyFocusedTextInputRef.current = undefined;
114
+ },
115
+ [clearKeyboardTimeout, onPageChangeCancel]
116
+ );
117
+
118
+ // Dismiss keyboard when screen loses focus (e.g. when pushing a new screen).
119
+ // This handles the "navigate forward" case so we don't dismiss the new screen's
120
+ // auto-focused input from handleTransition.
121
+ React.useLayoutEffect(() => {
122
+ if (enabledRef.current && !focused) {
123
+ Keyboard.dismiss();
124
+ }
125
+ }, [focused]);
126
+
127
+ // Dismiss keyboard if native focus is restored to an input in an unfocused screen.
128
+ // This can happen after dismissing a native alert when `autoFocus` is used on iOS.
129
+ React.useEffect(() => {
130
+ const blurInputInScreen = () => {
131
+ if (focusedRef.current || !enabledRef.current) {
132
+ return;
133
+ }
134
+
135
+ const input = TextInput.State.currentlyFocusedInput();
136
+
137
+ if (input && contentRef.current?.contains(input)) {
138
+ input.blur();
139
+ }
140
+ };
141
+
142
+ const subscriptions = [
143
+ Keyboard.addListener('keyboardWillShow', blurInputInScreen),
144
+ Keyboard.addListener('keyboardDidShow', blurInputInScreen),
145
+ ];
146
+
147
+ return () => {
148
+ subscriptions.forEach((subscription) => subscription.remove());
149
+ };
150
+ }, [contentRef]);
151
+
152
+ React.useLayoutEffect(() => {
153
+ enabledRef.current = enabled;
154
+ focusedRef.current = focused;
155
+ });
93
156
 
94
157
  React.useEffect(() => {
95
158
  return () => clearKeyboardTimeout();
@@ -1,23 +1,31 @@
1
1
  import * as React from 'react';
2
2
  import { View } from 'react-native';
3
- import type { PanGestureHandlerProperties } from 'react-native-gesture-handler';
3
+ import type {
4
+ PanGesture,
5
+ PanGestureConfig,
6
+ } from 'react-native-gesture-handler';
4
7
 
5
- const Dummy: any = ({ children }: { children: React.ReactNode }) => (
6
- <>{children}</>
7
- );
8
+ import { GestureHandlerContext } from '../utils/GestureHandlerContext';
8
9
 
9
- export const PanGestureHandler =
10
- Dummy as React.ComponentType<PanGestureHandlerProperties>;
10
+ export function GestureDetector({
11
+ gesture,
12
+ children,
13
+ }: {
14
+ gesture: PanGesture | undefined;
15
+ children: React.ReactNode;
16
+ }) {
17
+ return (
18
+ <GestureHandlerContext.Provider value={gesture ?? null}>
19
+ {children}
20
+ </GestureHandlerContext.Provider>
21
+ );
22
+ }
11
23
 
12
- export const GestureHandlerRootView = View;
24
+ // eslint-disable-next-line @eslint-react/hooks-extra/no-unnecessary-use-prefix, @eslint-react/hooks-extra/ensure-custom-hooks-using-other-hooks
25
+ export function usePanGesture(_config: PanGestureConfig) {
26
+ return undefined;
27
+ }
13
28
 
14
- export const GestureState = {
15
- UNDETERMINED: 0,
16
- FAILED: 1,
17
- BEGAN: 2,
18
- CANCELLED: 3,
19
- ACTIVE: 4,
20
- END: 5,
21
- };
29
+ export const GestureHandlerRootView = View;
22
30
 
23
- export type { PanGestureHandlerGestureEvent } from 'react-native-gesture-handler';
31
+ export type { PanGesture } from 'react-native-gesture-handler';
@@ -1,23 +1,34 @@
1
1
  import * as React from 'react';
2
2
  import {
3
- PanGestureHandler as PanGestureHandlerNative,
4
- type PanGestureHandlerProperties,
3
+ GestureDetector as GestureDetectorNative,
4
+ type PanGesture,
5
+ usePanGesture,
5
6
  } from 'react-native-gesture-handler';
6
7
 
7
- import { GestureHandlerRefContext } from '../utils/GestureHandlerRefContext';
8
+ import { GestureHandlerContext } from '../utils/GestureHandlerContext';
8
9
 
9
- export function PanGestureHandler(props: PanGestureHandlerProperties) {
10
- const gestureRef = React.useRef<PanGestureHandlerNative>(null);
10
+ export function GestureDetector({
11
+ gesture,
12
+ children,
13
+ }: {
14
+ gesture: PanGesture | undefined;
15
+ children: React.ReactNode;
16
+ }) {
17
+ if (gesture == null) {
18
+ throw new Error(
19
+ 'The provided gesture is undefined. A valid gesture must be passed to the GestureDetector on native platforms.'
20
+ );
21
+ }
11
22
 
12
23
  return (
13
- <GestureHandlerRefContext.Provider value={gestureRef}>
14
- <PanGestureHandlerNative {...props} ref={gestureRef} />
15
- </GestureHandlerRefContext.Provider>
24
+ <GestureHandlerContext.Provider value={gesture}>
25
+ <GestureDetectorNative gesture={gesture}>
26
+ {children}
27
+ </GestureDetectorNative>
28
+ </GestureHandlerContext.Provider>
16
29
  );
17
30
  }
18
31
 
19
- export type { PanGestureHandlerGestureEvent } from 'react-native-gesture-handler';
20
- export {
21
- GestureHandlerRootView,
22
- State as GestureState,
23
- } from 'react-native-gesture-handler';
32
+ export { usePanGesture };
33
+ export type { PanGesture } from 'react-native-gesture-handler';
34
+ export { GestureHandlerRootView } from 'react-native-gesture-handler';
@@ -11,6 +11,7 @@ import { HeaderSegment } from './HeaderSegment';
11
11
  export const Header = React.memo(function Header({
12
12
  back,
13
13
  progress,
14
+ inverted,
14
15
  options,
15
16
  route,
16
17
  navigation,
@@ -41,8 +42,8 @@ export const Header = React.memo(function Header({
41
42
  [navigation, route.key]
42
43
  );
43
44
 
44
- const isModal = React.useContext(ModalPresentationContext);
45
- const isParentHeaderShown = React.useContext(HeaderShownContext);
45
+ const isModal = React.use(ModalPresentationContext);
46
+ const isParentHeaderShown = React.use(HeaderShownContext);
46
47
 
47
48
  const statusBarHeight =
48
49
  options.headerStatusBarHeight !== undefined
@@ -54,6 +55,7 @@ export const Header = React.memo(function Header({
54
55
  return (
55
56
  <HeaderSegment
56
57
  {...options}
58
+ inverted={inverted}
57
59
  title={getHeaderTitle(options, route.name)}
58
60
  progress={progress}
59
61
  modal={isModal}
@@ -1,33 +1,41 @@
1
1
  import { getHeaderTitle, HeaderBackContext } from '@react-navigation/elements';
2
+ import { ActivityView } from '@react-navigation/elements/internal';
2
3
  import {
3
4
  NavigationProvider,
4
- type ParamListBase,
5
5
  type Route,
6
6
  useLinkBuilder,
7
+ useLocale,
7
8
  } from '@react-navigation/native';
8
9
  import * as React from 'react';
9
- import { type StyleProp, StyleSheet, View, type ViewStyle } from 'react-native';
10
+ import {
11
+ type LayoutChangeEvent,
12
+ type StyleProp,
13
+ StyleSheet,
14
+ View,
15
+ type ViewStyle,
16
+ } from 'react-native';
10
17
 
11
18
  import {
12
19
  forNoAnimation,
13
20
  forSlideLeft,
14
- forSlideRight,
15
21
  forSlideUp,
16
22
  } from '../../TransitionConfigs/HeaderStyleInterpolators';
17
- import type {
18
- Scene,
19
- StackHeaderMode,
20
- StackHeaderProps,
21
- StackNavigationProp,
22
- } from '../../types';
23
+ import type { Scene, StackHeaderMode, StackHeaderProps } from '../../types';
24
+ import { getInvertedMultiplier } from '../../utils/getInvertedMultiplier';
23
25
  import { Header } from './Header';
24
26
 
27
+ export type HeaderHeight = {
28
+ measured: boolean;
29
+ value: number;
30
+ };
31
+
25
32
  export type Props = {
26
33
  mode: StackHeaderMode;
27
34
  scenes: (Scene | undefined)[];
28
35
  getPreviousScene: (props: { route: Route<string> }) => Scene | undefined;
29
36
  getFocusedRoute: () => Route<string>;
30
- onContentHeightChange?: (props: {
37
+ contentHeight: HeaderHeight | undefined;
38
+ onContentHeightChange: (props: {
31
39
  route: Route<string>;
32
40
  height: number;
33
41
  }) => void;
@@ -39,19 +47,18 @@ export function HeaderContainer({
39
47
  scenes,
40
48
  getPreviousScene,
41
49
  getFocusedRoute,
50
+ contentHeight,
42
51
  onContentHeightChange,
43
52
  style,
44
53
  }: Props) {
45
54
  const focusedRoute = getFocusedRoute();
46
- const parentHeaderBack = React.useContext(HeaderBackContext);
55
+ const parentHeaderBack = React.use(HeaderBackContext);
47
56
  const { buildHref } = useLinkBuilder();
57
+ const { direction } = useLocale();
48
58
 
49
59
  return (
50
60
  <View style={[styles.container, style]}>
51
- {/* We render header only on two top-most headers as
52
- a workaround for https://github.com/react-navigation/react-navigation/issues/12456.
53
- If the header is persisted, it might be placed incorrectly when navigating back. */}
54
- {scenes.slice(-2).map((scene, i, self) => {
61
+ {scenes.map((scene, i, self) => {
55
62
  if ((mode === 'screen' && i !== self.length - 1) || !scene) {
56
63
  return null;
57
64
  }
@@ -96,6 +103,9 @@ export function HeaderContainer({
96
103
  headerMode: previousHeaderMode,
97
104
  } = previousDescriptor?.options || {};
98
105
 
106
+ const { headerTransparent: nextHeaderTransparent } =
107
+ nextDescriptor?.options || {};
108
+
99
109
  // If any of the next screens don't have a header or header is part of the screen
100
110
  // Then we need to move this header offscreen so that it doesn't cover it
101
111
  const nextHeaderlessScene = self.slice(i + 1).find((scene) => {
@@ -115,28 +125,54 @@ export function HeaderContainer({
115
125
  // We still need to animate when coming back from next scene
116
126
  // A hacky way to check this is if the next scene exists
117
127
  !nextDescriptor) ||
128
+ // If next header is transparent, we want to treat this header as static
129
+ // Otherwise it may show through the next header
130
+ nextHeaderTransparent ||
118
131
  nextHeaderlessScene;
119
132
 
120
133
  const props: StackHeaderProps = {
121
134
  back: headerBack,
122
135
  progress: scene.progress,
136
+ inverted: getInvertedMultiplier(
137
+ isHeaderStatic && nextHeaderlessGestureDirection
138
+ ? nextHeaderlessGestureDirection
139
+ : scene.descriptor.options.gestureDirection,
140
+ direction === 'rtl'
141
+ ),
123
142
  options: scene.descriptor.options,
124
143
  route: scene.descriptor.route,
125
- navigation: scene.descriptor
126
- .navigation as StackNavigationProp<ParamListBase>,
144
+ navigation: scene.descriptor.navigation,
127
145
  styleInterpolator:
128
146
  mode === 'float'
129
147
  ? isHeaderStatic
130
148
  ? nextHeaderlessGestureDirection === 'vertical' ||
131
149
  nextHeaderlessGestureDirection === 'vertical-inverted'
132
150
  ? forSlideUp
133
- : nextHeaderlessGestureDirection === 'horizontal-inverted'
134
- ? forSlideRight
135
- : forSlideLeft
151
+ : forSlideLeft
136
152
  : headerStyleInterpolator
137
153
  : forNoAnimation,
138
154
  };
139
155
 
156
+ const onRef = (node: React.ComponentRef<typeof View> | null) => {
157
+ node?.measure((_x, _y, _width, height) => {
158
+ if (height && !contentHeight?.measured) {
159
+ onContentHeightChange({
160
+ route: scene.descriptor.route,
161
+ height,
162
+ });
163
+ }
164
+ });
165
+ };
166
+
167
+ const onLayout = (e: LayoutChangeEvent) => {
168
+ const { height } = e.nativeEvent.layout;
169
+
170
+ onContentHeightChange({
171
+ route: scene.descriptor.route,
172
+ height,
173
+ });
174
+ };
175
+
140
176
  return (
141
177
  <NavigationProvider
142
178
  key={scene.descriptor.route.key}
@@ -144,31 +180,37 @@ export function HeaderContainer({
144
180
  route={scene.descriptor.route}
145
181
  >
146
182
  <View
147
- onLayout={
148
- onContentHeightChange
149
- ? (e) => {
150
- const { height } = e.nativeEvent.layout;
151
-
152
- onContentHeightChange({
153
- route: scene.descriptor.route,
154
- height,
155
- });
156
- }
157
- : undefined
158
- }
159
- aria-hidden={!isFocused}
160
183
  style={[
161
- // Avoid positioning the focused header absolutely
184
+ // Avoid absolutely positioning the focused header unnecessarily
162
185
  // Otherwise accessibility tools don't seem to be able to find it
163
186
  (mode === 'float' && !isFocused) || headerTransparent
164
- ? styles.header
187
+ ? [
188
+ styles.absolute,
189
+ mode === 'screen'
190
+ ? // For transparent headers, specify a min height
191
+ // This is needed for screen readers, and testing tools on Android
192
+ // For float header, it's handled in CardStack
193
+ { minHeight: contentHeight?.value }
194
+ : null,
195
+ ]
165
196
  : null,
166
197
  {
167
198
  pointerEvents: isFocused ? 'box-none' : 'none',
168
199
  },
169
200
  ]}
170
201
  >
171
- {header !== undefined ? header(props) : <Header {...props} />}
202
+ <View
203
+ ref={onRef}
204
+ onLayout={onLayout}
205
+ style={{ pointerEvents: 'box-none' }}
206
+ >
207
+ <ActivityView
208
+ mode={isFocused ? 'normal' : 'inert'}
209
+ visible={true}
210
+ >
211
+ {header !== undefined ? header(props) : <Header {...props} />}
212
+ </ActivityView>
213
+ </View>
172
214
  </View>
173
215
  </NavigationProvider>
174
216
  );
@@ -181,7 +223,7 @@ const styles = StyleSheet.create({
181
223
  container: {
182
224
  pointerEvents: 'box-none',
183
225
  },
184
- header: {
226
+ absolute: {
185
227
  position: 'absolute',
186
228
  top: 0,
187
229
  start: 0,
@@ -6,7 +6,6 @@ import {
6
6
  HeaderTitle,
7
7
  useFrameSize,
8
8
  } from '@react-navigation/elements';
9
- import { useLocale } from '@react-navigation/native';
10
9
  import * as React from 'react';
11
10
  import { StyleSheet, type ViewStyle } from 'react-native';
12
11
 
@@ -20,18 +19,19 @@ type Props = Omit<StackHeaderOptions, 'headerStatusBarHeight'> & {
20
19
  headerStatusBarHeight: number;
21
20
  title: string;
22
21
  modal: boolean;
23
- onGoBack?: () => void;
24
- backHref?: string;
22
+ onGoBack?: (() => void) | undefined;
23
+ backHref?: string | undefined;
25
24
  progress: SceneProgress;
25
+ inverted: 1 | -1;
26
26
  styleInterpolator: StackHeaderStyleInterpolator;
27
27
  };
28
28
 
29
29
  export function HeaderSegment(props: Props) {
30
- const { direction } = useLocale();
31
30
  const layout = useFrameSize((frame) => frame, true);
32
31
 
33
32
  const {
34
33
  progress,
34
+ inverted,
35
35
  modal,
36
36
  onGoBack,
37
37
  backHref,
@@ -40,7 +40,7 @@ export function HeaderSegment(props: Props) {
40
40
  ? (props: HeaderBackButtonProps) => <HeaderBackButton {...props} />
41
41
  : undefined,
42
42
  headerRight: right,
43
- headerBackImage,
43
+ headerBackIcon,
44
44
  headerBackTitle,
45
45
  headerBackButtonDisplayMode,
46
46
  headerBackTruncatedTitle,
@@ -64,9 +64,8 @@ export function HeaderSegment(props: Props) {
64
64
  topInset: headerStatusBarHeight,
65
65
  });
66
66
 
67
- const { height = defaultHeight } = StyleSheet.flatten(
68
- customHeaderStyle || {}
69
- ) as ViewStyle;
67
+ const { height = defaultHeight } = (StyleSheet.flatten(customHeaderStyle) ??
68
+ {}) as ViewStyle;
70
69
 
71
70
  const headerHeight = typeof height === 'number' ? height : defaultHeight;
72
71
 
@@ -76,7 +75,7 @@ export function HeaderSegment(props: Props) {
76
75
  styleInterpolator({
77
76
  current: { progress: progress.current },
78
77
  next: progress.next && { progress: progress.next },
79
- direction,
78
+ inverted,
80
79
  layouts: {
81
80
  header: {
82
81
  height: headerHeight,
@@ -85,7 +84,7 @@ export function HeaderSegment(props: Props) {
85
84
  screen: layout,
86
85
  },
87
86
  }),
88
- [styleInterpolator, progress, direction, headerHeight, layout]
87
+ [styleInterpolator, progress, inverted, headerHeight, layout]
89
88
  );
90
89
 
91
90
  const headerLeft: StackHeaderOptions['headerLeft'] = left
@@ -93,7 +92,7 @@ export function HeaderSegment(props: Props) {
93
92
  left({
94
93
  ...props,
95
94
  href: backHref,
96
- backImage: headerBackImage,
95
+ icon: headerBackIcon,
97
96
  accessibilityLabel: headerBackAccessibilityLabel,
98
97
  testID: headerBackTestID,
99
98
  allowFontScaling: headerBackAllowFontScaling,