@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.
- package/lib/module/TransitionConfigs/CardStyleInterpolators.js +280 -84
- package/lib/module/TransitionConfigs/CardStyleInterpolators.js.map +1 -1
- package/lib/module/TransitionConfigs/HeaderStyleInterpolators.js +11 -44
- package/lib/module/TransitionConfigs/HeaderStyleInterpolators.js.map +1 -1
- package/lib/module/TransitionConfigs/TransitionEasings.js +71 -0
- package/lib/module/TransitionConfigs/TransitionEasings.js.map +1 -0
- package/lib/module/TransitionConfigs/TransitionPresets.js +50 -10
- package/lib/module/TransitionConfigs/TransitionPresets.js.map +1 -1
- package/lib/module/TransitionConfigs/TransitionSpecs.js +69 -34
- package/lib/module/TransitionConfigs/TransitionSpecs.js.map +1 -1
- package/lib/module/index.js +2 -2
- package/lib/module/index.js.map +1 -1
- package/lib/module/navigators/createStackNavigator.js +37 -33
- package/lib/module/navigators/createStackNavigator.js.map +1 -1
- package/lib/module/utils/GestureHandlerContext.js +5 -0
- package/lib/module/utils/GestureHandlerContext.js.map +1 -0
- package/lib/module/utils/gestureActivationCriteria.js +10 -8
- package/lib/module/utils/gestureActivationCriteria.js.map +1 -1
- package/lib/module/utils/useCardAnimation.js +1 -1
- package/lib/module/utils/useCardAnimation.js.map +1 -1
- package/lib/module/utils/useGestureHandler.js +9 -0
- package/lib/module/utils/useGestureHandler.js.map +1 -0
- package/lib/module/utils/useKeyboardManager.js +71 -26
- package/lib/module/utils/useKeyboardManager.js.map +1 -1
- package/lib/module/views/GestureHandler.js +15 -14
- package/lib/module/views/GestureHandler.js.map +1 -1
- package/lib/module/views/GestureHandlerNative.js +16 -10
- package/lib/module/views/GestureHandlerNative.js.map +1 -1
- package/lib/module/views/Header/Header.js +4 -2
- package/lib/module/views/Header/Header.js.map +1 -1
- package/lib/module/views/Header/HeaderContainer.js +60 -21
- package/lib/module/views/Header/HeaderContainer.js.map +1 -1
- package/lib/module/views/Header/HeaderSegment.js +6 -9
- package/lib/module/views/Header/HeaderSegment.js.map +1 -1
- package/lib/module/views/Stack/Card.js +170 -123
- package/lib/module/views/Stack/Card.js.map +1 -1
- package/lib/module/views/Stack/CardContainer.js +96 -98
- package/lib/module/views/Stack/CardContainer.js.map +1 -1
- package/lib/module/views/Stack/CardContent.js +10 -37
- package/lib/module/views/Stack/CardContent.js.map +1 -1
- package/lib/module/views/Stack/CardStack.js +211 -147
- package/lib/module/views/Stack/CardStack.js.map +1 -1
- package/lib/module/views/Stack/StackView.js +106 -57
- package/lib/module/views/Stack/StackView.js.map +1 -1
- package/lib/typescript/src/TransitionConfigs/CardStyleInterpolators.d.ts +20 -12
- package/lib/typescript/src/TransitionConfigs/CardStyleInterpolators.d.ts.map +1 -1
- package/lib/typescript/src/TransitionConfigs/HeaderStyleInterpolators.d.ts +4 -4
- package/lib/typescript/src/TransitionConfigs/HeaderStyleInterpolators.d.ts.map +1 -1
- package/lib/typescript/src/TransitionConfigs/TransitionEasings.d.ts +38 -0
- package/lib/typescript/src/TransitionConfigs/TransitionEasings.d.ts.map +1 -0
- package/lib/typescript/src/TransitionConfigs/TransitionPresets.d.ts +15 -3
- package/lib/typescript/src/TransitionConfigs/TransitionPresets.d.ts.map +1 -1
- package/lib/typescript/src/TransitionConfigs/TransitionSpecs.d.ts +44 -15
- package/lib/typescript/src/TransitionConfigs/TransitionSpecs.d.ts.map +1 -1
- package/lib/typescript/src/index.d.ts +12 -12
- package/lib/typescript/src/index.d.ts.map +1 -1
- package/lib/typescript/src/navigators/createStackNavigator.d.ts +10 -15
- package/lib/typescript/src/navigators/createStackNavigator.d.ts.map +1 -1
- package/lib/typescript/src/types.d.ts +120 -108
- package/lib/typescript/src/types.d.ts.map +1 -1
- package/lib/typescript/src/utils/CardAnimationContext.d.ts +1 -1
- package/lib/typescript/src/utils/CardAnimationContext.d.ts.map +1 -1
- package/lib/typescript/src/utils/GestureHandlerContext.d.ts +3 -0
- package/lib/typescript/src/utils/GestureHandlerContext.d.ts.map +1 -0
- package/lib/typescript/src/utils/gestureActivationCriteria.d.ts +4 -51
- package/lib/typescript/src/utils/gestureActivationCriteria.d.ts.map +1 -1
- package/lib/typescript/src/utils/getDistanceForDirection.d.ts +1 -1
- package/lib/typescript/src/utils/getDistanceForDirection.d.ts.map +1 -1
- package/lib/typescript/src/utils/getInvertedMultiplier.d.ts +1 -1
- package/lib/typescript/src/utils/getInvertedMultiplier.d.ts.map +1 -1
- package/lib/typescript/src/utils/getModalRoutesKeys.d.ts +1 -1
- package/lib/typescript/src/utils/getModalRoutesKeys.d.ts.map +1 -1
- package/lib/typescript/src/utils/getShadowStyle.d.ts +5 -5
- package/lib/typescript/src/utils/useCardAnimation.d.ts +1 -1
- package/lib/typescript/src/utils/useCardAnimation.d.ts.map +1 -1
- package/lib/typescript/src/utils/useGestureHandler.d.ts +2 -0
- package/lib/typescript/src/utils/useGestureHandler.d.ts.map +1 -0
- package/lib/typescript/src/utils/useKeyboardManager.d.ts +12 -2
- package/lib/typescript/src/utils/useKeyboardManager.d.ts.map +1 -1
- package/lib/typescript/src/views/GestureHandler.android.d.ts +1 -1
- package/lib/typescript/src/views/GestureHandler.android.d.ts.map +1 -1
- package/lib/typescript/src/views/GestureHandler.d.ts +7 -11
- package/lib/typescript/src/views/GestureHandler.d.ts.map +1 -1
- package/lib/typescript/src/views/GestureHandler.ios.d.ts +1 -1
- package/lib/typescript/src/views/GestureHandler.ios.d.ts.map +1 -1
- package/lib/typescript/src/views/GestureHandlerNative.d.ts +9 -4
- package/lib/typescript/src/views/GestureHandlerNative.d.ts.map +1 -1
- package/lib/typescript/src/views/Header/Header.d.ts +2 -2
- package/lib/typescript/src/views/Header/Header.d.ts.map +1 -1
- package/lib/typescript/src/views/Header/HeaderContainer.d.ts +9 -3
- package/lib/typescript/src/views/Header/HeaderContainer.d.ts.map +1 -1
- package/lib/typescript/src/views/Header/HeaderSegment.d.ts +6 -4
- package/lib/typescript/src/views/Header/HeaderSegment.d.ts.map +1 -1
- package/lib/typescript/src/views/Stack/Card.d.ts +14 -8
- package/lib/typescript/src/views/Stack/Card.d.ts.map +1 -1
- package/lib/typescript/src/views/Stack/CardContainer.d.ts +7 -7
- package/lib/typescript/src/views/Stack/CardContainer.d.ts.map +1 -1
- package/lib/typescript/src/views/Stack/CardContent.d.ts +1 -1
- package/lib/typescript/src/views/Stack/CardContent.d.ts.map +1 -1
- package/lib/typescript/src/views/Stack/CardStack.d.ts +10 -6
- package/lib/typescript/src/views/Stack/CardStack.d.ts.map +1 -1
- package/lib/typescript/src/views/Stack/StackView.d.ts +14 -17
- package/lib/typescript/src/views/Stack/StackView.d.ts.map +1 -1
- package/package.json +23 -24
- package/src/TransitionConfigs/CardStyleInterpolators.tsx +399 -100
- package/src/TransitionConfigs/HeaderStyleInterpolators.tsx +15 -46
- package/src/TransitionConfigs/TransitionEasings.tsx +90 -0
- package/src/TransitionConfigs/TransitionPresets.tsx +56 -10
- package/src/TransitionConfigs/TransitionSpecs.tsx +68 -34
- package/src/index.tsx +3 -2
- package/src/navigators/createStackNavigator.tsx +74 -91
- package/src/types.tsx +149 -123
- package/src/utils/GestureHandlerContext.tsx +5 -0
- package/src/utils/gestureActivationCriteria.tsx +14 -10
- package/src/utils/useCardAnimation.tsx +1 -1
- package/src/utils/useGestureHandler.tsx +9 -0
- package/src/utils/useKeyboardManager.tsx +96 -33
- package/src/views/GestureHandler.tsx +24 -16
- package/src/views/GestureHandlerNative.tsx +24 -13
- package/src/views/Header/Header.tsx +4 -2
- package/src/views/Header/HeaderContainer.tsx +79 -37
- package/src/views/Header/HeaderSegment.tsx +10 -11
- package/src/views/Stack/Card.tsx +241 -154
- package/src/views/Stack/CardContainer.tsx +111 -127
- package/src/views/Stack/CardContent.tsx +13 -51
- package/src/views/Stack/CardStack.tsx +478 -335
- package/src/views/Stack/StackView.tsx +181 -82
- package/lib/module/utils/GestureHandlerRefContext.js +0 -5
- package/lib/module/utils/GestureHandlerRefContext.js.map +0 -1
- package/lib/module/utils/findLastIndex.js +0 -11
- package/lib/module/utils/findLastIndex.js.map +0 -1
- package/lib/module/utils/useGestureHandlerRef.js +0 -12
- package/lib/module/utils/useGestureHandlerRef.js.map +0 -1
- package/lib/module/views/Stack/CardA11yWrapper.js +0 -43
- package/lib/module/views/Stack/CardA11yWrapper.js.map +0 -1
- package/lib/typescript/src/utils/GestureHandlerRefContext.d.ts +0 -3
- package/lib/typescript/src/utils/GestureHandlerRefContext.d.ts.map +0 -1
- package/lib/typescript/src/utils/findLastIndex.d.ts +0 -2
- package/lib/typescript/src/utils/findLastIndex.d.ts.map +0 -1
- package/lib/typescript/src/utils/useGestureHandlerRef.d.ts +0 -3
- package/lib/typescript/src/utils/useGestureHandlerRef.d.ts.map +0 -1
- package/lib/typescript/src/views/Stack/CardA11yWrapper.d.ts +0 -15
- package/lib/typescript/src/views/Stack/CardA11yWrapper.d.ts.map +0 -1
- package/src/utils/GestureHandlerRefContext.tsx +0 -5
- package/src/utils/findLastIndex.tsx +0 -9
- package/src/utils/useGestureHandlerRef.tsx +0 -15
- 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(
|
|
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 =
|
|
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 (!
|
|
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
|
|
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 (!
|
|
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
|
|
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 {
|
|
3
|
+
import type {
|
|
4
|
+
PanGesture,
|
|
5
|
+
PanGestureConfig,
|
|
6
|
+
} from 'react-native-gesture-handler';
|
|
4
7
|
|
|
5
|
-
|
|
6
|
-
<>{children}</>
|
|
7
|
-
);
|
|
8
|
+
import { GestureHandlerContext } from '../utils/GestureHandlerContext';
|
|
8
9
|
|
|
9
|
-
export
|
|
10
|
-
|
|
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
|
-
|
|
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
|
|
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 {
|
|
31
|
+
export type { PanGesture } from 'react-native-gesture-handler';
|
|
@@ -1,23 +1,34 @@
|
|
|
1
1
|
import * as React from 'react';
|
|
2
2
|
import {
|
|
3
|
-
|
|
4
|
-
type
|
|
3
|
+
GestureDetector as GestureDetectorNative,
|
|
4
|
+
type PanGesture,
|
|
5
|
+
usePanGesture,
|
|
5
6
|
} from 'react-native-gesture-handler';
|
|
6
7
|
|
|
7
|
-
import {
|
|
8
|
+
import { GestureHandlerContext } from '../utils/GestureHandlerContext';
|
|
8
9
|
|
|
9
|
-
export function
|
|
10
|
-
|
|
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
|
-
<
|
|
14
|
-
<
|
|
15
|
-
|
|
24
|
+
<GestureHandlerContext.Provider value={gesture}>
|
|
25
|
+
<GestureDetectorNative gesture={gesture}>
|
|
26
|
+
{children}
|
|
27
|
+
</GestureDetectorNative>
|
|
28
|
+
</GestureHandlerContext.Provider>
|
|
16
29
|
);
|
|
17
30
|
}
|
|
18
31
|
|
|
19
|
-
export
|
|
20
|
-
export {
|
|
21
|
-
|
|
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.
|
|
45
|
-
const isParentHeaderShown = React.
|
|
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 {
|
|
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
|
-
|
|
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
|
-
|
|
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.
|
|
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
|
-
{
|
|
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
|
-
:
|
|
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
|
|
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
|
-
?
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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,
|
|
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
|
-
|
|
95
|
+
icon: headerBackIcon,
|
|
97
96
|
accessibilityLabel: headerBackAccessibilityLabel,
|
|
98
97
|
testID: headerBackTestID,
|
|
99
98
|
allowFontScaling: headerBackAllowFontScaling,
|