UNPKG

react-native-modal-filter-picker

Version:

Cross-platform modal picker for React Native which supports keyword filtering, custom rendering, etc

277 lines (249 loc) 6.85 kB
/* eslint import/extensions:0 */ /* eslint import/no-unresolved:0 */ import React, { Component } from 'react' import PropTypes from 'prop-types' import { Modal, View, FlatList, TouchableOpacity, Text, TextInput, KeyboardAvoidingView, Platform, SafeAreaView } from 'react-native' import styles from "./styles" export default class ModalFilterPicker extends Component { static keyExtractor(item, index) { return index } constructor(props, ctx) { super(props, ctx) this.state = { filter: "", ds: props.options } } componentWillReceiveProps(newProps) { if ( (!this.props.visible && newProps.visible) || this.props.options !== newProps.options ) { this.setState({ filter: "", ds: newProps.options }) } } render() { const { title, titleTextStyle, overlayStyle, cancelContainerStyle, renderList, renderCancelButton, modal, visible, onCancel } = this.props const renderedTitle = !title ? null : ( <Text style={titleTextStyle || styles.titleTextStyle}>{title}</Text> ) return ( <Modal onRequestClose={onCancel} {...modal} visible={visible} supportedOrientations={['portrait', 'landscape']} > <KeyboardAvoidingView behavior="padding" style={overlayStyle || styles.overlay} enabled={Platform.OS === 'ios'} > <SafeAreaView style={{ flex: 1 }}> <View>{renderedTitle}</View> {(renderList || this.renderList)()} <View style={cancelContainerStyle || styles.cancelContainer}> {(renderCancelButton || this.renderCancelButton)()} </View> </SafeAreaView> </KeyboardAvoidingView> </Modal> ) } renderList = () => { const { showFilter, autoFocus, listContainerStyle, androidUnderlineColor, placeholderText, placeholderTextColor, filterTextInputContainerStyle, filterTextInputStyle } = this.props const filter = !showFilter ? null : ( <View style={filterTextInputContainerStyle || styles.filterTextInputContainer} > <TextInput onChangeText={this.onFilterChange} autoCorrect={false} blurOnSubmit={true} autoFocus={autoFocus} autoCapitalize="none" underlineColorAndroid={androidUnderlineColor} placeholderTextColor={placeholderTextColor} placeholder={placeholderText} style={filterTextInputStyle || styles.filterTextInput} /> </View> ) return ( <View style={listContainerStyle || styles.listContainer}> {filter} {this.renderOptionList()} </View> ) } renderOptionList = () => { const { noResultsText, flatListProps, keyExtractor, keyboardShouldPersistTaps } = this.props const { ds } = this.state if (!ds.length) { return ( <FlatList data={ds} keyExtractor={keyExtractor || this.constructor.keyExtractor} {...flatListProps} renderItem={() => ( <View style={styles.noResults}> <Text style={styles.noResultsText}>{noResultsText}</Text> </View> )} /> ) } return ( <FlatList keyExtractor={keyExtractor || this.constructor.keyExtractor} {...flatListProps} data={ds} renderItem={this.renderOption} keyboardShouldPersistTaps={keyboardShouldPersistTaps} /> ) } renderOption = ({ item }) => { const { selectedOption, renderOption, optionTextStyle, selectedOptionTextStyle } = this.props const { key, label } = item let style = styles.optionStyle let textStyle = optionTextStyle || styles.optionTextStyle if (key === selectedOption) { style = styles.selectedOptionStyle textStyle = selectedOptionTextStyle || styles.selectedOptionTextStyle } if (renderOption) { return renderOption(item, key === selectedOption) } return ( <TouchableOpacity activeOpacity={0.7} style={style} onPress={() => this.props.onSelect(item)} > <Text style={textStyle}>{label}</Text> </TouchableOpacity> ) } renderCancelButton = () => { const { cancelButtonStyle, cancelButtonTextStyle, cancelButtonText } = this.props return ( <TouchableOpacity onPress={this.props.onCancel} activeOpacity={0.7} style={cancelButtonStyle || styles.cancelButton} > <Text style={cancelButtonTextStyle || styles.cancelButtonText}> {cancelButtonText} </Text> </TouchableOpacity> ) } onFilterChange = (text) => { const { options } = this.props const filter = text.toLowerCase() // apply filter to incoming data const filtered = !filter.length ? options : options.filter( ({ searchKey, label }) => label.toLowerCase().indexOf(filter) >= 0 || (searchKey && searchKey.toLowerCase().indexOf(filter) >= 0) ) /* eslint react/no-unused-state:0 */ this.setState({ filter: text.toLowerCase(), ds: filtered }) } } /* eslint react/forbid-prop-types:0 */ ModalFilterPicker.propTypes = { options: PropTypes.array.isRequired, onSelect: PropTypes.func.isRequired, onCancel: PropTypes.func.isRequired, placeholderText: PropTypes.string, placeholderTextColor: PropTypes.string, androidUnderlineColor: PropTypes.string, cancelButtonText: PropTypes.string, title: PropTypes.string, noResultsText: PropTypes.string, visible: PropTypes.bool, showFilter: PropTypes.bool, modal: PropTypes.object, selectedOption: PropTypes.string, renderOption: PropTypes.func, renderCancelButton: PropTypes.func, renderList: PropTypes.func, flatListProps: PropTypes.object, filterTextInputContainerStyle: PropTypes.any, filterTextInputStyle: PropTypes.any, cancelContainerStyle: PropTypes.any, cancelButtonStyle: PropTypes.any, cancelButtonTextStyle: PropTypes.any, titleTextStyle: PropTypes.any, overlayStyle: PropTypes.any, listContainerStyle: PropTypes.any, optionTextStyle:PropTypes.any, selectedOptionTextStyle:PropTypes.any, keyExtractor: PropTypes.any, autoFocus: PropTypes.any, keyboardShouldPersistTaps: PropTypes.string } ModalFilterPicker.defaultProps = { placeholderText: "Filter...", placeholderTextColor: "#ccc", androidUnderlineColor: "rgba(0,0,0,0)", cancelButtonText: "Cancel", noResultsText: "No matches", visible: true, showFilter: true, keyboardShouldPersistTaps: 'never' }