react-native-google-places-autocomplete 2.5.7 → 2.6.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/GooglePlacesAutocomplete.js +774 -648
- package/README.md +0 -18
- package/example/App.js +221 -0
- package/example/README.md +64 -0
- package/example/app.json +24 -0
- package/example/babel.config.js +6 -0
- package/example/package.json +23 -0
- package/package.json +3 -4
|
@@ -1,8 +1,7 @@
|
|
|
1
1
|
/* eslint-disable react-native/no-inline-styles */
|
|
2
2
|
import debounce from 'lodash.debounce';
|
|
3
|
-
import PropTypes from 'prop-types';
|
|
4
3
|
import Qs from 'qs';
|
|
5
|
-
import {
|
|
4
|
+
import { randomUUID } from 'expo-crypto';
|
|
6
5
|
import React, {
|
|
7
6
|
forwardRef,
|
|
8
7
|
useMemo,
|
|
@@ -26,6 +25,10 @@ import {
|
|
|
26
25
|
View,
|
|
27
26
|
} from 'react-native';
|
|
28
27
|
|
|
28
|
+
// ============================================================================
|
|
29
|
+
// CONSTANTS
|
|
30
|
+
// ============================================================================
|
|
31
|
+
|
|
29
32
|
const defaultStyles = {
|
|
30
33
|
container: {
|
|
31
34
|
flex: 1,
|
|
@@ -43,7 +46,9 @@ const defaultStyles = {
|
|
|
43
46
|
flex: 1,
|
|
44
47
|
marginBottom: 5,
|
|
45
48
|
},
|
|
46
|
-
listView: {
|
|
49
|
+
listView: {
|
|
50
|
+
backgroundColor: '#FFFFFF',
|
|
51
|
+
},
|
|
47
52
|
row: {
|
|
48
53
|
backgroundColor: '#FFFFFF',
|
|
49
54
|
padding: 13,
|
|
@@ -71,61 +76,134 @@ const defaultStyles = {
|
|
|
71
76
|
powered: {},
|
|
72
77
|
};
|
|
73
78
|
|
|
79
|
+
// ============================================================================
|
|
80
|
+
// COMPONENT
|
|
81
|
+
// ============================================================================
|
|
82
|
+
|
|
74
83
|
export const GooglePlacesAutocomplete = forwardRef((props, ref) => {
|
|
75
|
-
|
|
76
|
-
|
|
84
|
+
// ==========================================================================
|
|
85
|
+
// PROPS DESTRUCTURING
|
|
86
|
+
// ==========================================================================
|
|
87
|
+
const {
|
|
88
|
+
autoFillOnNotFound = false,
|
|
89
|
+
currentLocation = false,
|
|
90
|
+
currentLocationLabel = 'Current location',
|
|
91
|
+
debounce: debounceMs = 0,
|
|
92
|
+
disableScroll = false,
|
|
93
|
+
enableHighAccuracyLocation = true,
|
|
94
|
+
enablePoweredByContainer = true,
|
|
95
|
+
fetchDetails = false,
|
|
96
|
+
filterReverseGeocodingByTypes = [],
|
|
97
|
+
GooglePlacesDetailsQuery = {},
|
|
98
|
+
GooglePlacesSearchQuery = {
|
|
99
|
+
rankby: 'distance',
|
|
100
|
+
type: 'restaurant',
|
|
101
|
+
},
|
|
102
|
+
GoogleReverseGeocodingQuery = {},
|
|
103
|
+
isRowScrollable = true,
|
|
104
|
+
keyboardShouldPersistTaps = 'always',
|
|
105
|
+
listHoverColor = '#ececec',
|
|
106
|
+
listUnderlayColor = '#c8c7cc',
|
|
107
|
+
listViewDisplayed: listViewDisplayedProp = 'auto',
|
|
108
|
+
keepResultsAfterBlur = false,
|
|
109
|
+
minLength = 0,
|
|
110
|
+
nearbyPlacesAPI = 'GooglePlacesSearch',
|
|
111
|
+
numberOfLines = 1,
|
|
112
|
+
onFail = () => {},
|
|
113
|
+
onNotFound = () => {},
|
|
114
|
+
onPress = () => {},
|
|
115
|
+
onTimeout = () =>
|
|
116
|
+
console.warn('google places autocomplete: request timeout'),
|
|
117
|
+
placeholder = '',
|
|
118
|
+
predefinedPlaces: predefinedPlacesProp = [],
|
|
119
|
+
predefinedPlacesAlwaysVisible = false,
|
|
120
|
+
query = {
|
|
121
|
+
key: 'missing api key',
|
|
122
|
+
language: 'en',
|
|
123
|
+
types: 'geocode',
|
|
124
|
+
},
|
|
125
|
+
styles = {},
|
|
126
|
+
suppressDefaultStyles = false,
|
|
127
|
+
textInputHide = false,
|
|
128
|
+
textInputProps = {},
|
|
129
|
+
timeout = 20000,
|
|
130
|
+
isNewPlacesAPI = false,
|
|
131
|
+
fields = '*',
|
|
132
|
+
...restProps
|
|
133
|
+
} = props;
|
|
134
|
+
|
|
135
|
+
// ==========================================================================
|
|
136
|
+
// STATE & REFS
|
|
137
|
+
// ==========================================================================
|
|
138
|
+
const predefinedPlaces = useMemo(() => predefinedPlacesProp || [], [
|
|
139
|
+
predefinedPlacesProp,
|
|
140
|
+
]);
|
|
141
|
+
|
|
142
|
+
// Store results array - useRef prevents re-renders when updating results, allows access to latest results in callbacks
|
|
143
|
+
const resultsRef = useRef([]);
|
|
144
|
+
|
|
145
|
+
// Store active XMLHttpRequest objects - needed to abort requests when component unmounts or new search starts
|
|
146
|
+
const requestsRef = useRef([]);
|
|
147
|
+
|
|
148
|
+
// Track if navigator warning has been shown - prevents duplicate console warnings
|
|
149
|
+
const hasWarnedAboutNavigator = useRef(false);
|
|
150
|
+
|
|
151
|
+
// Reference to TextInput component - enables imperative methods (focus, blur, clear) via ref
|
|
152
|
+
const inputRef = useRef(null);
|
|
153
|
+
|
|
154
|
+
// Store current query object - allows access to latest query in callbacks without stale closures
|
|
155
|
+
const queryRef = useRef(query);
|
|
156
|
+
|
|
157
|
+
// Store previous query string - used to detect query changes without causing re-renders
|
|
158
|
+
const prevQueryStringRef = useRef(JSON.stringify(query));
|
|
159
|
+
|
|
160
|
+
// Store latest _request function - ensures debounced function always calls current version with latest closures
|
|
161
|
+
const requestRef = useRef(_request);
|
|
162
|
+
const queryString = useMemo(() => JSON.stringify(query), [query]);
|
|
77
163
|
|
|
78
|
-
const
|
|
164
|
+
const [stateText, setStateText] = useState('');
|
|
165
|
+
const [dataSource, setDataSource] = useState([]);
|
|
166
|
+
const [listViewDisplayed, setListViewDisplayed] = useState(
|
|
167
|
+
listViewDisplayedProp === 'auto' ? false : listViewDisplayedProp,
|
|
168
|
+
);
|
|
169
|
+
const [url, setUrl] = useState('');
|
|
170
|
+
const [listLoaderDisplayed, setListLoaderDisplayed] = useState(false);
|
|
171
|
+
const [sessionToken, setSessionToken] = useState(randomUUID());
|
|
172
|
+
|
|
173
|
+
// ==========================================================================
|
|
174
|
+
// UTILITY FUNCTIONS
|
|
175
|
+
// ==========================================================================
|
|
176
|
+
|
|
177
|
+
const hasNavigator = useCallback(() => {
|
|
79
178
|
if (navigator?.geolocation) {
|
|
80
179
|
return true;
|
|
81
|
-
}
|
|
180
|
+
}
|
|
181
|
+
if (!hasWarnedAboutNavigator.current) {
|
|
182
|
+
if (Platform.OS === 'web') {
|
|
183
|
+
console.warn(
|
|
184
|
+
'Geolocation is not available. For web, ensure your site is served over HTTPS or localhost to use geolocation features.',
|
|
185
|
+
);
|
|
186
|
+
} else {
|
|
187
|
+
console.warn(
|
|
188
|
+
'Geolocation is not available. For React Native, you may need to install and configure @react-native-community/geolocation or expo-location to enable currentLocation.',
|
|
189
|
+
);
|
|
190
|
+
}
|
|
191
|
+
hasWarnedAboutNavigator.current = true;
|
|
192
|
+
}
|
|
193
|
+
return false;
|
|
194
|
+
}, []);
|
|
195
|
+
|
|
196
|
+
const supportedPlatform = () => {
|
|
197
|
+
if (Platform.OS === 'web' && !props.requestUrl) {
|
|
82
198
|
console.warn(
|
|
83
|
-
'
|
|
199
|
+
'This library cannot be used for the web unless you specify the requestUrl prop. See https://git.io/JflFv for more for details.',
|
|
84
200
|
);
|
|
85
201
|
return false;
|
|
86
202
|
}
|
|
203
|
+
return true;
|
|
87
204
|
};
|
|
88
205
|
|
|
89
|
-
const
|
|
90
|
-
(results, text) => {
|
|
91
|
-
let res = [];
|
|
92
|
-
const shouldDisplayPredefinedPlaces = text
|
|
93
|
-
? results.length === 0 && text.length === 0
|
|
94
|
-
: results.length === 0;
|
|
95
|
-
if (
|
|
96
|
-
shouldDisplayPredefinedPlaces ||
|
|
97
|
-
props.predefinedPlacesAlwaysVisible === true
|
|
98
|
-
) {
|
|
99
|
-
res = [
|
|
100
|
-
...props.predefinedPlaces.filter(
|
|
101
|
-
(place) => place?.description.length,
|
|
102
|
-
),
|
|
103
|
-
];
|
|
104
|
-
|
|
105
|
-
if (props.currentLocation === true && hasNavigator()) {
|
|
106
|
-
res.unshift({
|
|
107
|
-
description: props.currentLocationLabel,
|
|
108
|
-
isCurrentLocation: true,
|
|
109
|
-
});
|
|
110
|
-
}
|
|
111
|
-
}
|
|
112
|
-
|
|
113
|
-
res = res.map((place) => ({
|
|
114
|
-
...place,
|
|
115
|
-
isPredefinedPlace: true,
|
|
116
|
-
}));
|
|
117
|
-
|
|
118
|
-
return [...res, ...results];
|
|
119
|
-
},
|
|
120
|
-
[
|
|
121
|
-
props.currentLocation,
|
|
122
|
-
props.currentLocationLabel,
|
|
123
|
-
props.predefinedPlaces,
|
|
124
|
-
props.predefinedPlacesAlwaysVisible,
|
|
125
|
-
],
|
|
126
|
-
);
|
|
127
|
-
|
|
128
|
-
const getRequestUrl = useCallback((requestUrl) => {
|
|
206
|
+
const getRequestUrl = (requestUrl) => {
|
|
129
207
|
if (requestUrl) {
|
|
130
208
|
if (requestUrl.useOnPlatform === 'all') {
|
|
131
209
|
return requestUrl.url;
|
|
@@ -136,295 +214,87 @@ export const GooglePlacesAutocomplete = forwardRef((props, ref) => {
|
|
|
136
214
|
default: 'https://maps.googleapis.com/maps/api',
|
|
137
215
|
});
|
|
138
216
|
}
|
|
139
|
-
} else {
|
|
140
|
-
return 'https://maps.googleapis.com/maps/api';
|
|
141
217
|
}
|
|
142
|
-
|
|
218
|
+
return 'https://maps.googleapis.com/maps/api';
|
|
219
|
+
};
|
|
143
220
|
|
|
144
221
|
const getRequestHeaders = (requestUrl) => {
|
|
145
222
|
return requestUrl?.headers || {};
|
|
146
223
|
};
|
|
147
224
|
|
|
148
225
|
const setRequestHeaders = (request, headers) => {
|
|
149
|
-
Object.keys(headers).
|
|
226
|
+
Object.keys(headers).forEach((headerKey) =>
|
|
150
227
|
request.setRequestHeader(headerKey, headers[headerKey]),
|
|
151
228
|
);
|
|
152
229
|
};
|
|
153
230
|
|
|
154
|
-
const
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
props.listViewDisplayed === 'auto' ? false : props.listViewDisplayed,
|
|
158
|
-
);
|
|
159
|
-
const [url, setUrl] = useState(getRequestUrl(props.requestUrl));
|
|
160
|
-
const [listLoaderDisplayed, setListLoaderDisplayed] = useState(false);
|
|
161
|
-
|
|
162
|
-
const inputRef = useRef();
|
|
163
|
-
const [sessionToken, setSessionToken] = useState(uuidv4());
|
|
164
|
-
useEffect(() => {
|
|
165
|
-
setUrl(getRequestUrl(props.requestUrl));
|
|
166
|
-
}, [getRequestUrl, props.requestUrl]);
|
|
167
|
-
|
|
168
|
-
useEffect(() => {
|
|
169
|
-
// This will load the search results after the query object ref gets changed
|
|
170
|
-
_handleChangeText(stateText);
|
|
171
|
-
return () => {
|
|
172
|
-
_abortRequests();
|
|
173
|
-
};
|
|
174
|
-
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
175
|
-
}, [props.query]);
|
|
231
|
+
const requestShouldUseWithCredentials = useCallback(() => {
|
|
232
|
+
return url === 'https://maps.googleapis.com/maps/api';
|
|
233
|
+
}, [url]);
|
|
176
234
|
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
useImperativeHandle(ref, () => ({
|
|
183
|
-
setAddressText: (address) => {
|
|
184
|
-
setStateText(address);
|
|
185
|
-
},
|
|
186
|
-
getAddressText: () => stateText,
|
|
187
|
-
blur: () => inputRef.current.blur(),
|
|
188
|
-
focus: () => inputRef.current.focus(),
|
|
189
|
-
isFocused: () => inputRef.current.isFocused(),
|
|
190
|
-
clear: () => inputRef.current.clear(),
|
|
191
|
-
getCurrentLocation,
|
|
192
|
-
}));
|
|
193
|
-
|
|
194
|
-
const requestShouldUseWithCredentials = () =>
|
|
195
|
-
url === 'https://maps.googleapis.com/maps/api';
|
|
196
|
-
|
|
197
|
-
const _abortRequests = () => {
|
|
198
|
-
_requests.map((i) => {
|
|
199
|
-
i.onreadystatechange = null;
|
|
200
|
-
i.abort();
|
|
235
|
+
const _abortRequests = useCallback(() => {
|
|
236
|
+
requestsRef.current.forEach((request) => {
|
|
237
|
+
request.onreadystatechange = null;
|
|
238
|
+
request.abort();
|
|
201
239
|
});
|
|
202
|
-
|
|
203
|
-
};
|
|
204
|
-
|
|
205
|
-
const supportedPlatform = () => {
|
|
206
|
-
if (Platform.OS === 'web' && !props.requestUrl) {
|
|
207
|
-
console.warn(
|
|
208
|
-
'This library cannot be used for the web unless you specify the requestUrl prop. See https://git.io/JflFv for more for details.',
|
|
209
|
-
);
|
|
210
|
-
return false;
|
|
211
|
-
} else {
|
|
212
|
-
return true;
|
|
213
|
-
}
|
|
214
|
-
};
|
|
215
|
-
|
|
216
|
-
const getCurrentLocation = () => {
|
|
217
|
-
let options = {
|
|
218
|
-
enableHighAccuracy: false,
|
|
219
|
-
timeout: 20000,
|
|
220
|
-
maximumAge: 1000,
|
|
221
|
-
};
|
|
222
|
-
|
|
223
|
-
if (props.enableHighAccuracyLocation && Platform.OS === 'android') {
|
|
224
|
-
options = {
|
|
225
|
-
enableHighAccuracy: true,
|
|
226
|
-
timeout: 20000,
|
|
227
|
-
};
|
|
228
|
-
}
|
|
229
|
-
const getCurrentPosition =
|
|
230
|
-
navigator.geolocation.getCurrentPosition ||
|
|
231
|
-
navigator.geolocation.default.getCurrentPosition;
|
|
232
|
-
|
|
233
|
-
getCurrentPosition &&
|
|
234
|
-
getCurrentPosition(
|
|
235
|
-
(position) => {
|
|
236
|
-
if (props.nearbyPlacesAPI === 'None') {
|
|
237
|
-
let currentLocation = {
|
|
238
|
-
description: props.currentLocationLabel,
|
|
239
|
-
geometry: {
|
|
240
|
-
location: {
|
|
241
|
-
lat: position.coords.latitude,
|
|
242
|
-
lng: position.coords.longitude,
|
|
243
|
-
},
|
|
244
|
-
},
|
|
245
|
-
};
|
|
246
|
-
|
|
247
|
-
_disableRowLoaders();
|
|
248
|
-
props.onPress(currentLocation, currentLocation);
|
|
249
|
-
} else {
|
|
250
|
-
_requestNearby(position.coords.latitude, position.coords.longitude);
|
|
251
|
-
}
|
|
252
|
-
},
|
|
253
|
-
(error) => {
|
|
254
|
-
_disableRowLoaders();
|
|
255
|
-
console.error(error.message);
|
|
256
|
-
},
|
|
257
|
-
options,
|
|
258
|
-
);
|
|
259
|
-
};
|
|
260
|
-
|
|
261
|
-
const _onPress = (rowData) => {
|
|
262
|
-
if (rowData.isPredefinedPlace !== true && props.fetchDetails === true) {
|
|
263
|
-
if (rowData.isLoading === true) {
|
|
264
|
-
// already requesting
|
|
265
|
-
return;
|
|
266
|
-
}
|
|
267
|
-
|
|
268
|
-
Keyboard.dismiss();
|
|
269
|
-
|
|
270
|
-
_abortRequests();
|
|
271
|
-
|
|
272
|
-
// display loader
|
|
273
|
-
_enableRowLoader(rowData);
|
|
274
|
-
|
|
275
|
-
// fetch details
|
|
276
|
-
const request = new XMLHttpRequest();
|
|
277
|
-
_requests.push(request);
|
|
278
|
-
request.timeout = props.timeout;
|
|
279
|
-
request.ontimeout = props.onTimeout;
|
|
280
|
-
request.onreadystatechange = () => {
|
|
281
|
-
if (request.readyState !== 4) return;
|
|
282
|
-
|
|
283
|
-
if (request.status === 200) {
|
|
284
|
-
const responseJSON = JSON.parse(request.responseText);
|
|
285
|
-
if (
|
|
286
|
-
responseJSON.status === 'OK' ||
|
|
287
|
-
(props.isNewPlacesAPI && responseJSON.id)
|
|
288
|
-
) {
|
|
289
|
-
// if (_isMounted === true) {
|
|
290
|
-
const details = props.isNewPlacesAPI
|
|
291
|
-
? responseJSON
|
|
292
|
-
: responseJSON.result;
|
|
293
|
-
_disableRowLoaders();
|
|
294
|
-
_onBlur();
|
|
295
|
-
|
|
296
|
-
setStateText(_renderDescription(rowData));
|
|
297
|
-
|
|
298
|
-
delete rowData.isLoading;
|
|
299
|
-
props.onPress(rowData, details);
|
|
300
|
-
// }
|
|
301
|
-
} else {
|
|
302
|
-
_disableRowLoaders();
|
|
303
|
-
|
|
304
|
-
if (props.autoFillOnNotFound) {
|
|
305
|
-
setStateText(_renderDescription(rowData));
|
|
306
|
-
delete rowData.isLoading;
|
|
307
|
-
}
|
|
308
|
-
|
|
309
|
-
if (!props.onNotFound) {
|
|
310
|
-
console.warn(
|
|
311
|
-
'google places autocomplete: ' + responseJSON.status,
|
|
312
|
-
);
|
|
313
|
-
} else {
|
|
314
|
-
props.onNotFound(responseJSON);
|
|
315
|
-
}
|
|
316
|
-
}
|
|
317
|
-
} else {
|
|
318
|
-
_disableRowLoaders();
|
|
319
|
-
|
|
320
|
-
if (!props.onFail) {
|
|
321
|
-
console.warn(
|
|
322
|
-
'google places autocomplete: request could not be completed or has been aborted',
|
|
323
|
-
);
|
|
324
|
-
} else {
|
|
325
|
-
props.onFail('request could not be completed or has been aborted');
|
|
326
|
-
}
|
|
327
|
-
}
|
|
328
|
-
};
|
|
329
|
-
|
|
330
|
-
if (props.isNewPlacesAPI) {
|
|
331
|
-
request.open(
|
|
332
|
-
'GET',
|
|
333
|
-
`${url}/v1/places/${rowData.place_id}?` +
|
|
334
|
-
Qs.stringify({
|
|
335
|
-
key: props.query.key,
|
|
336
|
-
sessionToken,
|
|
337
|
-
fields: props.fields,
|
|
338
|
-
}),
|
|
339
|
-
);
|
|
340
|
-
setSessionToken(uuidv4());
|
|
341
|
-
} else {
|
|
342
|
-
request.open(
|
|
343
|
-
'GET',
|
|
344
|
-
`${url}/place/details/json?` +
|
|
345
|
-
Qs.stringify({
|
|
346
|
-
key: props.query.key,
|
|
347
|
-
placeid: rowData.place_id,
|
|
348
|
-
language: props.query.language,
|
|
349
|
-
...props.GooglePlacesDetailsQuery,
|
|
350
|
-
}),
|
|
351
|
-
);
|
|
352
|
-
}
|
|
353
|
-
|
|
354
|
-
request.withCredentials = requestShouldUseWithCredentials();
|
|
355
|
-
setRequestHeaders(request, getRequestHeaders(props.requestUrl));
|
|
356
|
-
|
|
357
|
-
request.send();
|
|
358
|
-
} else if (rowData.isCurrentLocation === true) {
|
|
359
|
-
// display loader
|
|
360
|
-
_enableRowLoader(rowData);
|
|
361
|
-
|
|
362
|
-
setStateText(_renderDescription(rowData));
|
|
363
|
-
|
|
364
|
-
delete rowData.isLoading;
|
|
365
|
-
getCurrentLocation();
|
|
366
|
-
} else {
|
|
367
|
-
setStateText(_renderDescription(rowData));
|
|
368
|
-
|
|
369
|
-
_onBlur();
|
|
370
|
-
delete rowData.isLoading;
|
|
371
|
-
let predefinedPlace = _getPredefinedPlace(rowData);
|
|
240
|
+
requestsRef.current = [];
|
|
241
|
+
}, []);
|
|
372
242
|
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
};
|
|
243
|
+
// ==========================================================================
|
|
244
|
+
// DATA PROCESSING FUNCTIONS
|
|
245
|
+
// ==========================================================================
|
|
377
246
|
|
|
378
|
-
const
|
|
379
|
-
|
|
380
|
-
|
|
247
|
+
const buildRowsFromResults = useCallback(
|
|
248
|
+
(results, text) => {
|
|
249
|
+
let res = [];
|
|
250
|
+
// Show predefined places if:
|
|
251
|
+
// 1. No text entered and no results, OR
|
|
252
|
+
// 2. predefinedPlacesAlwaysVisible is true
|
|
253
|
+
const shouldDisplayPredefinedPlaces =
|
|
254
|
+
(!text || text.length === 0) && results.length === 0;
|
|
381
255
|
if (
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
rowData.isCurrentLocation === true)
|
|
256
|
+
shouldDisplayPredefinedPlaces ||
|
|
257
|
+
predefinedPlacesAlwaysVisible === true
|
|
385
258
|
) {
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
};
|
|
259
|
+
if (predefinedPlaces.length > 0) {
|
|
260
|
+
res = [
|
|
261
|
+
...predefinedPlaces.filter((place) => place?.description?.length),
|
|
262
|
+
];
|
|
263
|
+
}
|
|
392
264
|
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
265
|
+
if (currentLocation === true && hasNavigator()) {
|
|
266
|
+
res.unshift({
|
|
267
|
+
description: currentLocationLabel,
|
|
268
|
+
isCurrentLocation: true,
|
|
269
|
+
});
|
|
270
|
+
}
|
|
398
271
|
}
|
|
399
|
-
}
|
|
400
|
-
|
|
401
|
-
setDataSource(buildRowsFromResults(_results));
|
|
402
|
-
// }
|
|
403
|
-
};
|
|
404
272
|
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
for (let i = 0; i < props.predefinedPlaces.length; i++) {
|
|
411
|
-
if (props.predefinedPlaces[i].description === rowData.description) {
|
|
412
|
-
return props.predefinedPlaces[i];
|
|
413
|
-
}
|
|
414
|
-
}
|
|
273
|
+
res = res.map((place) => ({
|
|
274
|
+
...place,
|
|
275
|
+
isPredefinedPlace: true,
|
|
276
|
+
}));
|
|
415
277
|
|
|
416
|
-
|
|
417
|
-
|
|
278
|
+
return [...res, ...results];
|
|
279
|
+
},
|
|
280
|
+
[
|
|
281
|
+
predefinedPlacesAlwaysVisible,
|
|
282
|
+
predefinedPlaces,
|
|
283
|
+
currentLocation,
|
|
284
|
+
currentLocationLabel,
|
|
285
|
+
hasNavigator,
|
|
286
|
+
],
|
|
287
|
+
);
|
|
418
288
|
|
|
419
|
-
const _filterResultsByTypes = (unfilteredResults, types) => {
|
|
420
|
-
if (types.length === 0) return unfilteredResults;
|
|
289
|
+
const _filterResultsByTypes = useCallback((unfilteredResults, types) => {
|
|
290
|
+
if (!types || types.length === 0) return unfilteredResults;
|
|
421
291
|
|
|
422
292
|
const results = [];
|
|
423
293
|
for (let i = 0; i < unfilteredResults.length; i++) {
|
|
424
294
|
let found = false;
|
|
425
295
|
|
|
426
296
|
for (let j = 0; j < types.length; j++) {
|
|
427
|
-
if (unfilteredResults[i].types
|
|
297
|
+
if (unfilteredResults[i].types?.indexOf(types[j]) !== -1) {
|
|
428
298
|
found = true;
|
|
429
299
|
break;
|
|
430
300
|
}
|
|
@@ -435,7 +305,7 @@ export const GooglePlacesAutocomplete = forwardRef((props, ref) => {
|
|
|
435
305
|
}
|
|
436
306
|
}
|
|
437
307
|
return results;
|
|
438
|
-
};
|
|
308
|
+
}, []);
|
|
439
309
|
|
|
440
310
|
const _filterResultsByPlacePredictions = (unfilteredResults) => {
|
|
441
311
|
const results = [];
|
|
@@ -460,19 +330,150 @@ export const GooglePlacesAutocomplete = forwardRef((props, ref) => {
|
|
|
460
330
|
return results;
|
|
461
331
|
};
|
|
462
332
|
|
|
463
|
-
const
|
|
333
|
+
const _getPredefinedPlace = (rowData) => {
|
|
334
|
+
if (rowData.isPredefinedPlace !== true) {
|
|
335
|
+
return rowData;
|
|
336
|
+
}
|
|
337
|
+
|
|
338
|
+
if (predefinedPlaces.length > 0) {
|
|
339
|
+
for (let i = 0; i < predefinedPlaces.length; i++) {
|
|
340
|
+
if (predefinedPlaces[i].description === rowData.description) {
|
|
341
|
+
return predefinedPlaces[i];
|
|
342
|
+
}
|
|
343
|
+
}
|
|
344
|
+
}
|
|
345
|
+
|
|
346
|
+
return rowData;
|
|
347
|
+
};
|
|
348
|
+
|
|
349
|
+
// ==========================================================================
|
|
350
|
+
// API REQUEST FUNCTIONS
|
|
351
|
+
// ==========================================================================
|
|
352
|
+
|
|
353
|
+
const _requestNearby = useCallback(
|
|
354
|
+
(latitude, longitude) => {
|
|
355
|
+
_abortRequests();
|
|
356
|
+
|
|
357
|
+
if (
|
|
358
|
+
latitude !== undefined &&
|
|
359
|
+
longitude !== undefined &&
|
|
360
|
+
latitude !== null &&
|
|
361
|
+
longitude !== null
|
|
362
|
+
) {
|
|
363
|
+
const request = new XMLHttpRequest();
|
|
364
|
+
requestsRef.current.push(request);
|
|
365
|
+
request.timeout = timeout;
|
|
366
|
+
request.ontimeout = onTimeout;
|
|
367
|
+
request.onreadystatechange = () => {
|
|
368
|
+
if (request.readyState !== 4) {
|
|
369
|
+
setListLoaderDisplayed(true);
|
|
370
|
+
return;
|
|
371
|
+
}
|
|
372
|
+
|
|
373
|
+
setListLoaderDisplayed(false);
|
|
374
|
+
if (request.status === 200) {
|
|
375
|
+
const responseJSON = JSON.parse(request.responseText);
|
|
376
|
+
|
|
377
|
+
_disableRowLoaders();
|
|
378
|
+
|
|
379
|
+
if (typeof responseJSON.results !== 'undefined') {
|
|
380
|
+
let results = [];
|
|
381
|
+
if (nearbyPlacesAPI === 'GoogleReverseGeocoding') {
|
|
382
|
+
results = _filterResultsByTypes(
|
|
383
|
+
responseJSON.results,
|
|
384
|
+
filterReverseGeocodingByTypes,
|
|
385
|
+
);
|
|
386
|
+
} else {
|
|
387
|
+
results = responseJSON.results;
|
|
388
|
+
}
|
|
389
|
+
|
|
390
|
+
resultsRef.current = results;
|
|
391
|
+
const newDataSource = buildRowsFromResults(results);
|
|
392
|
+
setDataSource(newDataSource);
|
|
393
|
+
// Auto-show list when results arrive if in 'auto' mode
|
|
394
|
+
if (
|
|
395
|
+
listViewDisplayedProp === 'auto' &&
|
|
396
|
+
newDataSource.length > 0
|
|
397
|
+
) {
|
|
398
|
+
setListViewDisplayed(true);
|
|
399
|
+
}
|
|
400
|
+
}
|
|
401
|
+
if (typeof responseJSON.error_message !== 'undefined') {
|
|
402
|
+
if (!onFail) {
|
|
403
|
+
console.warn(
|
|
404
|
+
'google places autocomplete: ' + responseJSON.error_message,
|
|
405
|
+
);
|
|
406
|
+
} else {
|
|
407
|
+
onFail(responseJSON.error_message);
|
|
408
|
+
}
|
|
409
|
+
}
|
|
410
|
+
}
|
|
411
|
+
};
|
|
412
|
+
|
|
413
|
+
let requestUrl = '';
|
|
414
|
+
if (nearbyPlacesAPI === 'GoogleReverseGeocoding') {
|
|
415
|
+
// your key must be allowed to use Google Maps Geocoding API
|
|
416
|
+
requestUrl =
|
|
417
|
+
`${url}/geocode/json?` +
|
|
418
|
+
Qs.stringify({
|
|
419
|
+
latlng: latitude + ',' + longitude,
|
|
420
|
+
key: query.key,
|
|
421
|
+
...GoogleReverseGeocodingQuery,
|
|
422
|
+
});
|
|
423
|
+
} else {
|
|
424
|
+
requestUrl =
|
|
425
|
+
`${url}/place/nearbysearch/json?` +
|
|
426
|
+
Qs.stringify({
|
|
427
|
+
location: latitude + ',' + longitude,
|
|
428
|
+
key: query.key,
|
|
429
|
+
...GooglePlacesSearchQuery,
|
|
430
|
+
});
|
|
431
|
+
}
|
|
432
|
+
|
|
433
|
+
request.open('GET', requestUrl);
|
|
434
|
+
|
|
435
|
+
request.withCredentials = requestShouldUseWithCredentials();
|
|
436
|
+
setRequestHeaders(request, getRequestHeaders(props.requestUrl));
|
|
437
|
+
|
|
438
|
+
request.send();
|
|
439
|
+
} else {
|
|
440
|
+
resultsRef.current = [];
|
|
441
|
+
setDataSource(buildRowsFromResults([]));
|
|
442
|
+
}
|
|
443
|
+
},
|
|
444
|
+
[
|
|
445
|
+
_abortRequests,
|
|
446
|
+
timeout,
|
|
447
|
+
onTimeout,
|
|
448
|
+
_disableRowLoaders,
|
|
449
|
+
nearbyPlacesAPI,
|
|
450
|
+
_filterResultsByTypes,
|
|
451
|
+
filterReverseGeocodingByTypes,
|
|
452
|
+
buildRowsFromResults,
|
|
453
|
+
listViewDisplayedProp,
|
|
454
|
+
onFail,
|
|
455
|
+
url,
|
|
456
|
+
query,
|
|
457
|
+
GoogleReverseGeocodingQuery,
|
|
458
|
+
GooglePlacesSearchQuery,
|
|
459
|
+
requestShouldUseWithCredentials,
|
|
460
|
+
props.requestUrl,
|
|
461
|
+
],
|
|
462
|
+
);
|
|
463
|
+
|
|
464
|
+
const _request = (text) => {
|
|
464
465
|
_abortRequests();
|
|
465
466
|
|
|
466
|
-
if (
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
) {
|
|
467
|
+
if (!url) {
|
|
468
|
+
return;
|
|
469
|
+
}
|
|
470
|
+
|
|
471
|
+
if (supportedPlatform() && text && text.length >= minLength) {
|
|
472
472
|
const request = new XMLHttpRequest();
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
request.
|
|
473
|
+
requestsRef.current.push(request);
|
|
474
|
+
|
|
475
|
+
request.timeout = timeout;
|
|
476
|
+
request.ontimeout = onTimeout;
|
|
476
477
|
request.onreadystatechange = () => {
|
|
477
478
|
if (request.readyState !== 4) {
|
|
478
479
|
setListLoaderDisplayed(true);
|
|
@@ -480,177 +481,294 @@ export const GooglePlacesAutocomplete = forwardRef((props, ref) => {
|
|
|
480
481
|
}
|
|
481
482
|
|
|
482
483
|
setListLoaderDisplayed(false);
|
|
484
|
+
|
|
483
485
|
if (request.status === 200) {
|
|
484
486
|
const responseJSON = JSON.parse(request.responseText);
|
|
485
487
|
|
|
486
|
-
|
|
488
|
+
if (typeof responseJSON.predictions !== 'undefined') {
|
|
489
|
+
const results =
|
|
490
|
+
nearbyPlacesAPI === 'GoogleReverseGeocoding'
|
|
491
|
+
? _filterResultsByTypes(
|
|
492
|
+
responseJSON.predictions,
|
|
493
|
+
filterReverseGeocodingByTypes,
|
|
494
|
+
)
|
|
495
|
+
: responseJSON.predictions;
|
|
487
496
|
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
if
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
props.filterReverseGeocodingByTypes,
|
|
495
|
-
);
|
|
496
|
-
} else {
|
|
497
|
-
results = responseJSON.results;
|
|
497
|
+
resultsRef.current = results;
|
|
498
|
+
const newDataSource = buildRowsFromResults(results, text);
|
|
499
|
+
setDataSource(newDataSource);
|
|
500
|
+
// Auto-show list when results arrive if in 'auto' mode
|
|
501
|
+
if (listViewDisplayedProp === 'auto' && newDataSource.length > 0) {
|
|
502
|
+
setListViewDisplayed(true);
|
|
498
503
|
}
|
|
504
|
+
}
|
|
505
|
+
if (typeof responseJSON.suggestions !== 'undefined') {
|
|
506
|
+
const results = _filterResultsByPlacePredictions(
|
|
507
|
+
responseJSON.suggestions,
|
|
508
|
+
);
|
|
499
509
|
|
|
500
|
-
|
|
501
|
-
|
|
510
|
+
resultsRef.current = results;
|
|
511
|
+
const newDataSource = buildRowsFromResults(results, text);
|
|
512
|
+
setDataSource(newDataSource);
|
|
513
|
+
// Auto-show list when results arrive if in 'auto' mode
|
|
514
|
+
if (listViewDisplayedProp === 'auto' && newDataSource.length > 0) {
|
|
515
|
+
setListViewDisplayed(true);
|
|
516
|
+
}
|
|
502
517
|
}
|
|
503
518
|
if (typeof responseJSON.error_message !== 'undefined') {
|
|
504
|
-
if (!
|
|
519
|
+
if (!onFail) {
|
|
505
520
|
console.warn(
|
|
506
521
|
'google places autocomplete: ' + responseJSON.error_message,
|
|
507
522
|
);
|
|
508
|
-
else {
|
|
509
|
-
|
|
523
|
+
} else {
|
|
524
|
+
onFail(responseJSON.error_message);
|
|
510
525
|
}
|
|
511
526
|
}
|
|
512
527
|
} else {
|
|
513
|
-
|
|
528
|
+
console.warn(
|
|
529
|
+
'google places autocomplete: request could not be completed or has been aborted',
|
|
530
|
+
);
|
|
514
531
|
}
|
|
515
532
|
};
|
|
516
533
|
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
|
|
534
|
+
if (props.preProcess) {
|
|
535
|
+
setStateText(props.preProcess(text));
|
|
536
|
+
}
|
|
537
|
+
|
|
538
|
+
if (isNewPlacesAPI) {
|
|
539
|
+
const keyQueryParam = query.key
|
|
540
|
+
? '?' +
|
|
541
|
+
Qs.stringify({
|
|
542
|
+
key: query.key,
|
|
543
|
+
})
|
|
544
|
+
: '';
|
|
545
|
+
request.open('POST', `${url}/v1/places:autocomplete${keyQueryParam}`);
|
|
546
|
+
} else {
|
|
547
|
+
request.open(
|
|
548
|
+
'GET',
|
|
549
|
+
`${url}/place/autocomplete/json?input=` +
|
|
550
|
+
encodeURIComponent(text) +
|
|
551
|
+
'&' +
|
|
552
|
+
Qs.stringify(query),
|
|
553
|
+
);
|
|
554
|
+
}
|
|
555
|
+
|
|
556
|
+
request.withCredentials = requestShouldUseWithCredentials();
|
|
557
|
+
setRequestHeaders(request, getRequestHeaders(props.requestUrl));
|
|
558
|
+
|
|
559
|
+
if (isNewPlacesAPI) {
|
|
560
|
+
const { key, locationbias, types, ...rest } = query;
|
|
561
|
+
request.send(
|
|
562
|
+
JSON.stringify({
|
|
563
|
+
input: text,
|
|
564
|
+
sessionToken,
|
|
565
|
+
...rest,
|
|
566
|
+
}),
|
|
567
|
+
);
|
|
568
|
+
} else {
|
|
569
|
+
request.send();
|
|
570
|
+
}
|
|
571
|
+
} else {
|
|
572
|
+
resultsRef.current = [];
|
|
573
|
+
setDataSource(buildRowsFromResults([]));
|
|
574
|
+
}
|
|
575
|
+
};
|
|
576
|
+
|
|
577
|
+
const getCurrentLocation = useCallback(() => {
|
|
578
|
+
let options = {
|
|
579
|
+
enableHighAccuracy: false,
|
|
580
|
+
timeout: 20000,
|
|
581
|
+
maximumAge: 1000,
|
|
582
|
+
};
|
|
583
|
+
|
|
584
|
+
if (enableHighAccuracyLocation && Platform.OS === 'android') {
|
|
585
|
+
options = {
|
|
586
|
+
enableHighAccuracy: true,
|
|
587
|
+
timeout: 20000,
|
|
588
|
+
};
|
|
589
|
+
}
|
|
590
|
+
const getCurrentPosition =
|
|
591
|
+
navigator.geolocation.getCurrentPosition ||
|
|
592
|
+
navigator.geolocation.default?.getCurrentPosition;
|
|
593
|
+
|
|
594
|
+
if (getCurrentPosition) {
|
|
595
|
+
getCurrentPosition(
|
|
596
|
+
(position) => {
|
|
597
|
+
if (nearbyPlacesAPI === 'None') {
|
|
598
|
+
const currentLocationData = {
|
|
599
|
+
description: currentLocationLabel,
|
|
600
|
+
geometry: {
|
|
601
|
+
location: {
|
|
602
|
+
lat: position.coords.latitude,
|
|
603
|
+
lng: position.coords.longitude,
|
|
604
|
+
},
|
|
605
|
+
},
|
|
606
|
+
};
|
|
607
|
+
|
|
608
|
+
_disableRowLoaders();
|
|
609
|
+
onPress(currentLocationData, currentLocationData);
|
|
610
|
+
} else {
|
|
611
|
+
_requestNearby(position.coords.latitude, position.coords.longitude);
|
|
612
|
+
}
|
|
613
|
+
},
|
|
614
|
+
(error) => {
|
|
615
|
+
_disableRowLoaders();
|
|
616
|
+
console.error(error.message);
|
|
617
|
+
},
|
|
618
|
+
options,
|
|
619
|
+
);
|
|
620
|
+
}
|
|
621
|
+
}, [
|
|
622
|
+
enableHighAccuracyLocation,
|
|
623
|
+
currentLocationLabel,
|
|
624
|
+
nearbyPlacesAPI,
|
|
625
|
+
_disableRowLoaders,
|
|
626
|
+
onPress,
|
|
627
|
+
_requestNearby,
|
|
628
|
+
]);
|
|
629
|
+
|
|
630
|
+
// ==========================================================================
|
|
631
|
+
// EVENT HANDLERS
|
|
632
|
+
// ==========================================================================
|
|
633
|
+
|
|
634
|
+
const _enableRowLoader = (rowData) => {
|
|
635
|
+
const rows = buildRowsFromResults(resultsRef.current);
|
|
636
|
+
for (let i = 0; i < rows.length; i++) {
|
|
637
|
+
if (
|
|
638
|
+
rows[i].place_id === rowData.place_id ||
|
|
639
|
+
(rows[i].isCurrentLocation === true &&
|
|
640
|
+
rowData.isCurrentLocation === true)
|
|
641
|
+
) {
|
|
642
|
+
rows[i].isLoading = true;
|
|
643
|
+
setDataSource(rows);
|
|
644
|
+
break;
|
|
645
|
+
}
|
|
646
|
+
}
|
|
647
|
+
};
|
|
648
|
+
|
|
649
|
+
const _disableRowLoaders = useCallback(() => {
|
|
650
|
+
for (let i = 0; i < resultsRef.current.length; i++) {
|
|
651
|
+
if (resultsRef.current[i].isLoading === true) {
|
|
652
|
+
resultsRef.current[i].isLoading = false;
|
|
653
|
+
}
|
|
654
|
+
}
|
|
655
|
+
|
|
656
|
+
setDataSource(buildRowsFromResults(resultsRef.current));
|
|
657
|
+
}, [buildRowsFromResults]);
|
|
658
|
+
|
|
659
|
+
const _onPress = (rowData) => {
|
|
660
|
+
if (rowData.isPredefinedPlace !== true && fetchDetails === true) {
|
|
661
|
+
if (rowData.isLoading === true) {
|
|
662
|
+
// already requesting
|
|
663
|
+
return;
|
|
535
664
|
}
|
|
536
665
|
|
|
537
|
-
|
|
666
|
+
Keyboard.dismiss();
|
|
538
667
|
|
|
539
|
-
|
|
540
|
-
setRequestHeaders(request, getRequestHeaders(props.requestUrl));
|
|
668
|
+
_abortRequests();
|
|
541
669
|
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
_results = [];
|
|
545
|
-
setDataSource(buildRowsFromResults([]));
|
|
546
|
-
}
|
|
547
|
-
};
|
|
670
|
+
// display loader
|
|
671
|
+
_enableRowLoader(rowData);
|
|
548
672
|
|
|
549
|
-
|
|
550
|
-
_abortRequests();
|
|
551
|
-
if (!url) {
|
|
552
|
-
return;
|
|
553
|
-
}
|
|
554
|
-
if (supportedPlatform() && text && text.length >= props.minLength) {
|
|
673
|
+
// fetch details
|
|
555
674
|
const request = new XMLHttpRequest();
|
|
556
|
-
|
|
557
|
-
request.timeout =
|
|
558
|
-
request.ontimeout =
|
|
675
|
+
requestsRef.current.push(request);
|
|
676
|
+
request.timeout = timeout;
|
|
677
|
+
request.ontimeout = onTimeout;
|
|
559
678
|
request.onreadystatechange = () => {
|
|
560
|
-
if (request.readyState !== 4)
|
|
561
|
-
setListLoaderDisplayed(true);
|
|
562
|
-
return;
|
|
563
|
-
}
|
|
679
|
+
if (request.readyState !== 4) return;
|
|
564
680
|
|
|
565
|
-
setListLoaderDisplayed(false);
|
|
566
681
|
if (request.status === 200) {
|
|
567
682
|
const responseJSON = JSON.parse(request.responseText);
|
|
683
|
+
if (
|
|
684
|
+
responseJSON.status === 'OK' ||
|
|
685
|
+
(isNewPlacesAPI && responseJSON.id)
|
|
686
|
+
) {
|
|
687
|
+
const details = isNewPlacesAPI ? responseJSON : responseJSON.result;
|
|
688
|
+
_disableRowLoaders();
|
|
689
|
+
_onBlur();
|
|
568
690
|
|
|
569
|
-
|
|
570
|
-
// if (_isMounted === true) {
|
|
571
|
-
const results =
|
|
572
|
-
props.nearbyPlacesAPI === 'GoogleReverseGeocoding'
|
|
573
|
-
? _filterResultsByTypes(
|
|
574
|
-
responseJSON.predictions,
|
|
575
|
-
props.filterReverseGeocodingByTypes,
|
|
576
|
-
)
|
|
577
|
-
: responseJSON.predictions;
|
|
691
|
+
setStateText(_renderDescription(rowData));
|
|
578
692
|
|
|
579
|
-
|
|
580
|
-
|
|
581
|
-
|
|
582
|
-
|
|
583
|
-
if (typeof responseJSON.suggestions !== 'undefined') {
|
|
584
|
-
const results = _filterResultsByPlacePredictions(
|
|
585
|
-
responseJSON.suggestions,
|
|
586
|
-
);
|
|
693
|
+
delete rowData.isLoading;
|
|
694
|
+
onPress(rowData, details);
|
|
695
|
+
} else {
|
|
696
|
+
_disableRowLoaders();
|
|
587
697
|
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
|
|
591
|
-
|
|
592
|
-
|
|
698
|
+
if (autoFillOnNotFound) {
|
|
699
|
+
setStateText(_renderDescription(rowData));
|
|
700
|
+
delete rowData.isLoading;
|
|
701
|
+
}
|
|
702
|
+
|
|
703
|
+
if (!onNotFound) {
|
|
593
704
|
console.warn(
|
|
594
|
-
'google places autocomplete: ' + responseJSON.
|
|
705
|
+
'google places autocomplete: ' + responseJSON.status,
|
|
595
706
|
);
|
|
596
|
-
else {
|
|
597
|
-
|
|
707
|
+
} else {
|
|
708
|
+
onNotFound(responseJSON);
|
|
598
709
|
}
|
|
599
710
|
}
|
|
600
711
|
} else {
|
|
601
|
-
|
|
712
|
+
_disableRowLoaders();
|
|
713
|
+
|
|
714
|
+
if (!onFail) {
|
|
715
|
+
console.warn(
|
|
716
|
+
'google places autocomplete: request could not be completed or has been aborted',
|
|
717
|
+
);
|
|
718
|
+
} else {
|
|
719
|
+
onFail('request could not be completed or has been aborted');
|
|
720
|
+
}
|
|
602
721
|
}
|
|
603
722
|
};
|
|
604
723
|
|
|
605
|
-
if (
|
|
606
|
-
|
|
607
|
-
|
|
608
|
-
|
|
609
|
-
if (props.isNewPlacesAPI) {
|
|
610
|
-
const keyQueryParam = props.query.key
|
|
611
|
-
? '?' +
|
|
724
|
+
if (isNewPlacesAPI) {
|
|
725
|
+
request.open(
|
|
726
|
+
'GET',
|
|
727
|
+
`${url}/v1/places/${rowData.place_id}?` +
|
|
612
728
|
Qs.stringify({
|
|
613
|
-
key:
|
|
614
|
-
|
|
615
|
-
|
|
616
|
-
|
|
729
|
+
key: query.key,
|
|
730
|
+
sessionToken,
|
|
731
|
+
fields,
|
|
732
|
+
}),
|
|
733
|
+
);
|
|
734
|
+
setSessionToken(randomUUID());
|
|
617
735
|
} else {
|
|
618
736
|
request.open(
|
|
619
737
|
'GET',
|
|
620
|
-
`${url}/place/
|
|
621
|
-
|
|
622
|
-
|
|
623
|
-
|
|
738
|
+
`${url}/place/details/json?` +
|
|
739
|
+
Qs.stringify({
|
|
740
|
+
key: query.key,
|
|
741
|
+
placeid: rowData.place_id,
|
|
742
|
+
language: query.language,
|
|
743
|
+
...GooglePlacesDetailsQuery,
|
|
744
|
+
}),
|
|
624
745
|
);
|
|
625
746
|
}
|
|
626
747
|
|
|
627
748
|
request.withCredentials = requestShouldUseWithCredentials();
|
|
628
749
|
setRequestHeaders(request, getRequestHeaders(props.requestUrl));
|
|
629
750
|
|
|
630
|
-
|
|
631
|
-
|
|
632
|
-
|
|
633
|
-
|
|
634
|
-
|
|
635
|
-
|
|
636
|
-
|
|
637
|
-
|
|
638
|
-
|
|
639
|
-
} else {
|
|
640
|
-
request.send();
|
|
641
|
-
}
|
|
751
|
+
request.send();
|
|
752
|
+
} else if (rowData.isCurrentLocation === true) {
|
|
753
|
+
// display loader
|
|
754
|
+
_enableRowLoader(rowData);
|
|
755
|
+
|
|
756
|
+
setStateText(_renderDescription(rowData));
|
|
757
|
+
|
|
758
|
+
delete rowData.isLoading;
|
|
759
|
+
getCurrentLocation();
|
|
642
760
|
} else {
|
|
643
|
-
|
|
644
|
-
|
|
761
|
+
setStateText(_renderDescription(rowData));
|
|
762
|
+
|
|
763
|
+
_onBlur();
|
|
764
|
+
delete rowData.isLoading;
|
|
765
|
+
const predefinedPlace = _getPredefinedPlace(rowData);
|
|
766
|
+
|
|
767
|
+
// sending predefinedPlace as details for predefined places
|
|
768
|
+
onPress(predefinedPlace, predefinedPlace);
|
|
645
769
|
}
|
|
646
770
|
};
|
|
647
771
|
|
|
648
|
-
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
649
|
-
const debounceData = useMemo(() => debounce(_request, props.debounce), [
|
|
650
|
-
props.query,
|
|
651
|
-
url,
|
|
652
|
-
]);
|
|
653
|
-
|
|
654
772
|
const _onChangeText = (text) => {
|
|
655
773
|
setStateText(text);
|
|
656
774
|
debounceData(text);
|
|
@@ -659,38 +777,46 @@ export const GooglePlacesAutocomplete = forwardRef((props, ref) => {
|
|
|
659
777
|
const _handleChangeText = (text) => {
|
|
660
778
|
_onChangeText(text);
|
|
661
779
|
|
|
662
|
-
const onChangeText =
|
|
780
|
+
const onChangeText = textInputProps?.onChangeText;
|
|
663
781
|
|
|
664
782
|
if (onChangeText) {
|
|
665
783
|
onChangeText(text);
|
|
666
784
|
}
|
|
667
785
|
};
|
|
668
786
|
|
|
669
|
-
const
|
|
670
|
-
|
|
787
|
+
const isNewFocusInAutocompleteResultList = ({
|
|
788
|
+
relatedTarget,
|
|
789
|
+
currentTarget,
|
|
790
|
+
}) => {
|
|
791
|
+
if (!relatedTarget) return false;
|
|
792
|
+
|
|
793
|
+
let node = relatedTarget.parentNode;
|
|
794
|
+
|
|
795
|
+
while (node) {
|
|
796
|
+
if (node.id === 'result-list-id') return true;
|
|
797
|
+
node = node.parentNode;
|
|
798
|
+
}
|
|
799
|
+
|
|
800
|
+
return false;
|
|
671
801
|
};
|
|
672
802
|
|
|
673
|
-
const
|
|
674
|
-
if (
|
|
675
|
-
|
|
803
|
+
const _onBlur = (e) => {
|
|
804
|
+
if (e && isNewFocusInAutocompleteResultList(e)) return;
|
|
805
|
+
|
|
806
|
+
if (!keepResultsAfterBlur) {
|
|
807
|
+
setListViewDisplayed(false);
|
|
676
808
|
}
|
|
809
|
+
inputRef?.current?.blur();
|
|
810
|
+
};
|
|
677
811
|
|
|
678
|
-
|
|
679
|
-
|
|
680
|
-
style={[
|
|
681
|
-
props.suppressDefaultStyles ? {} : defaultStyles.description,
|
|
682
|
-
props.styles.description,
|
|
683
|
-
rowData.isPredefinedPlace
|
|
684
|
-
? props.styles.predefinedPlacesDescription
|
|
685
|
-
: {},
|
|
686
|
-
]}
|
|
687
|
-
numberOfLines={props.numberOfLines}
|
|
688
|
-
>
|
|
689
|
-
{_renderDescription(rowData)}
|
|
690
|
-
</Text>
|
|
691
|
-
);
|
|
812
|
+
const _onFocus = () => {
|
|
813
|
+
setListViewDisplayed(true);
|
|
692
814
|
};
|
|
693
815
|
|
|
816
|
+
// ==========================================================================
|
|
817
|
+
// RENDER FUNCTIONS
|
|
818
|
+
// ==========================================================================
|
|
819
|
+
|
|
694
820
|
const _renderDescription = (rowData) => {
|
|
695
821
|
if (props.renderDescription) {
|
|
696
822
|
return props.renderDescription(rowData);
|
|
@@ -699,13 +825,17 @@ export const GooglePlacesAutocomplete = forwardRef((props, ref) => {
|
|
|
699
825
|
return rowData.description || rowData.formatted_address || rowData.name;
|
|
700
826
|
};
|
|
701
827
|
|
|
828
|
+
const _getRowLoader = () => {
|
|
829
|
+
return <ActivityIndicator animating={true} size='small' />;
|
|
830
|
+
};
|
|
831
|
+
|
|
702
832
|
const _renderLoader = (rowData) => {
|
|
703
833
|
if (rowData.isLoading === true) {
|
|
704
834
|
return (
|
|
705
835
|
<View
|
|
706
836
|
style={[
|
|
707
|
-
|
|
708
|
-
|
|
837
|
+
suppressDefaultStyles ? {} : defaultStyles.loader,
|
|
838
|
+
styles?.loader,
|
|
709
839
|
]}
|
|
710
840
|
>
|
|
711
841
|
{_getRowLoader()}
|
|
@@ -716,26 +846,45 @@ export const GooglePlacesAutocomplete = forwardRef((props, ref) => {
|
|
|
716
846
|
return null;
|
|
717
847
|
};
|
|
718
848
|
|
|
849
|
+
const _renderRowData = (rowData, index) => {
|
|
850
|
+
if (props.renderRow) {
|
|
851
|
+
return props.renderRow(rowData, index);
|
|
852
|
+
}
|
|
853
|
+
|
|
854
|
+
return (
|
|
855
|
+
<Text
|
|
856
|
+
style={[
|
|
857
|
+
suppressDefaultStyles ? {} : defaultStyles.description,
|
|
858
|
+
styles?.description,
|
|
859
|
+
rowData.isPredefinedPlace ? styles?.predefinedPlacesDescription : {},
|
|
860
|
+
]}
|
|
861
|
+
numberOfLines={numberOfLines}
|
|
862
|
+
>
|
|
863
|
+
{_renderDescription(rowData)}
|
|
864
|
+
</Text>
|
|
865
|
+
);
|
|
866
|
+
};
|
|
867
|
+
|
|
719
868
|
const _renderRow = (rowData = {}, index) => {
|
|
720
869
|
return (
|
|
721
870
|
<ScrollView
|
|
722
871
|
contentContainerStyle={
|
|
723
|
-
|
|
872
|
+
isRowScrollable ? { minWidth: '100%' } : { width: '100%' }
|
|
724
873
|
}
|
|
725
|
-
scrollEnabled={
|
|
726
|
-
keyboardShouldPersistTaps={
|
|
874
|
+
scrollEnabled={isRowScrollable}
|
|
875
|
+
keyboardShouldPersistTaps={keyboardShouldPersistTaps}
|
|
727
876
|
horizontal={true}
|
|
728
877
|
showsHorizontalScrollIndicator={false}
|
|
729
878
|
showsVerticalScrollIndicator={false}
|
|
730
879
|
>
|
|
731
880
|
<Pressable
|
|
732
881
|
style={({ hovered, pressed }) => [
|
|
733
|
-
|
|
882
|
+
isRowScrollable ? { minWidth: '100%' } : { width: '100%' },
|
|
734
883
|
{
|
|
735
884
|
backgroundColor: pressed
|
|
736
|
-
?
|
|
885
|
+
? listUnderlayColor
|
|
737
886
|
: hovered
|
|
738
|
-
?
|
|
887
|
+
? listHoverColor
|
|
739
888
|
: undefined,
|
|
740
889
|
},
|
|
741
890
|
]}
|
|
@@ -744,9 +893,9 @@ export const GooglePlacesAutocomplete = forwardRef((props, ref) => {
|
|
|
744
893
|
>
|
|
745
894
|
<View
|
|
746
895
|
style={[
|
|
747
|
-
|
|
748
|
-
|
|
749
|
-
rowData.isPredefinedPlace ?
|
|
896
|
+
suppressDefaultStyles ? {} : defaultStyles.row,
|
|
897
|
+
styles?.row,
|
|
898
|
+
rowData.isPredefinedPlace ? styles?.specialItemRow : {},
|
|
750
899
|
]}
|
|
751
900
|
>
|
|
752
901
|
{_renderLoader(rowData)}
|
|
@@ -766,40 +915,29 @@ export const GooglePlacesAutocomplete = forwardRef((props, ref) => {
|
|
|
766
915
|
<View
|
|
767
916
|
key={`${sectionID}-${rowID}`}
|
|
768
917
|
style={[
|
|
769
|
-
|
|
770
|
-
|
|
918
|
+
suppressDefaultStyles ? {} : defaultStyles.separator,
|
|
919
|
+
styles?.separator,
|
|
771
920
|
]}
|
|
772
921
|
/>
|
|
773
922
|
);
|
|
774
923
|
};
|
|
775
924
|
|
|
776
|
-
const
|
|
777
|
-
|
|
778
|
-
|
|
779
|
-
|
|
780
|
-
if (!relatedTarget) return false;
|
|
925
|
+
const _shouldShowPoweredLogo = () => {
|
|
926
|
+
if (!enablePoweredByContainer || dataSource.length === 0) {
|
|
927
|
+
return false;
|
|
928
|
+
}
|
|
781
929
|
|
|
782
|
-
|
|
930
|
+
for (let i = 0; i < dataSource.length; i++) {
|
|
931
|
+
const row = dataSource[i];
|
|
783
932
|
|
|
784
|
-
|
|
785
|
-
|
|
786
|
-
|
|
933
|
+
if (!('isCurrentLocation' in row) && !('isPredefinedPlace' in row)) {
|
|
934
|
+
return true;
|
|
935
|
+
}
|
|
787
936
|
}
|
|
788
937
|
|
|
789
938
|
return false;
|
|
790
939
|
};
|
|
791
940
|
|
|
792
|
-
const _onBlur = (e) => {
|
|
793
|
-
if (e && isNewFocusInAutocompleteResultList(e)) return;
|
|
794
|
-
|
|
795
|
-
if (!props.keepResultsAfterBlur) {
|
|
796
|
-
setListViewDisplayed(false);
|
|
797
|
-
}
|
|
798
|
-
inputRef?.current?.blur();
|
|
799
|
-
};
|
|
800
|
-
|
|
801
|
-
const _onFocus = () => setListViewDisplayed(true);
|
|
802
|
-
|
|
803
941
|
const _renderPoweredLogo = () => {
|
|
804
942
|
if (!_shouldShowPoweredLogo()) {
|
|
805
943
|
return null;
|
|
@@ -808,15 +946,15 @@ export const GooglePlacesAutocomplete = forwardRef((props, ref) => {
|
|
|
808
946
|
return (
|
|
809
947
|
<View
|
|
810
948
|
style={[
|
|
811
|
-
|
|
949
|
+
suppressDefaultStyles ? {} : defaultStyles.row,
|
|
812
950
|
defaultStyles.poweredContainer,
|
|
813
|
-
|
|
951
|
+
styles?.poweredContainer,
|
|
814
952
|
]}
|
|
815
953
|
>
|
|
816
954
|
<Image
|
|
817
955
|
style={[
|
|
818
|
-
|
|
819
|
-
|
|
956
|
+
suppressDefaultStyles ? {} : defaultStyles.powered,
|
|
957
|
+
styles?.powered,
|
|
820
958
|
]}
|
|
821
959
|
resizeMode='contain'
|
|
822
960
|
source={require('./images/powered_by_google_on_white.png')}
|
|
@@ -825,71 +963,73 @@ export const GooglePlacesAutocomplete = forwardRef((props, ref) => {
|
|
|
825
963
|
);
|
|
826
964
|
};
|
|
827
965
|
|
|
828
|
-
const _shouldShowPoweredLogo = () => {
|
|
829
|
-
if (!props.enablePoweredByContainer || dataSource.length === 0) {
|
|
830
|
-
return false;
|
|
831
|
-
}
|
|
832
|
-
|
|
833
|
-
for (let i = 0; i < dataSource.length; i++) {
|
|
834
|
-
let row = dataSource[i];
|
|
835
|
-
|
|
836
|
-
if (
|
|
837
|
-
!row.hasOwnProperty('isCurrentLocation') &&
|
|
838
|
-
!row.hasOwnProperty('isPredefinedPlace')
|
|
839
|
-
) {
|
|
840
|
-
return true;
|
|
841
|
-
}
|
|
842
|
-
}
|
|
843
|
-
|
|
844
|
-
return false;
|
|
845
|
-
};
|
|
846
|
-
|
|
847
966
|
const _renderLeftButton = () => {
|
|
848
967
|
if (props.renderLeftButton) {
|
|
849
968
|
return props.renderLeftButton();
|
|
850
969
|
}
|
|
970
|
+
return null;
|
|
851
971
|
};
|
|
852
972
|
|
|
853
973
|
const _renderRightButton = () => {
|
|
854
974
|
if (props.renderRightButton) {
|
|
855
975
|
return props.renderRightButton();
|
|
856
976
|
}
|
|
977
|
+
return null;
|
|
857
978
|
};
|
|
858
979
|
|
|
859
980
|
const _getFlatList = () => {
|
|
860
|
-
const
|
|
981
|
+
const keyExtractor = (item, index) => {
|
|
982
|
+
// Use stable keys based on item data
|
|
983
|
+
if (item.place_id) {
|
|
984
|
+
return `place_${item.place_id}_${index}`;
|
|
985
|
+
}
|
|
986
|
+
if (item.isCurrentLocation) {
|
|
987
|
+
return 'current_location';
|
|
988
|
+
}
|
|
989
|
+
if (item.isPredefinedPlace && item.description) {
|
|
990
|
+
return `predefined_${item.description}_${index}`;
|
|
991
|
+
}
|
|
992
|
+
// Fallback to index-based key (should rarely happen)
|
|
993
|
+
return `item_${index}`;
|
|
994
|
+
};
|
|
861
995
|
|
|
862
|
-
if
|
|
996
|
+
// Show list if:
|
|
997
|
+
// 1. Platform is supported
|
|
998
|
+
// 2. There's data to show (dataSource has items)
|
|
999
|
+
// 3. listViewDisplayed is true OR we're in 'auto' mode (auto-shows when data exists)
|
|
1000
|
+
const isAutoMode =
|
|
1001
|
+
listViewDisplayedProp === 'auto' || listViewDisplayedProp === undefined;
|
|
1002
|
+
const shouldShowList =
|
|
863
1003
|
supportedPlatform() &&
|
|
864
|
-
|
|
865
|
-
|
|
866
|
-
|
|
867
|
-
|
|
868
|
-
) {
|
|
1004
|
+
dataSource.length > 0 &&
|
|
1005
|
+
(listViewDisplayed === true || isAutoMode);
|
|
1006
|
+
|
|
1007
|
+
if (shouldShowList) {
|
|
869
1008
|
return (
|
|
870
1009
|
<FlatList
|
|
871
1010
|
nativeID='result-list-id'
|
|
872
|
-
scrollEnabled={!
|
|
1011
|
+
scrollEnabled={!disableScroll}
|
|
1012
|
+
nestedScrollEnabled={true}
|
|
873
1013
|
style={[
|
|
874
|
-
|
|
875
|
-
|
|
1014
|
+
suppressDefaultStyles ? {} : defaultStyles.listView,
|
|
1015
|
+
styles?.listView,
|
|
876
1016
|
]}
|
|
877
1017
|
data={dataSource}
|
|
878
|
-
keyExtractor={
|
|
1018
|
+
keyExtractor={keyExtractor}
|
|
879
1019
|
extraData={[dataSource, props]}
|
|
880
1020
|
ItemSeparatorComponent={_renderSeparator}
|
|
881
1021
|
renderItem={({ item, index }) => _renderRow(item, index)}
|
|
882
1022
|
ListEmptyComponent={
|
|
883
1023
|
listLoaderDisplayed
|
|
884
1024
|
? props.listLoaderComponent
|
|
885
|
-
: stateText.length >
|
|
1025
|
+
: stateText.length > minLength && props.listEmptyComponent
|
|
886
1026
|
}
|
|
887
1027
|
ListHeaderComponent={
|
|
888
1028
|
props.renderHeaderComponent &&
|
|
889
1029
|
props.renderHeaderComponent(stateText)
|
|
890
1030
|
}
|
|
891
1031
|
ListFooterComponent={_renderPoweredLogo}
|
|
892
|
-
{...
|
|
1032
|
+
{...restProps}
|
|
893
1033
|
/>
|
|
894
1034
|
);
|
|
895
1035
|
}
|
|
@@ -897,52 +1037,141 @@ export const GooglePlacesAutocomplete = forwardRef((props, ref) => {
|
|
|
897
1037
|
return null;
|
|
898
1038
|
};
|
|
899
1039
|
|
|
900
|
-
|
|
901
|
-
|
|
902
|
-
|
|
903
|
-
|
|
1040
|
+
// ==========================================================================
|
|
1041
|
+
// EFFECTS
|
|
1042
|
+
// ==========================================================================
|
|
1043
|
+
|
|
1044
|
+
// Update query ref when query changes
|
|
1045
|
+
useEffect(() => {
|
|
1046
|
+
queryRef.current = query;
|
|
1047
|
+
}, [query]);
|
|
1048
|
+
|
|
1049
|
+
// Initialize URL from requestUrl prop
|
|
1050
|
+
useEffect(() => {
|
|
1051
|
+
setUrl(getRequestUrl(props.requestUrl));
|
|
1052
|
+
}, [props.requestUrl]);
|
|
1053
|
+
|
|
1054
|
+
// Initialize dataSource on mount
|
|
1055
|
+
useEffect(() => {
|
|
1056
|
+
setDataSource(buildRowsFromResults([]));
|
|
1057
|
+
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
1058
|
+
}, []);
|
|
1059
|
+
|
|
1060
|
+
// Keep requestRef updated
|
|
1061
|
+
requestRef.current = _request;
|
|
1062
|
+
|
|
1063
|
+
// Debounce setup
|
|
1064
|
+
const debounceData = useMemo(() => {
|
|
1065
|
+
return debounce((text) => requestRef.current(text), debounceMs);
|
|
1066
|
+
}, [debounceMs]);
|
|
1067
|
+
|
|
1068
|
+
useEffect(() => {
|
|
1069
|
+
return () => {
|
|
1070
|
+
// Cleanup debounced function on unmount
|
|
1071
|
+
if (debounceData.cancel) {
|
|
1072
|
+
debounceData.cancel();
|
|
1073
|
+
}
|
|
1074
|
+
};
|
|
1075
|
+
}, [debounceData]);
|
|
1076
|
+
|
|
1077
|
+
// Reload search when query changes (using string comparison to avoid object reference issues)
|
|
1078
|
+
useEffect(() => {
|
|
1079
|
+
const queryChanged = prevQueryStringRef.current !== queryString;
|
|
1080
|
+
|
|
1081
|
+
if (queryChanged) {
|
|
1082
|
+
prevQueryStringRef.current = queryString;
|
|
1083
|
+
if (stateText && stateText.length >= minLength) {
|
|
1084
|
+
debounceData(stateText);
|
|
1085
|
+
}
|
|
1086
|
+
}
|
|
1087
|
+
|
|
1088
|
+
return () => {
|
|
1089
|
+
_abortRequests();
|
|
1090
|
+
};
|
|
1091
|
+
}, [queryString, debounceData, stateText, minLength, _abortRequests]);
|
|
1092
|
+
|
|
1093
|
+
// Auto-show list when dataSource has items in 'auto' mode
|
|
1094
|
+
useEffect(() => {
|
|
1095
|
+
if (
|
|
1096
|
+
listViewDisplayedProp === 'auto' &&
|
|
1097
|
+
dataSource.length > 0 &&
|
|
1098
|
+
!listViewDisplayed
|
|
1099
|
+
) {
|
|
1100
|
+
setListViewDisplayed(true);
|
|
1101
|
+
}
|
|
1102
|
+
}, [dataSource.length, listViewDisplayedProp, listViewDisplayed]);
|
|
1103
|
+
|
|
1104
|
+
// ==========================================================================
|
|
1105
|
+
// IMPERATIVE HANDLE
|
|
1106
|
+
// ==========================================================================
|
|
1107
|
+
|
|
1108
|
+
useImperativeHandle(
|
|
1109
|
+
ref,
|
|
1110
|
+
() => ({
|
|
1111
|
+
setAddressText: (address) => {
|
|
1112
|
+
setStateText(address);
|
|
1113
|
+
},
|
|
1114
|
+
getAddressText: () => stateText,
|
|
1115
|
+
blur: () => inputRef.current?.blur(),
|
|
1116
|
+
focus: () => inputRef.current?.focus(),
|
|
1117
|
+
isFocused: () => inputRef.current?.isFocused(),
|
|
1118
|
+
clear: () => inputRef.current?.clear(),
|
|
1119
|
+
getCurrentLocation,
|
|
1120
|
+
}),
|
|
1121
|
+
[stateText, getCurrentLocation],
|
|
1122
|
+
);
|
|
1123
|
+
|
|
1124
|
+
// ==========================================================================
|
|
1125
|
+
// MAIN RENDER
|
|
1126
|
+
// ==========================================================================
|
|
1127
|
+
|
|
1128
|
+
const {
|
|
1129
|
+
onFocus: textInputOnFocus,
|
|
1130
|
+
onBlur: textInputOnBlur,
|
|
1131
|
+
onChangeText: textInputOnChangeText, // destructuring here stops this being set after onChangeText={_handleChangeText}
|
|
904
1132
|
clearButtonMode,
|
|
905
1133
|
InputComp,
|
|
906
1134
|
...userProps
|
|
907
|
-
} =
|
|
1135
|
+
} = textInputProps || {};
|
|
908
1136
|
const TextInputComp = InputComp || TextInput;
|
|
1137
|
+
|
|
909
1138
|
return (
|
|
910
1139
|
<View
|
|
911
1140
|
style={[
|
|
912
|
-
|
|
913
|
-
|
|
1141
|
+
suppressDefaultStyles ? {} : defaultStyles.container,
|
|
1142
|
+
styles?.container,
|
|
914
1143
|
]}
|
|
915
1144
|
pointerEvents='box-none'
|
|
916
1145
|
>
|
|
917
|
-
{!
|
|
1146
|
+
{!textInputHide && (
|
|
918
1147
|
<View
|
|
919
1148
|
style={[
|
|
920
|
-
|
|
921
|
-
|
|
1149
|
+
suppressDefaultStyles ? {} : defaultStyles.textInputContainer,
|
|
1150
|
+
styles?.textInputContainer,
|
|
922
1151
|
]}
|
|
923
1152
|
>
|
|
924
1153
|
{_renderLeftButton()}
|
|
925
1154
|
<TextInputComp
|
|
926
1155
|
ref={inputRef}
|
|
927
1156
|
style={[
|
|
928
|
-
|
|
929
|
-
|
|
1157
|
+
suppressDefaultStyles ? {} : defaultStyles.textInput,
|
|
1158
|
+
styles?.textInput,
|
|
930
1159
|
]}
|
|
931
1160
|
value={stateText}
|
|
932
|
-
placeholder={
|
|
1161
|
+
placeholder={placeholder}
|
|
933
1162
|
onFocus={
|
|
934
|
-
|
|
1163
|
+
textInputOnFocus
|
|
935
1164
|
? (e) => {
|
|
936
1165
|
_onFocus();
|
|
937
|
-
|
|
1166
|
+
textInputOnFocus(e);
|
|
938
1167
|
}
|
|
939
1168
|
: _onFocus
|
|
940
1169
|
}
|
|
941
1170
|
onBlur={
|
|
942
|
-
|
|
1171
|
+
textInputOnBlur
|
|
943
1172
|
? (e) => {
|
|
944
1173
|
_onBlur(e);
|
|
945
|
-
|
|
1174
|
+
textInputOnBlur(e);
|
|
946
1175
|
}
|
|
947
1176
|
: _onBlur
|
|
948
1177
|
}
|
|
@@ -960,109 +1189,6 @@ export const GooglePlacesAutocomplete = forwardRef((props, ref) => {
|
|
|
960
1189
|
);
|
|
961
1190
|
});
|
|
962
1191
|
|
|
963
|
-
GooglePlacesAutocomplete.propTypes = {
|
|
964
|
-
autoFillOnNotFound: PropTypes.bool,
|
|
965
|
-
currentLocation: PropTypes.bool,
|
|
966
|
-
currentLocationLabel: PropTypes.string,
|
|
967
|
-
debounce: PropTypes.number,
|
|
968
|
-
disableScroll: PropTypes.bool,
|
|
969
|
-
enableHighAccuracyLocation: PropTypes.bool,
|
|
970
|
-
enablePoweredByContainer: PropTypes.bool,
|
|
971
|
-
fetchDetails: PropTypes.bool,
|
|
972
|
-
filterReverseGeocodingByTypes: PropTypes.array,
|
|
973
|
-
GooglePlacesDetailsQuery: PropTypes.object,
|
|
974
|
-
GooglePlacesSearchQuery: PropTypes.object,
|
|
975
|
-
GoogleReverseGeocodingQuery: PropTypes.object,
|
|
976
|
-
inbetweenCompo: PropTypes.object,
|
|
977
|
-
isRowScrollable: PropTypes.bool,
|
|
978
|
-
keyboardShouldPersistTaps: PropTypes.oneOf(['never', 'always', 'handled']),
|
|
979
|
-
listEmptyComponent: PropTypes.element,
|
|
980
|
-
listLoaderComponent: PropTypes.element,
|
|
981
|
-
listHoverColor: PropTypes.string,
|
|
982
|
-
listUnderlayColor: PropTypes.string,
|
|
983
|
-
// Must write it this way: https://stackoverflow.com/a/54290946/7180620
|
|
984
|
-
listViewDisplayed: PropTypes.oneOfType([
|
|
985
|
-
PropTypes.bool,
|
|
986
|
-
PropTypes.oneOf(['auto']),
|
|
987
|
-
]),
|
|
988
|
-
keepResultsAfterBlur: PropTypes.bool,
|
|
989
|
-
minLength: PropTypes.number,
|
|
990
|
-
nearbyPlacesAPI: PropTypes.string,
|
|
991
|
-
numberOfLines: PropTypes.number,
|
|
992
|
-
onFail: PropTypes.func,
|
|
993
|
-
onNotFound: PropTypes.func,
|
|
994
|
-
onPress: PropTypes.func,
|
|
995
|
-
onTimeout: PropTypes.func,
|
|
996
|
-
placeholder: PropTypes.string,
|
|
997
|
-
predefinedPlaces: PropTypes.array,
|
|
998
|
-
predefinedPlacesAlwaysVisible: PropTypes.bool,
|
|
999
|
-
preProcess: PropTypes.func,
|
|
1000
|
-
query: PropTypes.object,
|
|
1001
|
-
renderDescription: PropTypes.func,
|
|
1002
|
-
renderHeaderComponent: PropTypes.func,
|
|
1003
|
-
renderLeftButton: PropTypes.func,
|
|
1004
|
-
renderRightButton: PropTypes.func,
|
|
1005
|
-
renderRow: PropTypes.func,
|
|
1006
|
-
requestUrl: PropTypes.shape({
|
|
1007
|
-
url: PropTypes.string,
|
|
1008
|
-
useOnPlatform: PropTypes.oneOf(['web', 'all']),
|
|
1009
|
-
headers: PropTypes.objectOf(PropTypes.string),
|
|
1010
|
-
}),
|
|
1011
|
-
styles: PropTypes.object,
|
|
1012
|
-
suppressDefaultStyles: PropTypes.bool,
|
|
1013
|
-
textInputHide: PropTypes.bool,
|
|
1014
|
-
textInputProps: PropTypes.object,
|
|
1015
|
-
timeout: PropTypes.number,
|
|
1016
|
-
isNewPlacesAPI: PropTypes.bool,
|
|
1017
|
-
fields: PropTypes.string,
|
|
1018
|
-
};
|
|
1019
|
-
|
|
1020
|
-
GooglePlacesAutocomplete.defaultProps = {
|
|
1021
|
-
autoFillOnNotFound: false,
|
|
1022
|
-
currentLocation: false,
|
|
1023
|
-
currentLocationLabel: 'Current location',
|
|
1024
|
-
debounce: 0,
|
|
1025
|
-
disableScroll: false,
|
|
1026
|
-
enableHighAccuracyLocation: true,
|
|
1027
|
-
enablePoweredByContainer: true,
|
|
1028
|
-
fetchDetails: false,
|
|
1029
|
-
filterReverseGeocodingByTypes: [],
|
|
1030
|
-
GooglePlacesDetailsQuery: {},
|
|
1031
|
-
GooglePlacesSearchQuery: {
|
|
1032
|
-
rankby: 'distance',
|
|
1033
|
-
type: 'restaurant',
|
|
1034
|
-
},
|
|
1035
|
-
GoogleReverseGeocodingQuery: {},
|
|
1036
|
-
isRowScrollable: true,
|
|
1037
|
-
keyboardShouldPersistTaps: 'always',
|
|
1038
|
-
listHoverColor: '#ececec',
|
|
1039
|
-
listUnderlayColor: '#c8c7cc',
|
|
1040
|
-
listViewDisplayed: 'auto',
|
|
1041
|
-
keepResultsAfterBlur: false,
|
|
1042
|
-
minLength: 0,
|
|
1043
|
-
nearbyPlacesAPI: 'GooglePlacesSearch',
|
|
1044
|
-
numberOfLines: 1,
|
|
1045
|
-
onFail: () => {},
|
|
1046
|
-
onNotFound: () => {},
|
|
1047
|
-
onPress: () => {},
|
|
1048
|
-
onTimeout: () => console.warn('google places autocomplete: request timeout'),
|
|
1049
|
-
placeholder: '',
|
|
1050
|
-
predefinedPlaces: [],
|
|
1051
|
-
predefinedPlacesAlwaysVisible: false,
|
|
1052
|
-
query: {
|
|
1053
|
-
key: 'missing api key',
|
|
1054
|
-
language: 'en',
|
|
1055
|
-
types: 'geocode',
|
|
1056
|
-
},
|
|
1057
|
-
styles: {},
|
|
1058
|
-
suppressDefaultStyles: false,
|
|
1059
|
-
textInputHide: false,
|
|
1060
|
-
textInputProps: {},
|
|
1061
|
-
timeout: 20000,
|
|
1062
|
-
isNewPlacesAPI: false,
|
|
1063
|
-
fields: '*',
|
|
1064
|
-
};
|
|
1065
|
-
|
|
1066
1192
|
GooglePlacesAutocomplete.displayName = 'GooglePlacesAutocomplete';
|
|
1067
1193
|
|
|
1068
1194
|
export default { GooglePlacesAutocomplete };
|