1+ import classNames from 'classnames' ;
12import React , { cloneElement } from 'react' ;
23import ReactDOM from 'react-dom' ;
3- import classNames from 'classnames' ;
4- import ownerDocument from './utils/ownerDocument' ;
5- import getContainer from './utils/getContainer' ;
6-
7- import { calcOverlayPosition } from './utils/overlayPositionUtils' ;
8-
94import mountable from 'react-prop-types/lib/mountable' ;
105
6+ import calculatePosition from './utils/calculatePosition' ;
7+ import getContainer from './utils/getContainer' ;
8+ import ownerDocument from './utils/ownerDocument' ;
9+
1110/**
12- * The Position component calculates the coordinates for its child, to
13- * position it relative to a `target` component or node. Useful for creating callouts and tooltips,
14- * the Position component injects a `style` props with `left` and `top` values for positioning your component.
11+ * The Position component calculates the coordinates for its child, to position
12+ * it relative to a `target` component or node. Useful for creating callouts
13+ * and tooltips, the Position component injects a `style` props with `left` and
14+ * `top` values for positioning your component.
1515 *
16- * It also injects "arrow" `left`, and `top` values for styling callout arrows for giving your components
17- * a sense of directionality.
16+ * It also injects "arrow" `left`, and `top` values for styling callout arrows
17+ * for giving your components a sense of directionality.
1818 */
1919class Position extends React . Component {
2020 constructor ( props , context ) {
@@ -32,7 +32,7 @@ class Position extends React.Component {
3232 }
3333
3434 componentDidMount ( ) {
35- this . updatePosition ( ) ;
35+ this . updatePosition ( this . getTarget ( ) ) ;
3636 }
3737
3838 componentWillReceiveProps ( ) {
@@ -42,16 +42,10 @@ class Position extends React.Component {
4242 componentDidUpdate ( prevProps ) {
4343 if ( this . _needsFlush ) {
4444 this . _needsFlush = false ;
45- this . updatePosition ( prevProps . placement !== this . props . placement ) ;
45+ this . maybeUpdatePosition ( this . props . placement !== prevProps . placement ) ;
4646 }
4747 }
4848
49- componentWillUnmount ( ) {
50- // Probably not necessary, but just in case holding a reference to the
51- // target causes problems somewhere.
52- this . _lastTarget = null ;
53- }
54-
5549 render ( ) {
5650 const { children, className, ...props } = this . props ;
5751 const { positionLeft, positionTop, ...arrowPosition } = this . state ;
@@ -68,7 +62,8 @@ class Position extends React.Component {
6862 {
6963 ...props ,
7064 ...arrowPosition ,
71- //do we need to also forward positionLeft and positionTop if they are set to style?
65+ // FIXME: Don't forward `positionLeft` and `positionTop` via both props
66+ // and `props.style`.
7267 positionLeft,
7368 positionTop,
7469 className : classNames ( className , child . props . className ) ,
@@ -81,27 +76,27 @@ class Position extends React.Component {
8176 ) ;
8277 }
8378
84- getTargetSafe ( ) {
85- if ( ! this . props . target ) {
86- return null ;
87- }
88-
89- const target = this . props . target ( this . props ) ;
90- if ( ! target ) {
91- // This is so we can just use === check below on all falsy targets.
92- return null ;
93- }
94-
95- return target ;
79+ getTarget ( ) {
80+ const { target } = this . props ;
81+ const targetElement = typeof target === 'function' ? target ( ) : target ;
82+ return targetElement && ReactDOM . findDOMNode ( targetElement ) || null ;
9683 }
9784
98- updatePosition ( placementChanged ) {
99- const target = this . getTargetSafe ( ) ;
85+ maybeUpdatePosition ( placementChanged ) {
86+ const target = this . getTarget ( ) ;
10087
101- if ( ! this . props . shouldUpdatePosition && target === this . _lastTarget && ! placementChanged ) {
88+ if (
89+ ! this . props . shouldUpdatePosition &&
90+ target === this . _lastTarget &&
91+ ! placementChanged
92+ ) {
10293 return ;
10394 }
10495
96+ this . updatePosition ( target ) ;
97+ }
98+
99+ updatePosition ( target ) {
105100 this . _lastTarget = target ;
106101
107102 if ( ! target ) {
@@ -116,9 +111,11 @@ class Position extends React.Component {
116111 }
117112
118113 const overlay = ReactDOM . findDOMNode ( this ) ;
119- const container = getContainer ( this . props . container , ownerDocument ( this ) . body ) ;
114+ const container = getContainer (
115+ this . props . container , ownerDocument ( this ) . body
116+ ) ;
120117
121- this . setState ( calcOverlayPosition (
118+ this . setState ( calculatePosition (
122119 this . props . placement ,
123120 overlay ,
124121 target ,
@@ -130,17 +127,18 @@ class Position extends React.Component {
130127
131128Position . propTypes = {
132129 /**
133- * Function mapping props to a DOM node the component is positioned next to
134- *
130+ * A node, element, or function that returns either. The child will be
131+ * be positioned next to the `target` specified.
135132 */
136- target : React . PropTypes . func ,
133+ target : React . PropTypes . oneOfType ( [
134+ mountable , React . PropTypes . func
135+ ] ) ,
137136
138137 /**
139138 * "offsetParent" of the component
140139 */
141140 container : React . PropTypes . oneOfType ( [
142- mountable ,
143- React . PropTypes . func
141+ mountable , React . PropTypes . func
144142 ] ) ,
145143 /**
146144 * Minimum spacing in pixels between container border and component border
0 commit comments