@react-navigation/stack 8.0.0-alpha.5 → 8.0.0-alpha.51
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 +36 -0
- package/lib/module/TransitionConfigs/CardStyleInterpolators.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 +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 +3 -3
- package/lib/module/views/Header/HeaderSegment.js.map +1 -1
- package/lib/module/views/Stack/Card.js +108 -93
- 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/CardContent.js +2 -0
- package/lib/module/views/Stack/CardContent.js.map +1 -1
- package/lib/module/views/Stack/CardStack.js +183 -132
- package/lib/module/views/Stack/CardStack.js.map +1 -1
- package/lib/module/views/Stack/StackView.js +103 -54
- 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 +52 -55
- 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 +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 +9 -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 +44 -0
- package/src/index.tsx +3 -2
- package/src/navigators/createStackNavigator.tsx +9 -40
- package/src/types.tsx +79 -75
- 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 +69 -33
- package/src/views/Header/HeaderSegment.tsx +6 -7
- package/src/views/Stack/Card.tsx +154 -119
- package/src/views/Stack/CardContainer.tsx +86 -127
- package/src/views/Stack/CardContent.tsx +1 -0
- package/src/views/Stack/CardStack.tsx +409 -330
- package/src/views/Stack/StackView.tsx +176 -79
- 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,18 @@
|
|
|
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
7
|
} from '@react-navigation/native';
|
|
8
8
|
import * as React from 'react';
|
|
9
|
-
import {
|
|
9
|
+
import {
|
|
10
|
+
type LayoutChangeEvent,
|
|
11
|
+
type StyleProp,
|
|
12
|
+
StyleSheet,
|
|
13
|
+
View,
|
|
14
|
+
type ViewStyle,
|
|
15
|
+
} from 'react-native';
|
|
10
16
|
|
|
11
17
|
import {
|
|
12
18
|
forNoAnimation,
|
|
@@ -14,20 +20,21 @@ import {
|
|
|
14
20
|
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';
|
|
23
24
|
import { Header } from './Header';
|
|
24
25
|
|
|
26
|
+
export type HeaderHeight = {
|
|
27
|
+
measured: boolean;
|
|
28
|
+
value: number;
|
|
29
|
+
};
|
|
30
|
+
|
|
25
31
|
export type Props = {
|
|
26
32
|
mode: StackHeaderMode;
|
|
27
33
|
scenes: (Scene | undefined)[];
|
|
28
34
|
getPreviousScene: (props: { route: Route<string> }) => Scene | undefined;
|
|
29
35
|
getFocusedRoute: () => Route<string>;
|
|
30
|
-
|
|
36
|
+
contentHeight: HeaderHeight | undefined;
|
|
37
|
+
onContentHeightChange: (props: {
|
|
31
38
|
route: Route<string>;
|
|
32
39
|
height: number;
|
|
33
40
|
}) => void;
|
|
@@ -39,19 +46,17 @@ export function HeaderContainer({
|
|
|
39
46
|
scenes,
|
|
40
47
|
getPreviousScene,
|
|
41
48
|
getFocusedRoute,
|
|
49
|
+
contentHeight,
|
|
42
50
|
onContentHeightChange,
|
|
43
51
|
style,
|
|
44
52
|
}: Props) {
|
|
45
53
|
const focusedRoute = getFocusedRoute();
|
|
46
|
-
const parentHeaderBack = React.
|
|
54
|
+
const parentHeaderBack = React.use(HeaderBackContext);
|
|
47
55
|
const { buildHref } = useLinkBuilder();
|
|
48
56
|
|
|
49
57
|
return (
|
|
50
58
|
<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) => {
|
|
59
|
+
{scenes.map((scene, i, self) => {
|
|
55
60
|
if ((mode === 'screen' && i !== self.length - 1) || !scene) {
|
|
56
61
|
return null;
|
|
57
62
|
}
|
|
@@ -96,6 +101,9 @@ export function HeaderContainer({
|
|
|
96
101
|
headerMode: previousHeaderMode,
|
|
97
102
|
} = previousDescriptor?.options || {};
|
|
98
103
|
|
|
104
|
+
const { headerTransparent: nextHeaderTransparent } =
|
|
105
|
+
nextDescriptor?.options || {};
|
|
106
|
+
|
|
99
107
|
// If any of the next screens don't have a header or header is part of the screen
|
|
100
108
|
// Then we need to move this header offscreen so that it doesn't cover it
|
|
101
109
|
const nextHeaderlessScene = self.slice(i + 1).find((scene) => {
|
|
@@ -115,6 +123,9 @@ export function HeaderContainer({
|
|
|
115
123
|
// We still need to animate when coming back from next scene
|
|
116
124
|
// A hacky way to check this is if the next scene exists
|
|
117
125
|
!nextDescriptor) ||
|
|
126
|
+
// If next header is tranparent, we want to treat this header as static
|
|
127
|
+
// Otherwise it may show through the next header
|
|
128
|
+
nextHeaderTransparent ||
|
|
118
129
|
nextHeaderlessScene;
|
|
119
130
|
|
|
120
131
|
const props: StackHeaderProps = {
|
|
@@ -122,8 +133,7 @@ export function HeaderContainer({
|
|
|
122
133
|
progress: scene.progress,
|
|
123
134
|
options: scene.descriptor.options,
|
|
124
135
|
route: scene.descriptor.route,
|
|
125
|
-
navigation: scene.descriptor
|
|
126
|
-
.navigation as StackNavigationProp<ParamListBase>,
|
|
136
|
+
navigation: scene.descriptor.navigation,
|
|
127
137
|
styleInterpolator:
|
|
128
138
|
mode === 'float'
|
|
129
139
|
? isHeaderStatic
|
|
@@ -137,6 +147,26 @@ export function HeaderContainer({
|
|
|
137
147
|
: forNoAnimation,
|
|
138
148
|
};
|
|
139
149
|
|
|
150
|
+
const onRef = (node: React.ComponentRef<typeof View> | null) => {
|
|
151
|
+
node?.measure((_x, _y, _width, height) => {
|
|
152
|
+
if (height && !contentHeight?.measured) {
|
|
153
|
+
onContentHeightChange({
|
|
154
|
+
route: scene.descriptor.route,
|
|
155
|
+
height,
|
|
156
|
+
});
|
|
157
|
+
}
|
|
158
|
+
});
|
|
159
|
+
};
|
|
160
|
+
|
|
161
|
+
const onLayout = (e: LayoutChangeEvent) => {
|
|
162
|
+
const { height } = e.nativeEvent.layout;
|
|
163
|
+
|
|
164
|
+
onContentHeightChange({
|
|
165
|
+
route: scene.descriptor.route,
|
|
166
|
+
height,
|
|
167
|
+
});
|
|
168
|
+
};
|
|
169
|
+
|
|
140
170
|
return (
|
|
141
171
|
<NavigationProvider
|
|
142
172
|
key={scene.descriptor.route.key}
|
|
@@ -144,31 +174,37 @@ export function HeaderContainer({
|
|
|
144
174
|
route={scene.descriptor.route}
|
|
145
175
|
>
|
|
146
176
|
<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
177
|
style={[
|
|
161
|
-
// Avoid positioning the focused header
|
|
178
|
+
// Avoid absolutely positioning the focused header unnecessarily
|
|
162
179
|
// Otherwise accessibility tools don't seem to be able to find it
|
|
163
180
|
(mode === 'float' && !isFocused) || headerTransparent
|
|
164
|
-
?
|
|
181
|
+
? [
|
|
182
|
+
styles.absolute,
|
|
183
|
+
mode === 'screen'
|
|
184
|
+
? // For transparent headers, specify a min height
|
|
185
|
+
// This is needed for screen readers, and testing tools on Android
|
|
186
|
+
// For float header, it's handled in CardStack
|
|
187
|
+
{ minHeight: contentHeight?.value }
|
|
188
|
+
: null,
|
|
189
|
+
]
|
|
165
190
|
: null,
|
|
166
191
|
{
|
|
167
192
|
pointerEvents: isFocused ? 'box-none' : 'none',
|
|
168
193
|
},
|
|
169
194
|
]}
|
|
170
195
|
>
|
|
171
|
-
|
|
196
|
+
<View
|
|
197
|
+
ref={onRef}
|
|
198
|
+
onLayout={onLayout}
|
|
199
|
+
style={{ pointerEvents: 'box-none' }}
|
|
200
|
+
>
|
|
201
|
+
<ActivityView
|
|
202
|
+
mode={isFocused ? 'normal' : 'inert'}
|
|
203
|
+
visible={true}
|
|
204
|
+
>
|
|
205
|
+
{header !== undefined ? header(props) : <Header {...props} />}
|
|
206
|
+
</ActivityView>
|
|
207
|
+
</View>
|
|
172
208
|
</View>
|
|
173
209
|
</NavigationProvider>
|
|
174
210
|
);
|
|
@@ -181,7 +217,7 @@ const styles = StyleSheet.create({
|
|
|
181
217
|
container: {
|
|
182
218
|
pointerEvents: 'box-none',
|
|
183
219
|
},
|
|
184
|
-
|
|
220
|
+
absolute: {
|
|
185
221
|
position: 'absolute',
|
|
186
222
|
top: 0,
|
|
187
223
|
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,
|
|
@@ -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
|
|
|
@@ -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,
|
package/src/views/Stack/Card.tsx
CHANGED
|
@@ -9,6 +9,7 @@ import {
|
|
|
9
9
|
View,
|
|
10
10
|
type ViewStyle,
|
|
11
11
|
} from 'react-native';
|
|
12
|
+
import type { PanGestureConfig } from 'react-native-gesture-handler';
|
|
12
13
|
import type { EdgeInsets } from 'react-native-safe-area-context';
|
|
13
14
|
import useLatestCallback from 'use-latest-callback';
|
|
14
15
|
|
|
@@ -23,11 +24,7 @@ import { gestureActivationCriteria } from '../../utils/gestureActivationCriteria
|
|
|
23
24
|
import { getDistanceForDirection } from '../../utils/getDistanceForDirection';
|
|
24
25
|
import { getInvertedMultiplier } from '../../utils/getInvertedMultiplier';
|
|
25
26
|
import { getShadowStyle } from '../../utils/getShadowStyle';
|
|
26
|
-
import {
|
|
27
|
-
GestureState,
|
|
28
|
-
PanGestureHandler,
|
|
29
|
-
type PanGestureHandlerGestureEvent,
|
|
30
|
-
} from '../GestureHandler';
|
|
27
|
+
import { GestureDetector, usePanGesture } from '../GestureHandler';
|
|
31
28
|
import { CardContent } from './CardContent';
|
|
32
29
|
|
|
33
30
|
type Props = {
|
|
@@ -58,7 +55,7 @@ type Props = {
|
|
|
58
55
|
overlayEnabled: boolean;
|
|
59
56
|
shadowEnabled: boolean | undefined;
|
|
60
57
|
gestureEnabled: boolean;
|
|
61
|
-
gestureResponseDistance?: number;
|
|
58
|
+
gestureResponseDistance?: number | undefined;
|
|
62
59
|
gestureVelocityImpact: number | undefined;
|
|
63
60
|
transitionSpec: {
|
|
64
61
|
open: TransitionSpec;
|
|
@@ -66,8 +63,8 @@ type Props = {
|
|
|
66
63
|
};
|
|
67
64
|
preloaded: boolean;
|
|
68
65
|
styleInterpolator: StackCardStyleInterpolator;
|
|
69
|
-
containerStyle?: StyleProp<ViewStyle
|
|
70
|
-
contentStyle?: StyleProp<ViewStyle
|
|
66
|
+
containerStyle?: StyleProp<ViewStyle> | undefined;
|
|
67
|
+
contentStyle?: StyleProp<ViewStyle> | undefined;
|
|
71
68
|
};
|
|
72
69
|
|
|
73
70
|
const GESTURE_VELOCITY_IMPACT = 0.3;
|
|
@@ -83,7 +80,11 @@ const hasOpacityStyle = (
|
|
|
83
80
|
if (style) {
|
|
84
81
|
const flattenedStyle = StyleSheet.flatten(style);
|
|
85
82
|
|
|
86
|
-
return
|
|
83
|
+
return (
|
|
84
|
+
flattenedStyle != null &&
|
|
85
|
+
'opacity' in flattenedStyle &&
|
|
86
|
+
flattenedStyle.opacity != null
|
|
87
|
+
);
|
|
87
88
|
}
|
|
88
89
|
|
|
89
90
|
return false;
|
|
@@ -151,14 +152,14 @@ function Card({
|
|
|
151
152
|
containerStyle: customContainerStyle,
|
|
152
153
|
contentStyle,
|
|
153
154
|
}: Props) {
|
|
154
|
-
const [, forceUpdate] = React.useReducer((x) => x + 1, 0);
|
|
155
|
-
|
|
156
155
|
const didInitiallyAnimate = React.useRef(false);
|
|
157
156
|
const lastToValueRef = React.useRef<number | undefined>(undefined);
|
|
158
157
|
|
|
159
158
|
const animationHandleRef = React.useRef<number | undefined>(undefined);
|
|
160
159
|
const pendingGestureCallbackRef =
|
|
161
160
|
React.useRef<ReturnType<typeof setTimeout>>(undefined);
|
|
161
|
+
const pendingOnCloseCallbackRef =
|
|
162
|
+
React.useRef<ReturnType<typeof setTimeout>>(undefined);
|
|
162
163
|
|
|
163
164
|
const [isClosing] = React.useState(() => new Animated.Value(FALSE));
|
|
164
165
|
|
|
@@ -218,10 +219,8 @@ function Card({
|
|
|
218
219
|
}
|
|
219
220
|
|
|
220
221
|
animationHandleRef.current = requestAnimationFrame(() => {
|
|
221
|
-
if
|
|
222
|
-
|
|
223
|
-
forceUpdate();
|
|
224
|
-
}
|
|
222
|
+
// Make sure to re-open screen if it wasn't removed
|
|
223
|
+
maybeAnimate();
|
|
225
224
|
});
|
|
226
225
|
};
|
|
227
226
|
|
|
@@ -245,79 +244,6 @@ function Card({
|
|
|
245
244
|
}
|
|
246
245
|
);
|
|
247
246
|
|
|
248
|
-
const onGestureStateChange = useLatestCallback(
|
|
249
|
-
({ nativeEvent }: PanGestureHandlerGestureEvent) => {
|
|
250
|
-
switch (nativeEvent.state) {
|
|
251
|
-
case GestureState.ACTIVE:
|
|
252
|
-
isSwiping.setValue(TRUE);
|
|
253
|
-
onGestureBegin?.();
|
|
254
|
-
break;
|
|
255
|
-
case GestureState.CANCELLED:
|
|
256
|
-
case GestureState.FAILED: {
|
|
257
|
-
isSwiping.setValue(FALSE);
|
|
258
|
-
|
|
259
|
-
const velocity =
|
|
260
|
-
gestureDirection === 'vertical' ||
|
|
261
|
-
gestureDirection === 'vertical-inverted'
|
|
262
|
-
? nativeEvent.velocityY
|
|
263
|
-
: nativeEvent.velocityX;
|
|
264
|
-
|
|
265
|
-
animate({
|
|
266
|
-
closing,
|
|
267
|
-
velocity,
|
|
268
|
-
});
|
|
269
|
-
|
|
270
|
-
onGestureCanceled?.();
|
|
271
|
-
break;
|
|
272
|
-
}
|
|
273
|
-
case GestureState.END: {
|
|
274
|
-
isSwiping.setValue(FALSE);
|
|
275
|
-
|
|
276
|
-
let distance;
|
|
277
|
-
let translation;
|
|
278
|
-
let velocity;
|
|
279
|
-
|
|
280
|
-
if (
|
|
281
|
-
gestureDirection === 'vertical' ||
|
|
282
|
-
gestureDirection === 'vertical-inverted'
|
|
283
|
-
) {
|
|
284
|
-
distance = layout.height;
|
|
285
|
-
translation = nativeEvent.translationY;
|
|
286
|
-
velocity = nativeEvent.velocityY;
|
|
287
|
-
} else {
|
|
288
|
-
distance = layout.width;
|
|
289
|
-
translation = nativeEvent.translationX;
|
|
290
|
-
velocity = nativeEvent.velocityX;
|
|
291
|
-
}
|
|
292
|
-
|
|
293
|
-
const shouldClose =
|
|
294
|
-
(translation + velocity * gestureVelocityImpact) *
|
|
295
|
-
getInvertedMultiplier(gestureDirection, direction === 'rtl') >
|
|
296
|
-
distance / 2
|
|
297
|
-
? velocity !== 0 || translation !== 0
|
|
298
|
-
: closing;
|
|
299
|
-
|
|
300
|
-
animate({ closing: shouldClose, velocity });
|
|
301
|
-
|
|
302
|
-
if (shouldClose) {
|
|
303
|
-
// We call onClose with a delay to make sure that the animation has already started
|
|
304
|
-
// This will make sure that the state update caused by this doesn't affect start of animation
|
|
305
|
-
pendingGestureCallbackRef.current = setTimeout(() => {
|
|
306
|
-
onClose();
|
|
307
|
-
|
|
308
|
-
// Trigger an update after we dispatch the action to remove the screen
|
|
309
|
-
// This will make sure that we check if the screen didn't get removed so we can cancel the animation
|
|
310
|
-
forceUpdate();
|
|
311
|
-
}, 32);
|
|
312
|
-
}
|
|
313
|
-
|
|
314
|
-
onGestureEnd?.();
|
|
315
|
-
break;
|
|
316
|
-
}
|
|
317
|
-
}
|
|
318
|
-
}
|
|
319
|
-
);
|
|
320
|
-
|
|
321
247
|
React.useLayoutEffect(() => {
|
|
322
248
|
layoutAnim.width.setValue(layout.width);
|
|
323
249
|
layoutAnim.height.setValue(layout.height);
|
|
@@ -350,17 +276,15 @@ function Card({
|
|
|
350
276
|
}
|
|
351
277
|
|
|
352
278
|
clearTimeout(pendingGestureCallbackRef.current);
|
|
279
|
+
clearTimeout(pendingOnCloseCallbackRef.current);
|
|
353
280
|
};
|
|
354
|
-
|
|
355
|
-
// We only want to clean up the animation on unmount
|
|
356
281
|
}, []);
|
|
357
282
|
|
|
358
|
-
const timeoutRef = React.useRef<ReturnType<typeof setTimeout
|
|
283
|
+
const timeoutRef = React.useRef<ReturnType<typeof setTimeout>>(undefined);
|
|
359
284
|
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
}
|
|
285
|
+
const maybeAnimate = useLatestCallback(() => {
|
|
286
|
+
clearTimeout(pendingGestureCallbackRef.current);
|
|
287
|
+
clearTimeout(pendingOnCloseCallbackRef.current);
|
|
364
288
|
|
|
365
289
|
if (!didInitiallyAnimate.current) {
|
|
366
290
|
// Animate the card in on initial mount
|
|
@@ -368,9 +292,8 @@ function Card({
|
|
|
368
292
|
// rending of the screen is done. This is especially important
|
|
369
293
|
// in the new architecture
|
|
370
294
|
// cf., https://github.com/react-navigation/react-navigation/issues/12401
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
}
|
|
295
|
+
clearTimeout(timeoutRef.current);
|
|
296
|
+
|
|
374
297
|
timeoutRef.current = setTimeout(() => {
|
|
375
298
|
didInitiallyAnimate.current = true;
|
|
376
299
|
animate({ closing });
|
|
@@ -410,6 +333,14 @@ function Card({
|
|
|
410
333
|
animate({ closing });
|
|
411
334
|
}
|
|
412
335
|
}
|
|
336
|
+
});
|
|
337
|
+
|
|
338
|
+
React.useEffect(() => {
|
|
339
|
+
if (preloaded) {
|
|
340
|
+
return;
|
|
341
|
+
}
|
|
342
|
+
|
|
343
|
+
maybeAnimate();
|
|
413
344
|
|
|
414
345
|
previousPropsRef.current = {
|
|
415
346
|
opening,
|
|
@@ -428,6 +359,7 @@ function Card({
|
|
|
428
359
|
layout,
|
|
429
360
|
opening,
|
|
430
361
|
preloaded,
|
|
362
|
+
maybeAnimate,
|
|
431
363
|
]);
|
|
432
364
|
|
|
433
365
|
const interpolationProps = React.useMemo(
|
|
@@ -463,23 +395,95 @@ function Card({
|
|
|
463
395
|
]
|
|
464
396
|
);
|
|
465
397
|
|
|
466
|
-
const { containerStyle, cardStyle, overlayStyle, shadowStyle } =
|
|
398
|
+
const { containerStyle, cardStyle, overlayStyle, dimStyle, shadowStyle } =
|
|
467
399
|
React.useMemo(
|
|
468
400
|
() => styleInterpolator(interpolationProps),
|
|
469
401
|
[styleInterpolator, interpolationProps]
|
|
470
402
|
);
|
|
471
403
|
|
|
472
|
-
const
|
|
404
|
+
const onGestureActivate: PanGestureConfig['onActivate'] = useLatestCallback(
|
|
405
|
+
() => {
|
|
406
|
+
clearTimeout(pendingGestureCallbackRef.current);
|
|
407
|
+
clearTimeout(pendingOnCloseCallbackRef.current);
|
|
408
|
+
isSwiping.setValue(TRUE);
|
|
409
|
+
onGestureBegin?.();
|
|
410
|
+
}
|
|
411
|
+
);
|
|
412
|
+
|
|
413
|
+
const onGestureDeactivate: PanGestureConfig['onDeactivate'] =
|
|
414
|
+
useLatestCallback((event) => {
|
|
415
|
+
isSwiping.setValue(FALSE);
|
|
416
|
+
|
|
417
|
+
let distance;
|
|
418
|
+
let translation;
|
|
419
|
+
let velocity;
|
|
420
|
+
|
|
421
|
+
if (
|
|
422
|
+
gestureDirection === 'vertical' ||
|
|
423
|
+
gestureDirection === 'vertical-inverted'
|
|
424
|
+
) {
|
|
425
|
+
distance = layout.height;
|
|
426
|
+
translation = event.translationY;
|
|
427
|
+
velocity = event.velocityY;
|
|
428
|
+
} else {
|
|
429
|
+
distance = layout.width;
|
|
430
|
+
translation = event.translationX;
|
|
431
|
+
velocity = event.velocityX;
|
|
432
|
+
}
|
|
433
|
+
|
|
434
|
+
gesture.setValue(translation);
|
|
435
|
+
|
|
436
|
+
if (event.canceled) {
|
|
437
|
+
animate({
|
|
438
|
+
closing,
|
|
439
|
+
velocity,
|
|
440
|
+
});
|
|
441
|
+
|
|
442
|
+
onGestureCanceled?.();
|
|
443
|
+
|
|
444
|
+
return;
|
|
445
|
+
}
|
|
446
|
+
|
|
447
|
+
const shouldClose =
|
|
448
|
+
(translation + velocity * gestureVelocityImpact) *
|
|
449
|
+
getInvertedMultiplier(gestureDirection, direction === 'rtl') >
|
|
450
|
+
distance / 2
|
|
451
|
+
? velocity !== 0 || translation !== 0
|
|
452
|
+
: closing;
|
|
453
|
+
|
|
454
|
+
animate({ closing: shouldClose, velocity });
|
|
455
|
+
|
|
456
|
+
if (shouldClose) {
|
|
457
|
+
// We call onClose with a delay to make sure that the animation has already started
|
|
458
|
+
// This will make sure that the state update caused by this doesn't affect start of animation
|
|
459
|
+
pendingGestureCallbackRef.current = setTimeout(() => {
|
|
460
|
+
onClose();
|
|
461
|
+
|
|
462
|
+
// Check if the screen is still closing with a delay
|
|
463
|
+
// So state update from onClose has a chance to go through
|
|
464
|
+
// If route wasn't removed after onClose, re-open it
|
|
465
|
+
pendingOnCloseCallbackRef.current = setTimeout(() => {
|
|
466
|
+
maybeAnimate();
|
|
467
|
+
}, 32);
|
|
468
|
+
}, 16);
|
|
469
|
+
}
|
|
470
|
+
|
|
471
|
+
onGestureEnd?.();
|
|
472
|
+
});
|
|
473
|
+
|
|
474
|
+
const onGestureEvent: PanGestureConfig['onUpdate'] = React.useMemo(
|
|
473
475
|
() =>
|
|
474
476
|
gestureEnabled
|
|
475
477
|
? Animated.event(
|
|
476
478
|
[
|
|
477
479
|
{
|
|
478
|
-
nativeEvent:
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
480
|
+
nativeEvent: {
|
|
481
|
+
handlerData:
|
|
482
|
+
gestureDirection === 'vertical' ||
|
|
483
|
+
gestureDirection === 'vertical-inverted'
|
|
484
|
+
? { translationY: gesture }
|
|
485
|
+
: { translationX: gesture },
|
|
486
|
+
},
|
|
483
487
|
},
|
|
484
488
|
],
|
|
485
489
|
{ useNativeDriver }
|
|
@@ -488,7 +492,36 @@ function Card({
|
|
|
488
492
|
[gesture, gestureDirection, gestureEnabled]
|
|
489
493
|
);
|
|
490
494
|
|
|
491
|
-
const
|
|
495
|
+
const panGestureConfig = React.useMemo(
|
|
496
|
+
(): PanGestureConfig => ({
|
|
497
|
+
enabled: layout.width !== 0 && gestureEnabled,
|
|
498
|
+
onActivate: onGestureActivate,
|
|
499
|
+
onDeactivate: onGestureDeactivate,
|
|
500
|
+
onUpdate: onGestureEvent,
|
|
501
|
+
disableReanimated: true,
|
|
502
|
+
useAnimated: true,
|
|
503
|
+
...gestureActivationCriteria({
|
|
504
|
+
layout,
|
|
505
|
+
direction,
|
|
506
|
+
gestureDirection,
|
|
507
|
+
gestureResponseDistance,
|
|
508
|
+
}),
|
|
509
|
+
}),
|
|
510
|
+
[
|
|
511
|
+
direction,
|
|
512
|
+
gestureDirection,
|
|
513
|
+
gestureEnabled,
|
|
514
|
+
gestureResponseDistance,
|
|
515
|
+
layout,
|
|
516
|
+
onGestureActivate,
|
|
517
|
+
onGestureDeactivate,
|
|
518
|
+
onGestureEvent,
|
|
519
|
+
]
|
|
520
|
+
);
|
|
521
|
+
|
|
522
|
+
const panGesture = usePanGesture(panGestureConfig);
|
|
523
|
+
|
|
524
|
+
const backgroundColor = StyleSheet.flatten(contentStyle)?.backgroundColor;
|
|
492
525
|
|
|
493
526
|
const isTransparent =
|
|
494
527
|
typeof backgroundColor === 'string'
|
|
@@ -519,20 +552,10 @@ function Card({
|
|
|
519
552
|
<Animated.View
|
|
520
553
|
style={[styles.container, containerStyle, customContainerStyle]}
|
|
521
554
|
>
|
|
522
|
-
<
|
|
523
|
-
enabled={layout.width !== 0 && gestureEnabled}
|
|
524
|
-
onGestureEvent={onGestureEvent}
|
|
525
|
-
onHandlerStateChange={onGestureStateChange}
|
|
526
|
-
{...gestureActivationCriteria({
|
|
527
|
-
layout,
|
|
528
|
-
direction,
|
|
529
|
-
gestureDirection,
|
|
530
|
-
gestureResponseDistance,
|
|
531
|
-
})}
|
|
532
|
-
>
|
|
555
|
+
<GestureDetector gesture={panGesture}>
|
|
533
556
|
<Animated.View
|
|
534
557
|
needsOffscreenAlphaCompositing={hasOpacityStyle(cardStyle)}
|
|
535
|
-
style={[styles.
|
|
558
|
+
style={[styles.card, cardStyle]}
|
|
536
559
|
>
|
|
537
560
|
{shadowEnabled && shadowStyle && !isTransparent ? (
|
|
538
561
|
<Animated.View
|
|
@@ -557,8 +580,9 @@ function Card({
|
|
|
557
580
|
>
|
|
558
581
|
{children}
|
|
559
582
|
</CardContent>
|
|
583
|
+
{dimStyle ? <Animated.View style={[styles.dim, dimStyle]} /> : null}
|
|
560
584
|
</Animated.View>
|
|
561
|
-
</
|
|
585
|
+
</GestureDetector>
|
|
562
586
|
</Animated.View>
|
|
563
587
|
</CardAnimationContext.Provider>
|
|
564
588
|
);
|
|
@@ -571,11 +595,22 @@ const styles = StyleSheet.create({
|
|
|
571
595
|
flex: 1,
|
|
572
596
|
pointerEvents: 'box-none',
|
|
573
597
|
},
|
|
598
|
+
card: {
|
|
599
|
+
flex: 1,
|
|
600
|
+
// This is necessary for gestures to work
|
|
601
|
+
// Without this, gestures won't work if the child view is flattened
|
|
602
|
+
pointerEvents: 'auto',
|
|
603
|
+
},
|
|
574
604
|
overlay: {
|
|
575
605
|
flex: 1,
|
|
576
606
|
backgroundColor: '#000',
|
|
577
607
|
pointerEvents: 'none',
|
|
578
608
|
},
|
|
609
|
+
dim: {
|
|
610
|
+
...StyleSheet.absoluteFill,
|
|
611
|
+
backgroundColor: '#000',
|
|
612
|
+
pointerEvents: 'none',
|
|
613
|
+
},
|
|
579
614
|
shadow: {
|
|
580
615
|
position: 'absolute',
|
|
581
616
|
pointerEvents: 'none',
|