Last active
October 7, 2022 21:02
-
-
Save kumarharsh/5ada99a2f6b5867984991b70bdc453a1 to your computer and use it in GitHub Desktop.
Shows how to use react-select with portals provided by react-overlay
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import ReactSelect from 'react-select'; | |
import Portal from 'react-overlays/lib/Portal'; | |
import 'react-select/dist/react-select.css'; // I'm using css-modules, but you can use whatever you like | |
import css from './Select.css'; | |
import defaultMenuRenderer from 'react-select/lib/utils/defaultMenuRenderer'; // this renders the actual menu - we can reuse the same component | |
class _SelectMenu extends React.Component { | |
props: { // flow types | |
selectProps: {}, | |
selectDOM: HTMLElement, | |
}; | |
componentDidMount() { | |
window.addEventListener('scroll', this._handleScroll); // just forceUpdate on scroll | |
} | |
componentWillUnmount() { | |
window.removeEventListener('scroll', this._handleScroll); | |
} | |
getDocumentBody() { | |
return document.body; | |
} | |
_handleScroll = () => { | |
this.forceUpdate(); // just need to re-render on scroll so that the menu is not stuck in mid-air while the select input scrolls off-screen | |
} | |
render() { | |
const rect = this.props.selectDOM.getBoundingClientRect(); | |
const computedStyle = window.getComputedStyle(this.props.selectDOM); // inherit as many styles as you want from the parent select | |
const fontSize = computedStyle.getPropertyValue('font-size'); | |
const lineHeight = computedStyle.getPropertyValue('line-height'); | |
const position = { | |
top: rect.bottom - 1, | |
left: rect.left, | |
width: rect.width, | |
}; | |
return ( | |
<Portal container={this.getDocumentBody}> | |
<div className={cx(css.selectMenuPortal, 'Select-menu-outer')} style={{...position, fontSize, lineHeight}}> | |
<div className="Select-menu">{ defaultMenuRenderer(this.props.selectProps) }</div> | |
</div> | |
</Portal> | |
); | |
} | |
} | |
export default class Select extends React.Component { | |
... other stuff ... | |
_selectDOM: HTMLElement = null; | |
componentDidMount() { | |
this._selectDOM = findDOMNode(this._select).children[0]; | |
} | |
componentDidUpdate() { | |
this._selectDOM = findDOMNode(this._select).children[0]; | |
} | |
_getRef = (c) => { this._select = c; } | |
_renderMenu = (selectProps) => { | |
if (!this._selectDOM) { | |
return defaultMenuRenderer(selectProps); | |
} | |
return ( | |
<_SelectMenu | |
selectProps={selectProps} | |
selectDOM={this._selectDOM} // pass the select's dom node (for getting styles & bounding-box calcs | |
/> | |
); | |
} | |
render() { | |
const { | |
wrapperClassName, | |
...otherProps, | |
} = this.props; | |
return ( | |
<div | |
ref={this._getRef} // I am using some custom styles, so using a wrapping div. You may use <ReactSelect ...> directly here. | |
className={cx( | |
css.container, | |
wrapperClassName, | |
)} | |
> | |
<ReactSelect | |
{...otherProps} | |
menuRenderer={this._renderMenu} | |
/> | |
</div> | |
); | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
@kumarharsh, thanks for the example. Can you throw a license (e.g., MIT) on this just to make it clear if/how folks can use it?