├── .expo
├── packager-info.json
└── settings.json
├── .gitignore
├── .npmignore
├── README.md
├── example
├── .babelrc
├── .gitignore
├── .watchmanconfig
├── App.js
├── app.json
├── assets
│ └── icons
│ │ ├── app.png
│ │ └── loading.png
├── package-lock.json
└── package.json
├── index.js
├── license.md
├── package.json
├── react-native-progressive-input-1.1.0.tgz
└── screenshot.gif
/.expo/packager-info.json:
--------------------------------------------------------------------------------
1 | {
2 | "expoServerPort": null,
3 | "expoServerNgrokUrl": null,
4 | "packagerNgrokUrl": null,
5 | "ngrokPid": null,
6 | "packagerPort": null,
7 | "packagerPid": null
8 | }
--------------------------------------------------------------------------------
/.expo/settings.json:
--------------------------------------------------------------------------------
1 | {
2 | "hostType": "tunnel",
3 | "lanType": "ip",
4 | "dev": true,
5 | "strict": false,
6 | "minify": false,
7 | "urlType": "exp",
8 | "urlRandomness": null
9 | }
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | node_modules/
2 | yarn-error.log
--------------------------------------------------------------------------------
/.npmignore:
--------------------------------------------------------------------------------
1 | screenshot.gif
2 | example/
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 |
2 | # Progressive Input
3 | 
4 |
5 | [Progressive Input](https://github.com/khaiql/react-native-progressive-input) is used as a part of autocomplete solution. The control has clear button to clear text and activity indicator to show that background job is being performed.
6 |
7 | ## Getting started
8 |
9 | `$ npm install react-native-progressive-input --save`
10 |
11 | If you dont own the dependency `react-native-vector-icons`, please make sure you also run:
12 |
13 | * `npm install react-native-vector-icons --save`
14 |
15 | * `react-native link`
16 |
17 | on the terminal. This will add some necessary fonts and Info.plist updates on your xcode project.
18 |
19 | ## Usage
20 | ```javascript
21 | import ProgressiveInput from 'react-native-progressive-input';
22 |
23 | class Screen extends Component {
24 | constructor(props) {
25 | super(props);
26 |
27 | this.state = {
28 | value: '',
29 | isLoading: false
30 | };
31 | }
32 |
33 | _onChangeText(text) {
34 | this.setState({isLoading: true, value: text});
35 |
36 | fetch("YOUR_URL_FOR_GETTING_SUGGESTION")
37 | .then((result) => {
38 | // Process list of suggestions
39 |
40 | this.setState({isLoading: false});
41 | });
42 | }
43 |
44 | render() {
45 |
50 | }
51 | }
52 |
53 | export default Screen;
54 | ```
55 |
56 | ## Properties
57 |
58 | | Name | Type |
59 | |------------------------|-----------------------------------|
60 | | autoCorrect | PropTypes.bool |
61 | | keyboardType | TextInput.propTypes.keyboardType |
62 | | multiline | PropTypes.bool |
63 | | placeholderTextColor | PropTypes.string |
64 | | returnKeyType | TextInput.propTypes.returnKeyType |
65 | | selectTextOnFocus | PropTypes.bool |
66 | | placeholder | PropTypes.string |
67 | | editable | PropTypes.bool |
68 | | autoCapitalize | PropTypes.bool |
69 | | maxLength | PropTypes.number |
70 | | multiline | PropTypes.bool |
71 | | onEndEditing | PropTypes.func |
72 | | onChange | PropTypes.func |
73 | | value | PropTypes.string |
74 | | isLoading | PropTypes.bool |
75 | | textInputStyle | TextInput.propTypes.style |
76 | | clearButtonIcon | PropTypes.string |
77 | | clearButtonColor | PropTypes.string |
78 | | clearButtonSize | PropTypes.number |
79 | | clearButtonStyle | PropTypes.object |
80 | | activityIndicatorStyle | ActivityIndicator.propTypes.style |
81 | | onBlur | PropTypes.func |
82 | | onChangeText | PropTypes.func |
83 | | onFocus | PropTypes.func |
84 | | onInputCleared | PropTypes.func |
85 |
86 | ## Author
87 | - Khai Le (Scott)
88 | - Blog: [lequangkhai.wordpress.com](https://lequangkhai.wordpress.com)
89 |
90 | ## License
91 | MIT
92 |
--------------------------------------------------------------------------------
/example/.babelrc:
--------------------------------------------------------------------------------
1 | {
2 | "presets": ["babel-preset-expo"],
3 | "env": {
4 | "development": {
5 | "plugins": ["transform-react-jsx-source"]
6 | }
7 | }
8 | }
9 |
--------------------------------------------------------------------------------
/example/.gitignore:
--------------------------------------------------------------------------------
1 | node_modules/**/*
2 | .expo/*
3 | npm-debug.*
4 |
--------------------------------------------------------------------------------
/example/.watchmanconfig:
--------------------------------------------------------------------------------
1 | {}
2 |
--------------------------------------------------------------------------------
/example/App.js:
--------------------------------------------------------------------------------
1 | import Expo, { Components } from 'expo';
2 | import React from 'react';
3 | import ProgressiveInput from 'react-native-progressive-input';
4 | import {
5 | StyleSheet,
6 | Text,
7 | View,
8 | ListView,
9 | TouchableOpacity,
10 | } from 'react-native';
11 |
12 | const GOOGLE_API_KEY = 'AIzaSyB7-8qph-zszuxivIm7cwT5b37D22bm1A4';
13 | const ds = new ListView.DataSource({
14 | rowHasChanged: (r1, r2) => r1.id !== r2.id,
15 | });
16 | const latitudeDelta = 0.0922;
17 | const longitudeDelta = 0.0421;
18 |
19 | export default class App extends React.Component {
20 | constructor(props) {
21 | super(props);
22 | this.state = {
23 | isLoading: false,
24 | dataSource: ds.cloneWithRows([]),
25 | value: '',
26 | };
27 | this.searchLocation = this.searchLocation.bind(this);
28 | this.renderRow = this.renderRow.bind(this);
29 | this.renderSeparator = this.renderSeparator.bind(this);
30 | this.onInputCleared = this.onInputCleared.bind(this);
31 | }
32 |
33 | async searchLocation(query) {
34 | const url = `https://maps.googleapis.com/maps/api/place/autocomplete/json?key=${
35 | GOOGLE_API_KEY
36 | }&input=${query}`;
37 | this.setState({ isLoading: true, value: query });
38 | const response = await fetch(url);
39 | const jsonResponse = await response.json();
40 | this.setState({
41 | isLoading: false,
42 | dataSource: ds.cloneWithRows(jsonResponse.predictions),
43 | });
44 | }
45 |
46 | renderRow(prediction) {
47 | return (
48 | this.onListItemClicked(prediction)}
50 | style={styles.listItem}
51 | >
52 | {prediction.description}
53 |
54 | );
55 | }
56 |
57 | renderSeparator() {
58 | return ;
59 | }
60 |
61 | onInputCleared() {
62 | this.setState({
63 | value: '',
64 | isLoading: false,
65 | dataSource: ds.cloneWithRows([]),
66 | });
67 | }
68 |
69 | async onListItemClicked(prediction) {
70 | this.setState({
71 | value: prediction.description,
72 | dataSource: ds.cloneWithRows([]),
73 | isLoading: true,
74 | });
75 | const url = `https://maps.googleapis.com/maps/api/place/details/json?placeid=${
76 | prediction.place_id
77 | }&key=${GOOGLE_API_KEY}`;
78 | const response = await fetch(url);
79 | const jsonResponse = await response.json();
80 | const { lat, lng } = jsonResponse.result.geometry.location;
81 | this.mapView.animateToRegion({
82 | longitude: lng,
83 | latitude: lat,
84 | latitudeDelta,
85 | longitudeDelta,
86 | });
87 | this.setState({ isLoading: false });
88 | }
89 |
90 | render() {
91 | return (
92 |
93 | (this.mapView = m)}
96 | initialRegion={{
97 | latitude: 37.78825,
98 | longitude: -122.4324,
99 | latitudeDelta,
100 | longitudeDelta,
101 | }}
102 | />
103 |
110 |
111 |
118 |
119 |
120 | );
121 | }
122 | }
123 |
124 | const styles = StyleSheet.create({
125 | container: {
126 | flex: 1,
127 | backgroundColor: 'grey',
128 | flexDirection: 'column',
129 | justifyContent: 'flex-start',
130 | },
131 | map: {
132 | position: 'absolute',
133 | top: 0,
134 | right: 0,
135 | left: 0,
136 | bottom: 0,
137 | },
138 | progressiveInput: {
139 | marginTop: 20,
140 | marginLeft: 10,
141 | marginRight: 10,
142 | },
143 | listViewContainer: {
144 | flex: 0,
145 | },
146 | listView: {
147 | backgroundColor: 'white',
148 | margin: 10,
149 | },
150 | listItem: {
151 | padding: 10,
152 | },
153 | listItemSeparator: {
154 | borderWidth: 0.5,
155 | borderColor: 'lightgrey',
156 | },
157 | });
158 |
--------------------------------------------------------------------------------
/example/app.json:
--------------------------------------------------------------------------------
1 | {
2 | "expo": {
3 | "name": "Progressive Input Example",
4 | "description": "A simple demo for 'react-native-progressive-input'",
5 | "slug": "progressive-input-example",
6 | "privacy": "public",
7 | "sdkVersion": "23.0.0",
8 | "version": "1.0.0",
9 | "orientation": "portrait",
10 | "primaryColor": "#cccccc",
11 | "icon": "./assets/icons/app.png",
12 | "loading": {
13 | "icon": "./assets/icons/loading.png",
14 | "hideExponentText": false
15 | }
16 | }
17 | }
18 |
--------------------------------------------------------------------------------
/example/assets/icons/app.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/khaiql/react-native-progressive-input/14377e5a54f209c48c06f3d8a76a162634d0a1b8/example/assets/icons/app.png
--------------------------------------------------------------------------------
/example/assets/icons/loading.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/khaiql/react-native-progressive-input/14377e5a54f209c48c06f3d8a76a162634d0a1b8/example/assets/icons/loading.png
--------------------------------------------------------------------------------
/example/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "progressive-input-example",
3 | "version": "0.0.0",
4 | "description": "A simple demo for 'react-native-progressive-input'",
5 | "author": null,
6 | "private": true,
7 | "main": "node_modules/expo/AppEntry.js",
8 | "dependencies": {
9 | "expo": "23.0.1",
10 | "react": "16.1.1",
11 | "react-native": "https://github.com/expo/react-native/archive/sdk-23.0.0.tar.gz",
12 | "react-native-progressive-input": "../react-native-progressive-input-1.1.0.tgz",
13 | "react-native-vector-icons": "^4.1.0"
14 | }
15 | }
16 |
--------------------------------------------------------------------------------
/index.js:
--------------------------------------------------------------------------------
1 | import React, { Component } from 'react';
2 | import PropTypes from 'prop-types'; // 15.6.0
3 | import {
4 | TextInput,
5 | View,
6 | ActivityIndicator,
7 | TouchableOpacity,
8 | StyleSheet,
9 | } from 'react-native';
10 | import { Ionicons } from '@expo/vector-icons'; // 6.2.2
11 |
12 | class ProgressiveInput extends Component {
13 | static propTypes = {
14 | ...TextInput.propTypes,
15 | value: PropTypes.string,
16 | isLoading: PropTypes.bool,
17 | textInputStyle: TextInput.propTypes.style,
18 | clearButtonIcon: PropTypes.string,
19 | clearButtonColor: PropTypes.string,
20 | clearButtonSize: PropTypes.number,
21 | clearButtonStyle: PropTypes.object,
22 | activityIndicatorStyle: ActivityIndicator.propTypes.style,
23 | onBlur: PropTypes.func,
24 | onChangeText: PropTypes.func,
25 | onFocus: PropTypes.func,
26 | onInputCleared: PropTypes.func,
27 | underlineColorAndroid: PropTypes.string,
28 | };
29 |
30 | static defaultProps = {
31 | editable: true,
32 | clearButtonIcon: 'ios-close-circle',
33 | clearButtonColor: 'lightgrey',
34 | clearButtonSize: 20,
35 | underlineColorAndroid: 'transparent',
36 | };
37 |
38 | constructor(props) {
39 | super(props);
40 |
41 | this.state = {
42 | showClearButton: false,
43 | value: this.props.value,
44 | };
45 | }
46 |
47 | componentWillReceiveProps(nextProps) {
48 | this.setState({ value: nextProps.value });
49 | }
50 |
51 | clearInput() {
52 | this.setState({ value: '', focus: false });
53 | if (this.props.onInputCleared) {
54 | this.props.onInputCleared();
55 | }
56 | }
57 |
58 | isFocused() {
59 | return this.input.isFocused();
60 | }
61 |
62 | render() {
63 | return (
64 |
65 | {this._renderClearButton()}
66 | (this.input = input)}
68 | style={[styles.textInput, this.props.textInputStyle]}
69 | focus={this.state.focus}
70 | value={this.state.value}
71 | editable={this.props.editable}
72 | onFocus={this._onFocus}
73 | placeholder={this.props.placeholder}
74 | onChangeText={this._onChangeText}
75 | selectTextOnFocus={this.props.selectTextOnFocus}
76 | onBlur={this._onBlur}
77 | autoCorrect={this.props.autoCorrect}
78 | keyboardType={this.props.keyboardType}
79 | multiline={this.props.multiline}
80 | placeholderTextColor={this.props.placeholderTextColor}
81 | returnKeyType={this.props.returnKeyType}
82 | autoCapitalize={this.props.autoCapitalize}
83 | maxLength={this.props.maxLength}
84 | onEndEditing={this.props.onEndEditing}
85 | onChange={this.props.onChange}
86 | underlineColorAndroid={this.props.underlineColorAndroid}
87 | />
88 | {this._renderActivityIndicator()}
89 |
90 | );
91 | }
92 |
93 | _renderActivityIndicator = () => {
94 | let size = this.props.isLoading ? {} : { width: 0, height: 0 };
95 | return (
96 |
104 | );
105 | };
106 |
107 | _renderClearButton = () => {
108 | if (this.state.showClearButton) {
109 | return (
110 | this.clearInput()}>
111 |
117 |
118 | );
119 | }
120 | };
121 |
122 | _onFocus = () => {
123 | this._shouldShowClearButton();
124 | if (this.props.onFocus) {
125 | this.props.onFocus();
126 | }
127 | };
128 |
129 | _onChangeText = text => {
130 | this.setState({ value: text });
131 | this._shouldShowClearButton(text);
132 | if (this.props.onChangeText) {
133 | this.props.onChangeText(text);
134 | }
135 | };
136 |
137 | _shouldShowClearButton = value => {
138 | const v = value || this.state.value;
139 | const showClearButton = v ? true : false;
140 | this.setState({ showClearButton });
141 | };
142 |
143 | _onBlur = () => this.setState({ showClearButton: false });
144 | }
145 |
146 | const styles = StyleSheet.create({
147 | container: {
148 | flexDirection: 'row',
149 | alignItems: 'center',
150 | borderWidth: 1,
151 | borderColor: 'lightgrey',
152 | backgroundColor: 'white',
153 | },
154 | clearIcon: {
155 | marginLeft: 5,
156 | },
157 | textInput: {
158 | flex: 1,
159 | height: 40,
160 | marginLeft: 10,
161 | },
162 | activityIndicator: {
163 | marginLeft: 5,
164 | marginRight: 5,
165 | },
166 | });
167 |
168 | export default ProgressiveInput;
169 |
--------------------------------------------------------------------------------
/license.md:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2016 Scott Le
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "react-native-progressive-input",
3 | "version": "1.1.0",
4 | "description":
5 | "Use this to create autocomplete text input which the text box can have both clear button and an activity indicator to show the background job",
6 | "main": "index.js",
7 | "repository": "https://github.com/khaiql/react-native-progressive-input",
8 | "keywords": [
9 | "react-native",
10 | "uber-text-input",
11 | "autocomplete",
12 | "progressive-input",
13 | "clearable-input"
14 | ],
15 | "author": "scott ",
16 | "license": "MIT",
17 | "peerDependencies": {
18 | "react-native": "0.x",
19 | "prop-types": "^15.6.0"
20 | },
21 | "dependencies": {
22 | "@expo/vector-icons": "^6.2.2"
23 | }
24 | }
25 |
--------------------------------------------------------------------------------
/react-native-progressive-input-1.1.0.tgz:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/khaiql/react-native-progressive-input/14377e5a54f209c48c06f3d8a76a162634d0a1b8/react-native-progressive-input-1.1.0.tgz
--------------------------------------------------------------------------------
/screenshot.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/khaiql/react-native-progressive-input/14377e5a54f209c48c06f3d8a76a162634d0a1b8/screenshot.gif
--------------------------------------------------------------------------------