This repository was archived by the owner on Jan 20, 2022. It is now read-only.
forked from Semantic-Org/Semantic-UI-React
-
Notifications
You must be signed in to change notification settings - Fork 2
Expand file tree
/
Copy pathPortal.tsx
More file actions
96 lines (79 loc) · 2.3 KB
/
Portal.tsx
File metadata and controls
96 lines (79 loc) · 2.3 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
96
import React, { cloneElement } from 'react'
import ReactDOM from 'react-dom'
import PropTypes from 'prop-types'
import { AutoControlledComponent, eventStack, makeDebugger } from '../../lib'
const debug = makeDebugger('portal')
class Portal extends AutoControlledComponent {
static propTypes = {
trigger: PropTypes.node,
open: PropTypes.bool,
defaultOpen: PropTypes.bool,
}
static autoControlledProps = ['open']
handleTriggerClick = () => {
debug('handleTriggerClick()')
this.props.trigger.props.onClick()
this.trySetState({ open: !this.state.open })
}
handlePortalMouseEnter = () => {
debug('handlePortalMouseEnter()')
}
componentDidMount() {
debug('componentDidMount()', this.state)
this.state.open ? this.createPortal() : this.destroyPortal()
}
componentDidUpdate() {
debug('componentDidUpdate()', this.state)
this.state.open ? this.createPortal() : this.destroyPortal()
}
componentWillUnmount() {
debug('componentWillUnmount()')
this.destroyPortal()
}
createPortal() {
if (this.state.portalEl) {
return
}
debug('creating portalEl')
const portalEl = document.createElement('div')
document.body.appendChild(portalEl)
eventStack.sub('mouseenter', this.handlePortalMouseEnter, {
target: portalEl,
})
this.setState({
portalEl,
})
}
destroyPortal() {
if (!this.state.portalEl) {
return
}
debug('destroying portalEl')
// TODO: unsubscribe from all events
const { portalEl } = this.state
portalEl.parentNode.removeChild(portalEl)
this.setState({ portalEl: undefined })
}
// To discuss:
// when to create rootNode? (it is required in render, componentWillMount is deprecated)
// should multiple portals share it? (how would mouseenter/mouseleave on portalEl work then?)
// when to destroy it (it is too early in componentWillUnmount)
render() {
const { trigger } = this.props
debug('render')
if (!trigger) {
return
}
return (
<React.Fragment>
{cloneElement(trigger, {
onClick: this.handleTriggerClick,
})}
{this.state.open &&
this.state.portalEl &&
ReactDOM.createPortal(this.props.children, this.state.portalEl)}
</React.Fragment>
)
}
}
export default Portal