// @flow import Downshift from 'downshift' import React, { PureComponent } from 'react' import { contains, map, pluck } from 'ramda' import ErrorMessage from '../ErrorMessage' import InputWrapper from '../MSInputWrapper' import Item from '../MSItem' import Label from '../Label' import Menu from '../MSMenu' import TagItem from '../MSTagItem' import Relative from '../Relative' import { mapIndexed } from '../../utils' import { type SelectedItemType, type EventType, type ChangesType } from '../../types' type Props = { handleChangeValue: (Array, SelectedItemType | Array) => void | () => void, input: { value: Array, }, label: string, meta: { touched: boolean, error: string, dispatch: (() => void) => void, }, name: string, options: Array<{ value: string, label: string, }>, } type State = { selectedItems: Array, isOpen: boolean, } const getSelectedItems = (props) => { let items = [] const { input: { value } } = props if (value) { items = map(val => ({ label: val, tag: val, value: val }))(value) } return items } class Multiselect extends PureComponent { input = null inputWrapper = null constructor(props: Props) { super() this.state = { selectedItems: getSelectedItems(props), isOpen: false, } } onChange = ( selectedItem: SelectedItemType, other: { selectedItem: SelectedItemType | Array }, ) => { const { selectedItems } = this.state if (selectedItems.includes(selectedItem)) { this.removeItem(selectedItem, other.selectedItem) } else { this.addItem(selectedItem, other.selectedItem) } } onWrapperClick = (e: EventType) => { if (this.inputWrapper === e.target || this.input === e.target) { this.focusOnInput() e.stopPropagation() e.preventDefault() } } addItem = (value: SelectedItemType, prevState: SelectedItemType | Array) => { this.setState(({ selectedItems }, props) => { const newValues = [...selectedItems, value] props.handleChangeValue(newValues, prevState) return { selectedItems: newValues } }) } removeItem = (value: SelectedItemType, prevState: SelectedItemType | Array) => { this.setState(({ selectedItems }, props) => { const newValues = selectedItems.filter(i => i !== value) props.handleChangeValue(newValues, prevState) return { selectedItems: newValues } }) } handleToggleMenu = () => { this.setState(({ isOpen }) => ({ isOpen: !isOpen })) } handleStateChange = (changes: ChangesType) => { const { isOpen, type } = changes const { isOpen: stateIsOpen } = this.state if (type === Downshift.stateChangeTypes.mouseUp && isOpen !== stateIsOpen) { this.setState(() => ({ isOpen })) } } itemToString = (value: { value: string }) => pluck('value', value) inputRef = (c: HTMLElement) => { this.input = c } inputWrapperRef = (c: HTMLElement) => { this.inputWrapper = c } focusOnInput() { if (this.input != null) { this.input.focus() } // $FlowFixMe if (this.input && typeof this.input.getInput === 'function') { this.input.getInput().focus() } } render() { const { name, options, label, meta, } = this.props const { selectedItems, isOpen } = this.state return (
{({ getLabelProps, getInputProps, getItemProps, isOpen: isMenuOpen, selectedItem, highlightedIndex, }) => (
{(selectedItem.length > 0) && map(tag => ( { e.stopPropagation() this.onChange(tag, { selectedItem: selectedItems }) }} onClick={(e) => { e.stopPropagation() this.onChange(tag, { selectedItem: selectedItems }) }} > {tag.tag} ), selectedItems) } {!isMenuOpen ? null : ( { mapIndexed((item, index) => ( {item.label} ), options) } )}
)}
{meta.touched && meta.error && ( ) }
) } } export default Multiselect