@@ -31,19 +31,17 @@ export default class Dropdown extends Component {
31
31
closeOnEsc : PropTypes . bool ,
32
32
closeOnOutsideClick : PropTypes . bool ,
33
33
offset : PropTypes . string ,
34
- viewportPadding : PropTypes . number ,
35
- cssPadding : PropTypes . number ,
36
34
target : PropTypes . element . isRequired ,
37
35
targetNode : PropTypes . any ,
38
36
useTargetWidth : PropTypes . bool ,
39
37
useTargetMinHeight : PropTypes . bool ,
38
+ viewportPadding : PropTypes . number ,
40
39
}
41
40
42
41
static defaultProps = {
43
42
align : 'tl tl' ,
44
43
closeOnEsc : true ,
45
44
closeOnOutsideClick : true ,
46
- cssPadding : 10 ,
47
45
offset : '0 0' ,
48
46
viewportPadding : 10 ,
49
47
}
@@ -59,101 +57,115 @@ export default class Dropdown extends Component {
59
57
applyStyles ( node , styles , this . _reactInternalInstance )
60
58
}
61
59
62
- onOpen ( portal ) {
60
+ onOpen ( portalNode ) {
63
61
const {
64
- align,
65
- offset,
66
- cssPadding, // padding for styles (needed if menu is out of bounds)
67
- viewportPadding, // padding for viewport (needed if menu is out of bounds)
68
- useTargetWidth, // dropdown will inherit target width
69
- useTargetMinHeight, // dropdown will inherit target height as min-height
62
+ align, offset, useTargetWidth, useTargetMinHeight, viewportPadding : pad
70
63
} = this . props
71
64
72
- // parse position
73
- let [ ay , ax , ty , tx ] = align . split ( '' ) . map ( a => a && POS [ a ] ) . filter ( a => a )
74
-
75
- // parse offset
76
- let [ oy , ox ] = offset . split ( ' ' ) . map ( o => parseInt ( o ) )
77
-
78
65
// window is our boundary
79
- const { innerHeight } = window
66
+ const { innerWidth , innerHeight } = window
80
67
81
68
// get target node
82
- const target = this . props . targetNode || findDOMNode ( this )
69
+ const targetNode = this . props . targetNode || findDOMNode ( this )
83
70
84
71
// get bounding rects
85
- const portalRect = portal . getBoundingClientRect ( )
86
- const targetRect = target . getBoundingClientRect ( )
72
+ const portal = portalNode . getBoundingClientRect ( )
73
+ const target = targetNode . getBoundingClientRect ( )
87
74
88
- // calculate padding
89
- const padding = viewportPadding + cssPadding + oy
75
+ // parse position
76
+ let [ ay , ax , ty , tx ] = align . split ( '' ) . map ( a => a && POS [ a ] ) . filter ( a => a )
77
+
78
+ // parse offset
79
+ let [ oy , ox ] = offset . split ( ' ' ) . map ( o => parseInt ( o ) )
90
80
91
81
// calculate space above and below target
92
82
let spaceAbove , spaceBelow
93
- if ( ty === 'middle' ) {
94
- spaceAbove = targetRect . top + ( targetRect . height / 2 ) - padding
95
- spaceBelow = innerHeight - targetRect . bottom + ( targetRect . height / 2 ) - padding
96
- } else if ( ty === 'top' ) {
83
+ if ( ty === 'top' ) {
97
84
if ( ay === 'top' ) {
98
- spaceAbove = targetRect . bottom - padding
99
- spaceBelow = innerHeight - targetRect . top - padding
100
- } else {
101
- spaceAbove = targetRect . top - padding
102
- spaceBelow = innerHeight - targetRect . bottom - padding
85
+ spaceAbove = target . bottom - pad + oy
86
+ spaceBelow = innerHeight - target . top - pad + oy
87
+ } else if ( ay === 'bottom' ) {
88
+ spaceAbove = target . top - pad + oy
89
+ spaceBelow = innerHeight - target . bottom - pad + oy
90
+ } else if ( ay === 'middle' ) {
103
91
}
104
- } else {
92
+ } else if ( ty === 'bottom' ) {
105
93
if ( ay === 'top' ) {
106
- spaceAbove = targetRect . top - padding
107
- spaceBelow = innerHeight - targetRect . bottom - padding
108
- } else {
109
- spaceAbove = targetRect . bottom - padding
110
- spaceBelow = innerHeight - targetRect . bottom - padding
94
+ spaceAbove = target . top - pad + oy
95
+ spaceBelow = innerHeight - target . bottom - pad + oy
96
+ } else if ( ay === 'bottom' ) {
97
+ spaceAbove = target . bottom - pad + oy
98
+ spaceBelow = innerHeight - target . bottom - pad + oy
99
+ } else if ( ay === 'middle' ) {
100
+ }
101
+ } else if ( ty === 'middle' ) {
102
+ if ( ay === 'top' || ay === 'bottom' ) {
103
+ spaceAbove = target . top + ( target . height / 2 ) - pad + oy
104
+ spaceBelow = innerHeight - target . bottom + ( target . height / 2 ) - pad + oy
105
+ } else if ( ay === 'middle' ) {
106
+ spaceAbove = innerHeight - ( pad * 2 )
107
+ spaceBelow = innerHeight - ( pad * 2 )
111
108
}
112
109
}
113
110
114
- // calculate max height
115
- const maxHeight = Math . max ( spaceAbove , spaceBelow )
116
-
117
- // flip if neccesary
118
- if ( ay === 'top' && spaceAbove > spaceBelow ) {
119
-
111
+ // flip y if neccessary
112
+ if ( ay === 'top' && portal . height > spaceBelow && spaceAbove > spaceBelow ) {
120
113
// flip up
121
114
ay = 'bottom'
122
115
if ( ty === 'top' ) {
123
116
ty = 'bottom'
124
- } else {
117
+ } else if ( ty === 'bottom' ) {
125
118
ty = 'top'
126
119
}
127
-
128
- } else if ( ay === 'bottom' && spaceBelow > spaceAbove ) {
129
-
120
+ } else if ( ay === 'bottom' && portal . height > spaceAbove && spaceBelow > spaceAbove ) {
130
121
// flip down
131
122
ay = 'top'
132
123
if ( ty === 'top' ) {
133
124
ty = 'bottom'
134
- } else {
125
+ } else if ( ty === 'bottom' ) {
135
126
ty = 'top'
136
127
}
128
+ }
137
129
130
+ // flip x if neccessary
131
+ if ( ax === 'left' ) {
132
+ // flip left
133
+ if ( tx === 'left' && ( target . left + portal . width + pad + ox ) > innerWidth ) {
134
+ tx = 'right'
135
+ ax = 'right'
136
+ } else if ( tx === 'right' && ( target . right + portal . width + pad + ox ) > innerWidth ) {
137
+ tx = 'left'
138
+ ax = 'right'
139
+ }
140
+ } else if ( ax === 'right' ) {
141
+ // flip left
142
+ if ( tx === 'left' && ( target . left - portal . width ) < 0 ) {
143
+ tx = 'right'
144
+ ax = 'left'
145
+ } else if ( tx === 'right' && ( target . right - portal . width ) < 0 ) {
146
+ tx = 'left'
147
+ ax = 'left'
148
+ }
138
149
}
139
150
140
151
// apply max height
141
- this . applyStyles ( portal , { maxHeight : `${ maxHeight } px` } )
152
+ const maxHeight = Math . max ( spaceAbove , spaceBelow )
153
+ this . applyStyles ( portalNode , { maxHeight : `${ maxHeight } px` } )
142
154
143
155
// use target width
144
156
if ( useTargetWidth ) {
145
- this . applyStyles ( portal , { width : `${ targetRect . width } px` } )
157
+ this . applyStyles ( portalNode , { width : `${ target . width } px` } )
146
158
}
147
159
148
160
// use target height as min-height
149
161
if ( useTargetMinHeight ) {
150
- this . applyStyles ( portal , { minHeight : `${ targetRect . height } px` } )
162
+ this . applyStyles ( portalNode , { minHeight : `${ target . height } px` } )
151
163
}
152
164
153
165
// tether
154
166
this . tether = new Tether ( {
155
- element : portal ,
156
- target : target ,
167
+ element : portalNode ,
168
+ target : targetNode ,
157
169
attachment : `${ ay } ${ ax } ` ,
158
170
targetAttachment : `${ ty } ${ tx } ` ,
159
171
offset : `${ oy } ${ ox } ` ,
@@ -164,15 +176,15 @@ export default class Dropdown extends Component {
164
176
} )
165
177
166
178
// fade in
167
- this . applyStyles ( portal , { opacity : 1 } )
179
+ this . applyStyles ( portalNode , { opacity : 1 } )
168
180
169
181
// force reposition
170
- if ( portalRect . height > maxHeight ) {
182
+ if ( portal . height > maxHeight ) {
171
183
this . tether . position ( )
172
184
}
173
185
}
174
186
175
- beforeClose ( portal , remove ) {
187
+ beforeClose ( portalNode , remove ) {
176
188
if ( this . tether ) {
177
189
this . tether . destroy ( )
178
190
}
0 commit comments