Skip to content

Commit

Permalink
extenalize state of GeocoderWidge
Browse files Browse the repository at this point in the history
  • Loading branch information
zbigg committed Apr 28, 2022
1 parent 5abfac5 commit cb97447
Show file tree
Hide file tree
Showing 4 changed files with 120 additions and 93 deletions.
106 changes: 106 additions & 0 deletions packages/react-widgets/src/hooks/useGeocoderWidgetController.js
Original file line number Diff line number Diff line change
@@ -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 <GeocoderWidget /> 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
};
}
3 changes: 2 additions & 1 deletion packages/react-widgets/src/index.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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';
export { default as FeatureSelectionLayer } from './layers/FeatureSelectionLayer';
export { default as useGeocoderWidgetController } from './hooks/useGeocoderWidgetController';
1 change: 1 addition & 0 deletions packages/react-widgets/src/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -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';
103 changes: 11 additions & 92 deletions packages/react-widgets/src/widgets/GeocoderWidget.js
Original file line number Diff line number Diff line change
@@ -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: {
Expand Down Expand Up @@ -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(
Expand All @@ -77,86 +64,17 @@ 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 (
<Paper className={`${props.className} ${classes.paperInput}`} elevation={2}>
{loading ? (
<CircularProgress size={20} className={classes.icon} />
) : (
<SearchIcon className={classes.icon} />
)}

<InputBase
type='search'
tabIndex={-1}
inputRef={inputRef}
size='small'
placeholder='Search address'
className={classes.input}
Expand All @@ -173,7 +91,8 @@ function GeocoderWidget(props) {

GeocoderWidget.propTypes = {
className: PropTypes.string,
onError: PropTypes.func
onError: PropTypes.func,
zoomToResult: PropTypes.bool
};

GeocoderWidget.defaultProps = {};
Expand Down

0 comments on commit cb97447

Please sign in to comment.