import React, { useState, useEffect } from 'react'; import { View, Text, TouchableWithoutFeedback, Modal, KeyboardAvoidingView, Animated, Easing, StyleSheet } from 'react-native'; import PropTypes from 'prop-types'; import { BLOCK_CONTEXT } from '@rocket.chat/ui-kit'; import Button from '../../Button'; import TextInput from '../../TextInput'; import { textParser } from '../utils'; import { themes } from '../../../constants/colors'; import I18n from '../../../i18n'; import { isIOS } from '../../../utils/deviceInfo'; import Chips from './Chips'; import Items from './Items'; import Input from './Input'; import styles from './styles'; const ANIMATION_DURATION = 200; const ANIMATION_PROPS = { duration: ANIMATION_DURATION, easing: Easing.inOut(Easing.quad), useNativeDriver: true }; const animatedValue = new Animated.Value(0); const behavior = isIOS ? 'padding' : null; export const MultiSelect = React.memo(({ options = [], onChange, placeholder = { text: 'Search' }, context, loading, value: values, multiselect = false, onSearch, onClose, disabled, inputStyle, theme }) => { const [selected, select] = useState(Array.isArray(values) ? values : []); const [open, setOpen] = useState(false); const [search, onSearchChange] = useState(''); const [currentValue, setCurrentValue] = useState(''); const [showContent, setShowContent] = useState(false); useEffect(() => { if (Array.isArray(values)) { select(values); } }, [values]); useEffect(() => { setOpen(showContent); }, [showContent]); useEffect(() => { if (values && values.length && !multiselect) { setCurrentValue(values[0].text); } }, []); const onShow = () => { Animated.timing( animatedValue, { toValue: 1, ...ANIMATION_PROPS } ).start(); setShowContent(true); }; const onHide = () => { onClose(); Animated.timing( animatedValue, { toValue: 0, ...ANIMATION_PROPS } ).start(() => setShowContent(false)); }; const onSelect = (item) => { const { value, text: { text } } = item; if (multiselect) { let newSelect = []; if (!selected.includes(value)) { newSelect = [...selected, value]; } else { newSelect = selected.filter(s => s !== value); } select(newSelect); onChange({ value: newSelect }); } else { onChange({ value }); setCurrentValue(text); onHide(); } }; const renderContent = () => { const items = onSearch ? options : options.filter(option => textParser([option.text]).toLowerCase().includes(search.toLowerCase())); return ( <View style={[styles.modal, { backgroundColor: themes[theme].backgroundColor }]}> <View style={[styles.content, { backgroundColor: themes[theme].backgroundColor }]}> <TextInput testID='multi-select-search' onChangeText={onSearch || onSearchChange} placeholder={I18n.t('Search')} theme={theme} /> <Items items={items} selected={selected} onSelect={onSelect} theme={theme} /> </View> </View> ); }; const translateY = animatedValue.interpolate({ inputRange: [0, 1], outputRange: [600, 0] }); let button = multiselect ? ( <Button title={`${ selected.length } selecteds`} onPress={onShow} loading={loading} theme={theme} /> ) : ( <Input onPress={onShow} theme={theme} loading={loading} disabled={disabled} inputStyle={inputStyle} > <Text style={[styles.pickerText, { color: currentValue ? themes[theme].titleText : themes[theme].auxiliaryText }]}>{currentValue || placeholder.text}</Text> </Input> ); if (context === BLOCK_CONTEXT.FORM) { const items = options.filter(option => selected.includes(option.value)); button = ( <Input onPress={onShow} theme={theme} loading={loading} disabled={disabled} inputStyle={inputStyle} > {items.length ? <Chips items={items} onSelect={onSelect} theme={theme} /> : <Text style={[styles.pickerText, { color: themes[theme].auxiliaryText }]}>{placeholder.text}</Text>} </Input> ); } return ( <> <Modal animationType='fade' transparent visible={open} onRequestClose={onHide} onShow={onShow} > <TouchableWithoutFeedback onPress={onHide}> <View style={styles.container}> <View style={{ ...StyleSheet.absoluteFill, opacity: themes[theme].backdropOpacity, backgroundColor: themes[theme].backdropColor }} /> <KeyboardAvoidingView style={styles.keyboardView} behavior={behavior}> <Animated.View style={[styles.animatedContent, { transform: [{ translateY }] }]}> {showContent ? renderContent() : null} </Animated.View> </KeyboardAvoidingView> </View> </TouchableWithoutFeedback> </Modal> {button} </> ); }); MultiSelect.propTypes = { options: PropTypes.array, onChange: PropTypes.func, placeholder: PropTypes.object, context: PropTypes.number, loading: PropTypes.bool, multiselect: PropTypes.bool, onSearch: PropTypes.func, onClose: PropTypes.func, inputStyle: PropTypes.object, value: PropTypes.array, disabled: PropTypes.bool, theme: PropTypes.string }; MultiSelect.defaultProps = { onClose: () => {} };