UNPKG

@hashiprobr/react-native-paper-dropdown

Version:

A fork of Fateh Farooqui's react-native-paper-dropdown with additional configurability

154 lines (153 loc) 6.81 kB
import { ScrollView, View, } from "react-native"; import { Checkbox, Divider, Menu, TextInput, TouchableRipple, useTheme, } from "react-native-paper"; import React, { forwardRef, useState, useCallback, Fragment, } from "react"; const { useUpdate } = require("@hashiprobr/react-use-mount-and-update"); const DropDown = forwardRef((props, ref) => { const activeTheme = useTheme(); const { multiSelect = false, value, onChangeValue, activeColor, mode, label, placeholder, list, dropDownContainerMaxHeight, dropDownContainerHeight, theme, dropDownStyle, dropDownItemStyle, dropDownItemSelectedStyle, dropDownItemTextStyle, dropDownItemSelectedTextStyle, accessibilityLabel, disabled, onFocus = () => { }, onBlur = () => { }, style = {}, editable, borderless, background, centered, rippleColor, underlayColor, touchableStyle, error, selectionColor, underlineColor, activeUnderlineColor, outlineColor, activeOutlineColor, dense, iconColor, iconStyle, } = props; const [displayValue, setDisplayValue] = useState(""); const [inputLayout, setInputLayout] = useState({ height: 0, width: 0, x: 0, y: 0, }); const [visible, setVisible] = useState(false); const onDismiss = () => { setVisible(false); }; const showDropDown = () => { if (editable !== false) { setVisible(true); } }; const onLayout = (event) => { setInputLayout(event.nativeEvent.layout); }; useUpdate(() => { if (multiSelect) { const _labels = list .filter((_) => value.indexOf(_.value) !== -1) .map((_) => _.label) .join(", "); setDisplayValue(_labels); } else { const _label = list.find((_) => _.value === value)?.label; if (_label) { setDisplayValue(_label); } } }, [list, value]); const isActive = useCallback((currentValue) => { if (multiSelect) { return value.indexOf(currentValue) !== -1; } else { return value === currentValue; } }, [value]); const setActive = useCallback((currentValue) => { if (multiSelect) { const valueIndex = value.indexOf(currentValue); const values = value.split(","); if (valueIndex === -1) { onChangeValue([...values, currentValue].join(",")); } else { onChangeValue([...values].filter((value) => value !== currentValue).join(",")); } } else { onChangeValue(currentValue); } }, [value]); return (<Menu visible={visible} onDismiss={onDismiss} theme={theme} anchor={<TouchableRipple ref={ref} onPress={showDropDown} onLayout={onLayout} accessibilityLabel={accessibilityLabel} onFocus={onFocus} onBlur={onBlur} borderless={borderless} background={background} centered={centered} disabled={disabled} rippleColor={rippleColor} underlayColor={underlayColor} style={{ ...touchableStyle, flexGrow: 1, flexDirection: 'column', flexWrap: 'nowrap', justifyContent: 'flex-start', alignItems: 'stretch', margin: style.margin, marginTop: style.marginTop, marginRight: style.marginRight, marginBottom: style.marginBottom, marginLeft: style.marginLeft, padding: 0, paddingTop: 0, paddingRight: 0, paddingBottom: 0, paddingLeft: 0, overflow: 'visible', }} theme={theme}> <View pointerEvents={"none"} style={{ flexGrow: 1, }}> <TextInput value={displayValue} mode={mode} label={label} placeholder={placeholder} pointerEvents={"none"} theme={theme} right={<TextInput.Icon name={visible ? "menu-up" : "menu-down"} disabled={disabled} forceTextInputFocus={false} color={iconColor} style={iconStyle} theme={theme}/>} disabled={disabled} error={error} selectionColor={selectionColor} underlineColor={underlineColor} activeUnderlineColor={activeUnderlineColor} outlineColor={outlineColor} activeOutlineColor={activeOutlineColor} dense={dense} multiline={false} numberOfLines={1} style={{ ...style, flexGrow: 1, alignSelf: 'stretch', margin: 0, marginTop: 0, marginRight: 0, marginBottom: 0, marginLeft: 0, }} editable={false}/> </View> </TouchableRipple>} style={{ maxWidth: inputLayout?.width, width: inputLayout?.width, marginTop: inputLayout?.height, ...dropDownStyle, }}> <ScrollView bounces={false} style={{ ...(dropDownContainerHeight ? { height: dropDownContainerHeight, } : { maxHeight: dropDownContainerMaxHeight || 200, }), }}> {list.map((_item, _index) => (<Fragment key={_item.value}> <TouchableRipple style={{ flexDirection: "row", alignItems: "center", }} onPress={() => { setActive(_item.value); if (onDismiss) { onDismiss(); } }}> <Fragment> <Menu.Item titleStyle={{ color: isActive(_item.value) ? activeColor || (theme || activeTheme).colors.primary : (theme || activeTheme).colors.text, ...(isActive(_item.value) ? dropDownItemSelectedTextStyle : dropDownItemTextStyle), }} title={_item.custom || _item.label} style={{ flex: 1, maxWidth: inputLayout?.width, ...(isActive(_item.value) ? dropDownItemSelectedStyle : dropDownItemStyle), }} onPress={() => { setActive(_item.value); if (onDismiss) { onDismiss(); } }}/> {multiSelect && (<Checkbox.Android theme={{ colors: { accent: activeTheme?.colors.primary }, }} status={isActive(_item.value) ? "checked" : "unchecked"} onPress={() => setActive(_item.value)}/>)} </Fragment> </TouchableRipple> <Divider /> </Fragment>))} </ScrollView> </Menu>); }); export default DropDown;