UNPKG

wix-style-react

Version:
210 lines (187 loc) 5.47 kB
import classNames from 'classnames'; import PropTypes from 'prop-types'; import isUndefined from 'lodash/isUndefined'; import defaultTo from 'lodash/defaultTo'; import differenceBy from 'lodash/differenceBy'; import { allValidators, extendPropTypes } from '../utils/propTypes'; import deprecationLog from '../utils/deprecationLog'; import InputWithOptions from '../InputWithOptions/InputWithOptions'; import styles from './Dropdown.scss'; const NO_SELECTED_ID = null; class Dropdown extends InputWithOptions { constructor(props) { super(props); if (props.upgrade) { this.state = { value: '', selectedId: NO_SELECTED_ID, ...Dropdown.getNextState( props, defaultTo(props.selectedId, props.initialSelectedId), ), }; } else { this.deprecatedUpdate(props, { isFirstTime: true }); } } isSelectedIdControlled() { const { upgrade, selectedId } = this.props; return upgrade && !isUndefined(selectedId); } isControlledSupported() { return this.props.upgrade; } static isOptionsEqual(optionsA, optionsB) { return differenceBy(optionsA, optionsB, o => o.id).length === 0; } getSelectedId() { if (this.isControlledSupported()) { return this.isSelectedIdControlled() ? this.props.selectedId : this.state.selectedId; } else { return this.state.selectedId; } } _onInputClicked(event) { if ( this.state.showOptions && Date.now() - this.state.lastOptionsShow > 200 ) { this.hideOptions(); } else { this.showOptions(); } if (this.props.onInputClicked) { this.props.onInputClicked(event); } } /** * Updates the value by the selectedId. * If selectedId is not found in options, then value is NOT changed. */ static getNextState(props, selectedId) { if (!isUndefined(selectedId)) { const option = props.options.find(_option => { return _option.id === selectedId; }); if (option) { const value = props.valueParser(option) || ''; return { value, selectedId }; } else { return { value: '', selectedId: NO_SELECTED_ID }; } } return {}; } deprecatedUpdate(props, { isFirstTime }) { let value = '', selectedId = -1; if (!isUndefined(props.selectedId)) { const option = props.options.find(_option => { return _option.id === props.selectedId; }); if (option) { value = props.valueParser(option); selectedId = option.id; } } if (isFirstTime) { this.state = { value, selectedId }; } else { this.setState({ value, selectedId }); } } componentWillReceiveProps(nextProps) { if (this.props.upgrade) { if ( nextProps.selectedId !== this.props.selectedId || !Dropdown.isOptionsEqual(this.props.options, nextProps.options) ) { this.setState( Dropdown.getNextState( nextProps, defaultTo(nextProps.selectedId, this.state.selectedId), ), ); } } else { this.deprecatedUpdate(nextProps, { isFirstTime: false }); } } inputClasses() { const classes = { [styles.readonly]: true }; classes[styles.noBorder] = this.props.noBorder; return classNames(classes); } dropdownAdditionalProps() { return { selectedId: this.getSelectedId(), value: this.state.value, tabIndex: -1, }; } inputAdditionalProps() { return { readOnly: true, value: this.state.value, }; } _onSelect(option) { if (!this.isControlledSupported() || !this.isSelectedIdControlled()) { this.setState({ value: this.props.valueParser(option), selectedId: option.id, }); } super._onSelect(option); } _onChange(event) { this.setState({ value: event.target.value }); super._onChange(event); } } Dropdown.propTypes = { ...InputWithOptions.propTypes, /** When true, then `selectedId` is used for Controlled mode, and `initialSelectedId` for Uncontrolled mode. */ upgrade: PropTypes.bool, /** Sets the selected option id. (Implies Controlled mode) */ selectedId: InputWithOptions.propTypes.selectedId, /** An initial selected option id. (Implies Uncontrolled mode) */ initialSelectedId: InputWithOptions.propTypes.selectedId, }; extendPropTypes(Dropdown, { selectedId: allValidators( InputWithOptions.propTypes.selectedId, (props, propName) => { if ( props[propName] !== undefined && props['initialSelectedId'] !== undefined ) { return new Error( `'selectedId' and 'initialSelectedId' cannot both be used at the same time.`, ); } }, ), initialSelectedId: allValidators( InputWithOptions.propTypes.selectedId, (props, propName) => { if (props[propName] !== undefined && !props['upgrade']) { return new Error( `'initialSelectedId' can be used only if you pass 'upgrade=true' as well.`, ); } }, ), upgrade: allValidators(PropTypes.bool, (props, propName) => { if (!props[propName]) { deprecationLog( `Dropdown: New API! Please upgrade by passing the prop 'upgrade=true', and refer to documentation.`, ); } }), }); Dropdown.defaultProps = InputWithOptions.defaultProps; Dropdown.displayName = 'Dropdown'; export default Dropdown;