-
Notifications
You must be signed in to change notification settings - Fork 6
/
ModalBase.web.tsx
105 lines (91 loc) · 2.88 KB
/
ModalBase.web.tsx
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
96
97
98
99
100
101
102
103
104
105
import createFocusTrap, { FocusTrap } from 'focus-trap';
import React from 'react';
import ReactDOM from 'react-dom';
import { animated, useSpring } from 'react-spring/web.cjs';
import { springDefaultConfig } from '../../constants/Animation';
import { useElement, useLockBodyScroll } from '../../hooks';
import { ModalBaseProps } from './ModalBase';
// Temporary usage until it is integrated
// https://github.com/necolas/react-native-web/issues/1020
// eslint-disable-next-line
export const ModalBase = (props: ModalBaseProps): React.ReactPortal | null => {
const {
children,
transparent,
visible,
shouldLockBodyScroll = true,
onRequestClose,
animationType = 'none',
} = props;
const isUnmountingRef = React.useRef(false);
const targetElement = useElement();
const [isInView, setIsInView] = React.useState(visible);
const elementRef = React.useRef<HTMLDivElement>(null);
const focusTrapRef = React.useRef<FocusTrap>(null);
useLockBodyScroll({ isLocked: !!(shouldLockBodyScroll && visible) });
React.useEffect(() => {
const deactivateFocus = () => {
if (focusTrapRef.current) {
focusTrapRef.current.deactivate();
// @ts-ignore
focusTrapRef.current = null;
}
};
const activateFocus = () => {
if (elementRef.current && !focusTrapRef.current) {
// @ts-ignore
focusTrapRef.current = createFocusTrap(elementRef.current, {
initialFocus: elementRef.current,
onDeactivate: () => {
if (onRequestClose && visible && !isUnmountingRef.current) {
onRequestClose();
}
},
});
focusTrapRef.current.activate();
}
};
if (visible) {
activateFocus();
setIsInView(true);
} else {
deactivateFocus();
}
return () => {
isUnmountingRef.current = true;
deactivateFocus();
};
}, [onRequestClose, visible]);
const { opacity, y } = useSpring({
onRest: () => !visible && isInView && setIsInView(false),
config: springDefaultConfig,
opacity: animationType === 'fade' ? (visible ? 1 : 0) : 1,
y: animationType === 'slide' ? (visible ? 0 : 100) : 0,
});
// It will not work if targetElement is falsy so we exit early
// This may happen e.g. during SSR
if (!targetElement) return null;
return ReactDOM.createPortal(
<animated.div
tabIndex={-1}
ref={elementRef}
// @ts-ignore
style={{
backgroundColor: transparent ? 'transparent' : 'white',
bottom: 0,
display: isInView ? 'flex' : 'none',
flexDirection: 'column',
left: 0,
opacity,
position: shouldLockBodyScroll ? 'fixed' : 'absolute',
right: 0,
top: 0,
transform: y.interpolate(v => `translateY(${v}%)`),
zIndex: 1000,
}}
>
{visible ? children : null}
</animated.div>,
targetElement,
);
};