-
Notifications
You must be signed in to change notification settings - Fork 9
/
portal-popper.jsx
127 lines (108 loc) · 3.08 KB
/
portal-popper.jsx
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
import _ from 'lodash'
import React, { Component } from 'react'
import PropTypes from 'prop-types'
import Popper from 'popper.js'
import Portal from './portal'
const initialArrowProps = {
left: 0,
top: 0,
}
const initialPopperProps = {
left: 0,
position: 'absolute',
top: 0,
}
class PortalPopper extends Component {
static propTypes = {
placement: PropTypes.string.isRequired,
getTargetNode: PropTypes.func.isRequired,
title: PropTypes.node.isRequired,
className: PropTypes.string,
}
static defaultProps = {
Popper,
className: '',
}
state = {
arrowProps: initialArrowProps,
popperProps: initialPopperProps,
flipped: false,
}
render () {
const { className, placement, title } = this.props
const prefix = _.last(className.split(' '))
const flippedClass = this.state.flipped ? ` ${prefix}-flipped` : ''
return (
<Portal appendTo={this.props.appendTo}>
<div
ref={(node) => this.portalNode = node}
className={`${className} ${prefix}-${placement}${flippedClass}`}
style={this._getPopperStyle()}
>
<span>{title}</span>
<div
ref={(node) => this.arrowNode = node}
className={`${prefix}-arrow`}
style={this._getArrowStyle()}
>
<svg xmlns='http://www.w3.org/2000/svg' version='1.1'>
<polygon points='5,0 10,5 5,10 0,5' />
</svg>
</div>
</div>
</Portal>
)
}
componentDidMount () {
this.popper = new this.props.Popper(this.props.getTargetNode(), this.portalNode, {
content: this.props.title,
placement: this.props.placement,
modifiers: {
arrow: { element: this.arrowNode },
preventOverflow: {
boundariesElement: this.props.boundary,
},
},
onCreate: this._updateData,
onUpdate: this._updateData,
})
this.popper.scheduleUpdate()
}
componentDidUpdate (prevProps) {
if (prevProps.updateCue !== this.props.updateCue) {
this.popper.scheduleUpdate()
}
}
_updateData = (data) => {
if (this.isUnmounted) return
const newState = {}
if (data.offsets.arrow) newState.arrowProps = data.offsets.arrow
if (data.offsets.popper) newState.popperProps = data.offsets.popper
if (data.flipped != null) newState.flipped = data.flipped
this.setState(newState)
}
_getPopperStyle () {
const left = Math.round(this.state.popperProps.left)
const top = Math.round(this.state.popperProps.top)
const transform = `translate3d(${left}px, ${top}px, 0)`
return {
position: this.state.popperProps.position,
transform,
WebkitTransform: transform,
}
}
_getArrowStyle () {
return {
left: this._getArrowProp('left'),
top: this._getArrowProp('top'),
}
}
_getArrowProp (position) {
return _.isNumber(this.state.arrowProps[position]) ? Math.round(this.state.arrowProps[position]) : null
}
componentWillUnmount () {
this.isUnmounted = true
this.popper && this.popper.destroy()
}
}
export default PortalPopper