client/src/js/containers/map/MapContainer.jsx
import React, { Component, Fragment } from 'react';
import PropTypes from 'prop-types';
import { connect } from 'react-redux';
import scrollToElement from 'animated-scroll-to';
import { openMarker, closeMarker } from 'actions/mapActions';
import { changePage } from 'actions/listActions';
import Map from 'containers/map/Map';
/**
* The MapArea component.
* Renders the map.
*/
export class MapContainer extends Component {
/**
* Used to create the Map.
* needed to allow use of this keyword in handler.
* @param props
*/
constructor(props) {
super(props);
this.handleMarkerClick = this.handleMarkerClick.bind(this);
this.handleMarkerClose = this.handleMarkerClose.bind(this);
}
/**
* Generates an array of marker objects to use on the map
*/
getMarkers() {
const { locations, markerImagePath } = this.props;
const markers = [];
let i;
// eslint-disable-next-line no-plusplus
for (i = 0; i < locations.length; i++) {
const location = locations[i];
const { Lat, Lng } = location;
const loc = {
...location,
EmailText: ss.i18n._t('Locator.EMAIL_TEXT', 'Email'),
WebsiteText: ss.i18n._t('Locator.WEBSITE_TEXT', 'Website'),
}
markers[markers.length] = {
position: {
lat: Number(Lat),
lng: Number(Lng),
},
key: location.ID,
defaultAnimation: 2,
defaultIcon: markerImagePath,
icon: location.MarkerIcon,
info: loc,
};
}
return markers;
}
/**
* Fires and event for clicking a marker
* @param target The marker that was clicked
*/
handleMarkerClick(target) {
const { dispatch, locations, defaultLimit } = this.props;
const location = locations.find(loc => loc.ID === target.key);
dispatch(openMarker(location));
// change the page
const index = locations.findIndex(l => l.ID === target.key) + 1;
const page = Math.ceil(index / defaultLimit);
dispatch(changePage(page));
// scroll to location in list
const element = document.getElementById(`loc-${target.key}`);
if (element !== null) {
const scrollContainer = document.getElementsByClassName('loc-list-container')[0];
scrollToElement(element, {
element: scrollContainer,
minDuration: 500,
maxDuration: 750,
cancelOnUserAction: false,
});
}
}
/**
* Fires event for closing a marker info box
* @param target The marker that had its info box closed
*/
handleMarkerClose() {
const { dispatch } = this.props;
dispatch(closeMarker());
}
render() {
const { current, showCurrent, clusters, center, defaultCenter, mapStyle, searchMarkerImagePath,
search, searchCenter, clusterStyles } = this.props;
return (
<Fragment>
<Map
containerElement={
<div className="map" />
}
mapElement={
<div style={{ height: '100%' }} />
}
mapStyle={mapStyle}
markers={this.getMarkers()}
onMarkerClick={this.handleMarkerClick}
onMarkerClose={this.handleMarkerClose}
current={current}
showCurrent={showCurrent}
clusters={clusters}
clusterStyles={clusterStyles}
center={center}
defaultCenter={defaultCenter}
searchMarkerImagePath={searchMarkerImagePath}
search={search}
searchCenter={searchCenter}
/>
</Fragment>
);
}
}
/**
* Defines the prop types
* @type {{locations: shim, dispatch: *, current: *, showCurrent: *, clusters: *, mapStyle: shim, center: (shim|*), defaultCenter: (shim|*)}}
*/
MapContainer.propTypes = {
// eslint-disable-next-line react/forbid-prop-types
locations: PropTypes.array,
dispatch: PropTypes.func.isRequired,
current: PropTypes.number.isRequired,
showCurrent: PropTypes.bool.isRequired,
clusters: PropTypes.bool.isRequired,
clusterStyles: PropTypes.array,
mapStyle: PropTypes.oneOfType([
() => {return null;},
PropTypes.object,
]),
markerImagePath: PropTypes.oneOfType([
PropTypes.bool,
PropTypes.string,
]).isRequired,
center: PropTypes.shape({
Lat: PropTypes.number.isRequired,
Lng: PropTypes.number.isRequired,
}).isRequired,
defaultCenter: PropTypes.shape({
lat: PropTypes.number.isRequired,
lng: PropTypes.number.isRequired,
}).isRequired,
};
/**
* Defines the default values of the props
* @type {{locations: Array, mapStyle: null}}
*/
MapContainer.defaultProps = {
locations: [],
mapStyle: null,
};
/**
* Maps that state to props
* @param state
* @returns {{current}}
*/
export function mapStateToProps(state) {
return {
current: state.locator.map.current,
showCurrent: state.locator.map.showCurrent,
clusters: state.locator.settings.clusters,
mapStyle: state.locator.settings.mapStyle,
markerImagePath: state.locator.settings.markerImagePath,
searchMarkerImagePath: state.locator.settings.searchMarkerImagePath,
clusterStyles: state.locator.settings.clusterStyles,
locations: state.locator.locations.locations,
center: state.locator.map.center,
defaultCenter: state.locator.settings.defaultCenter,
defaultLimit: state.locator.settings.defaultLimit,
emailText: state.locator.settings.emailText,
websiteText: state.locator.settings.websiteText,
search: state.locator.search.Address,
searchCenter: state.locator.map.searchCenter,
};
}
export default connect(mapStateToProps)(MapContainer);