@react-navigation/stack 8.0.0-alpha.4 → 8.0.0-alpha.41
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/index.js +2 -2
- package/lib/module/index.js.map +1 -1
- package/lib/module/navigators/createStackNavigator.js +3 -7
- 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 +2 -2
- package/lib/module/views/Header/Header.js.map +1 -1
- package/lib/module/views/Header/HeaderContainer.js +52 -18
- package/lib/module/views/Header/HeaderContainer.js.map +1 -1
- package/lib/module/views/Header/HeaderSegment.js +2 -2
- package/lib/module/views/Header/HeaderSegment.js.map +1 -1
- package/lib/module/views/Stack/Card.js +90 -87
- package/lib/module/views/Stack/Card.js.map +1 -1
- package/lib/module/views/Stack/CardContainer.js +72 -98
- package/lib/module/views/Stack/CardContainer.js.map +1 -1
- package/lib/module/views/Stack/CardStack.js +171 -126
- package/lib/module/views/Stack/CardStack.js.map +1 -1
- package/lib/module/views/Stack/StackView.js +103 -53
- package/lib/module/views/Stack/StackView.js.map +1 -1
- package/lib/typescript/src/TransitionConfigs/CardStyleInterpolators.d.ts +1 -1
- package/lib/typescript/src/TransitionConfigs/CardStyleInterpolators.d.ts.map +1 -1
- package/lib/typescript/src/TransitionConfigs/HeaderStyleInterpolators.d.ts +1 -1
- package/lib/typescript/src/TransitionConfigs/HeaderStyleInterpolators.d.ts.map +1 -1
- package/lib/typescript/src/TransitionConfigs/TransitionPresets.d.ts +1 -1
- package/lib/typescript/src/TransitionConfigs/TransitionPresets.d.ts.map +1 -1
- package/lib/typescript/src/TransitionConfigs/TransitionSpecs.d.ts +1 -1
- 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 +46 -53
- 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/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 +5 -4
- package/lib/typescript/src/views/Header/HeaderSegment.d.ts.map +1 -1
- package/lib/typescript/src/views/Stack/Card.d.ts +5 -5
- package/lib/typescript/src/views/Stack/Card.d.ts.map +1 -1
- package/lib/typescript/src/views/Stack/CardContainer.d.ts +5 -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 +6 -5
- package/lib/typescript/src/views/Stack/CardStack.d.ts.map +1 -1
- package/lib/typescript/src/views/Stack/StackView.d.ts +10 -13
- package/lib/typescript/src/views/Stack/StackView.d.ts.map +1 -1
- package/package.json +21 -23
- package/src/index.tsx +3 -2
- package/src/navigators/createStackNavigator.tsx +9 -40
- package/src/types.tsx +73 -73
- 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 +27 -16
- package/src/views/GestureHandlerNative.tsx +26 -12
- package/src/views/Header/Header.tsx +2 -2
- package/src/views/Header/HeaderContainer.tsx +67 -24
- package/src/views/Header/HeaderSegment.tsx +4 -4
- package/src/views/Stack/Card.tsx +134 -115
- package/src/views/Stack/CardContainer.tsx +86 -127
- package/src/views/Stack/CardStack.tsx +380 -323
- package/src/views/Stack/StackView.tsx +173 -71
- 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,4 +1,5 @@
|
|
|
1
1
|
import type { LocaleDirection } from '@react-navigation/native';
|
|
2
|
+
import type { PanGestureConfig } from 'react-native-gesture-handler';
|
|
2
3
|
|
|
3
4
|
import type { GestureDirection, Layout } from '../types';
|
|
4
5
|
import { getInvertedMultiplier } from './getInvertedMultiplier';
|
|
@@ -17,9 +18,9 @@ export const gestureActivationCriteria = ({
|
|
|
17
18
|
}: {
|
|
18
19
|
direction: LocaleDirection;
|
|
19
20
|
gestureDirection: GestureDirection;
|
|
20
|
-
gestureResponseDistance?: number;
|
|
21
|
+
gestureResponseDistance?: number | undefined;
|
|
21
22
|
layout: Layout;
|
|
22
|
-
}) => {
|
|
23
|
+
}): PanGestureConfig => {
|
|
23
24
|
const enableTrackpadTwoFingerGesture = true;
|
|
24
25
|
|
|
25
26
|
const distance =
|
|
@@ -30,17 +31,20 @@ export const gestureActivationCriteria = ({
|
|
|
30
31
|
? GESTURE_RESPONSE_DISTANCE_VERTICAL
|
|
31
32
|
: GESTURE_RESPONSE_DISTANCE_HORIZONTAL;
|
|
32
33
|
|
|
34
|
+
const failOffsetX: [number, number] = [-15, 15];
|
|
35
|
+
const failOffsetY: [number, number] = [-20, 20];
|
|
36
|
+
|
|
33
37
|
if (gestureDirection === 'vertical') {
|
|
34
38
|
return {
|
|
35
|
-
|
|
36
|
-
|
|
39
|
+
failOffsetX,
|
|
40
|
+
activeOffsetY: 5,
|
|
37
41
|
hitSlop: { bottom: -layout.height + distance },
|
|
38
42
|
enableTrackpadTwoFingerGesture,
|
|
39
43
|
};
|
|
40
44
|
} else if (gestureDirection === 'vertical-inverted') {
|
|
41
45
|
return {
|
|
42
|
-
|
|
43
|
-
|
|
46
|
+
failOffsetX,
|
|
47
|
+
activeOffsetY: -5,
|
|
44
48
|
hitSlop: { top: -layout.height + distance },
|
|
45
49
|
enableTrackpadTwoFingerGesture,
|
|
46
50
|
};
|
|
@@ -53,15 +57,15 @@ export const gestureActivationCriteria = ({
|
|
|
53
57
|
|
|
54
58
|
if (invertedMultiplier === 1) {
|
|
55
59
|
return {
|
|
56
|
-
|
|
57
|
-
|
|
60
|
+
activeOffsetX: 5,
|
|
61
|
+
failOffsetY,
|
|
58
62
|
hitSlop: { right: hitSlop },
|
|
59
63
|
enableTrackpadTwoFingerGesture,
|
|
60
64
|
};
|
|
61
65
|
} else {
|
|
62
66
|
return {
|
|
63
|
-
|
|
64
|
-
|
|
67
|
+
activeOffsetX: -5,
|
|
68
|
+
failOffsetY,
|
|
65
69
|
hitSlop: { left: hitSlop },
|
|
66
70
|
enableTrackpadTwoFingerGesture,
|
|
67
71
|
};
|
|
@@ -3,7 +3,7 @@ import * as React from 'react';
|
|
|
3
3
|
import { CardAnimationContext } from './CardAnimationContext';
|
|
4
4
|
|
|
5
5
|
export function useCardAnimation() {
|
|
6
|
-
const animation = React.
|
|
6
|
+
const animation = React.use(CardAnimationContext);
|
|
7
7
|
|
|
8
8
|
if (animation === undefined) {
|
|
9
9
|
throw new Error(
|
|
@@ -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,34 @@
|
|
|
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 {
|
|
32
|
+
PanGesture,
|
|
33
|
+
PanGestureActiveEvent,
|
|
34
|
+
} from 'react-native-gesture-handler';
|
|
@@ -1,23 +1,37 @@
|
|
|
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
|
-
|
|
32
|
+
export { usePanGesture };
|
|
33
|
+
export type {
|
|
34
|
+
PanGesture,
|
|
35
|
+
PanGestureActiveEvent,
|
|
23
36
|
} from 'react-native-gesture-handler';
|
|
37
|
+
export { GestureHandlerRootView } from 'react-native-gesture-handler';
|
|
@@ -41,8 +41,8 @@ export const Header = React.memo(function Header({
|
|
|
41
41
|
[navigation, route.key]
|
|
42
42
|
);
|
|
43
43
|
|
|
44
|
-
const isModal = React.
|
|
45
|
-
const isParentHeaderShown = React.
|
|
44
|
+
const isModal = React.use(ModalPresentationContext);
|
|
45
|
+
const isParentHeaderShown = React.use(HeaderShownContext);
|
|
46
46
|
|
|
47
47
|
const statusBarHeight =
|
|
48
48
|
options.headerStatusBarHeight !== undefined
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { getHeaderTitle, HeaderBackContext } from '@react-navigation/elements';
|
|
2
|
+
import { ActivityView } from '@react-navigation/elements/internal';
|
|
2
3
|
import {
|
|
3
4
|
NavigationProvider,
|
|
4
5
|
type ParamListBase,
|
|
@@ -6,7 +7,13 @@ import {
|
|
|
6
7
|
useLinkBuilder,
|
|
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,
|
|
@@ -22,12 +29,18 @@ import type {
|
|
|
22
29
|
} from '../../types';
|
|
23
30
|
import { Header } from './Header';
|
|
24
31
|
|
|
32
|
+
export type HeaderHeight = {
|
|
33
|
+
measured: boolean;
|
|
34
|
+
value: number;
|
|
35
|
+
};
|
|
36
|
+
|
|
25
37
|
export type Props = {
|
|
26
38
|
mode: StackHeaderMode;
|
|
27
39
|
scenes: (Scene | undefined)[];
|
|
28
40
|
getPreviousScene: (props: { route: Route<string> }) => Scene | undefined;
|
|
29
41
|
getFocusedRoute: () => Route<string>;
|
|
30
|
-
|
|
42
|
+
contentHeight: HeaderHeight | undefined;
|
|
43
|
+
onContentHeightChange: (props: {
|
|
31
44
|
route: Route<string>;
|
|
32
45
|
height: number;
|
|
33
46
|
}) => void;
|
|
@@ -39,19 +52,17 @@ export function HeaderContainer({
|
|
|
39
52
|
scenes,
|
|
40
53
|
getPreviousScene,
|
|
41
54
|
getFocusedRoute,
|
|
55
|
+
contentHeight,
|
|
42
56
|
onContentHeightChange,
|
|
43
57
|
style,
|
|
44
58
|
}: Props) {
|
|
45
59
|
const focusedRoute = getFocusedRoute();
|
|
46
|
-
const parentHeaderBack = React.
|
|
60
|
+
const parentHeaderBack = React.use(HeaderBackContext);
|
|
47
61
|
const { buildHref } = useLinkBuilder();
|
|
48
62
|
|
|
49
63
|
return (
|
|
50
64
|
<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) => {
|
|
65
|
+
{scenes.map((scene, i, self) => {
|
|
55
66
|
if ((mode === 'screen' && i !== self.length - 1) || !scene) {
|
|
56
67
|
return null;
|
|
57
68
|
}
|
|
@@ -96,6 +107,9 @@ export function HeaderContainer({
|
|
|
96
107
|
headerMode: previousHeaderMode,
|
|
97
108
|
} = previousDescriptor?.options || {};
|
|
98
109
|
|
|
110
|
+
const { headerTransparent: nextHeaderTransparent } =
|
|
111
|
+
nextDescriptor?.options || {};
|
|
112
|
+
|
|
99
113
|
// If any of the next screens don't have a header or header is part of the screen
|
|
100
114
|
// Then we need to move this header offscreen so that it doesn't cover it
|
|
101
115
|
const nextHeaderlessScene = self.slice(i + 1).find((scene) => {
|
|
@@ -115,6 +129,9 @@ export function HeaderContainer({
|
|
|
115
129
|
// We still need to animate when coming back from next scene
|
|
116
130
|
// A hacky way to check this is if the next scene exists
|
|
117
131
|
!nextDescriptor) ||
|
|
132
|
+
// If next header is tranparent, we want to treat this header as static
|
|
133
|
+
// Otherwise it may show through the next header
|
|
134
|
+
nextHeaderTransparent ||
|
|
118
135
|
nextHeaderlessScene;
|
|
119
136
|
|
|
120
137
|
const props: StackHeaderProps = {
|
|
@@ -137,6 +154,26 @@ export function HeaderContainer({
|
|
|
137
154
|
: forNoAnimation,
|
|
138
155
|
};
|
|
139
156
|
|
|
157
|
+
const onRef = (node: View | null) => {
|
|
158
|
+
node?.measure((_x, _y, _width, height) => {
|
|
159
|
+
if (height && !contentHeight?.measured) {
|
|
160
|
+
onContentHeightChange({
|
|
161
|
+
route: scene.descriptor.route,
|
|
162
|
+
height,
|
|
163
|
+
});
|
|
164
|
+
}
|
|
165
|
+
});
|
|
166
|
+
};
|
|
167
|
+
|
|
168
|
+
const onLayout = (e: LayoutChangeEvent) => {
|
|
169
|
+
const { height } = e.nativeEvent.layout;
|
|
170
|
+
|
|
171
|
+
onContentHeightChange({
|
|
172
|
+
route: scene.descriptor.route,
|
|
173
|
+
height,
|
|
174
|
+
});
|
|
175
|
+
};
|
|
176
|
+
|
|
140
177
|
return (
|
|
141
178
|
<NavigationProvider
|
|
142
179
|
key={scene.descriptor.route.key}
|
|
@@ -144,31 +181,37 @@ export function HeaderContainer({
|
|
|
144
181
|
route={scene.descriptor.route}
|
|
145
182
|
>
|
|
146
183
|
<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
184
|
style={[
|
|
161
|
-
// Avoid positioning the focused header
|
|
185
|
+
// Avoid absolutely positioning the focused header unnecessarily
|
|
162
186
|
// Otherwise accessibility tools don't seem to be able to find it
|
|
163
187
|
(mode === 'float' && !isFocused) || headerTransparent
|
|
164
|
-
?
|
|
188
|
+
? [
|
|
189
|
+
styles.absolute,
|
|
190
|
+
mode === 'screen'
|
|
191
|
+
? // For transparent headers, specify a min height
|
|
192
|
+
// This is needed for screen readers, and testing tools on Android
|
|
193
|
+
// For float header, it's handled in CardStack
|
|
194
|
+
{ minHeight: contentHeight?.value }
|
|
195
|
+
: null,
|
|
196
|
+
]
|
|
165
197
|
: null,
|
|
166
198
|
{
|
|
167
199
|
pointerEvents: isFocused ? 'box-none' : 'none',
|
|
168
200
|
},
|
|
169
201
|
]}
|
|
170
202
|
>
|
|
171
|
-
|
|
203
|
+
<View
|
|
204
|
+
ref={onRef}
|
|
205
|
+
onLayout={onLayout}
|
|
206
|
+
style={{ pointerEvents: 'box-none' }}
|
|
207
|
+
>
|
|
208
|
+
<ActivityView
|
|
209
|
+
mode={isFocused ? 'normal' : 'inert'}
|
|
210
|
+
visible={true}
|
|
211
|
+
>
|
|
212
|
+
{header !== undefined ? header(props) : <Header {...props} />}
|
|
213
|
+
</ActivityView>
|
|
214
|
+
</View>
|
|
172
215
|
</View>
|
|
173
216
|
</NavigationProvider>
|
|
174
217
|
);
|
|
@@ -181,7 +224,7 @@ const styles = StyleSheet.create({
|
|
|
181
224
|
container: {
|
|
182
225
|
pointerEvents: 'box-none',
|
|
183
226
|
},
|
|
184
|
-
|
|
227
|
+
absolute: {
|
|
185
228
|
position: 'absolute',
|
|
186
229
|
top: 0,
|
|
187
230
|
start: 0,
|
|
@@ -20,8 +20,8 @@ type Props = Omit<StackHeaderOptions, 'headerStatusBarHeight'> & {
|
|
|
20
20
|
headerStatusBarHeight: number;
|
|
21
21
|
title: string;
|
|
22
22
|
modal: boolean;
|
|
23
|
-
onGoBack?: () => void;
|
|
24
|
-
backHref?: string;
|
|
23
|
+
onGoBack?: (() => void) | undefined;
|
|
24
|
+
backHref?: string | undefined;
|
|
25
25
|
progress: SceneProgress;
|
|
26
26
|
styleInterpolator: StackHeaderStyleInterpolator;
|
|
27
27
|
};
|
|
@@ -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,
|
|
@@ -93,7 +93,7 @@ export function HeaderSegment(props: Props) {
|
|
|
93
93
|
left({
|
|
94
94
|
...props,
|
|
95
95
|
href: backHref,
|
|
96
|
-
|
|
96
|
+
icon: headerBackIcon,
|
|
97
97
|
accessibilityLabel: headerBackAccessibilityLabel,
|
|
98
98
|
testID: headerBackTestID,
|
|
99
99
|
allowFontScaling: headerBackAllowFontScaling,
|