UNPKG

react-native-element-dropdown

Version:

React Native Element Dropdown is a library that provides a customizable dropdown component for React Native applications.

467 lines 17.7 kB
function _extends() { _extends = Object.assign ? Object.assign.bind() : function (target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i]; for (var key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = source[key]; } } } return target; }; return _extends.apply(this, arguments); } /* eslint-disable @typescript-eslint/no-shadow */ import _assign from 'lodash/assign'; import _differenceWith from 'lodash/differenceWith'; import _findIndex from 'lodash/findIndex'; import _get from 'lodash/get'; import _isEqual from 'lodash/isEqual'; import { debounce } from 'lodash'; import React, { useCallback, useEffect, useImperativeHandle, useMemo, useRef, useState } from 'react'; import { Dimensions, FlatList, I18nManager, Image, Keyboard, Modal, StatusBar, StyleSheet, Text, TouchableHighlight, TouchableWithoutFeedback, View } from 'react-native'; import { useDetectDevice } from '../../toolkits'; import { useDeviceOrientation } from '../../useDeviceOrientation'; import CInput from '../TextInput'; import { styles } from './styles'; const { isTablet } = useDetectDevice; const ic_down = require('../../assets/down.png'); const statusBarHeight = StatusBar.currentHeight || 0; const DropdownComponent = /*#__PURE__*/React.forwardRef((props, currentRef) => { const orientation = useDeviceOrientation(); const { testID, itemTestIDField, onChange, style = {}, containerStyle, placeholderStyle, selectedTextStyle, itemContainerStyle, itemTextStyle, inputSearchStyle, iconStyle, selectedTextProps = {}, data = [], labelField, valueField, searchField, value, activeColor = '#F6F7F8', fontFamily, iconColor = 'gray', searchPlaceholder, searchPlaceholderTextColor = 'gray', placeholder = 'Select item', search = false, maxHeight = 340, minHeight = 0, disable = false, keyboardAvoiding = true, inverted = true, renderLeftIcon, renderRightIcon, renderItem, renderInputSearch, onFocus, onBlur, autoScroll = true, showsVerticalScrollIndicator = true, dropdownPosition = 'auto', flatListProps, searchQuery, backgroundColor, onChangeText, confirmSelectItem, onConfirmSelectItem, accessibilityLabel, itemAccessibilityLabelField, mode = 'default', closeModalWhenSelectedItem = true, excludeItems = [], excludeSearchItems = [] } = props; const ref = useRef(null); const refList = useRef(null); const [visible, setVisible] = useState(false); const [currentValue, setCurrentValue] = useState(null); const [listData, setListData] = useState(data); const [position, setPosition] = useState(); const [keyboardHeight, setKeyboardHeight] = useState(0); const [searchText, setSearchText] = useState(''); const { width: W, height: H } = Dimensions.get('window'); const styleContainerVertical = useMemo(() => { return { backgroundColor: 'rgba(0,0,0,0.1)', alignItems: 'center' }; }, []); const styleHorizontal = useMemo(() => { return { width: orientation === 'LANDSCAPE' ? W / 2 : '100%', alignSelf: 'center' }; }, [W, orientation]); useImperativeHandle(currentRef, () => { return { open: eventOpen, close: eventClose }; }); useEffect(() => { return eventClose; // eslint-disable-next-line react-hooks/exhaustive-deps }, []); const excludeData = useCallback(data => { if (excludeItems.length > 0) { const getData = _differenceWith(data, excludeItems, (obj1, obj2) => _get(obj1, valueField) === _get(obj2, valueField)); return getData || []; } else { return data || []; } }, [excludeItems, valueField]); useEffect(() => { if (data && searchText.length === 0) { const filterData = excludeData(data); setListData([...filterData]); } if (searchText) { onSearch(searchText); } // eslint-disable-next-line react-hooks/exhaustive-deps }, [data, searchText]); const eventOpen = () => { if (!disable) { _measure(); setVisible(true); if (onFocus) { onFocus(); } if (searchText.length > 0) { onSearch(searchText); } } }; const eventClose = useCallback(() => { if (!disable) { setVisible(false); if (onBlur) { onBlur(); } } }, [disable, onBlur]); const font = useCallback(() => { if (fontFamily) { return { fontFamily: fontFamily }; } else { return {}; } }, [fontFamily]); const _measure = useCallback(() => { if (ref && ref !== null && ref !== void 0 && ref.current) { ref.current.measureInWindow((pageX, pageY, width, height) => { let isFull = isTablet ? false : mode === 'modal' || orientation === 'LANDSCAPE'; if (mode === 'auto') { isFull = false; } const top = isFull ? 20 : height + pageY + 2; const bottom = H - top + height; const left = I18nManager.isRTL ? W - width - pageX : pageX; setPosition({ isFull, width: Math.floor(width), top: Math.floor(top + statusBarHeight), bottom: Math.floor(bottom - statusBarHeight), left: Math.floor(left), height: Math.floor(height) }); }); } }, [H, W, orientation, mode]); const onKeyboardDidShow = useCallback(e => { _measure(); setKeyboardHeight(e.endCoordinates.height); }, [_measure]); const onKeyboardDidHide = useCallback(() => { setKeyboardHeight(0); _measure(); }, [_measure]); useEffect(() => { const susbcriptionKeyboardDidShow = Keyboard.addListener('keyboardDidShow', onKeyboardDidShow); const susbcriptionKeyboardDidHide = Keyboard.addListener('keyboardDidHide', onKeyboardDidHide); return () => { if (typeof (susbcriptionKeyboardDidShow === null || susbcriptionKeyboardDidShow === void 0 ? void 0 : susbcriptionKeyboardDidShow.remove) === 'function') { susbcriptionKeyboardDidShow.remove(); } if (typeof (susbcriptionKeyboardDidHide === null || susbcriptionKeyboardDidHide === void 0 ? void 0 : susbcriptionKeyboardDidHide.remove) === 'function') { susbcriptionKeyboardDidHide.remove(); } }; }, [onKeyboardDidHide, onKeyboardDidShow]); const getValue = useCallback(() => { const defaultValue = typeof value === 'object' ? _get(value, valueField) : value; const getItem = data.filter(e => _isEqual(defaultValue, _get(e, valueField))); if (getItem.length > 0) { setCurrentValue(getItem[0]); } else { setCurrentValue(null); } }, [data, value, valueField]); useEffect(() => { getValue(); }, [value, data, getValue]); const scrollIndex = debounce(useCallback(() => { if (autoScroll && (data === null || data === void 0 ? void 0 : data.length) > 0 && (listData === null || listData === void 0 ? void 0 : listData.length) === (data === null || data === void 0 ? void 0 : data.length)) { if (refList && refList !== null && refList !== void 0 && refList.current) { const defaultValue = typeof value === 'object' ? _get(value, valueField) : value; const index = _findIndex(listData, e => _isEqual(defaultValue, _get(e, valueField))); if ((listData === null || listData === void 0 ? void 0 : listData.length) > 0 && index > -1 && index <= (listData === null || listData === void 0 ? void 0 : listData.length) - 1) { try { refList.current.scrollToIndex({ index: index, animated: false }); } catch (error) { console.warn(`scrollToIndex error: ${error}`); } } } } }, [autoScroll, data.length, listData, value, valueField]), 200); const showOrClose = useCallback(() => { if (!disable) { const visibleStatus = !visible; if (keyboardHeight > 0 && !visibleStatus) { return Keyboard.dismiss(); } if (!visibleStatus) { if (onChangeText) { onChangeText(''); } setSearchText(''); onSearch(''); } _measure(); setVisible(visibleStatus); if (data) { const filterData = excludeData(data); setListData(filterData); } if (visibleStatus) { if (onFocus) { onFocus(); } } else { if (onBlur) { onBlur(); } } if (searchText.length > 0) { onSearch(searchText); } } // eslint-disable-next-line react-hooks/exhaustive-deps }, [disable, keyboardHeight, visible, _measure, data, searchText, onFocus, onBlur]); const onSearch = useCallback(text => { if (text.length > 0) { const defaultFilterFunction = e => { var _get2; const item = (_get2 = _get(e, searchField || labelField)) === null || _get2 === void 0 ? void 0 : _get2.toLowerCase().replace(/\s/g, '').normalize('NFD').replace(/[\u0300-\u036f]/g, ''); const key = text.toLowerCase().replace(/\s/g, '').normalize('NFD').replace(/[\u0300-\u036f]/g, ''); return item.indexOf(key) >= 0; }; const propSearchFunction = e => { const labelText = _get(e, searchField || labelField); return searchQuery === null || searchQuery === void 0 ? void 0 : searchQuery(text, labelText); }; const dataSearch = data.filter(searchQuery ? propSearchFunction : defaultFilterFunction); if (excludeSearchItems.length > 0 || excludeItems.length > 0) { const excludeSearchData = _differenceWith(dataSearch, excludeSearchItems, (obj1, obj2) => _get(obj1, valueField) === _get(obj2, valueField)); const filterData = excludeData(excludeSearchData); setListData(filterData); } else { setListData(dataSearch); } } else { const filterData = excludeData(data); setListData(filterData); } }, [data, searchQuery, excludeSearchItems, excludeItems, searchField, labelField, valueField, excludeData]); const onSelect = useCallback(item => { if (confirmSelectItem && onConfirmSelectItem) { return onConfirmSelectItem(item); } setCurrentValue(item); onChange(item); if (closeModalWhenSelectedItem) { if (onChangeText) { onChangeText(''); } setSearchText(''); onSearch(''); eventClose(); } }, [confirmSelectItem, eventClose, onChange, onChangeText, onConfirmSelectItem, onSearch, closeModalWhenSelectedItem]); const _renderDropdown = () => { const isSelected = currentValue && _get(currentValue, valueField); return /*#__PURE__*/React.createElement(TouchableWithoutFeedback, { testID: testID, accessible: !!accessibilityLabel, accessibilityLabel: accessibilityLabel, onPress: showOrClose }, /*#__PURE__*/React.createElement(View, { style: styles.dropdown }, renderLeftIcon === null || renderLeftIcon === void 0 ? void 0 : renderLeftIcon(visible), /*#__PURE__*/React.createElement(Text, _extends({ style: [styles.textItem, isSelected !== null ? selectedTextStyle : placeholderStyle, font()] }, selectedTextProps), isSelected !== null ? _get(currentValue, labelField) : placeholder), renderRightIcon ? renderRightIcon(visible) : /*#__PURE__*/React.createElement(Image, { source: ic_down, style: StyleSheet.flatten([styles.icon, { tintColor: iconColor }, iconStyle]) }))); }; const _renderItem = useCallback(_ref => { let { item, index } = _ref; const isSelected = currentValue && _get(currentValue, valueField); const selected = _isEqual(_get(item, valueField), isSelected); _assign(item, { _index: index }); return /*#__PURE__*/React.createElement(TouchableHighlight, { key: index.toString(), testID: _get(item, itemTestIDField || labelField), accessible: !!accessibilityLabel, accessibilityLabel: _get(item, itemAccessibilityLabelField || labelField), underlayColor: activeColor, onPress: () => onSelect(item) }, /*#__PURE__*/React.createElement(View, { style: StyleSheet.flatten([itemContainerStyle, selected && { backgroundColor: activeColor }]) }, renderItem ? renderItem(item, selected) : /*#__PURE__*/React.createElement(View, { style: styles.item }, /*#__PURE__*/React.createElement(Text, { style: StyleSheet.flatten([styles.textItem, itemTextStyle, font()]) }, _get(item, labelField))))); }, [accessibilityLabel, activeColor, currentValue, font, itemAccessibilityLabelField, itemContainerStyle, itemTestIDField, itemTextStyle, labelField, onSelect, renderItem, valueField]); const renderSearch = useCallback(() => { if (search) { if (renderInputSearch) { return renderInputSearch(text => { if (onChangeText) { setSearchText(text); onChangeText(text); } onSearch(text); }); } else { return /*#__PURE__*/React.createElement(CInput, { testID: testID + ' input', accessibilityLabel: accessibilityLabel + ' input', style: [styles.input, inputSearchStyle], inputStyle: [inputSearchStyle, font()], value: searchText, autoCorrect: false, placeholder: searchPlaceholder, onChangeText: e => { if (onChangeText) { setSearchText(e); onChangeText(e); } onSearch(e); }, placeholderTextColor: searchPlaceholderTextColor, showIcon: true, iconStyle: [{ tintColor: iconColor }, iconStyle] }); } } return null; }, [accessibilityLabel, font, iconColor, iconStyle, inputSearchStyle, onChangeText, onSearch, renderInputSearch, search, searchPlaceholder, searchPlaceholderTextColor, testID, searchText]); const _renderList = useCallback(isTopPosition => { const isInverted = !inverted ? false : isTopPosition; const _renderListHelper = () => { return /*#__PURE__*/React.createElement(FlatList, _extends({ testID: testID + ' flatlist', accessibilityLabel: accessibilityLabel + ' flatlist' }, flatListProps, { keyboardShouldPersistTaps: "handled", ref: refList, onContentSizeChange: scrollIndex, onScrollToIndexFailed: scrollIndex, data: listData, inverted: isTopPosition ? inverted : false, renderItem: _renderItem, keyExtractor: (_item, index) => index.toString(), showsVerticalScrollIndicator: showsVerticalScrollIndicator })); }; return /*#__PURE__*/React.createElement(TouchableWithoutFeedback, null, /*#__PURE__*/React.createElement(View, { style: styles.flexShrink }, isInverted && _renderListHelper(), renderSearch(), !isInverted && _renderListHelper())); }, [_renderItem, accessibilityLabel, flatListProps, listData, inverted, renderSearch, scrollIndex, showsVerticalScrollIndicator, testID]); const _renderModal = useCallback(() => { if (visible && position) { const { isFull, width, height, top, bottom, left } = position; const onAutoPosition = () => { if (keyboardHeight > 0) { return bottom < keyboardHeight + height; } return bottom < (search ? 150 : 100); }; if (width && top && bottom) { const styleVertical = { left: left, maxHeight: maxHeight, minHeight: minHeight }; const isTopPosition = dropdownPosition === 'auto' ? onAutoPosition() : dropdownPosition === 'top'; let keyboardStyle = {}; let extendHeight = !isTopPosition ? top : bottom; if (keyboardAvoiding && keyboardHeight > 0 && isTopPosition && dropdownPosition === 'auto') { extendHeight = keyboardHeight; } return /*#__PURE__*/React.createElement(Modal, { transparent: true, statusBarTranslucent: true, visible: visible, supportedOrientations: ['landscape', 'portrait'], onRequestClose: showOrClose }, /*#__PURE__*/React.createElement(TouchableWithoutFeedback, { onPress: showOrClose }, /*#__PURE__*/React.createElement(View, { style: StyleSheet.flatten([styles.flex1, isFull && styleContainerVertical, backgroundColor && { backgroundColor: backgroundColor }, keyboardStyle]) }, /*#__PURE__*/React.createElement(View, { style: StyleSheet.flatten([styles.flex1, !isTopPosition ? { paddingTop: extendHeight } : { justifyContent: 'flex-end', paddingBottom: extendHeight }, isFull && styles.fullScreen]) }, /*#__PURE__*/React.createElement(View, { style: StyleSheet.flatten([styles.container, isFull ? styleHorizontal : styleVertical, { width }, containerStyle]) }, _renderList(isTopPosition)))))); } return null; } return null; }, [visible, search, position, keyboardHeight, maxHeight, minHeight, dropdownPosition, keyboardAvoiding, showOrClose, styleContainerVertical, backgroundColor, containerStyle, styleHorizontal, _renderList]); return /*#__PURE__*/React.createElement(View, { style: StyleSheet.flatten([styles.mainWrap, style]), ref: ref, onLayout: _measure }, _renderDropdown(), _renderModal()); }); export default DropdownComponent; //# sourceMappingURL=index.js.map