diff --git a/src/client/actions/index.js b/src/client/actions/index.js index 20d1b1992b605d935fcec02e446c88b1324e8978..c0cf06cea12a6b6b8f42030ef44f3bb8b6e7fa34 100644 --- a/src/client/actions/index.js +++ b/src/client/actions/index.js @@ -1,6 +1,7 @@ export const UPDATE_QUERY = 'UPDATE_QUERY'; export const TOGGLE_DATASET = 'TOGGLE_DATASET'; export const BOUNCE_MARKER = 'BOUNCE_MARKER'; +export const OPEN_MARKER_POPUP = 'OPEN_MARKER_POPUP'; export const START_SPINNER = 'START_SPINNER'; export const FETCH_SUGGESTIONS = 'FETCH_SUGGESTIONS'; export const FETCH_SUGGESTIONS_FAILED = 'FETCH_SUGGESTIONS_FAILED'; @@ -35,6 +36,11 @@ export const bounceMarker = (uri) => ({ uri }); +export const openMarkerPopup = (uri) => ({ + type: OPEN_MARKER_POPUP, + uri +}); + export const startSpinner = () => ({ type: START_SPINNER, }); diff --git a/src/client/components/VirtualizedTable.js b/src/client/components/VirtualizedTable.js index 34f259adc37f2ee8fd99206055247ea8b1219cc2..bbba40f62b632e73f27ec25666f798e0c13a378d 100644 --- a/src/client/components/VirtualizedTable.js +++ b/src/client/components/VirtualizedTable.js @@ -130,6 +130,7 @@ class VirtualizedTable extends React.PureComponent { if (typeof rowData.lat !== 'undefined' || typeof rowData.long !== 'undefined') { marker = ( <IconButton + onMouseOver={handleMarkerHover(rowData.s)} onClick={handleMarkerClick(rowData.s)} aria-label="Marker" > @@ -145,6 +146,10 @@ class VirtualizedTable extends React.PureComponent { }; const handleMarkerClick = value => () => { + this.props.openMarkerPopup(value); + }; + + const handleMarkerHover = value => () => { this.props.bounceMarker(value); }; @@ -324,7 +329,8 @@ VirtualizedTable.propTypes = { clearSuggestions: PropTypes.func.isRequired, fetchResults: PropTypes.func.isRequired, clearResults: PropTypes.func.isRequired, - bounceMarker: PropTypes.func.isRequired + bounceMarker: PropTypes.func.isRequired, + openMarkerPopup: PropTypes.func.isRequired }; export default withStyles(styles)(VirtualizedTable); diff --git a/src/client/components/map/LeafletMap.js b/src/client/components/map/LeafletMap.js index 428f0400c301b12a45e3390f77c9b4f1a7fe1d43..64afab4da997771ffe9c1f68524a4a0f897b5abb 100644 --- a/src/client/components/map/LeafletMap.js +++ b/src/client/components/map/LeafletMap.js @@ -138,6 +138,8 @@ class LeafletMap extends React.Component { position: 'bottomleft' }).addTo(this.map); + L.Marker.setBouncingOptions({ exclusive: true }); + // map.on('fullscreenchange', function () { // if (map.isFullscreen()) { // console.log('entered fullscreen'); @@ -146,12 +148,9 @@ class LeafletMap extends React.Component { // } // }); - - //this.createOpacitySlider(); - } - componentDidUpdate({ results, mapMode, geoJSONKey, bouncingMarker }) { + componentDidUpdate({ results, mapMode, geoJSONKey, bouncingMarkerKey, openPopupMarkerKey }) { // check if results data or mapMode have changed if (this.props.results !== results || this.props.mapMode !== mapMode) { if (this.props.mapMode === 'cluster') { @@ -161,8 +160,12 @@ class LeafletMap extends React.Component { } } - if (this.props.bouncingMarker !== bouncingMarker) { - this.markers[this.props.bouncingMarker].bounce(5); + if (this.props.bouncingMarkerKey !== bouncingMarkerKey) { + this.markers[this.props.bouncingMarker].bounce(3); + } + + if (this.props.openPopupMarkerKey !== openPopupMarkerKey) { + this.markers[this.props.openPopupMarker].openPopup(); } // check if geoJSON has updated @@ -206,9 +209,6 @@ class LeafletMap extends React.Component { } else { const latLng = [+lat, +long]; const marker = L.marker(latLng, {icon: icon}) - .on('click', function() { - this.toggleBouncing(); - }) .bindPopup(this.createPopUpContent(result)); return marker; } @@ -249,7 +249,7 @@ class LeafletMap extends React.Component { createOpacitySlider() { L.Control.OpacitySlider = L.Control.extend({ - onAdd: function(map) { + onAdd: function() { const slider = L.DomUtil.create('input', 'opacity-slider'); slider.type = 'range'; slider.min = 0; @@ -277,7 +277,10 @@ LeafletMap.propTypes = { geoJSON: PropTypes.array, geoJSONKey: PropTypes.number.isRequired, getGeoJSON: PropTypes.func.isRequired, - bouncingMarker: PropTypes.string.isRequired + bouncingMarker: PropTypes.string.isRequired, + bouncingMarkerKey: PropTypes.number.isRequired, + openPopupMarker: PropTypes.string.isRequired, + openPopupMarkerKey: PropTypes.number.isRequired }; export default LeafletMap; diff --git a/src/client/containers/MapApp.js b/src/client/containers/MapApp.js index afa9e1c8a04f8bf6ea8d4215e9e9e5236abb064b..b6f0c412ce63ebfacb0e6316a48b908f75adb9be 100644 --- a/src/client/containers/MapApp.js +++ b/src/client/containers/MapApp.js @@ -30,6 +30,7 @@ import { updateResultsFilter, sortResults, bounceMarker, + openMarkerPopup, } from '../actions'; const styles = theme => ({ @@ -124,7 +125,7 @@ const styles = theme => ({ }); let MapApp = (props) => { - const { classes, mapMode, resultFormat, browser } = props; + const { classes, options, browser, search, map, results, resultValues } = props; //error, let oneColumnView = browser.lessThan.extraLarge; @@ -134,13 +135,13 @@ let MapApp = (props) => { // console.log('mapMode', mapMode) let table = ''; - if ((oneColumnView && resultFormat === 'table') || (!oneColumnView)) { + if ((oneColumnView && options.resultFormat === 'table') || (!oneColumnView)) { table = ( <div className={oneColumnView ? classes.resultTableOneColumn : classes.resultTable}> <VirtualizedTable - list={Immutable.List(props.results)} - resultValues={props.resultValues} - search={props.search} + list={Immutable.List(results)} + resultValues={resultValues} + search={search} sortResults={props.sortResults} updateResultsFilter={props.updateResultsFilter} updateQuery={props.updateQuery} @@ -149,15 +150,16 @@ let MapApp = (props) => { fetchSuggestions={props.fetchSuggestions} clearSuggestions={props.clearSuggestions} bounceMarker={props.bounceMarker} + openMarkerPopup={props.openMarkerPopup} /> </div> ); } - let map = ''; - if ((oneColumnView && resultFormat === 'map') || (!oneColumnView)) { - if (mapMode === 'heatmap') { - map = ( + let mapElement = ''; + if ((oneColumnView && options.resultFormat === 'map') || (!oneColumnView)) { + if (options.mapMode === 'heatmap') { + mapElement = ( <GMap results={props.results} googleMapURL="https://maps.googleapis.com/maps/api/js?key=AIzaSyCKWw5FjhwLsfp_l2gjVAifPkT3cxGXhA4&v=3.exp&libraries=geometry,drawing,places,visualization" @@ -167,14 +169,17 @@ let MapApp = (props) => { /> ); } else { - map = ( + mapElement = ( <LeafletMap results={props.results} - mapMode={props.mapMode} - geoJSON={props.geoJSON} - geoJSONKey={props.geoJSONKey} + mapMode={options.mapMode} + geoJSON={map.geoJSON} + geoJSONKey={map.geoJSONKey} getGeoJSON={props.getGeoJSON} - bouncingMarker={props.bouncingMarker} + bouncingMarker={map.bouncingMarker} + bouncingMarkerKey={map.bouncingMarkerKey} + openPopupMarker={map.openPopupMarker} + openPopupMarkerKey={map.openPopupMarkerKey} // sliderValue={100} /> ); @@ -182,7 +187,7 @@ let MapApp = (props) => { } let statistics = ''; - if ((oneColumnView && resultFormat === 'statistics') || (!oneColumnView)) { + if ((oneColumnView && options.resultFormat === 'statistics') || (!oneColumnView)) { statistics = ( <div className={oneColumnView ? classes.statisticsOneColumn : classes.statistics}> <Pie data={props.results} groupBy={props.search.groupBy} query={props.search.query} /> @@ -192,7 +197,7 @@ let MapApp = (props) => { let mainResultsView = ''; if (oneColumnView) { - switch(props.resultFormat) { + switch(options.resultFormat) { case 'table': { mainResultsView = table; break; @@ -220,13 +225,13 @@ let MapApp = (props) => { <div className={classes.root}> <div className={classes.appFrame}> <TopBar - results={props.results} + results={results} oneColumnView={oneColumnView} - mapMode={props.mapMode} - resultFormat={props.resultFormat} + mapMode={options.mapMode} + resultFormat={options.resultFormat} updateMapMode={props.updateMapMode} updateResultFormat={props.updateResultFormat} - datasets={props.search.datasets} + datasets={search.datasets} toggleDataset={props.toggleDataset} /> <div className={classes.mainContainer}> @@ -234,7 +239,7 @@ let MapApp = (props) => { {!oneColumnView && <div className={classes.rightColumn}> <div className={classes.map}> - {map} + {mapElement} </div> {statistics} </div> @@ -254,16 +259,12 @@ let MapApp = (props) => { const mapStateToProps = (state) => { return { + options: state.options, + browser: state.browser, search: state.search, + map: state.map, results: getVisibleResults(state.search), resultValues: getVisibleValues(state.search), - mapMode: state.options.mapMode, - error: state.error, - geoJSON: state.map.geoJSON, - geoJSONKey: state.map.geoJSONKey, - bouncingMarker: state.map.bouncingMarker, - resultFormat: state.options.resultFormat, - browser: state.browser }; }; @@ -279,14 +280,22 @@ const mapDispatchToProps = ({ updateResultFormat, updateMapMode, updateResultsFilter, - bounceMarker + bounceMarker, + openMarkerPopup, }); MapApp.propTypes = { classes: PropTypes.object.isRequired, theme: PropTypes.object.isRequired, + //error: PropTypes.object.isRequired, + browser: PropTypes.object.isRequired, + + options: PropTypes.object.isRequired, search: PropTypes.object.isRequired, - error: PropTypes.object.isRequired, + map: PropTypes.object.isRequired, + results: PropTypes.array, + resultValues: PropTypes.object, + updateQuery: PropTypes.func.isRequired, toggleDataset: PropTypes.func.isRequired, fetchSuggestions: PropTypes.func.isRequired, @@ -294,19 +303,12 @@ MapApp.propTypes = { fetchResults: PropTypes.func.isRequired, clearResults: PropTypes.func.isRequired, sortResults: PropTypes.func.isRequired, - geoJSON: PropTypes.array.isRequired, - geoJSONKey: PropTypes.number.isRequired, getGeoJSON: PropTypes.func.isRequired, bounceMarker: PropTypes.func.isRequired, + openMarkerPopup: PropTypes.func.isRequired, updateResultFormat: PropTypes.func.isRequired, updateMapMode: PropTypes.func.isRequired, - resultFormat: PropTypes.string.isRequired, - mapMode: PropTypes.string.isRequired, - results: PropTypes.array, - resultValues: PropTypes.object, updateResultsFilter: PropTypes.func.isRequired, - browser: PropTypes.object.isRequired, - bouncingMarker: PropTypes.string.isRequired }; export default compose( diff --git a/src/client/reducers/map.js b/src/client/reducers/map.js index 09db665fc36521959536d500986ca69de073425c..d3e19da4591a0894e4338a3d3aafdf65339d1796 100644 --- a/src/client/reducers/map.js +++ b/src/client/reducers/map.js @@ -1,6 +1,7 @@ import { UPDATE_GEOJSON, BOUNCE_MARKER, + OPEN_MARKER_POPUP, } from '../actions'; export const INITIAL_STATE = { @@ -9,7 +10,10 @@ export const INITIAL_STATE = { 'features': [] }], geoJSONKey: 0, - bouncingMarker: '' + bouncingMarker: '', + bouncingMarkerKey: 0, + openPopupMarker: '', + openPopupMarkerKey: 0, }; const map = (state = INITIAL_STATE, action) => { @@ -23,7 +27,14 @@ const map = (state = INITIAL_STATE, action) => { case BOUNCE_MARKER: return { ...state, - bouncingMarker: action.uri + bouncingMarker: action.uri, + bouncingMarkerKey: state.bouncingMarkerKey + 1 + }; + case OPEN_MARKER_POPUP: + return { + ...state, + openPopupMarker: action.uri, + openPopupMarkerKey: state.openPopupMarkerKey + 1 }; default: return state;