Created
July 13, 2018 19:15
-
-
Save diiq/af5b2e1e59866ca87e0289415e95fc23 to your computer and use it in GitHub Desktop.
Portals for ReactSelect
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 * as React from 'react' | |
import * as ReactDOM from 'react-dom' | |
import ReactSelect from 'react-select' | |
import { TouchEvent } from 'react'; | |
export class Select extends (ReactSelect) { | |
wrapper: any | |
menuContainer: HTMLDivElement | |
handleMenuScroll: () => void | |
handleMouseDownOnMenu: () => void | |
_instancePrefix: string | |
menu: HTMLDivElement | |
renderOuter(options: any, valueArray: any, focusedOption: any) { | |
const dimensions = this.wrapper ? this.wrapper.getBoundingClientRect() : null | |
// @ts-ignore because we're inheriting from something I have no control over. | |
const menu = super.renderMenu(options, valueArray, focusedOption) | |
if (!menu || !dimensions) return null | |
const maxHeight = document.body.offsetHeight - (dimensions.top + dimensions.height) | |
return ReactDOM.createPortal( | |
<div | |
ref={ref => { this.menuContainer = ref }} | |
className="Select-menu-outer" | |
onClick={(e) => { e.stopPropagation() }} | |
onTouchStart={(e) => { e.stopPropagation() }} | |
style={{ | |
...this.props.menuContainerStyle, | |
zIndex: 9999, | |
position: 'absolute', | |
overflow: 'hidden', | |
width: dimensions.width, | |
top: dimensions.top + dimensions.height, | |
left: dimensions.left, | |
maxHeight: Math.min(maxHeight, 200), | |
}}> | |
<div | |
ref={ref => { this.menu = ref }} | |
role="listbox" | |
tabIndex={-1} | |
className="Select-menu" | |
id={`${this._instancePrefix}-list`} | |
style={{ | |
...this.props.menuStyle, | |
maxHeight: Math.min(maxHeight, 200) | |
}} | |
onScroll={this.handleMenuScroll} | |
onMouseDown={this.handleMouseDownOnMenu}> | |
{menu} | |
</div> | |
</div>, | |
document.body | |
) | |
} | |
handleTouchOutside(event: TouchEvent<HTMLElement>) { | |
// The original react-select code is modified to also check if the touch came from inside the tethered container | |
if (this.wrapper && !this.wrapper.contains(event.target) && !this.menuContainer.contains(event.target as HTMLElement)) { | |
(this as any).closeMenu(); | |
} | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment