import React, { useEffect, useMemo, useState, useRef, useCallback } from 'react' import PropTypes from 'prop-types' import { Trans } from 'react-i18next' import { matchSorter } from 'match-sorter' import { useCombobox } from 'downshift' import classnames from 'classnames' import Icon from '../../../shared/components/icon' export default function SelectCollaborators({ loading, options, placeholder, multipleSelectionProps }) { const { getSelectedItemProps, getDropdownProps, addSelectedItem, removeSelectedItem, selectedItems } = multipleSelectionProps const [inputValue, setInputValue] = useState('') const selectedEmails = useMemo(() => selectedItems.map(item => item.email), [ selectedItems ]) const unselectedOptions = useMemo( () => options.filter(option => !selectedEmails.includes(option.email)), [options, selectedEmails] ) const filteredOptions = useMemo( () => matchSorter(unselectedOptions, inputValue, { keys: ['name', 'email'], threshold: matchSorter.rankings.CONTAINS }), [unselectedOptions, inputValue] ) const inputRef = useRef(null) const focusInput = useCallback(() => { if (inputRef.current) { window.setTimeout(() => { inputRef.current.focus() }, 10) } }, [inputRef]) const isValidInput = useMemo(() => { if (inputValue.includes('@')) { for (const selectedItem of selectedItems) { if (selectedItem.email === inputValue) { return false } } } return true }, [inputValue, selectedItems]) const { isOpen, getLabelProps, getMenuProps, getInputProps, getComboboxProps, highlightedIndex, getItemProps, reset } = useCombobox({ inputValue, defaultHighlightedIndex: 0, items: filteredOptions, itemToString: item => item && item.name, onStateChange: ({ inputValue, type, selectedItem }) => { switch (type) { // set inputValue when the input changes case useCombobox.stateChangeTypes.InputChange: setInputValue(inputValue) break // add a selected item on Enter (keypress), click or blur case useCombobox.stateChangeTypes.InputKeyDownEnter: case useCombobox.stateChangeTypes.ItemClick: case useCombobox.stateChangeTypes.InputBlur: if (selectedItem) { setInputValue('') addSelectedItem(selectedItem) } break } } }) const addNewItem = useCallback( (email, focus = true) => { if ( isValidInput && email.includes('@') && !selectedEmails.includes(email) ) { addSelectedItem({ email, display: email, type: 'user' }) setInputValue('') reset() if (focus) { focusInput() } return true } }, [addSelectedItem, selectedEmails, isValidInput, focusInput, reset] ) // close and reset the menu when there are no matching items useEffect(() => { if (isOpen && filteredOptions.length === 0) { reset() } }, [reset, isOpen, filteredOptions.length]) return (