UNPKG

sc-react-ions

Version:

An open source set of React components that implement Ambassador's Design and UX patterns.

198 lines (166 loc) 5.27 kB
import React from 'react' import PropTypes from 'prop-types' import optclass from '../internal/OptClass' import TagList from '../internal/TagList' import style from './style.scss' class MultiSelect extends React.Component { constructor(props) { super(props) } static defaultProps = { disabled: false } static propTypes = { /** * A string to display as the placeholder text. */ placeholder: PropTypes.string, /** * An array of objects which will be used as the options for the MultiSelect component. */ options: PropTypes.array.isRequired, /** * The values of the options to be selected. */ value: PropTypes.array, /** * Which field in the option object will be used as the value of the MultiSelect component. */ valueProp: PropTypes.string.isRequired, /** * Which field in the option object will be used as the display of the MultiSelect component. */ displayProp: PropTypes.string.isRequired, /** * Whether the MultiSelect component is disabled. */ disabled: PropTypes.bool, /** * A callback function to be called when an option is selected. */ changeCallback: PropTypes.func, /** * Optional CSS class(es) to be used for local styles (string or array of strings) */ optClass: PropTypes.oneOfType([ PropTypes.array, PropTypes.string ]) } state = { isOpen: false, value: this.props.value || [] } componentWillMount = () => { // Set state if (this.state.value instanceof Array && this.state.value.length > 0 && this.containsValidValue(this.state.value, this.props.options)) { this.setState({selected: this.getSelectedOptions(this.state.value), value: this.state.value}) } // No value is passed in else { this.setState({selected: [], value: []}) } } componentWillReceiveProps = nextProps => { if (nextProps.value !== this.state.value) { // Set state if (nextProps.value instanceof Array && (this.containsValidValue(nextProps.value, nextProps.options) || nextProps.value.length === 0)) { this.setState({selected: this.getSelectedOptions(nextProps.value), value: nextProps.value}) } // No value is passed in else { this.setState({selected: [], value: []}) } } } getIndex = value => { let optionIndex = -1 this.props.options.map((option, index) => { if (option[this.props.valueProp] === value) { optionIndex = index } }) return optionIndex } containsValidValue = (values, options) => { let isValid = false for (let i = 0; i < values.length; i++) { if (this.getIndex(values[i], options) > -1) { isValid = true } } return isValid } getSelectedOptions = values => { let selectedOptions = [] values.map((value, index) => { this.props.options.map((option, index) => { if (option[this.props.valueProp] === value) { selectedOptions.push(option) } }) }) return selectedOptions } handleChange = event => { // when value & option are empty it means that reset button was clicked if (event.target.value !== '' && event.target.option !== '') { let values = this.state.value values.push(event.target.value) this.setState({selected: this.getSelectedOptions(values), value: values}, () => { if (this.props.changeCallback) { this.props.changeCallback({ target: { name: this.props.name, value: this.state.value, options: this.state.selected } }) } }) } } filterOptions = option => { const optionValue = option[this.props.valueProp] return (this.state.value.indexOf(optionValue) === -1) } getElements = children => { let {options, value, ...props} = this.props props.options = this.props.options.filter(this.filterOptions) props.changeCallback = this.handleChange if (['WrappedTypeahead', 'Wrappedt'].includes(children.type.displayName)) { props.resetAfterSelection = true props.optionsFilterPredicate = this.filterOptions } return React.Children.map(children, child => { let {options, value, ...childProps} = child.props return React.cloneElement(child, Object.assign(childProps, props)) }) } onRemove = index => { const values = this.state.value.slice() values.splice(index, 1) this.setState({selected: this.getSelectedOptions(values), value: values}, () => { if (this.props.changeCallback) { this.props.changeCallback({ target: { name: this.props.name, value: this.state.value, options: this.state.selected } }) } }) } render() { const multiSelectClasses = optclass(style, 'multi-select', this.props.optClass) const elements = this.getElements(this.props.children) return ( <div className={multiSelectClasses}> {elements} <TagList tags={this.state.selected} displayProp={this.props.displayProp} onRemove={this.onRemove} /> </div> ) } } export default MultiSelect