@@ -31,19 +31,17 @@ export default class Dropdown extends Component {
3131 closeOnEsc : PropTypes . bool ,
3232 closeOnOutsideClick : PropTypes . bool ,
3333 offset : PropTypes . string ,
34- viewportPadding : PropTypes . number ,
35- cssPadding : PropTypes . number ,
3634 target : PropTypes . element . isRequired ,
3735 targetNode : PropTypes . any ,
3836 useTargetWidth : PropTypes . bool ,
3937 useTargetMinHeight : PropTypes . bool ,
38+ viewportPadding : PropTypes . number ,
4039 }
4140
4241 static defaultProps = {
4342 align : 'tl tl' ,
4443 closeOnEsc : true ,
4544 closeOnOutsideClick : true ,
46- cssPadding : 10 ,
4745 offset : '0 0' ,
4846 viewportPadding : 10 ,
4947 }
@@ -59,101 +57,115 @@ export default class Dropdown extends Component {
5957 applyStyles ( node , styles , this . _reactInternalInstance )
6058 }
6159
62- onOpen ( portal ) {
60+ onOpen ( portalNode ) {
6361 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
7063 } = this . props
7164
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-
7865 // window is our boundary
79- const { innerHeight } = window
66+ const { innerWidth , innerHeight } = window
8067
8168 // get target node
82- const target = this . props . targetNode || findDOMNode ( this )
69+ const targetNode = this . props . targetNode || findDOMNode ( this )
8370
8471 // get bounding rects
85- const portalRect = portal . getBoundingClientRect ( )
86- const targetRect = target . getBoundingClientRect ( )
72+ const portal = portalNode . getBoundingClientRect ( )
73+ const target = targetNode . getBoundingClientRect ( )
8774
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 ) )
9080
9181 // calculate space above and below target
9282 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' ) {
9784 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' ) {
10391 }
104- } else {
92+ } else if ( ty === 'bottom' ) {
10593 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 )
111108 }
112109 }
113110
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 ) {
120113 // flip up
121114 ay = 'bottom'
122115 if ( ty === 'top' ) {
123116 ty = 'bottom'
124- } else {
117+ } else if ( ty === 'bottom' ) {
125118 ty = 'top'
126119 }
127-
128- } else if ( ay === 'bottom' && spaceBelow > spaceAbove ) {
129-
120+ } else if ( ay === 'bottom' && portal . height > spaceAbove && spaceBelow > spaceAbove ) {
130121 // flip down
131122 ay = 'top'
132123 if ( ty === 'top' ) {
133124 ty = 'bottom'
134- } else {
125+ } else if ( ty === 'bottom' ) {
135126 ty = 'top'
136127 }
128+ }
137129
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+ }
138149 }
139150
140151 // apply max height
141- this . applyStyles ( portal , { maxHeight : `${ maxHeight } px` } )
152+ const maxHeight = Math . max ( spaceAbove , spaceBelow )
153+ this . applyStyles ( portalNode , { maxHeight : `${ maxHeight } px` } )
142154
143155 // use target width
144156 if ( useTargetWidth ) {
145- this . applyStyles ( portal , { width : `${ targetRect . width } px` } )
157+ this . applyStyles ( portalNode , { width : `${ target . width } px` } )
146158 }
147159
148160 // use target height as min-height
149161 if ( useTargetMinHeight ) {
150- this . applyStyles ( portal , { minHeight : `${ targetRect . height } px` } )
162+ this . applyStyles ( portalNode , { minHeight : `${ target . height } px` } )
151163 }
152164
153165 // tether
154166 this . tether = new Tether ( {
155- element : portal ,
156- target : target ,
167+ element : portalNode ,
168+ target : targetNode ,
157169 attachment : `${ ay } ${ ax } ` ,
158170 targetAttachment : `${ ty } ${ tx } ` ,
159171 offset : `${ oy } ${ ox } ` ,
@@ -164,15 +176,15 @@ export default class Dropdown extends Component {
164176 } )
165177
166178 // fade in
167- this . applyStyles ( portal , { opacity : 1 } )
179+ this . applyStyles ( portalNode , { opacity : 1 } )
168180
169181 // force reposition
170- if ( portalRect . height > maxHeight ) {
182+ if ( portal . height > maxHeight ) {
171183 this . tether . position ( )
172184 }
173185 }
174186
175- beforeClose ( portal , remove ) {
187+ beforeClose ( portalNode , remove ) {
176188 if ( this . tether ) {
177189 this . tether . destroy ( )
178190 }
0 commit comments