Skip to content
This repository was archived by the owner on Jul 19, 2019. It is now read-only.

Commit 208aaa2

Browse files
committed
Use 'absolute' positioning for menu instead of 'fixed'
The 'fixed' approach has a major downside: The 'fixed' referential may not be the document when a parent node has CSS transforms. But getBoundedClientRect() remains relative to the document no matter what. Calculating a fixed position from it is difficult. Using 'absolute' positioning makes things a lot easier. It is how bootstrap dropdown menu works for example.
1 parent 8268d25 commit 208aaa2

File tree

1 file changed

+26
-10
lines changed

1 file changed

+26
-10
lines changed

lib/Autocomplete.js

Lines changed: 26 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -24,15 +24,16 @@ let Autocomplete = React.createClass({
2424
return <div style={{...style, ...this.menuStyle}} children={items}/>
2525
},
2626
shouldItemRender () { return true },
27+
menuMinSize: 100,
2728
menuStyle: {
2829
borderRadius: '3px',
2930
boxShadow: '0 2px 12px rgba(0, 0, 0, 0.1)',
3031
background: 'rgba(255, 255, 255, 0.9)',
3132
padding: '2px 0',
3233
fontSize: '90%',
33-
position: 'fixed',
34-
overflow: 'auto',
35-
maxHeight: '50%', // TODO: don't cheat, let it flow to the bottom
34+
position: 'absolute',
35+
left: '0',
36+
overflow: 'auto'
3637
}
3738
}
3839
},
@@ -76,6 +77,7 @@ let Autocomplete = React.createClass({
7677
},
7778

7879
handleKeyDown (event) {
80+
console.log(event);
7981
if (this.keyDownHandlers[event.key])
8082
this.keyDownHandlers[event.key].call(this, event)
8183
else {
@@ -220,11 +222,21 @@ let Autocomplete = React.createClass({
220222
var marginBottom = parseInt(computedStyle.marginBottom, 10)
221223
var marginLeft = parseInt(computedStyle.marginLeft, 10)
222224
var marginRight = parseInt(computedStyle.marginRight, 10)
225+
var marginTop = parseInt(computedStyle.marginTop, 10)
226+
227+
var inputTop = rect.top - marginTop;
228+
var inputBottom = rect.bottom + marginBottom;
229+
230+
var heightAfter = window.scrollY + window.screen.height - inputBottom;
231+
var heightBefore = inputTop - window.scrollY;
232+
233+
var displayBefore = heightAfter < (this.props.menuMinSize + 10) && heightBefore > heightAfter;
234+
223235
this.setState({
224-
menuTop: rect.bottom + marginBottom,
225-
menuLeft: rect.left + marginLeft,
226-
menuWidth: rect.width + marginLeft + marginRight
227-
})
236+
menuWidth: rect.width + marginLeft + marginRight,
237+
menuMaxHeight: (displayBefore ? heightBefore : heightAfter) - 10,
238+
menuPosition: displayBefore ? 'before' : 'after'
239+
});
228240
},
229241

230242
highlightItemFromMouse (index) {
@@ -262,9 +274,13 @@ let Autocomplete = React.createClass({
262274
})
263275
})
264276
var style = {
265-
left: this.state.menuLeft,
266-
top: this.state.menuTop,
267277
minWidth: this.state.menuWidth,
278+
maxHeight: this.state.menuMaxHeight
279+
}
280+
if (this.state.menuPosition === 'before') {
281+
style.bottom = '100%';
282+
} else if (this.state.menuPosition === 'after') {
283+
style.top = '100%';
268284
}
269285
var menu = this.props.renderMenu(items, this.state.value, style)
270286
return React.cloneElement(menu, { ref: 'menu' })
@@ -298,7 +314,7 @@ let Autocomplete = React.createClass({
298314
})
299315
}
300316
return (
301-
<div style={{display: 'inline-block'}}>
317+
<div style={{display: 'inline-block', position: 'relative'}}>
302318
<input
303319
{...this.props.inputProps}
304320
role="combobox"

0 commit comments

Comments
 (0)