src/components/InputSelect.jsx
import { emphasize, makeStyles, useTheme } from '@material-ui/core/styles'
import CancelIcon from '@material-ui/icons/Cancel'
import classnames from 'classnames'
import CreatableSelect from 'react-select/creatable'
import Chip from '@material-ui/core/Chip'
import MenuItem from '@material-ui/core/MenuItem'
import NoSsr from '@material-ui/core/NoSsr'
import Paper from '@material-ui/core/Paper'
import PropTypes from 'prop-types'
import React from 'react'
import TextField from '@material-ui/core/TextField'
import Typography from '@material-ui/core/Typography'
const useStyles = makeStyles(theme => ({
root: {
flexGrow: 1
},
input: {
display: 'flex',
padding: 0,
height: 'auto'
},
valueContainer: {
display: 'flex',
flexWrap: 'wrap',
flex: 1,
alignItems: 'center',
overflow: 'hidden'
},
chip: {
margin: theme.spacing(0.5, 0.25)
},
chipFocused: {
backgroundColor: emphasize(
theme.palette.type === 'light' ? theme.palette.grey[300] : theme.palette.grey[700],
0.08
)
},
noOptionsMessage: {
padding: theme.spacing(1, 2)
},
singleValue: {
fontSize: 16
},
placeholder: {
position: 'absolute',
left: 2,
bottom: 6,
fontSize: 16
},
paper: {
position: 'absolute',
zIndex: 10,
marginTop: theme.spacing(1),
left: 0,
right: 0
},
divider: {
height: theme.spacing(2)
}
}))
function NoOptionsMessage (props) {
return (
<Typography
color='textSecondary'
className={props.selectProps.classes.noOptionsMessage}
{...props.innerProps}
>
{props.children}
</Typography>
)
}
NoOptionsMessage.propTypes = {
children: PropTypes.node,
innerProps: PropTypes.object,
selectProps: PropTypes.object.isRequired
}
function inputComponent ({ inputRef, ...props }) {
return <div ref={inputRef} {...props} />
}
inputComponent.propTypes = {
inputRef: PropTypes.oneOfType([PropTypes.func, PropTypes.object])
}
function Control (props) {
const {
children,
innerProps,
innerRef,
selectProps: { classes, TextFieldProps }
} = props
return (
<TextField
fullWidth
InputProps={{
inputComponent,
inputProps: {
className: classes.input,
ref: innerRef,
children,
...innerProps
}
}}
{...TextFieldProps}
/>
)
}
Control.propTypes = {
children: PropTypes.node,
innerProps: PropTypes.object,
innerRef: PropTypes.oneOfType([PropTypes.func, PropTypes.object]),
selectProps: PropTypes.object.isRequired
}
function Option (props) {
return (
<MenuItem
ref={props.innerRef}
selected={props.isFocused}
component='div'
style={{
fontWeight: props.isSelected ? 500 : 400
}}
{...props.innerProps}
>
{props.data.isNew && 'Create '}
{props.children}
</MenuItem>
)
}
Option.propTypes = {
children: PropTypes.node,
innerProps: PropTypes.object,
innerRef: PropTypes.oneOfType([PropTypes.func, PropTypes.object]),
isFocused: PropTypes.bool,
isSelected: PropTypes.bool
}
function Placeholder (props) {
return (
<Typography
color='textSecondary'
className={props.selectProps.classes.placeholder}
{...props.innerProps}
>
{props.children}
</Typography>
)
}
Placeholder.propTypes = {
children: PropTypes.node,
innerProps: PropTypes.object,
selectProps: PropTypes.object.isRequired
}
function ValueContainer (props) {
return <div className={props.selectProps.classes.valueContainer}>{props.children}</div>
}
ValueContainer.propTypes = {
children: PropTypes.node,
selectProps: PropTypes.object.isRequired
}
function MultiValue (props) {
return (
<Chip
tabIndex={-1}
label={props.children}
className={classnames(props.selectProps.classes.chip, {
[props.selectProps.classes.chipFocused]: props.isFocused
})}
onDelete={props.removeProps.onClick}
deleteIcon={<CancelIcon {...props.removeProps} />}
/>
)
}
MultiValue.propTypes = {
children: PropTypes.node,
isFocused: PropTypes.bool,
removeProps: PropTypes.object.isRequired,
selectProps: PropTypes.object.isRequired
}
function Menu (props) {
return (
<Paper square className={props.selectProps.classes.paper} {...props.innerProps}>
{props.children}
</Paper>
)
}
Menu.propTypes = {
children: PropTypes.node,
innerProps: PropTypes.object,
selectProps: PropTypes.object
}
const components = {
Control,
Menu,
MultiValue,
NoOptionsMessage,
Option,
Placeholder,
ValueContainer
}
export function InputSelect ({ label, options, value, ...selectProps }) {
const classes = useStyles()
const theme = useTheme()
const selectStyles = {
input: base => ({
...base,
color: theme.palette.text.primary,
'& input': {
font: 'inherit'
}
}),
menuPortal: base => ({
...base,
zIndex: 9999
})
}
return (
<div className={classes.root}>
<NoSsr>
<div className={classes.divider} />
<CreatableSelect
{...selectProps}
classes={classes}
styles={selectStyles}
inputId='react-select-multiple'
menuPortalTarget={document.body}
TextFieldProps={{
label,
InputLabelProps: {
htmlFor: 'react-select-multiple',
shrink: true
},
placeholder: 'Select multiple tags'
}}
components={components}
options={options}
onBlur={selectProps.onBlur && (() => selectProps.onBlur())}
value={value}
/>
</NoSsr>
</div>
)
}