From e8afe45516709ee8a4dcc47a6facacf76c188d7b Mon Sep 17 00:00:00 2001 From: Nikhil Srivastava Date: Wed, 3 Apr 2019 17:19:26 +0530 Subject: [PATCH 1/2] add: RN Virtualised list :heavy_plus_sign: --- package.json | 14 +- src/AutoComplete/AutoComplete/index.css | 3 + src/AutoComplete/AutoComplete/index.js | 252 ++++++++++++++++++++++++ src/AutoComplete/index.js | 243 ++++++++++++++++------- 4 files changed, 433 insertions(+), 79 deletions(-) create mode 100644 src/AutoComplete/AutoComplete/index.css create mode 100644 src/AutoComplete/AutoComplete/index.js diff --git a/package.json b/package.json index 2a66fc1..44dd857 100644 --- a/package.json +++ b/package.json @@ -5,14 +5,17 @@ "main": "dist", "dependencies": { "babel-runtime": "^6.23.0", + "lodash": "^4.17.11", "moment": "^2.18.1", + "npm": "^6.9.0", "prop-types": "^15.5.8", - "react": "15.x.x", - "react-native": ">=0.40.0", + "react": "^16.6.x", + "react-dom": "^16.6.x", + "react-native": "0.55.x", "react-native-elements": "^0.11.2", "react-native-linear-gradient": "^2.0.0", "react-native-looped-carousel": "^0.1.7", - "react-native-vector-icons": "^4.0.0", + "react-native-vector-icons": "^6.2.0", "react-slick": "^0.14.11", "slick-carousel": "^1.6.0" }, @@ -23,12 +26,9 @@ "babel-plugin-transform-runtime": "^6.23.0", "babel-preset-react-native": "^1.9.1", "json-loader": "^0.5.4", - "react": "15.x.x", - "react-dom": "15.x.x", - "react-native-web": "0.0.94" + "react-native-web": "^0.9.x" }, "scripts": { - "prepublish": ". ./scripts/transpile.sh", "test": "echo \"Error: no test specified\" && exit 1" }, "author": "", diff --git a/src/AutoComplete/AutoComplete/index.css b/src/AutoComplete/AutoComplete/index.css new file mode 100644 index 0000000..21e0836 --- /dev/null +++ b/src/AutoComplete/AutoComplete/index.css @@ -0,0 +1,3 @@ +.active{ + background-color: rgba(1, 140, 207, 0.46); +} \ No newline at end of file diff --git a/src/AutoComplete/AutoComplete/index.js b/src/AutoComplete/AutoComplete/index.js new file mode 100644 index 0000000..df23af9 --- /dev/null +++ b/src/AutoComplete/AutoComplete/index.js @@ -0,0 +1,252 @@ +import React from 'react'; +import PropTypes from 'prop-types'; +import _ from 'lodash' +// import './index.css'; +import { + TouchableHighlight, + ScrollView, + Keyboard, + VirtualizedList, + Text, + TextInput, + TouchableOpacity, + View +} from 'react-native'; + +const inbuiltStyles = { + container: { + width: '100%', + + } +}; + +export default class AutoComplete extends React.Component { + constructor(props) { + super(props); + this.state = { + cursor: 0, + counter: 0, + allData: props.list, + fullData: props.list, + data: props.list, + query: '', + showList: false, + }; + } + + + //////////////////////////////////////////////////// + handleSearch = (text) => { + + const data = _.filter(this.state.fullData, user => { + return this.containsQuery(user, text); + }); + this.setState({query: text, value: text, data, showList: true}) + }; + + containsQuery = ({name}, query) => { + if (name.toLowerCase().includes(query.toLowerCase())) { + return true; + } else { + return false; + } + }; + ///////////////////////////////////////////////////// + + + // arrow up/down button should select next/previous list element and enter key press should simulate item selection + handleKeyDown(e) { + e.persist(); + const {cursor, data} = this.state + if (e.keyCode === 38 && cursor > 0) { + let counter = this.state.counter + --counter; + let c = this.state.cursor; + --c; + this.setState({cursor: c, counter}, () => this.handleScroll('up')) + } else if (e.keyCode === 40 && cursor <= data.length - 1) { + let counter = this.state.counter + ++counter; + let c = this.state.cursor; + ++c; + this.setState({cursor: c, counter}, () => this.handleScroll('down')) + } else { + if (e.key === 'Enter') + { + + if (this.state.cursor !== 0) { + console.log('VTlist',this.VTListRef) + console.log('he') + /*const eventItemName = e.target.nextElementSibling.children[0].children[0].getElementsByClassName('active')[0].children[0].innerText + this.setState({ + showList: false, + }, () => { + this.props.list.map(item => { + if (item[this.props.searchQuery] === eventItemName) { + if (this.props.onItemSelected) { + this.props.onItemSelected(item); + this.setState({value: item[this.props.searchQuery]}) + } + } + }) + });*/ + } + } + } + } + + handleScroll = (scrollType) => { + if (scrollType === 'down') { + if (this.state.counter % Math.floor(this.state.listContainerHeight / this.props.listItemStyle.height) === 0 && this.state.cursor > 0) + this.VTListRef.scrollToIndex({index: this.state.cursor - 1}) + } else if (this.state.cursor > 0) + this.VTListRef.scrollToIndex({index: this.state.cursor - 1}) + } + + + render() { + let index = 0; + const {cursor} = this.state + const propStyles = this.props; + return ( + + + { + this.setState({ + showList: false, + cursor: 0, + counter: 0 + }) + }} + onChangeText={value => this.handleSearch(value)} + onKeyPress={(e) => this.handleKeyDown(e)} + /> + + {this.state.showList ? + { + let {height} = event.nativeEvent.layout + this.setState({listContainerHeight: height}) + }} + style={propStyles.listContainerStyle}> + data.length} + ref={(ref) => { + this.VTListRef = ref; + }} + keyboardShouldPersistTaps='always' + getItem={(data, index) => data[index]} + keyExtractor={(item, index) => item.name} + renderItem={ + (rowData, key) => ( + { + let {height} = event.nativeEvent.layout + this.setState({listItemHeight: height}) + }} + style={{backgroundColor:cursor===index?'rgba(1, 140, 207, 0.46)':null}} + > + { + this.setState({ + showList: false, + query: rowData.item, + value: rowData.item[this.props.searchQuery] + }, () => { + Keyboard.dismiss() + if (this.props.onItemSelected) + this.props.onItemSelected(this.state.query); + }); + }}> + + {rowData.item[this.props.searchQuery]} + + + + ) + } + /> + + : null + } + + ) + } + +} + +AutoComplete.defaultProps = { + ignoreCase: false, + listContainerStyle: { + zIndex: 1, + height:300, + position: 'absolute', + marginTop: 40, + backgroundColor: 'transparent', + width: 200 + }, + autoCompleteInputStyle: { + width: 200, + height: 40, + }, + listItemStyle: { + height: 40, + justifyContent: 'center', + borderWidth: 2, + borderStyle: 'solid', + borderColor: '#e0e0e0', + borderTopWidth: 0 + }, + searchQuery: 'name', + list: [{ + name: 'React', + email: 'react@gmail.com' + }, + { + name: 'Redux', + email: 'Redux@gmail.com' + }, + { + name: 'CSS', + email: 'css3@gmail.com' + }, + { + name: "React Native", + email: 'RN@gmail.com' + }, + { + name: 'GraphQL', + email: 'GraphQL@gmail.com' + }, + { + name: 'python', + email: 'python@gmail.com' + }, + { + name: 'django', + email: 'django@gmail.com' + }, + { + name: 'JavaScript', + email: 'javascript@gmail.com' + }, + { + name: 'Java', + email: 'oracle@gmail.com' + } + ] +}; + +AutoComplete.propTypes = { + placeholder: PropTypes.string, + searchQuery: PropTypes.string, + listContainerStyle: PropTypes.object, + listItemStyle: PropTypes.object, + autoCompleteInputStyle: PropTypes.object, + list: PropTypes.array.isRequired, + ignoreCase: PropTypes.bool, + onItemSelected: PropTypes.func, +}; diff --git a/src/AutoComplete/index.js b/src/AutoComplete/index.js index 5abd8bc..026f342 100644 --- a/src/AutoComplete/index.js +++ b/src/AutoComplete/index.js @@ -1,114 +1,213 @@ import React from 'react'; import PropTypes from 'prop-types'; -import { ListView, Text, TextInput, TouchableOpacity, View } from 'react-native'; +import _ from 'lodash' +import {VirtualizedList, Text, TextInput, TouchableOpacity, View} from 'react-native'; -const styles = { +const inbuiltStyles = { container: { - width: 200, - }, - autoCompleteInput: { - width: 200, - height: 40, - }, - listContainer: { - zIndex: 1, - position: 'absolute', - marginTop: 40, - backgroundColor: '#FFF', - width: 'inherit' - }, - listItem: { - height: 40, - justifyContent: 'center', - borderWidth: 2, - borderStyle: 'solid', - borderColor: '#e0e0e0', - borderTopWidth: 0, + // width: '100%', }, + }; export default class AutoComplete extends React.Component { constructor(props) { super(props); - - const ds = new ListView.DataSource({rowHasChanged: (r1, r2) => r1 !== r2}); this.state = { + cursor: 0, + counter: 0, allData: props.list, - dataSource: ds.cloneWithRows([]), + fullData: props.list, + data: props.list, query: '', showList: false, }; } + + handleSearch = (text) => { + const data = _.filter(this.state.fullData, user => { + return this.containsQuery(user, text); + }); + this.setState({query: text, value: text, data, showList: true}) + }; + + containsQuery = ({name}, query) => { + if (name.toLowerCase().includes(query)) + return true; + else + return false; + }; + + + // arrow up/down button should select next/previous list element and enter key press should simulate item selection + handleKeyDown(e) { + e.persist(); + const {cursor, data} = this.state + if (e.keyCode === 38 && cursor > 0) { + let counter = this.state.counter + --counter; + let c = this.state.cursor; + --c; + this.setState({cursor: c, counter}, () => this.handleScroll('up')) + } else if (e.keyCode === 40 && cursor <= data.length - 1) { + let counter = this.state.counter + ++counter; + let c = this.state.cursor; + ++c; + this.setState({cursor: c, counter}, () => this.handleScroll('down')) + } else { + if (e.key === 'Enter') { + const eventItemName = e.target.nextElementSibling.children[0].children[0].getElementsByClassName('active')[0].children[0].innerText + this.setState({ + showList: false, + }, () => { + this.props.list.map(item => { + if (item[this.props.searchQuery] === eventItemName) { + if (this.props.onItemSelected) { + this.props.onItemSelected(item); + this.setState({value:item[this.props.searchQuery]}) + } + } + }) + }); + } + } + } + + handleScroll = (scroll) => { + if (scroll === 'down') { + if (this.state.counter % Math.floor(this.props.listContainerStyle.height / this.props.listItemStyle.height) === 0 && this.state.cursor > 0) + this.VTListRef.scrollToIndex({index: this.state.cursor - 1}) + } else if (this.state.cursor > 0) + this.VTListRef.scrollToIndex({index: this.state.cursor - 1}) + } + + render() { + let index = 0; + const {cursor} = this.state + const propStyles = this.props; return ( - - + this.setState({showList: false})} - onChangeText={this.onQueryChange}/> + value={this.state.value} + onBlur={() => this.setState({showList: false, cursor: 0, counter: 0})} + onChangeText={value => this.handleSearch(value)} + onKeyPress={(e) => this.handleKeyDown(e)} + + /> {this.state.showList && - - ( - { - this.setState({ - showList: false, query: rowData - }, () => { - if (this.props.onItemSelected) - this.props.onItemSelected(this.state.query); - }); - }}> - - {rowData} - - + + data.length} + ref={(ref) => { + this.VTListRef = ref; + }} + getItem={(data, index) => data[index]} + renderItem={ + (rowData, key) => ( + + { + this.setState({ + showList: false, query: rowData + }, () => { + if (this.props.onItemSelected) + this.props.onItemSelected(this.state.query); + }); + }}> + + {rowData.item[this.props.searchQuery]} + + + ) } /> + } ) } - onQueryChange = (value) => { - this.setState({query: value}); - this.filter(value); - }; - - filter = (query) => { - query = query.trim(); - let filteredData = this.state.allData.filter((item) => { - if (this.props.ignoreCase) - return item.toLowerCase().includes(query.toLowerCase()); - - return item.includes(query); - }); - - const ds = new ListView.DataSource({rowHasChanged: (r1, r2) => r1 !== r2}); - this.setState({ - dataSource: ds.cloneWithRows(filteredData), - showList: true - } - ); - }; } AutoComplete.defaultProps = { ignoreCase: false, + listContainerStyle: { + zIndex: 1, + height: 300, + position: 'absolute', + marginTop: 40, + backgroundColor: '#FFF', + width: 200 + }, + autoCompleteInputStyle: { + width: 200, + height: 40, + }, + listItemStyle: { + height: 40, + justifyContent: 'center', + borderWidth: 2, + borderStyle: 'solid', + borderColor: '#e0e0e0', + borderTopWidth: 0 + }, + searchQuery:'name', + list: [{ + name: 'React', + email:'react@gmail.com' + }, + { + name: 'Redux', + email:'Redux@gmail.com' + }, + { + name: 'CSS', + email:'css3@gmail.com' + }, + { + name: "React Native", + email:'RN@gmail.com' + }, + { + name: 'GraphQL', + email:'GraphQL@gmail.com' + }, + { + name: 'python', + email:'python@gmail.com' + }, + { + name: 'django', + email:'django@gmail.com' + }, + { + name: 'JavaScript', + email:'javascript@gmail.com' + }, + { + name: 'Java', + email:'oracle@gmail.com' + } + ] }; AutoComplete.propTypes = { placeholder: PropTypes.string, + searchQuery:PropTypes.string, + listContainerStyle: PropTypes.object, + listItemStyle: PropTypes.object, + autoCompleteInputStyle: PropTypes.object, list: PropTypes.array.isRequired, ignoreCase: PropTypes.bool, onItemSelected: PropTypes.func, From f23122532a64c4090292ee62154b34e932ea3a29 Mon Sep 17 00:00:00 2001 From: Nikhil Srivastava Date: Wed, 3 Apr 2019 20:29:58 +0530 Subject: [PATCH 2/2] fix: minor bug fixes and Platform support --- src/AutoComplete/AutoComplete/index.css | 3 - .../index.js => index.android.js} | 107 ++++---- src/AutoComplete/index.ios.js | 247 ++++++++++++++++++ src/AutoComplete/index.js | 197 ++++++++------ 4 files changed, 413 insertions(+), 141 deletions(-) delete mode 100644 src/AutoComplete/AutoComplete/index.css rename src/AutoComplete/{AutoComplete/index.js => index.android.js} (71%) create mode 100644 src/AutoComplete/index.ios.js diff --git a/src/AutoComplete/AutoComplete/index.css b/src/AutoComplete/AutoComplete/index.css deleted file mode 100644 index 21e0836..0000000 --- a/src/AutoComplete/AutoComplete/index.css +++ /dev/null @@ -1,3 +0,0 @@ -.active{ - background-color: rgba(1, 140, 207, 0.46); -} \ No newline at end of file diff --git a/src/AutoComplete/AutoComplete/index.js b/src/AutoComplete/index.android.js similarity index 71% rename from src/AutoComplete/AutoComplete/index.js rename to src/AutoComplete/index.android.js index df23af9..b88b9d0 100644 --- a/src/AutoComplete/AutoComplete/index.js +++ b/src/AutoComplete/index.android.js @@ -1,24 +1,28 @@ import React from 'react'; import PropTypes from 'prop-types'; import _ from 'lodash' -// import './index.css'; import { - TouchableHighlight, - ScrollView, Keyboard, VirtualizedList, Text, + Platform, + StyleSheet, TextInput, TouchableOpacity, View } from 'react-native'; -const inbuiltStyles = { +const inbuiltStyles = StyleSheet.create({ container: { width: '100%', - + ...Platform.select({ + web: { + zIndex: 999, + } + }) + // flexDirection:'row' } -}; +}); export default class AutoComplete extends React.Component { constructor(props) { @@ -37,7 +41,6 @@ export default class AutoComplete extends React.Component { //////////////////////////////////////////////////// handleSearch = (text) => { - const data = _.filter(this.state.fullData, user => { return this.containsQuery(user, text); }); @@ -71,12 +74,14 @@ export default class AutoComplete extends React.Component { ++c; this.setState({cursor: c, counter}, () => this.handleScroll('down')) } else { - if (e.key === 'Enter') - { - + if (e.key === 'Enter') { if (this.state.cursor !== 0) { - console.log('VTlist',this.VTListRef) - console.log('he') + if (this.props.onItemSelected) + { + this.props.onItemSelected(this.VTListRef.props.data[--this.state.cursor]) + this.setState({value: this.VTListRef.props.data[this.state.cursor][this.props.searchQuery]}) + } + /*const eventItemName = e.target.nextElementSibling.children[0].children[0].getElementsByClassName('active')[0].children[0].innerText this.setState({ showList: false, @@ -110,46 +115,56 @@ export default class AutoComplete extends React.Component { const propStyles = this.props; return ( - - { - this.setState({ - showList: false, - cursor: 0, - counter: 0 - }) - }} - onChangeText={value => this.handleSearch(value)} - onKeyPress={(e) => this.handleKeyDown(e)} - /> - + { + this.setState({ + showList: false, + cursor: 0, + counter: 0 + }) + }} + onChangeText={value => this.handleSearch(value)} + onKeyPress={(e) => this.handleKeyDown(e)} + /> {this.state.showList ? { let {height} = event.nativeEvent.layout this.setState({listContainerHeight: height}) }} - style={propStyles.listContainerStyle}> + style={[{position: 'absolute', zIndex: 999, marginTop: 40, backgroundColor: '#fff'} + , propStyles.listContainerStyle]}> data.length} ref={(ref) => { this.VTListRef = ref; }} - keyboardShouldPersistTaps='always' + keyboardShouldPersistTaps='handled' getItem={(data, index) => data[index]} keyExtractor={(item, index) => item.name} renderItem={ (rowData, key) => ( - { - let {height} = event.nativeEvent.layout - this.setState({listItemHeight: height}) - }} - style={{backgroundColor:cursor===index?'rgba(1, 140, 207, 0.46)':null}} - > + { + let {height} = event.nativeEvent.layout + this.setState({listItemHeight: height}) + }} + style={{backgroundColor: cursor === index ? 'rgba(1, 140, 207, 0.46)' : null}}> { this.setState({ showList: false, @@ -180,26 +195,6 @@ export default class AutoComplete extends React.Component { AutoComplete.defaultProps = { ignoreCase: false, - listContainerStyle: { - zIndex: 1, - height:300, - position: 'absolute', - marginTop: 40, - backgroundColor: 'transparent', - width: 200 - }, - autoCompleteInputStyle: { - width: 200, - height: 40, - }, - listItemStyle: { - height: 40, - justifyContent: 'center', - borderWidth: 2, - borderStyle: 'solid', - borderColor: '#e0e0e0', - borderTopWidth: 0 - }, searchQuery: 'name', list: [{ name: 'React', diff --git a/src/AutoComplete/index.ios.js b/src/AutoComplete/index.ios.js new file mode 100644 index 0000000..b88b9d0 --- /dev/null +++ b/src/AutoComplete/index.ios.js @@ -0,0 +1,247 @@ +import React from 'react'; +import PropTypes from 'prop-types'; +import _ from 'lodash' +import { + Keyboard, + VirtualizedList, + Text, + Platform, + StyleSheet, + TextInput, + TouchableOpacity, + View +} from 'react-native'; + +const inbuiltStyles = StyleSheet.create({ + container: { + width: '100%', + ...Platform.select({ + web: { + zIndex: 999, + } + }) + // flexDirection:'row' + } +}); + +export default class AutoComplete extends React.Component { + constructor(props) { + super(props); + this.state = { + cursor: 0, + counter: 0, + allData: props.list, + fullData: props.list, + data: props.list, + query: '', + showList: false, + }; + } + + + //////////////////////////////////////////////////// + handleSearch = (text) => { + const data = _.filter(this.state.fullData, user => { + return this.containsQuery(user, text); + }); + this.setState({query: text, value: text, data, showList: true}) + }; + + containsQuery = ({name}, query) => { + if (name.toLowerCase().includes(query.toLowerCase())) { + return true; + } else { + return false; + } + }; + ///////////////////////////////////////////////////// + + + // arrow up/down button should select next/previous list element and enter key press should simulate item selection + handleKeyDown(e) { + e.persist(); + const {cursor, data} = this.state + if (e.keyCode === 38 && cursor > 0) { + let counter = this.state.counter + --counter; + let c = this.state.cursor; + --c; + this.setState({cursor: c, counter}, () => this.handleScroll('up')) + } else if (e.keyCode === 40 && cursor <= data.length - 1) { + let counter = this.state.counter + ++counter; + let c = this.state.cursor; + ++c; + this.setState({cursor: c, counter}, () => this.handleScroll('down')) + } else { + if (e.key === 'Enter') { + if (this.state.cursor !== 0) { + if (this.props.onItemSelected) + { + this.props.onItemSelected(this.VTListRef.props.data[--this.state.cursor]) + this.setState({value: this.VTListRef.props.data[this.state.cursor][this.props.searchQuery]}) + } + + /*const eventItemName = e.target.nextElementSibling.children[0].children[0].getElementsByClassName('active')[0].children[0].innerText + this.setState({ + showList: false, + }, () => { + this.props.list.map(item => { + if (item[this.props.searchQuery] === eventItemName) { + if (this.props.onItemSelected) { + this.props.onItemSelected(item); + this.setState({value: item[this.props.searchQuery]}) + } + } + }) + });*/ + } + } + } + } + + handleScroll = (scrollType) => { + if (scrollType === 'down') { + if (this.state.counter % Math.floor(this.state.listContainerHeight / this.props.listItemStyle.height) === 0 && this.state.cursor > 0) + this.VTListRef.scrollToIndex({index: this.state.cursor - 1}) + } else if (this.state.cursor > 0) + this.VTListRef.scrollToIndex({index: this.state.cursor - 1}) + } + + + render() { + let index = 0; + const {cursor} = this.state + const propStyles = this.props; + return ( + + { + this.setState({ + showList: false, + cursor: 0, + counter: 0 + }) + }} + onChangeText={value => this.handleSearch(value)} + onKeyPress={(e) => this.handleKeyDown(e)} + /> + {this.state.showList ? + { + let {height} = event.nativeEvent.layout + this.setState({listContainerHeight: height}) + }} + style={[{position: 'absolute', zIndex: 999, marginTop: 40, backgroundColor: '#fff'} + , propStyles.listContainerStyle]}> + data.length} + ref={(ref) => { + this.VTListRef = ref; + }} + keyboardShouldPersistTaps='handled' + getItem={(data, index) => data[index]} + keyExtractor={(item, index) => item.name} + renderItem={ + (rowData, key) => ( + { + let {height} = event.nativeEvent.layout + this.setState({listItemHeight: height}) + }} + style={{backgroundColor: cursor === index ? 'rgba(1, 140, 207, 0.46)' : null}}> + { + this.setState({ + showList: false, + query: rowData.item, + value: rowData.item[this.props.searchQuery] + }, () => { + Keyboard.dismiss() + if (this.props.onItemSelected) + this.props.onItemSelected(this.state.query); + }); + }}> + + {rowData.item[this.props.searchQuery]} + + + + ) + } + /> + + : null + } + + ) + } + +} + +AutoComplete.defaultProps = { + ignoreCase: false, + searchQuery: 'name', + list: [{ + name: 'React', + email: 'react@gmail.com' + }, + { + name: 'Redux', + email: 'Redux@gmail.com' + }, + { + name: 'CSS', + email: 'css3@gmail.com' + }, + { + name: "React Native", + email: 'RN@gmail.com' + }, + { + name: 'GraphQL', + email: 'GraphQL@gmail.com' + }, + { + name: 'python', + email: 'python@gmail.com' + }, + { + name: 'django', + email: 'django@gmail.com' + }, + { + name: 'JavaScript', + email: 'javascript@gmail.com' + }, + { + name: 'Java', + email: 'oracle@gmail.com' + } + ] +}; + +AutoComplete.propTypes = { + placeholder: PropTypes.string, + searchQuery: PropTypes.string, + listContainerStyle: PropTypes.object, + listItemStyle: PropTypes.object, + autoCompleteInputStyle: PropTypes.object, + list: PropTypes.array.isRequired, + ignoreCase: PropTypes.bool, + onItemSelected: PropTypes.func, +}; diff --git a/src/AutoComplete/index.js b/src/AutoComplete/index.js index 026f342..b88b9d0 100644 --- a/src/AutoComplete/index.js +++ b/src/AutoComplete/index.js @@ -1,14 +1,28 @@ import React from 'react'; import PropTypes from 'prop-types'; import _ from 'lodash' -import {VirtualizedList, Text, TextInput, TouchableOpacity, View} from 'react-native'; - -const inbuiltStyles = { +import { + Keyboard, + VirtualizedList, + Text, + Platform, + StyleSheet, + TextInput, + TouchableOpacity, + View +} from 'react-native'; + +const inbuiltStyles = StyleSheet.create({ container: { - // width: '100%', - }, - -}; + width: '100%', + ...Platform.select({ + web: { + zIndex: 999, + } + }) + // flexDirection:'row' + } +}); export default class AutoComplete extends React.Component { constructor(props) { @@ -25,6 +39,7 @@ export default class AutoComplete extends React.Component { } + //////////////////////////////////////////////////// handleSearch = (text) => { const data = _.filter(this.state.fullData, user => { return this.containsQuery(user, text); @@ -33,11 +48,13 @@ export default class AutoComplete extends React.Component { }; containsQuery = ({name}, query) => { - if (name.toLowerCase().includes(query)) + if (name.toLowerCase().includes(query.toLowerCase())) { return true; - else + } else { return false; + } }; + ///////////////////////////////////////////////////// // arrow up/down button should select next/previous list element and enter key press should simulate item selection @@ -58,26 +75,34 @@ export default class AutoComplete extends React.Component { this.setState({cursor: c, counter}, () => this.handleScroll('down')) } else { if (e.key === 'Enter') { - const eventItemName = e.target.nextElementSibling.children[0].children[0].getElementsByClassName('active')[0].children[0].innerText - this.setState({ - showList: false, - }, () => { - this.props.list.map(item => { + if (this.state.cursor !== 0) { + if (this.props.onItemSelected) + { + this.props.onItemSelected(this.VTListRef.props.data[--this.state.cursor]) + this.setState({value: this.VTListRef.props.data[this.state.cursor][this.props.searchQuery]}) + } + + /*const eventItemName = e.target.nextElementSibling.children[0].children[0].getElementsByClassName('active')[0].children[0].innerText + this.setState({ + showList: false, + }, () => { + this.props.list.map(item => { if (item[this.props.searchQuery] === eventItemName) { if (this.props.onItemSelected) { this.props.onItemSelected(item); - this.setState({value:item[this.props.searchQuery]}) + this.setState({value: item[this.props.searchQuery]}) } } }) - }); + });*/ + } } } } - handleScroll = (scroll) => { - if (scroll === 'down') { - if (this.state.counter % Math.floor(this.props.listContainerStyle.height / this.props.listItemStyle.height) === 0 && this.state.cursor > 0) + handleScroll = (scrollType) => { + if (scrollType === 'down') { + if (this.state.counter % Math.floor(this.state.listContainerHeight / this.props.listItemStyle.height) === 0 && this.state.cursor > 0) this.VTListRef.scrollToIndex({index: this.state.cursor - 1}) } else if (this.state.cursor > 0) this.VTListRef.scrollToIndex({index: this.state.cursor - 1}) @@ -90,49 +115,77 @@ export default class AutoComplete extends React.Component { const propStyles = this.props; return ( - this.setState({showList: false, cursor: 0, counter: 0})} + underlineColorAndroid={'transparent'} + onBlur={() => { + this.setState({ + showList: false, + cursor: 0, + counter: 0 + }) + }} onChangeText={value => this.handleSearch(value)} onKeyPress={(e) => this.handleKeyDown(e)} - /> - {this.state.showList - && - - data.length} - ref={(ref) => { - this.VTListRef = ref; - }} - getItem={(data, index) => data[index]} - renderItem={ - (rowData, key) => ( - - { + {this.state.showList ? + { + let {height} = event.nativeEvent.layout + this.setState({listContainerHeight: height}) + }} + style={[{position: 'absolute', zIndex: 999, marginTop: 40, backgroundColor: '#fff'} + , propStyles.listContainerStyle]}> + data.length} + ref={(ref) => { + this.VTListRef = ref; + }} + keyboardShouldPersistTaps='handled' + getItem={(data, index) => data[index]} + keyExtractor={(item, index) => item.name} + renderItem={ + (rowData, key) => ( + { + let {height} = event.nativeEvent.layout + this.setState({listItemHeight: height}) + }} + style={{backgroundColor: cursor === index ? 'rgba(1, 140, 207, 0.46)' : null}}> + { this.setState({ - showList: false, query: rowData + showList: false, + query: rowData.item, + value: rowData.item[this.props.searchQuery] }, () => { + Keyboard.dismiss() if (this.props.onItemSelected) this.props.onItemSelected(this.state.query); }); }}> - - {rowData.item[this.props.searchQuery]} - - - - ) - } - /> + + {rowData.item[this.props.searchQuery]} + + + + ) + } + /> - + : null } ) @@ -142,69 +195,49 @@ export default class AutoComplete extends React.Component { AutoComplete.defaultProps = { ignoreCase: false, - listContainerStyle: { - zIndex: 1, - height: 300, - position: 'absolute', - marginTop: 40, - backgroundColor: '#FFF', - width: 200 - }, - autoCompleteInputStyle: { - width: 200, - height: 40, - }, - listItemStyle: { - height: 40, - justifyContent: 'center', - borderWidth: 2, - borderStyle: 'solid', - borderColor: '#e0e0e0', - borderTopWidth: 0 - }, - searchQuery:'name', + searchQuery: 'name', list: [{ name: 'React', - email:'react@gmail.com' - }, + email: 'react@gmail.com' + }, { name: 'Redux', - email:'Redux@gmail.com' + email: 'Redux@gmail.com' }, { name: 'CSS', - email:'css3@gmail.com' + email: 'css3@gmail.com' }, { name: "React Native", - email:'RN@gmail.com' + email: 'RN@gmail.com' }, { name: 'GraphQL', - email:'GraphQL@gmail.com' + email: 'GraphQL@gmail.com' }, { name: 'python', - email:'python@gmail.com' + email: 'python@gmail.com' }, { name: 'django', - email:'django@gmail.com' + email: 'django@gmail.com' }, { name: 'JavaScript', - email:'javascript@gmail.com' + email: 'javascript@gmail.com' }, { name: 'Java', - email:'oracle@gmail.com' + email: 'oracle@gmail.com' } ] }; AutoComplete.propTypes = { placeholder: PropTypes.string, - searchQuery:PropTypes.string, + searchQuery: PropTypes.string, listContainerStyle: PropTypes.object, listItemStyle: PropTypes.object, autoCompleteInputStyle: PropTypes.object,