generator-thundr-gae-react
Version:
Yeoman generator for a React app that runs atop Thundr on Google App Engine
153 lines (134 loc) • 4.17 kB
JSX
/* eslint-disable jsx-a11y/no-static-element-interactions, jsx-a11y/click-events-have-key-events */
import React from 'react';
import * as PropTypes from 'prop-types';
import { Chip, MenuItem, Typography } from 'material-ui';
import Select from 'react-select';
import ArrowDropDownIcon from 'material-ui-icons/ArrowDropDown';
import CancelIcon from 'material-ui-icons/Cancel';
import ClearIcon from 'material-ui-icons/Clear';
import ArrowDropUpIcon from 'material-ui-icons/ArrowDropUp';
import ReactDOM from 'react-dom';
class Option extends React.Component {
propTypes = {
children: PropTypes.array.isRequired,
isFocused: PropTypes.array.isRequired,
isSelected: PropTypes.array.isRequired,
onFocus: PropTypes.array.isRequired,
onSelect: PropTypes.func.isRequired,
option: PropTypes.object.isRequired,
};
defaultValues = {
}
handleClick = (event) => {
this.props.onSelect(this.props.option, event);
};
render() {
const {
children,
isFocused,
isSelected,
onFocus,
} = this.props;
return (
<MenuItem
onFocus={onFocus}
selected={isFocused}
onClick={this.handleClick}
component="div"
style={{
fontWeight: isSelected ? 500 : 400,
}}
>
{children}
</MenuItem>
);
}
}
// https://github.com/JedWatson/react-select/issues/810
class SelectWithPortal extends Select {
renderOuter(options, valueArray, focusedOption) {
const dimensions = this.wrapper ? this.wrapper.getBoundingClientRect() : null;
const menu = super.renderMenu(options, valueArray, focusedOption);
if (!menu || !dimensions) return null;
const maxHeight = document.body.offsetHeight - (dimensions.top + dimensions.height);
// eslint-disable-next-line no-underscore-dangle
const instancePrefix = this._instancePrefix;
return ReactDOM.createPortal(
<div
ref={(ref) => { this.menuContainer = ref; }}
className="Select-menu-outer"
onClick={(e) => { e.stopPropagation(); }}
style={{
...this.props.menuContainerStyle,
zIndex: 9999,
position: 'absolute',
width: dimensions.width,
top: dimensions.top + dimensions.height,
left: dimensions.left,
maxHeight: Math.min(maxHeight, 200),
overflow: 'hidden',
}}
>
<div
ref={(ref) => { this.menu = ref; }}
role="listbox"
tabIndex={-1}
className="Select-menu"
id={`${instancePrefix}-list`}
style={{
...this.props.menuStyle,
maxHeight: Math.min(maxHeight, 200),
}}
onScroll={this.handleMenuScroll}
onMouseDown={this.handleMouseDownOnMenu}
>
{menu}
</div>
</div>,
document.body,
);
}
}
const SelectWrapped = (props) => {
const {
classes,
...other
} = props;
const arrowRenderer = arrowProps => (
arrowProps.isOpen
? <ArrowDropUpIcon />
: <ArrowDropDownIcon />);
return (
<SelectWithPortal
optionComponent={Option}
noResultsText={<Typography>No results found</Typography>}
arrowRenderer={arrowRenderer}
clearRenderer={() => <ClearIcon />}
valueComponent={(valueProps) => {
const { value, children, onRemove } = valueProps;
const onDelete = (event) => {
event.preventDefault();
event.stopPropagation();
onRemove(value);
};
if (onRemove) {
return (
<Chip
tabIndex={-1}
label={children}
className={classes.chip}
deleteIcon={<CancelIcon onTouchEnd={onDelete} />}
onDelete={onDelete}
/>
);
}
return <div className="Select-value">{children}</div>;
}}
{...other}
/>
);
};
SelectWrapped.propTypes = {
classes: PropTypes.object.isRequired,
};
export default SelectWrapped;