@@ -33,13 +33,15 @@ const OverlayTrigger = React.createClass({
33
33
delayShow : React . PropTypes . number ,
34
34
delayHide : React . PropTypes . number ,
35
35
defaultOverlayShown : React . PropTypes . bool ,
36
- overlay : React . PropTypes . node . isRequired
36
+ overlay : React . PropTypes . node . isRequired ,
37
+ containerPadding : React . PropTypes . number
37
38
} ,
38
39
39
40
getDefaultProps ( ) {
40
41
return {
41
42
placement : 'right' ,
42
- trigger : [ 'hover' , 'focus' ]
43
+ trigger : [ 'hover' , 'focus' ] ,
44
+ containerPadding : 0
43
45
} ;
44
46
} ,
45
47
@@ -48,7 +50,9 @@ const OverlayTrigger = React.createClass({
48
50
isOverlayShown : this . props . defaultOverlayShown == null ?
49
51
false : this . props . defaultOverlayShown ,
50
52
overlayLeft : null ,
51
- overlayTop : null
53
+ overlayTop : null ,
54
+ arrowOffsetLeft : null ,
55
+ arrowOffsetTop : null
52
56
} ;
53
57
} ,
54
58
@@ -85,18 +89,20 @@ const OverlayTrigger = React.createClass({
85
89
onRequestHide : this . hide ,
86
90
placement : this . props . placement ,
87
91
positionLeft : this . state . overlayLeft ,
88
- positionTop : this . state . overlayTop
92
+ positionTop : this . state . overlayTop ,
93
+ arrowOffsetLeft : this . state . arrowOffsetLeft ,
94
+ arrowOffsetTop : this . state . arrowOffsetTop
89
95
}
90
96
) ;
91
97
} ,
92
98
93
99
render ( ) {
94
- let child = React . Children . only ( this . props . children ) ;
100
+ const child = React . Children . only ( this . props . children ) ;
95
101
if ( this . props . trigger === 'manual' ) {
96
102
return child ;
97
103
}
98
104
99
- let props = { } ;
105
+ const props = { } ;
100
106
101
107
props . onClick = createChainedFunction ( child . props . onClick , this . props . onClick ) ;
102
108
if ( isOneOf ( 'click' , this . props . trigger ) ) {
@@ -136,7 +142,7 @@ const OverlayTrigger = React.createClass({
136
142
return ;
137
143
}
138
144
139
- let delay = this . props . delayShow != null ?
145
+ const delay = this . props . delayShow != null ?
140
146
this . props . delayShow : this . props . delay ;
141
147
142
148
if ( ! delay ) {
@@ -157,7 +163,7 @@ const OverlayTrigger = React.createClass({
157
163
return ;
158
164
}
159
165
160
- let delay = this . props . delayHide != null ?
166
+ const delay = this . props . delayHide != null ?
161
167
this . props . delayHide : this . props . delay ;
162
168
163
169
if ( ! delay ) {
@@ -176,52 +182,112 @@ const OverlayTrigger = React.createClass({
176
182
return ;
177
183
}
178
184
179
- let pos = this . calcOverlayPosition ( ) ;
180
-
181
- this . setState ( {
182
- overlayLeft : pos . left ,
183
- overlayTop : pos . top
184
- } ) ;
185
+ this . setState ( this . calcOverlayPosition ( ) ) ;
185
186
} ,
186
187
187
188
calcOverlayPosition ( ) {
188
- let childOffset = this . getPosition ( ) ;
189
-
190
- let overlayNode = this . getOverlayDOMNode ( ) ;
191
- let overlayHeight = overlayNode . offsetHeight ;
192
- let overlayWidth = overlayNode . offsetWidth ;
193
-
194
- switch ( this . props . placement ) {
195
- case 'right' :
196
- return {
197
- top : childOffset . top + childOffset . height / 2 - overlayHeight / 2 ,
198
- left : childOffset . left + childOffset . width
199
- } ;
200
- case 'left' :
201
- return {
202
- top : childOffset . top + childOffset . height / 2 - overlayHeight / 2 ,
203
- left : childOffset . left - overlayWidth
204
- } ;
205
- case 'top' :
206
- return {
207
- top : childOffset . top - overlayHeight ,
208
- left : childOffset . left + childOffset . width / 2 - overlayWidth / 2
209
- } ;
210
- case 'bottom' :
211
- return {
212
- top : childOffset . top + childOffset . height ,
213
- left : childOffset . left + childOffset . width / 2 - overlayWidth / 2
214
- } ;
215
- default :
216
- throw new Error ( 'calcOverlayPosition(): No such placement of "' + this . props . placement + '" found.' ) ;
189
+ const childOffset = this . getPosition ( ) ;
190
+
191
+ const overlayNode = this . getOverlayDOMNode ( ) ;
192
+ const overlayHeight = overlayNode . offsetHeight ;
193
+ const overlayWidth = overlayNode . offsetWidth ;
194
+
195
+ const placement = this . props . placement ;
196
+ let overlayLeft , overlayTop , arrowOffsetLeft , arrowOffsetTop ;
197
+
198
+ if ( placement === 'left' || placement === 'right' ) {
199
+ overlayTop = childOffset . top + ( childOffset . height - overlayHeight ) / 2 ;
200
+
201
+ if ( placement === 'left' ) {
202
+ overlayLeft = childOffset . left - overlayWidth ;
203
+ } else {
204
+ overlayLeft = childOffset . left + childOffset . width ;
205
+ }
206
+
207
+ const topDelta = this . _getTopDelta ( overlayTop , overlayHeight ) ;
208
+ overlayTop += topDelta ;
209
+ arrowOffsetTop = 50 * ( 1 - 2 * topDelta / overlayHeight ) + '%' ;
210
+ arrowOffsetLeft = null ;
211
+ } else if ( placement === 'top' || placement === 'bottom' ) {
212
+ overlayLeft = childOffset . left + ( childOffset . width - overlayWidth ) / 2 ;
213
+
214
+ if ( placement === 'top' ) {
215
+ overlayTop = childOffset . top - overlayHeight ;
216
+ } else {
217
+ overlayTop = childOffset . top + childOffset . height ;
218
+ }
219
+
220
+ const leftDelta = this . _getLeftDelta ( overlayLeft , overlayWidth ) ;
221
+ overlayLeft += leftDelta ;
222
+ arrowOffsetLeft = 50 * ( 1 - 2 * leftDelta / overlayWidth ) + '%' ;
223
+ arrowOffsetTop = null ;
224
+ } else {
225
+ throw new Error (
226
+ 'calcOverlayPosition(): No such placement of "' +
227
+ this . props . placement + '" found.'
228
+ ) ;
229
+ }
230
+
231
+ return { overlayLeft, overlayTop, arrowOffsetLeft, arrowOffsetTop} ;
232
+ } ,
233
+
234
+ _getTopDelta ( top , overlayHeight ) {
235
+ const containerDimensions = this . _getContainerDimensions ( ) ;
236
+ const containerScroll = containerDimensions . scroll ;
237
+ const containerHeight = containerDimensions . height ;
238
+
239
+ const padding = this . props . containerPadding ;
240
+ const topEdgeOffset = top - padding - containerScroll ;
241
+ const bottomEdgeOffset = top + padding - containerScroll + overlayHeight ;
242
+
243
+ if ( topEdgeOffset < 0 ) {
244
+ return - topEdgeOffset ;
245
+ } else if ( bottomEdgeOffset > containerHeight ) {
246
+ return containerHeight - bottomEdgeOffset ;
247
+ } else {
248
+ return 0 ;
249
+ }
250
+ } ,
251
+
252
+ _getLeftDelta ( left , overlayWidth ) {
253
+ const containerDimensions = this . _getContainerDimensions ( ) ;
254
+ const containerWidth = containerDimensions . width ;
255
+
256
+ const padding = this . props . containerPadding ;
257
+ const leftEdgeOffset = left - padding ;
258
+ const rightEdgeOffset = left + padding + overlayWidth ;
259
+
260
+ if ( leftEdgeOffset < 0 ) {
261
+ return - leftEdgeOffset ;
262
+ } else if ( rightEdgeOffset > containerWidth ) {
263
+ return containerWidth - rightEdgeOffset ;
264
+ } else {
265
+ return 0 ;
266
+ }
267
+ } ,
268
+
269
+ _getContainerDimensions ( ) {
270
+ const containerNode = this . getContainerDOMNode ( ) ;
271
+ let width , height ;
272
+ if ( containerNode . tagName === 'BODY' ) {
273
+ width = window . innerWidth ;
274
+ height = window . innerHeight ;
275
+ } else {
276
+ width = containerNode . offsetWidth ;
277
+ height = containerNode . offsetHeight ;
217
278
}
279
+
280
+ return {
281
+ width, height,
282
+ scroll : containerNode . scrollTop
283
+ } ;
218
284
} ,
219
285
220
286
getPosition ( ) {
221
- let node = React . findDOMNode ( this ) ;
222
- let container = this . getContainerDOMNode ( ) ;
287
+ const node = React . findDOMNode ( this ) ;
288
+ const container = this . getContainerDOMNode ( ) ;
223
289
224
- let offset = container . tagName === 'BODY' ?
290
+ const offset = container . tagName === 'BODY' ?
225
291
domUtils . getOffset ( node ) : domUtils . getPosition ( node , container ) ;
226
292
227
293
return assign ( { } , offset , {
0 commit comments