123123
123123
/* eslint-disable */
import Fuse from 'fuse.js';
import FormControl from '@material-ui/core/FormControl';
import FormHelperText from '@material-ui/core/FormHelperText';
import InputLabel from '@material-ui/core/InputLabel';
import OutlinedInput from '@material-ui/core/OutlinedInput';
import PropTypes from 'prop-types';
import React from 'react';
import ReactDOM from 'react-dom';
import SelectMUI from '@material-ui/core/Select';
import Show from '@reviewpro/js-browser/lib/Show';
import Checkbox from '../Checkbox';
import Search from '../icons/Search';
import TextField from '../TextField';
import Typography from '../Typography';
import withStyles from '../styles/withStyles';
import FullScreenDelegator from '../FullScreenDelegator';
const ITEM_HEIGHT = 46;
const TEXT_FIELD_HEIGHT = 56;
const customIdNameValidator = (props, propName, componentName) => {
const { id, name } = props;
if (!id && !name) {
return new Error(`${componentName} required a id or name prop`);
}
return null;
};
const styles = ({ palette, spacing, zIndex }) => ({
filter: {
margin: `${spacing.unit}px`,
},
formControl: {
width: '100%',
},
input: {
height: '24px',
paddingBottom: `${spacing.unit}px`,
paddingLeft: `${spacing.unit}px`,
paddingRight: `${spacing.unit * 4}px`,
paddingTop: `${spacing.unit * 2}px`,
},
inputFocused: {
color: palette.secondary.main,
},
inputLabel: {
'&$inputFocused': {
color: palette.secondary.main,
},
},
inputOutlinedInput: {
'&$inputFocused $notchedOutline': {
borderColor: palette.secondary.main,
},
},
modal: {
// TODO: Fix this after refactor of MobileFullScreen components
zIndex: zIndex.tooltip,
},
notchedOutline: {},
outlined: {
'&$shrink': {
transform: 'translate(14px, -6px) scale(0.75)',
},
pointerEvents: 'none',
transform: 'translate(16px, 16px) scale(1)',
zIndex: 1,
},
rootList: {
backgroundColor: 'inherit',
},
shrink: {
transform: 'translate(14px, -6px) scale(0.75)',
},
});
class Select extends React.Component {
static getDerivedStateFromProps(nextProps, prevState) {
if (prevState.value !== nextProps.value) {
return {
...prevState,
value: nextProps.value,
};
}
return null;
}
constructor(props) {
super(props);
this.state = {
filter: '',
handleResize: () => {},
labelWidth: 0,
focused: false,
value: props.value,
};
}
componentDidMount() {
this.setState({
labelWidth: ReactDOM.findDOMNode(this.InputLabelRef).offsetWidth,
});
}
handleFocus = () => {
this.setState({ focused: true });
};
handleBlur = () => {
this.setState({ focused: false });
};
renderValue = item => {
const { allSelectedText, children, partiallySelectedText } = this.props;
const selectedChildren = React.Children.toArray(children).filter(({ props }) => item.includes(props.value));
let resultString;
if (allSelectedText && partiallySelectedText) {
if (selectedChildren.length > 1) {
const isAllSelected = selectedChildren.length === React.Children.toArray(children).length;
if (isAllSelected) {
resultString = allSelectedText;
} else {
resultString = partiallySelectedText;
}
} else if (selectedChildren.length) {
resultString = selectedChildren[0].props.children;
}
} else {
resultString = selectedChildren.map(child => child.props.children).join(', ');
}
return resultString;
};
render() {
const { allSelectedText, children, classes, disabled, helperText, id, label, minItems, multiple, name, noItemsText, onChange, ...props } = this.props;
const { filter, labelWidth, focused, handleResize, value } = this.state;
let extendedProps = {};
let items = React.Children.toArray(children);
console.log(items);
const trimFilter = filter.trim().toLowerCase();
if (trimFilter) {
if (multiple && allSelectedText) {
items.shift();
}
items = items.filter(({ props: p }) => {
const searchObject = [
{
child: typeof p.children === 'string' || p.children instanceof String ? p.children : undefined,
searchFields: (Array.isArray(p.searchFields) ? p.searchFields : Array.of(p.searchFields))
.filter(field => !!field)
.map(field => field.toString()),
value: p.value,
},
];
const fuseValues = new Fuse(searchObject, {
distance: 100,
keys: ['child', 'searchFields', 'value'],
location: 0,
matchAllTokens: true,
maxPatternLength: 32,
minMatchCharLength: 1,
threshold: 0.3,
tokenize: true,
});
return !!fuseValues.search(trimFilter).length;
});
}
if (multiple && Array.isArray(value)) {
extendedProps = {
renderValue: this.renderValue,
};
items = items.map(child =>
React.cloneElement(
child,
{},
<React.Fragment>
<Checkbox checked={value.includes(child.props.value)} />
{child.props.children}
</React.Fragment>
)
);
}
const handleItemClick = item => {
console.log(item);
debugger;
setState({ value: item.value, focused: false });
};
items = items.map(item => {
React.cloneElement(item, {
onClick: (...args) => {
handleItemClick(item);
item.onClick && item.onClick(...args);
},
});
});
return (
<FormControl className={classes.formControl} variant="outlined">
<InputLabel
classes={{
focused: classes.inputFocused,
outlined: classes.outlined,
root: classes.inputLabel,
shrink: classes.shrink,
}}
htmlFor={id || name}
ref={ref => {
this.InputLabelRef = ref;
}}
>
{label}
</InputLabel>
<SelectMUI
{...props}
{...extendedProps}
input={
<OutlinedInput
classes={{
focused: classes.inputFocused,
input: classes.input,
notchedOutline: classes.notchedOutline,
root: classes.inputOutlinedInput,
}}
disabled={disabled}
id={id || name}
labelWidth={labelWidth}
name={name || id}
/>
}
MenuProps={{
action: actions => this.setState({ handleResize: actions.updatePosition }),
anchorOrigin: {
horizontal: 'center',
vertical: 'center',
},
disableAutoFocusItem: React.Children.count(children) > minItems,
getContentAnchorEl: null,
MenuListProps: {
classes: {
root: classes.rootList,
},
disablePadding: true,
},
onExited: () => {
this.setState({ filter: '' });
handleResize();
},
PaperProps: {
style: {
// This will show minItems and a half item to display scroll
// It also prevent the size of the filter text field
maxHeight: ITEM_HEIGHT * minItems + (React.Children.count(children) > minItems ? TEXT_FIELD_HEIGHT : 0) + ITEM_HEIGHT * 0.5,
},
},
transformOrigin: {
horizontal: 'center',
vertical: 'center',
},
}}
multiple={multiple}
onChange={onChange}
onOpen={this.handleFocus}
value={value}
>
<Show condition={React.Children.count(children) > minItems}>
<div className={classes.filter}>
<TextField
onFocus={this.handleFocus}
autoFocus
icon={Search}
onChange={event => {
this.setState({ filter: event.target.value });
handleResize();
}}
placeholder={label}
type="text"
value={filter}
/>
</div>
</Show>
<Show condition={!!(items.length <= 0 && noItemsText)}>
<Typography variant="caption">{noItemsText}</Typography>
</Show>
{items}
</SelectMUI>
{focused && (
<FullScreenDelegator>
<Show condition={React.Children.count(children) > minItems}>
<div className={classes.filter}>
<TextField
onFocus={this.handleFocus}
autoFocus
icon={Search}
onChange={event => {
this.setState({ filter: event.target.value });
handleResize();
}}
placeholder={label}
type="text"
value={filter}
/>
</div>
</Show>
<Show condition={!!(items.length <= 0 && noItemsText)}>
<Typography variant="caption">{noItemsText}</Typography>
</Show>
{items}
</FullScreenDelegator>
)}
<Show condition={!!helperText}>
<FormHelperText>{helperText}</FormHelperText>
</Show>
</FormControl>
);
}
}
/**
* Definition of props types
*
* @param {string} allSelectedText - This string shows when Select has more
* than 1 item and all of them are selected
* @param {node} children - A node to render as an icon
* @param {Object} classes - A serie of clases defined in the styles
* @param {bool} disabled - Bool to disable component
* @param {string} helperText - String to show in the bottom
* @param {string} label - Label to show inside/top component
* @param {string} id - Identifier for component
* @param {number} minItems - The number of min items to show the
* filter box
* @param {bool} multiple - Bool to enable multiple selection
* @param {string} name - Component name
* @param {func} onChange - Callback function fired when a menu item
* is selected. "Event" is received, you
* can pick the value with
* "event.target.value"
* @param {string} noItemsText - The string to show when no items found
* with the filter in the box
* @param {string} partiallySelectedText - String to show when Select have more
* than 1 item, but not all of them are
* selected
* @param {string|array} value - The value or values (multiple) selected
*/
Select.propTypes = {
allSelectedText: PropTypes.string,
children: PropTypes.node.isRequired,
classes: PropTypes.object.isRequired,
disabled: PropTypes.bool,
helperText: PropTypes.string,
id: customIdNameValidator,
label: PropTypes.string.isRequired,
minItems: PropTypes.number,
multiple: PropTypes.bool,
name: customIdNameValidator,
noItemsText: PropTypes.string,
onChange: PropTypes.func.isRequired,
partiallySelectedText: PropTypes.string,
value: PropTypes.oneOfType([PropTypes.string, PropTypes.number, PropTypes.array]).isRequired,
};
Select.defaultProps = {
allSelectedText: '',
disabled: false,
helperText: '',
id: null,
minItems: 10,
multiple: false,
name: null,
noItemsText: '',
partiallySelectedText: '',
};
Select.rpName = 'Select';
export default withStyles(styles)(Select);