From cb97447302c332b9226765000c355fe644e9b52b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Zbigniew=20Zag=C3=B3rski?= Date: Thu, 28 Apr 2022 16:14:31 +0200 Subject: [PATCH] extenalize state of GeocoderWidge --- .../src/hooks/useGeocoderWidgetController.js | 106 ++++++++++++++++++ packages/react-widgets/src/index.d.ts | 3 +- packages/react-widgets/src/index.js | 1 + .../src/widgets/GeocoderWidget.js | 103 ++--------------- 4 files changed, 120 insertions(+), 93 deletions(-) create mode 100644 packages/react-widgets/src/hooks/useGeocoderWidgetController.js diff --git a/packages/react-widgets/src/hooks/useGeocoderWidgetController.js b/packages/react-widgets/src/hooks/useGeocoderWidgetController.js new file mode 100644 index 000000000..2176ba3e5 --- /dev/null +++ b/packages/react-widgets/src/hooks/useGeocoderWidgetController.js @@ -0,0 +1,106 @@ +import { selectOAuthCredentials, setViewState } from '@carto/react-redux/'; +import { useState } from 'react'; +import { useDispatch, useSelector } from 'react-redux'; +import { geocodeStreetPoint } from '../models/GeocodingModel'; + +const DEFAULT_COUNTRY = ''; // 'SPAIN', 'USA' + +const setGeocoderResult = (payload) => ({ + type: 'carto/setGeocoderResult', + payload +}); + +/** + * Controller for component. + * + * @param {object} props + * @param {Function} [props.onError] - Function to handle error messages from the widget. + * @param {Boolean=} [props.zoomToResult] - Optional, default true. Whether control should zoom map on result. + */ +export default function useGeocoderWidgetController(props) { + const oauthCredentials = useSelector(selectOAuthCredentials); + const globalCredentials = useSelector((state) => state.carto.credentials); + const credentials = oauthCredentials || globalCredentials; + // Component local state and events handling + const [searchText, setSearchText] = useState(''); + const [loading, setIsLoading] = useState(false); + + // Actions dispatched + const dispatch = useDispatch(); + + const handleChange = (e) => { + setSearchText(e.target.value); + }; + + const handleInput = (e) => { + if (e.target.value === '') { + updateMarker(null); + } + }; + + const handleKeyPress = async (e) => { + if (e.keyCode === 13) { + // Force blur to hide virtual keyboards on mobile and search + e.target.blur(); + } + }; + + // Needed to handle keyboard "Done" button on iOS + const handleBlur = async () => { + if (searchText.length) { + handleSearch(); + } + }; + + const handleSearch = async () => { + if (credentials) { + try { + setIsLoading(true); + const result = await geocodeStreetPoint(credentials, { + searchText, + country: DEFAULT_COUNTRY + }); + if (result) { + if (props.zoomToResult !== false) { + zoomToResult(result); + } + updateMarker(result); + } + } catch (e) { + handleGeocodeError(e); + } finally { + setIsLoading(false); + } + } + }; + + const zoomToResult = (result) => { + dispatch( + setViewState({ + longitude: result.longitude, + latitude: result.latitude, + zoom: 16, + transitionDuration: 500 + }) + ); + }; + + const updateMarker = (result) => { + dispatch(setGeocoderResult(result)); + }; + + const handleGeocodeError = (error) => { + if (props.onError) { + props.onError(error); + } + }; + + return { + searchText, + loading, + handleChange, + handleInput, + handleKeyPress, + handleBlur + }; +} diff --git a/packages/react-widgets/src/index.d.ts b/packages/react-widgets/src/index.d.ts index 1456c11f8..5a2bf46c4 100644 --- a/packages/react-widgets/src/index.d.ts +++ b/packages/react-widgets/src/index.d.ts @@ -17,4 +17,5 @@ export { export { default as TimeSeriesWidget } from './widgets/TimeSeriesWidget'; export { default as useSourceFilters } from './hooks/useSourceFilters'; export { default as FeatureSelectionWidget } from './widgets/FeatureSelectionWidget'; -export { default as FeatureSelectionLayer } from './layers/FeatureSelectionLayer'; \ No newline at end of file +export { default as FeatureSelectionLayer } from './layers/FeatureSelectionLayer'; +export { default as useGeocoderWidgetController } from './hooks/useGeocoderWidgetController'; \ No newline at end of file diff --git a/packages/react-widgets/src/index.js b/packages/react-widgets/src/index.js index 516f94cd1..7d511da50 100644 --- a/packages/react-widgets/src/index.js +++ b/packages/react-widgets/src/index.js @@ -18,3 +18,4 @@ export { } from './models'; export { default as useSourceFilters } from './hooks/useSourceFilters'; export { default as FeatureSelectionLayer } from './layers/FeatureSelectionLayer'; +export { default as useGeocoderWidgetController } from './hooks/useGeocoderWidgetController'; diff --git a/packages/react-widgets/src/widgets/GeocoderWidget.js b/packages/react-widgets/src/widgets/GeocoderWidget.js index 85eaaf63c..d2f9b4502 100644 --- a/packages/react-widgets/src/widgets/GeocoderWidget.js +++ b/packages/react-widgets/src/widgets/GeocoderWidget.js @@ -1,19 +1,12 @@ -import React, { useState, useEffect, useRef } from 'react'; -import { useSelector, useDispatch } from 'react-redux'; +import React, { useEffect } from 'react'; +import { useDispatch } from 'react-redux'; import { PropTypes } from 'prop-types'; -import { geocodeStreetPoint } from '../models'; -import { addLayer, selectOAuthCredentials, setViewState } from '@carto/react-redux'; +import { addLayer } from '@carto/react-redux'; import { CircularProgress, InputBase, Paper, SvgIcon } from '@material-ui/core'; import { makeStyles } from '@material-ui/core/styles'; - -const DEFAULT_COUNTRY = ''; // 'SPAIN', 'USA' - -const setGeocoderResult = (payload) => ({ - type: 'carto/setGeocoderResult', - payload -}); +import useGeocoderWidgetController from '../hooks/useGeocoderWidgetController'; const useStyles = makeStyles((theme) => ({ paperInput: { @@ -54,20 +47,14 @@ const SearchIcon = (args) => ( * @param {Object} [props.className] - Material-UI withStyle class for styling * @param {Function} [props.onError] - Function to handle error messages from the widget. * @param {Boolean=} [props.zoomToResult] - Optional, default true. Whether control should zoom map on result. - * - * @see geocodeStreetPoint */ function GeocoderWidget(props) { - const inputRef = useRef(); - const oauthCredentials = useSelector(selectOAuthCredentials); - const globalCredentials = useSelector((state) => state.carto.credentials); - const credentials = oauthCredentials || globalCredentials; - // Component local state and events handling - const [searchText, setSearchText] = useState(''); - const [loading, setIsLoading] = useState(false); - // Actions dispatched + const classes = useStyles(); const dispatch = useDispatch(); + const { searchText, loading, handleChange, handleInput, handleKeyPress, handleBlur } = + useGeocoderWidgetController(props); + useEffect(() => { // layer to display the geocoded direction marker dispatch( @@ -77,75 +64,6 @@ function GeocoderWidget(props) { ); }, [dispatch]); - const classes = useStyles(); - - const handleChange = (e) => { - setSearchText(e.target.value); - }; - - const handleInput = (e) => { - if (e.target.value === '') { - updateMarker(null); - } - }; - - const handleKeyPress = async (e) => { - if (e.keyCode === 13) { - // Force blur to hide virtual keyboards on mobile and search - e.target.blur(); - } - }; - - // Needed to handle keyboard "Done" button on iOS - const handleBlur = async () => { - if (searchText.length) { - handleSearch(); - } - }; - - const handleSearch = async () => { - if (credentials) { - try { - setIsLoading(true); - const result = await geocodeStreetPoint(credentials, { - searchText, - country: DEFAULT_COUNTRY - }); - if (result) { - if (props.zoomToResult !== false) { - zoomToResult(result); - } - updateMarker(result); - } - } catch (e) { - handleGeocodeError(e); - } finally { - setIsLoading(false); - } - } - }; - - const zoomToResult = (result) => { - dispatch( - setViewState({ - longitude: result.longitude, - latitude: result.latitude, - zoom: 16, - transitionDuration: 500 - }) - ); - }; - - const updateMarker = (result) => { - dispatch(setGeocoderResult(result)); - }; - - const handleGeocodeError = (error) => { - if (props.onError) { - props.onError(error); - } - }; - return ( {loading ? ( @@ -153,10 +71,10 @@ function GeocoderWidget(props) { ) : ( )} +