diff --git a/less/control.less b/less/control.less index 6a5ced741d..f474608513 100644 --- a/less/control.less +++ b/less/control.less @@ -76,7 +76,6 @@ } .is-open > .Select-control { - .border-bottom-radius( 0 ); background: @select-input-bg; border-color: darken(@select-input-border-color, 10%) @select-input-border-color lighten(@select-input-border-color, 5%); @@ -86,6 +85,14 @@ border-color: transparent transparent @select-arrow-color; border-width: 0 @select-arrow-width @select-arrow-width; } + + &--on-bottom { + .border-bottom-radius( 0 ); + } + + &--on-top { + .border-top-radius( 0 ); + } } .is-searchable { diff --git a/less/menu.less b/less/menu.less index 8b9fe457c0..0051b12e7c 100644 --- a/less/menu.less +++ b/less/menu.less @@ -6,23 +6,32 @@ // wrapper around the menu .Select-menu-outer { - // Unfortunately, having both border-radius and allows scrolling using overflow defined on the same - // element forces the browser to repaint on scroll. However, if these definitions are split into an - // outer and an inner element, the browser is able to optimize the scrolling behavior and does not - // have to repaint on scroll. - .border-bottom-radius( @select-input-border-radius ); + position: absolute; background-color: @select-input-bg; border: 1px solid @select-input-border-color; - border-top-color: mix(@select-input-bg, @select-input-border-color, 50%); box-shadow: 0 1px 0 rgba(0, 0, 0, 0.06); box-sizing: border-box; - margin-top: -1px; max-height: @select-menu-max-height; - position: absolute; - top: 100%; width: 100%; z-index: @select-menu-zindex; -webkit-overflow-scrolling: touch; + + + // Unfortunately, having both border-radius and allows scrolling using overflow defined on the same + // element forces the browser to repaint on scroll. However, if these definitions are split into an + // outer and an inner element, the browser is able to optimize the scrolling behavior and does not + // have to repaint on scroll. + &[data-placement=bottom] { + .border-bottom-radius( @select-input-border-radius ); + border-top-color: mix(@select-input-bg, @select-input-border-color, 50%); + margin-top: -1px; + } + + &[data-placement=top] { + .border-top-radius( @select-input-border-radius ); + border-bottom-color: mix(@select-input-bg, @select-input-border-color, 50%); + margin-bottom: -1px; + } } diff --git a/package.json b/package.json index 12151a0505..86f29014ca 100644 --- a/package.json +++ b/package.json @@ -33,6 +33,7 @@ "react-dom": "^15.0", "react-gravatar": "^2.4.5", "react-highlight-words": "^0.3.0", + "react-popper": "^0.4.3", "react-virtualized": "^7.23.0", "react-virtualized-select": "^1.4.0", "sinon": "^1.17.5", diff --git a/src/Select.js b/src/Select.js index 17b8f3b0ff..19c95ad8e6 100644 --- a/src/Select.js +++ b/src/Select.js @@ -5,8 +5,9 @@ */ import React from 'react'; -import ReactDOM from 'react-dom'; +import ReactDOM, { findDOMNode } from 'react-dom'; import AutosizeInput from 'react-input-autosize'; +import { Manager, Popper, Target } from 'react-popper'; import classNames from 'classnames'; import defaultArrowRenderer from './utils/defaultArrowRenderer'; @@ -170,6 +171,7 @@ const Select = React.createClass({ isOpen: false, isPseudoFocused: false, required: false, + placement: 'bottom', }; }, @@ -1045,6 +1047,17 @@ const Select = React.createClass({ return null; }, + updatePlacement () { + return { + enabled: true, + order: 890, + function: (data) => { + this.setState({ placement: data.placement }); + return data; + }, + }; + }, + renderOuter (options, valueArray, focusedOption) { let menu = this.renderMenu(options, valueArray, focusedOption); if (!menu) { @@ -1052,14 +1065,19 @@ const Select = React.createClass({ } return ( -