-
-
Notifications
You must be signed in to change notification settings - Fork 99
/
UncontrolledActivated.js
138 lines (120 loc) 路 3.37 KB
/
UncontrolledActivated.js
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
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
import React, { memo, useCallback, useEffect, useRef, useState } from 'react'
import { createPortal } from 'react-dom'
import { func, node, number, object, string } from 'prop-types'
import useEvent from 'react-use/lib/useEvent'
import useWindowSize from 'react-use/lib/useWindowSize'
import { getModalContentStyle, getModalOverlayStyle } from './helpers'
import cn from './Activated.css'
import sharedCn from './Shared.css'
const UncontrolledActivated = ({
children,
closeText,
onUnload,
onLoad,
overlayBgColorEnd,
overlayBgColorStart,
parentRef,
portalEl,
scrollableEl,
transitionDuration,
zoomMargin,
zoomZindex
}) => {
const btnRef = useRef(null)
const [, forceUpdate] = useState(0)
const [isLoaded, setIsLoaded] = useState(false)
const [isUnloading, setIsUnloading] = useState(false)
const { width: innerWidth, height: innerHeight } = useWindowSize()
// on click, begin unloading
const handleClick = useCallback(e => {
e.preventDefault()
setIsUnloading(true)
}, [])
// on escape, begin unloading
const handleKeyDown = useCallback(e => {
if (e.key === 'Escape' || e.keyCode === 27) {
e.stopPropagation()
setIsUnloading(true)
}
}, [])
const handleScroll = useCallback(() => {
forceUpdate(n => n + 1)
if (!isUnloading) {
setIsUnloading(true)
}
}, [isUnloading])
// listen for keydown on the document
useEvent('keydown', handleKeyDown, document)
// listen for scroll and close
useEvent('scroll', handleScroll, scrollableEl)
// set loaded on mount and focus
useEffect(() => {
setIsLoaded(true)
onLoad()
if (btnRef.current) {
btnRef.current.focus({ preventScroll: true })
}
}, [onLoad])
// if unloading, tell parent that we're all done here after Nms
useEffect(() => {
const unloadTimeout = isUnloading
? setTimeout(onUnload, transitionDuration)
: null
return () => {
clearTimeout(unloadTimeout)
}
}, [isUnloading, onUnload, transitionDuration])
// get parent item's dimensions
const { height, left, top, width } = parentRef.current.getBoundingClientRect()
const overlayStyle = getModalOverlayStyle({
isLoaded,
isUnloading,
overlayBgColorEnd,
overlayBgColorStart,
transitionDuration,
zoomZindex
})
const contentStyle = getModalContentStyle({
height,
isLoaded,
innerHeight,
innerWidth,
isUnloading,
left,
originalTransform: parentRef.current.style.transform,
top,
transitionDuration,
width,
zoomMargin
})
const btnCn = `${sharedCn.trigger} ${cn.btn}`
return createPortal(
<div aria-modal className={cn.overlay} role="dialog" style={overlayStyle}>
<div className={cn.content} style={contentStyle}>
{children}
</div>
<button
aria-label={closeText}
className={btnCn}
onClick={handleClick}
ref={btnRef}
/>
</div>,
portalEl
)
}
UncontrolledActivated.propTypes = {
children: node.isRequired,
closeText: string.isRequired,
onUnload: func.isRequired,
onLoad: func.isRequired,
overlayBgColorEnd: string.isRequired,
overlayBgColorStart: string.isRequired,
parentRef: object.isRequired,
portalEl: object.isRequired,
scrollableEl: object.isRequired,
transitionDuration: number.isRequired,
zoomMargin: number.isRequired,
zoomZindex: number.isRequired
}
export default memo(UncontrolledActivated)