@react-navigation/stack 7.6.2 → 7.6.4
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/utils/gestureActivationCriteria.js +60 -0
- package/lib/module/utils/gestureActivationCriteria.js.map +1 -0
- package/lib/module/views/Header/HeaderContainer.js +3 -3
- package/lib/module/views/Header/HeaderContainer.js.map +1 -1
- package/lib/module/views/Stack/Card.js +263 -302
- package/lib/module/views/Stack/Card.js.map +1 -1
- package/lib/module/views/Stack/CardStack.js +5 -8
- package/lib/module/views/Stack/CardStack.js.map +1 -1
- package/lib/typescript/src/utils/gestureActivationCriteria.d.ts +57 -0
- package/lib/typescript/src/utils/gestureActivationCriteria.d.ts.map +1 -0
- package/lib/typescript/src/views/Header/HeaderContainer.d.ts +2 -2
- package/lib/typescript/src/views/Header/HeaderContainer.d.ts.map +1 -1
- package/lib/typescript/src/views/Stack/Card.d.ts +7 -37
- package/lib/typescript/src/views/Stack/Card.d.ts.map +1 -1
- package/lib/typescript/src/views/Stack/CardStack.d.ts.map +1 -1
- package/package.json +7 -6
- package/src/utils/gestureActivationCriteria.tsx +70 -0
- package/src/views/Header/HeaderContainer.tsx +8 -11
- package/src/views/Stack/Card.tsx +451 -453
- package/src/views/Stack/CardStack.tsx +2 -9
- package/lib/module/utils/memoize.js +0 -29
- package/lib/module/utils/memoize.js.map +0 -1
- package/lib/typescript/src/utils/memoize.d.ts +0 -2
- package/lib/typescript/src/utils/memoize.d.ts.map +0 -1
- package/src/utils/memoize.tsx +0 -33
|
@@ -3,148 +3,138 @@
|
|
|
3
3
|
import Color from 'color';
|
|
4
4
|
import * as React from 'react';
|
|
5
5
|
import { Animated, InteractionManager, Platform, StyleSheet, View } from 'react-native';
|
|
6
|
+
import useLatestCallback from 'use-latest-callback';
|
|
6
7
|
import { CardAnimationContext } from "../../utils/CardAnimationContext.js";
|
|
8
|
+
import { gestureActivationCriteria } from "../../utils/gestureActivationCriteria.js";
|
|
7
9
|
import { getDistanceForDirection } from "../../utils/getDistanceForDirection.js";
|
|
8
10
|
import { getInvertedMultiplier } from "../../utils/getInvertedMultiplier.js";
|
|
9
11
|
import { getShadowStyle } from "../../utils/getShadowStyle.js";
|
|
10
|
-
import { memoize } from "../../utils/memoize.js";
|
|
11
12
|
import { GestureState, PanGestureHandler } from '../GestureHandler';
|
|
12
13
|
import { CardContent } from "./CardContent.js";
|
|
13
14
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
14
15
|
const GESTURE_VELOCITY_IMPACT = 0.3;
|
|
15
16
|
const TRUE = 1;
|
|
16
17
|
const FALSE = 0;
|
|
17
|
-
|
|
18
|
-
/**
|
|
19
|
-
* The distance of touch start from the edge of the screen where the gesture will be recognized
|
|
20
|
-
*/
|
|
21
|
-
const GESTURE_RESPONSE_DISTANCE_HORIZONTAL = 50;
|
|
22
|
-
const GESTURE_RESPONSE_DISTANCE_VERTICAL = 135;
|
|
23
18
|
const useNativeDriver = Platform.OS !== 'web';
|
|
24
19
|
const hasOpacityStyle = style => {
|
|
25
20
|
if (style) {
|
|
26
21
|
const flattenedStyle = StyleSheet.flatten(style);
|
|
27
|
-
return flattenedStyle.opacity != null;
|
|
22
|
+
return 'opacity' in flattenedStyle && flattenedStyle.opacity != null;
|
|
28
23
|
}
|
|
29
24
|
return false;
|
|
30
25
|
};
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
style: [styles.overlay, style]
|
|
41
|
-
}) : null
|
|
42
|
-
};
|
|
43
|
-
componentDidMount() {
|
|
44
|
-
if (!this.props.preloaded) {
|
|
45
|
-
this.animate({
|
|
46
|
-
closing: this.props.closing
|
|
47
|
-
});
|
|
48
|
-
}
|
|
49
|
-
this.isCurrentlyMounted = true;
|
|
26
|
+
const getAnimateToValue = ({
|
|
27
|
+
closing: isClosing,
|
|
28
|
+
layout: currentLayout,
|
|
29
|
+
gestureDirection: currentGestureDirection,
|
|
30
|
+
direction: currentDirection,
|
|
31
|
+
preloaded: isPreloaded
|
|
32
|
+
}) => {
|
|
33
|
+
if (!isClosing && !isPreloaded) {
|
|
34
|
+
return 0;
|
|
50
35
|
}
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
36
|
+
return getDistanceForDirection(currentLayout, currentGestureDirection, currentDirection === 'rtl');
|
|
37
|
+
};
|
|
38
|
+
const defaultOverlay = ({
|
|
39
|
+
style
|
|
40
|
+
}) => style ? /*#__PURE__*/_jsx(Animated.View, {
|
|
41
|
+
pointerEvents: "none",
|
|
42
|
+
style: [styles.overlay, style]
|
|
43
|
+
}) : null;
|
|
44
|
+
function Card({
|
|
45
|
+
shadowEnabled = false,
|
|
46
|
+
gestureEnabled = true,
|
|
47
|
+
gestureVelocityImpact = GESTURE_VELOCITY_IMPACT,
|
|
48
|
+
overlay = defaultOverlay,
|
|
49
|
+
animated,
|
|
50
|
+
interpolationIndex,
|
|
51
|
+
opening,
|
|
52
|
+
closing,
|
|
53
|
+
next,
|
|
54
|
+
current,
|
|
55
|
+
gesture,
|
|
56
|
+
layout,
|
|
57
|
+
insets,
|
|
58
|
+
direction,
|
|
59
|
+
pageOverflowEnabled,
|
|
60
|
+
gestureDirection,
|
|
61
|
+
onOpen,
|
|
62
|
+
onClose,
|
|
63
|
+
onTransition,
|
|
64
|
+
onGestureBegin,
|
|
65
|
+
onGestureCanceled,
|
|
66
|
+
onGestureEnd,
|
|
67
|
+
children,
|
|
68
|
+
overlayEnabled,
|
|
69
|
+
gestureResponseDistance,
|
|
70
|
+
transitionSpec,
|
|
71
|
+
preloaded,
|
|
72
|
+
styleInterpolator,
|
|
73
|
+
containerStyle: customContainerStyle,
|
|
74
|
+
contentStyle
|
|
75
|
+
}) {
|
|
76
|
+
const [, forceUpdate] = React.useReducer(x => x + 1, 0);
|
|
77
|
+
const didInitiallyAnimate = React.useRef(false);
|
|
78
|
+
const lastToValueRef = React.useRef(undefined);
|
|
79
|
+
const interactionHandleRef = React.useRef(undefined);
|
|
80
|
+
const animationHandleRef = React.useRef(undefined);
|
|
81
|
+
const pendingGestureCallbackRef = React.useRef(undefined);
|
|
82
|
+
const [isClosing] = React.useState(() => new Animated.Value(FALSE));
|
|
83
|
+
const [inverted] = React.useState(() => new Animated.Value(getInvertedMultiplier(gestureDirection, direction === 'rtl')));
|
|
84
|
+
const [layoutAnim] = React.useState(() => ({
|
|
85
|
+
width: new Animated.Value(layout.width),
|
|
86
|
+
height: new Animated.Value(layout.height)
|
|
87
|
+
}));
|
|
88
|
+
const [isSwiping] = React.useState(() => new Animated.Value(FALSE));
|
|
89
|
+
const onStartInteraction = useLatestCallback(() => {
|
|
90
|
+
if (interactionHandleRef.current === undefined) {
|
|
91
|
+
interactionHandleRef.current = InteractionManager.createInteractionHandle();
|
|
69
92
|
}
|
|
70
|
-
|
|
71
|
-
|
|
93
|
+
});
|
|
94
|
+
const onEndInteraction = useLatestCallback(() => {
|
|
95
|
+
if (interactionHandleRef.current !== undefined) {
|
|
96
|
+
InteractionManager.clearInteractionHandle(interactionHandleRef.current);
|
|
97
|
+
interactionHandleRef.current = undefined;
|
|
72
98
|
}
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
// The route might have been closed by a `POP` action or by a gesture
|
|
77
|
-
// When route was closed due to a gesture, the animation would've happened already
|
|
78
|
-
// It's still important to trigger the animation so that `onClose` is called
|
|
79
|
-
// If `onClose` is not called, cleanup step won't be performed for gestures
|
|
80
|
-
this.animate({
|
|
81
|
-
closing
|
|
82
|
-
});
|
|
83
|
-
} else if (opening && !prevProps.opening) {
|
|
84
|
-
// This can happen when screen somewhere below in the stack comes into focus via rearranging
|
|
85
|
-
// Also reset the animated value to make sure that the animation starts from the beginning
|
|
86
|
-
gesture.setValue(getDistanceForDirection(layout, gestureDirection, direction === 'rtl'));
|
|
87
|
-
this.animate({
|
|
88
|
-
closing
|
|
89
|
-
});
|
|
90
|
-
}
|
|
91
|
-
}
|
|
92
|
-
componentWillUnmount() {
|
|
93
|
-
this.props.gesture?.stopAnimation();
|
|
94
|
-
this.isCurrentlyMounted = false;
|
|
95
|
-
this.handleEndInteraction();
|
|
96
|
-
}
|
|
97
|
-
isCurrentlyMounted = false;
|
|
98
|
-
isClosing = new Animated.Value(FALSE);
|
|
99
|
-
inverted = new Animated.Value(getInvertedMultiplier(this.props.gestureDirection, this.props.direction === 'rtl'));
|
|
100
|
-
layout = {
|
|
101
|
-
width: new Animated.Value(this.props.layout.width),
|
|
102
|
-
height: new Animated.Value(this.props.layout.height)
|
|
103
|
-
};
|
|
104
|
-
isSwiping = new Animated.Value(FALSE);
|
|
105
|
-
animate = ({
|
|
106
|
-
closing,
|
|
99
|
+
});
|
|
100
|
+
const animate = useLatestCallback(({
|
|
101
|
+
closing: isClosingParam,
|
|
107
102
|
velocity
|
|
108
103
|
}) => {
|
|
109
|
-
const {
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
gesture
|
|
116
|
-
} = this.props;
|
|
117
|
-
const toValue = this.getAnimateToValue({
|
|
118
|
-
...this.props,
|
|
119
|
-
closing
|
|
104
|
+
const toValue = getAnimateToValue({
|
|
105
|
+
closing: isClosingParam,
|
|
106
|
+
layout,
|
|
107
|
+
gestureDirection,
|
|
108
|
+
direction,
|
|
109
|
+
preloaded
|
|
120
110
|
});
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
const spec =
|
|
111
|
+
lastToValueRef.current = toValue;
|
|
112
|
+
isClosing.setValue(isClosingParam ? TRUE : FALSE);
|
|
113
|
+
const spec = isClosingParam ? transitionSpec.close : transitionSpec.open;
|
|
124
114
|
const animation = spec.animation === 'spring' ? Animated.spring : Animated.timing;
|
|
125
|
-
clearTimeout(
|
|
126
|
-
if (
|
|
127
|
-
cancelAnimationFrame(
|
|
115
|
+
clearTimeout(pendingGestureCallbackRef.current);
|
|
116
|
+
if (animationHandleRef.current !== undefined) {
|
|
117
|
+
cancelAnimationFrame(animationHandleRef.current);
|
|
128
118
|
}
|
|
129
119
|
onTransition?.({
|
|
130
|
-
closing,
|
|
120
|
+
closing: isClosingParam,
|
|
131
121
|
gesture: velocity !== undefined
|
|
132
122
|
});
|
|
133
123
|
const onFinish = () => {
|
|
134
|
-
if (
|
|
124
|
+
if (isClosingParam) {
|
|
135
125
|
onClose();
|
|
136
126
|
} else {
|
|
137
127
|
onOpen();
|
|
138
128
|
}
|
|
139
|
-
|
|
140
|
-
if (
|
|
129
|
+
animationHandleRef.current = requestAnimationFrame(() => {
|
|
130
|
+
if (didInitiallyAnimate.current) {
|
|
141
131
|
// Make sure to re-open screen if it wasn't removed
|
|
142
|
-
|
|
132
|
+
forceUpdate();
|
|
143
133
|
}
|
|
144
134
|
});
|
|
145
135
|
};
|
|
146
136
|
if (animated) {
|
|
147
|
-
|
|
137
|
+
onStartInteraction();
|
|
148
138
|
animation(gesture, {
|
|
149
139
|
...spec.config,
|
|
150
140
|
velocity,
|
|
@@ -154,8 +144,8 @@ export class Card extends React.Component {
|
|
|
154
144
|
}).start(({
|
|
155
145
|
finished
|
|
156
146
|
}) => {
|
|
157
|
-
|
|
158
|
-
clearTimeout(
|
|
147
|
+
onEndInteraction();
|
|
148
|
+
clearTimeout(pendingGestureCallbackRef.current);
|
|
159
149
|
if (finished) {
|
|
160
150
|
onFinish();
|
|
161
151
|
}
|
|
@@ -163,57 +153,24 @@ export class Card extends React.Component {
|
|
|
163
153
|
} else {
|
|
164
154
|
onFinish();
|
|
165
155
|
}
|
|
166
|
-
};
|
|
167
|
-
|
|
168
|
-
closing,
|
|
169
|
-
layout,
|
|
170
|
-
gestureDirection,
|
|
171
|
-
direction,
|
|
172
|
-
preloaded
|
|
173
|
-
}) => {
|
|
174
|
-
if (!closing && !preloaded) {
|
|
175
|
-
return 0;
|
|
176
|
-
}
|
|
177
|
-
return getDistanceForDirection(layout, gestureDirection, direction === 'rtl');
|
|
178
|
-
};
|
|
179
|
-
handleStartInteraction = () => {
|
|
180
|
-
if (this.interactionHandle === undefined) {
|
|
181
|
-
this.interactionHandle = InteractionManager.createInteractionHandle();
|
|
182
|
-
}
|
|
183
|
-
};
|
|
184
|
-
handleEndInteraction = () => {
|
|
185
|
-
if (this.interactionHandle !== undefined) {
|
|
186
|
-
InteractionManager.clearInteractionHandle(this.interactionHandle);
|
|
187
|
-
this.interactionHandle = undefined;
|
|
188
|
-
}
|
|
189
|
-
};
|
|
190
|
-
handleGestureStateChange = ({
|
|
156
|
+
});
|
|
157
|
+
const onGestureStateChange = useLatestCallback(({
|
|
191
158
|
nativeEvent
|
|
192
159
|
}) => {
|
|
193
|
-
const {
|
|
194
|
-
direction,
|
|
195
|
-
layout,
|
|
196
|
-
onClose,
|
|
197
|
-
onGestureBegin,
|
|
198
|
-
onGestureCanceled,
|
|
199
|
-
onGestureEnd,
|
|
200
|
-
gestureDirection,
|
|
201
|
-
gestureVelocityImpact
|
|
202
|
-
} = this.props;
|
|
203
160
|
switch (nativeEvent.state) {
|
|
204
161
|
case GestureState.ACTIVE:
|
|
205
|
-
|
|
206
|
-
|
|
162
|
+
isSwiping.setValue(TRUE);
|
|
163
|
+
onStartInteraction();
|
|
207
164
|
onGestureBegin?.();
|
|
208
165
|
break;
|
|
209
166
|
case GestureState.CANCELLED:
|
|
210
167
|
case GestureState.FAILED:
|
|
211
168
|
{
|
|
212
|
-
|
|
213
|
-
|
|
169
|
+
isSwiping.setValue(FALSE);
|
|
170
|
+
onEndInteraction();
|
|
214
171
|
const velocity = gestureDirection === 'vertical' || gestureDirection === 'vertical-inverted' ? nativeEvent.velocityY : nativeEvent.velocityX;
|
|
215
|
-
|
|
216
|
-
closing
|
|
172
|
+
animate({
|
|
173
|
+
closing,
|
|
217
174
|
velocity
|
|
218
175
|
});
|
|
219
176
|
onGestureCanceled?.();
|
|
@@ -221,7 +178,7 @@ export class Card extends React.Component {
|
|
|
221
178
|
}
|
|
222
179
|
case GestureState.END:
|
|
223
180
|
{
|
|
224
|
-
|
|
181
|
+
isSwiping.setValue(FALSE);
|
|
225
182
|
let distance;
|
|
226
183
|
let translation;
|
|
227
184
|
let velocity;
|
|
@@ -234,33 +191,103 @@ export class Card extends React.Component {
|
|
|
234
191
|
translation = nativeEvent.translationX;
|
|
235
192
|
velocity = nativeEvent.velocityX;
|
|
236
193
|
}
|
|
237
|
-
const
|
|
238
|
-
|
|
239
|
-
closing,
|
|
194
|
+
const shouldClose = (translation + velocity * gestureVelocityImpact) * getInvertedMultiplier(gestureDirection, direction === 'rtl') > distance / 2 ? velocity !== 0 || translation !== 0 : closing;
|
|
195
|
+
animate({
|
|
196
|
+
closing: shouldClose,
|
|
240
197
|
velocity
|
|
241
198
|
});
|
|
242
|
-
if (
|
|
199
|
+
if (shouldClose) {
|
|
243
200
|
// We call onClose with a delay to make sure that the animation has already started
|
|
244
201
|
// This will make sure that the state update caused by this doesn't affect start of animation
|
|
245
|
-
|
|
202
|
+
pendingGestureCallbackRef.current = setTimeout(() => {
|
|
246
203
|
onClose();
|
|
247
204
|
|
|
248
205
|
// Trigger an update after we dispatch the action to remove the screen
|
|
249
206
|
// This will make sure that we check if the screen didn't get removed so we can cancel the animation
|
|
250
|
-
|
|
207
|
+
forceUpdate();
|
|
251
208
|
}, 32);
|
|
252
209
|
}
|
|
253
210
|
onGestureEnd?.();
|
|
254
211
|
break;
|
|
255
212
|
}
|
|
256
213
|
}
|
|
257
|
-
};
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
214
|
+
});
|
|
215
|
+
React.useLayoutEffect(() => {
|
|
216
|
+
layoutAnim.width.setValue(layout.width);
|
|
217
|
+
layoutAnim.height.setValue(layout.height);
|
|
218
|
+
inverted.setValue(getInvertedMultiplier(gestureDirection, direction === 'rtl'));
|
|
219
|
+
}, [gestureDirection, direction, inverted, layoutAnim.width, layoutAnim.height, layout.width, layout.height]);
|
|
220
|
+
const previousPropsRef = React.useRef(null);
|
|
221
|
+
React.useEffect(() => {
|
|
222
|
+
return () => {
|
|
223
|
+
onEndInteraction();
|
|
224
|
+
if (animationHandleRef.current) {
|
|
225
|
+
cancelAnimationFrame(animationHandleRef.current);
|
|
226
|
+
}
|
|
227
|
+
clearTimeout(pendingGestureCallbackRef.current);
|
|
228
|
+
};
|
|
261
229
|
|
|
262
|
-
|
|
263
|
-
|
|
230
|
+
// We only want to clean up the animation on unmount
|
|
231
|
+
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
232
|
+
}, []);
|
|
233
|
+
const timeoutRef = React.useRef(null);
|
|
234
|
+
React.useEffect(() => {
|
|
235
|
+
if (preloaded) {
|
|
236
|
+
return;
|
|
237
|
+
}
|
|
238
|
+
if (!didInitiallyAnimate.current) {
|
|
239
|
+
// Animate the card in on initial mount
|
|
240
|
+
// Wrap in setTimeout to ensure animation starts after
|
|
241
|
+
// rending of the screen is done. This is especially important
|
|
242
|
+
// in the new architecture
|
|
243
|
+
// cf., https://github.com/react-navigation/react-navigation/issues/12401
|
|
244
|
+
if (timeoutRef.current) {
|
|
245
|
+
clearTimeout(timeoutRef.current);
|
|
246
|
+
}
|
|
247
|
+
timeoutRef.current = setTimeout(() => {
|
|
248
|
+
didInitiallyAnimate.current = true;
|
|
249
|
+
animate({
|
|
250
|
+
closing
|
|
251
|
+
});
|
|
252
|
+
}, 0);
|
|
253
|
+
} else {
|
|
254
|
+
const previousOpening = previousPropsRef.current?.opening;
|
|
255
|
+
const previousToValue = previousPropsRef.current ? getAnimateToValue(previousPropsRef.current) : null;
|
|
256
|
+
const toValue = getAnimateToValue({
|
|
257
|
+
closing,
|
|
258
|
+
layout,
|
|
259
|
+
gestureDirection,
|
|
260
|
+
direction,
|
|
261
|
+
preloaded
|
|
262
|
+
});
|
|
263
|
+
if (previousToValue !== toValue || lastToValueRef.current !== toValue) {
|
|
264
|
+
// We need to trigger the animation when route was closed
|
|
265
|
+
// The route might have been closed by a `POP` action or by a gesture
|
|
266
|
+
// When route was closed due to a gesture, the animation would've happened already
|
|
267
|
+
// It's still important to trigger the animation so that `onClose` is called
|
|
268
|
+
// If `onClose` is not called, cleanup step won't be performed for gestures
|
|
269
|
+
animate({
|
|
270
|
+
closing
|
|
271
|
+
});
|
|
272
|
+
} else if (typeof previousOpening === 'boolean' && opening && !previousOpening) {
|
|
273
|
+
// This can happen when screen somewhere below in the stack comes into focus via rearranging
|
|
274
|
+
// Also reset the animated value to make sure that the animation starts from the beginning
|
|
275
|
+
gesture.setValue(getDistanceForDirection(layout, gestureDirection, direction === 'rtl'));
|
|
276
|
+
animate({
|
|
277
|
+
closing
|
|
278
|
+
});
|
|
279
|
+
}
|
|
280
|
+
}
|
|
281
|
+
previousPropsRef.current = {
|
|
282
|
+
opening,
|
|
283
|
+
closing,
|
|
284
|
+
layout,
|
|
285
|
+
gestureDirection,
|
|
286
|
+
direction,
|
|
287
|
+
preloaded
|
|
288
|
+
};
|
|
289
|
+
}, [animate, closing, direction, gesture, gestureDirection, layout, opening, preloaded]);
|
|
290
|
+
const interpolationProps = React.useMemo(() => ({
|
|
264
291
|
index: interpolationIndex,
|
|
265
292
|
current: {
|
|
266
293
|
progress: current
|
|
@@ -268,157 +295,91 @@ export class Card extends React.Component {
|
|
|
268
295
|
next: next && {
|
|
269
296
|
progress: next
|
|
270
297
|
},
|
|
271
|
-
closing:
|
|
272
|
-
swiping:
|
|
273
|
-
inverted
|
|
298
|
+
closing: isClosing,
|
|
299
|
+
swiping: isSwiping,
|
|
300
|
+
inverted,
|
|
274
301
|
layouts: {
|
|
275
302
|
screen: layout
|
|
276
303
|
},
|
|
277
304
|
insets: {
|
|
278
|
-
top:
|
|
279
|
-
right:
|
|
280
|
-
bottom:
|
|
281
|
-
left:
|
|
305
|
+
top: insets.top,
|
|
306
|
+
right: insets.right,
|
|
307
|
+
bottom: insets.bottom,
|
|
308
|
+
left: insets.left
|
|
282
309
|
}
|
|
283
|
-
}));
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
maxDeltaX: 15,
|
|
296
|
-
minOffsetY: 5,
|
|
297
|
-
hitSlop: {
|
|
298
|
-
bottom: -layout.height + distance
|
|
299
|
-
},
|
|
300
|
-
enableTrackpadTwoFingerGesture
|
|
301
|
-
};
|
|
302
|
-
} else if (gestureDirection === 'vertical-inverted') {
|
|
303
|
-
return {
|
|
304
|
-
maxDeltaX: 15,
|
|
305
|
-
minOffsetY: -5,
|
|
306
|
-
hitSlop: {
|
|
307
|
-
top: -layout.height + distance
|
|
308
|
-
},
|
|
309
|
-
enableTrackpadTwoFingerGesture
|
|
310
|
-
};
|
|
311
|
-
} else {
|
|
312
|
-
const hitSlop = -layout.width + distance;
|
|
313
|
-
const invertedMultiplier = getInvertedMultiplier(gestureDirection, direction === 'rtl');
|
|
314
|
-
if (invertedMultiplier === 1) {
|
|
315
|
-
return {
|
|
316
|
-
minOffsetX: 5,
|
|
317
|
-
maxDeltaY: 20,
|
|
318
|
-
hitSlop: {
|
|
319
|
-
right: hitSlop
|
|
320
|
-
},
|
|
321
|
-
enableTrackpadTwoFingerGesture
|
|
322
|
-
};
|
|
323
|
-
} else {
|
|
324
|
-
return {
|
|
325
|
-
minOffsetX: -5,
|
|
326
|
-
maxDeltaY: 20,
|
|
327
|
-
hitSlop: {
|
|
328
|
-
left: hitSlop
|
|
329
|
-
},
|
|
330
|
-
enableTrackpadTwoFingerGesture
|
|
331
|
-
};
|
|
332
|
-
}
|
|
310
|
+
}), [interpolationIndex, current, next, isClosing, isSwiping, inverted, layout, insets.top, insets.right, insets.bottom, insets.left]);
|
|
311
|
+
const {
|
|
312
|
+
containerStyle,
|
|
313
|
+
cardStyle,
|
|
314
|
+
overlayStyle,
|
|
315
|
+
shadowStyle
|
|
316
|
+
} = React.useMemo(() => styleInterpolator(interpolationProps), [styleInterpolator, interpolationProps]);
|
|
317
|
+
const onGestureEvent = React.useMemo(() => gestureEnabled ? Animated.event([{
|
|
318
|
+
nativeEvent: gestureDirection === 'vertical' || gestureDirection === 'vertical-inverted' ? {
|
|
319
|
+
translationY: gesture
|
|
320
|
+
} : {
|
|
321
|
+
translationX: gesture
|
|
333
322
|
}
|
|
334
|
-
}
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
children,
|
|
351
|
-
containerStyle: customContainerStyle,
|
|
352
|
-
contentStyle
|
|
353
|
-
} = this.props;
|
|
354
|
-
const interpolationProps = this.getCardAnimation(interpolationIndex, current, next, layout, insets.top, insets.right, insets.bottom, insets.left);
|
|
355
|
-
const interpolatedStyle = this.getInterpolatedStyle(styleInterpolator, interpolationProps);
|
|
356
|
-
const {
|
|
357
|
-
containerStyle,
|
|
358
|
-
cardStyle,
|
|
359
|
-
overlayStyle,
|
|
360
|
-
shadowStyle
|
|
361
|
-
} = interpolatedStyle;
|
|
362
|
-
const handleGestureEvent = gestureEnabled ? Animated.event([{
|
|
363
|
-
nativeEvent: gestureDirection === 'vertical' || gestureDirection === 'vertical-inverted' ? {
|
|
364
|
-
translationY: gesture
|
|
365
|
-
} : {
|
|
366
|
-
translationX: gesture
|
|
323
|
+
}], {
|
|
324
|
+
useNativeDriver
|
|
325
|
+
}) : undefined, [gesture, gestureDirection, gestureEnabled]);
|
|
326
|
+
const {
|
|
327
|
+
backgroundColor
|
|
328
|
+
} = StyleSheet.flatten(contentStyle || {});
|
|
329
|
+
const isTransparent = typeof backgroundColor === 'string' ? Color(backgroundColor).alpha() === 0 : false;
|
|
330
|
+
return /*#__PURE__*/_jsxs(CardAnimationContext.Provider, {
|
|
331
|
+
value: interpolationProps,
|
|
332
|
+
children: [Platform.OS !== 'web' ? /*#__PURE__*/_jsx(Animated.View, {
|
|
333
|
+
style: {
|
|
334
|
+
// This is a dummy style that doesn't actually change anything visually.
|
|
335
|
+
// Animated needs the animated value to be used somewhere, otherwise things don't update properly.
|
|
336
|
+
// If we disable animations and hide header, it could end up making the value unused.
|
|
337
|
+
// So we have this dummy style that will always be used regardless of what else changed.
|
|
338
|
+
opacity: current
|
|
367
339
|
}
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
340
|
+
// Make sure that this view isn't removed. If this view is removed, our style with animated value won't apply
|
|
341
|
+
,
|
|
342
|
+
collapsable: false
|
|
343
|
+
}) : null, overlayEnabled ? /*#__PURE__*/_jsx(View, {
|
|
344
|
+
pointerEvents: "box-none",
|
|
345
|
+
style: StyleSheet.absoluteFill,
|
|
346
|
+
children: overlay({
|
|
347
|
+
style: overlayStyle
|
|
348
|
+
})
|
|
349
|
+
}) : null, /*#__PURE__*/_jsx(Animated.View, {
|
|
350
|
+
pointerEvents: "box-none",
|
|
351
|
+
style: [styles.container, containerStyle, customContainerStyle],
|
|
352
|
+
children: /*#__PURE__*/_jsx(PanGestureHandler, {
|
|
353
|
+
enabled: layout.width !== 0 && gestureEnabled,
|
|
354
|
+
onGestureEvent: onGestureEvent,
|
|
355
|
+
onHandlerStateChange: onGestureStateChange,
|
|
356
|
+
...gestureActivationCriteria({
|
|
357
|
+
layout,
|
|
358
|
+
direction,
|
|
359
|
+
gestureDirection,
|
|
360
|
+
gestureResponseDistance
|
|
361
|
+
}),
|
|
362
|
+
children: /*#__PURE__*/_jsxs(Animated.View, {
|
|
363
|
+
pointerEvents: "box-none",
|
|
364
|
+
needsOffscreenAlphaCompositing: hasOpacityStyle(cardStyle),
|
|
365
|
+
style: [styles.container, cardStyle],
|
|
366
|
+
children: [shadowEnabled && shadowStyle && !isTransparent ? /*#__PURE__*/_jsx(Animated.View, {
|
|
367
|
+
pointerEvents: "none",
|
|
368
|
+
style: [styles.shadow, gestureDirection === 'horizontal' ? [styles.shadowHorizontal, styles.shadowStart] : gestureDirection === 'horizontal-inverted' ? [styles.shadowHorizontal, styles.shadowEnd] : gestureDirection === 'vertical' ? [styles.shadowVertical, styles.shadowTop] : [styles.shadowVertical, styles.shadowBottom], {
|
|
369
|
+
backgroundColor
|
|
370
|
+
}, shadowStyle]
|
|
371
|
+
}) : null, /*#__PURE__*/_jsx(CardContent, {
|
|
372
|
+
enabled: pageOverflowEnabled,
|
|
373
|
+
layout: layout,
|
|
374
|
+
style: contentStyle,
|
|
375
|
+
children: children
|
|
376
|
+
})]
|
|
393
377
|
})
|
|
394
|
-
})
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
children: /*#__PURE__*/_jsx(PanGestureHandler, {
|
|
398
|
-
enabled: layout.width !== 0 && gestureEnabled,
|
|
399
|
-
onGestureEvent: handleGestureEvent,
|
|
400
|
-
onHandlerStateChange: this.handleGestureStateChange,
|
|
401
|
-
...this.gestureActivationCriteria(),
|
|
402
|
-
children: /*#__PURE__*/_jsxs(Animated.View, {
|
|
403
|
-
needsOffscreenAlphaCompositing: hasOpacityStyle(cardStyle),
|
|
404
|
-
style: [styles.container, cardStyle],
|
|
405
|
-
children: [shadowEnabled && shadowStyle && !isTransparent ? /*#__PURE__*/_jsx(Animated.View, {
|
|
406
|
-
style: [styles.shadow, gestureDirection === 'horizontal' ? [styles.shadowHorizontal, styles.shadowStart] : gestureDirection === 'horizontal-inverted' ? [styles.shadowHorizontal, styles.shadowEnd] : gestureDirection === 'vertical' ? [styles.shadowVertical, styles.shadowTop] : [styles.shadowVertical, styles.shadowBottom], {
|
|
407
|
-
backgroundColor
|
|
408
|
-
}, shadowStyle],
|
|
409
|
-
pointerEvents: "none"
|
|
410
|
-
}) : null, /*#__PURE__*/_jsx(CardContent, {
|
|
411
|
-
enabled: pageOverflowEnabled,
|
|
412
|
-
layout: layout,
|
|
413
|
-
style: contentStyle,
|
|
414
|
-
children: children
|
|
415
|
-
})]
|
|
416
|
-
})
|
|
417
|
-
})
|
|
418
|
-
})]
|
|
419
|
-
});
|
|
420
|
-
}
|
|
378
|
+
})
|
|
379
|
+
})]
|
|
380
|
+
});
|
|
421
381
|
}
|
|
382
|
+
export { Card };
|
|
422
383
|
const styles = StyleSheet.create({
|
|
423
384
|
container: {
|
|
424
385
|
flex: 1
|