-
Notifications
You must be signed in to change notification settings - Fork 4k
Expand file tree
/
Copy pathuseModalCardStyleInterpolator.ts
More file actions
95 lines (73 loc) · 3.58 KB
/
Copy pathuseModalCardStyleInterpolator.ts
File metadata and controls
95 lines (73 loc) · 3.58 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
import useResponsiveLayout from '@hooks/useResponsiveLayout';
import useSidePanelState from '@hooks/useSidePanelState';
import useStyleUtils from '@hooks/useStyleUtils';
import useThemeStyles from '@hooks/useThemeStyles';
import {isMobileChrome, isMobileSafari} from '@libs/Browser';
import variables from '@styles/variables';
import type {StackCardInterpolatedStyle, StackCardInterpolationProps} from '@react-navigation/stack';
// Import Animated directly from 'react-native' as animations are used with navigation.
// eslint-disable-next-line no-restricted-imports
import {Animated} from 'react-native';
type EnterAnimation = {kind: 'slide-and-fade'; distancePx: number} | {kind: 'slide-from-width'} | {kind: 'fade'} | {kind: 'none'};
type ModalCardStyleInterpolatorProps = {
props: StackCardInterpolationProps;
enter: EnterAnimation;
applySidePanelOffset?: boolean;
};
type ModalCardStyleInterpolator = (props: ModalCardStyleInterpolatorProps) => StackCardInterpolatedStyle;
const useModalCardStyleInterpolator = (): ModalCardStyleInterpolator => {
const {shouldUseNarrowLayout} = useResponsiveLayout();
const StyleUtils = useStyleUtils();
const styles = useThemeStyles();
const {sidePanelOffset, sidePanelNVP, isSidePanelTransitionEnded} = useSidePanelState();
// Narrow mobile browsers compositing the stacked, transparent modal cards can flicker mid-slide on low-end devices.
// Hardening the animated card (opaque background + dedicated compositor layer) avoids that glitch while keeping the animation.
const shouldHardenAnimatedCardForMobileBrowser = (isMobileChrome() || isMobileSafari()) && shouldUseNarrowLayout;
const modalCardStyleInterpolator: ModalCardStyleInterpolator = ({
props: {
current: {progress},
inverted,
layouts: {screen},
},
enter,
applySidePanelOffset = false,
}) => {
const cardStyle = StyleUtils.getCardStyles(screen.width);
if (applySidePanelOffset) {
cardStyle.paddingRight = sidePanelOffset.current;
}
// Suppress card entry animation while the side panel is mid-transition on narrow layout — keeps the
// existing behavior that prevents a janky double-animation when the side panel slides in/out.
const sidePanelGateAllowsEntry = isSidePanelTransitionEnded || !!sidePanelNVP?.openNarrowScreen || !shouldUseNarrowLayout;
if (enter.kind === 'none' || !sidePanelGateAllowsEntry) {
return {containerStyle: {overflow: 'hidden'}, cardStyle};
}
if (enter.kind === 'fade') {
return {cardStyle: {...cardStyle, opacity: progress}};
}
const widthFallback = shouldUseNarrowLayout ? screen.width : variables.sideBarWidth;
const distancePx = enter.kind === 'slide-and-fade' ? enter.distancePx : widthFallback;
const translateX = Animated.multiply(
progress.interpolate({
inputRange: [0, 1],
outputRange: [distancePx, 0],
extrapolate: 'clamp',
}),
inverted,
);
if (shouldHardenAnimatedCardForMobileBrowser) {
Object.assign(cardStyle, styles.appBG, styles.willChangeTransform);
}
cardStyle.transform = [{translateX}];
if (enter.kind === 'slide-and-fade') {
cardStyle.opacity = progress;
}
return {
containerStyle: {overflow: 'hidden'},
cardStyle,
};
};
return modalCardStyleInterpolator;
};
export type {EnterAnimation};
export default useModalCardStyleInterpolator;