BaseMap.jsx 3.97 KB
Newer Older
1
import React, { Component } from "react";
2
import withStyles from "@material-ui/core/styles/withStyles";
3
import { compose } from "recompose";
4
5
import PropTypes from "prop-types";

6
import ReactMapboxGl, { Feature, Layer, Popup } from "react-mapbox-gl";
7
8
9
10
11
import UnivMapPopup from "./UnivMapPopup";

const Map = ReactMapboxGl({
  dragRotate: false,
  pitchWithRotate: false,
12
  maxZoom: 12
13
14
15
16
17
18
19
20
});

/**
 * Custom react-wrapper on top of MapBoxGlJs to handle our maps in a generic manner.
 *
 * If an id is provided, the state of the map will be automatically saved and regenerated.
 */
class BaseMap extends Component {
Florent Chehab's avatar
Florent Chehab committed
21
  // Static variable to hold the map center in a generic way without globalState
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
  static allMaps = {};

  // campusesMarkers = [];

  /**
   * @static
   * @private
   * @constant
   * @type {{center: number[], zoom: number[]}}
   */
  static DEFAULTS = {
    center: [53.94, 41.13],
    zoom: [0.86]
  };

  state = {
    popup: undefined
  };

  saveStatus(map) {
    if (typeof this.props.id !== "undefined") {
      const center = map.getCenter();
      BaseMap.allMaps[this.props.id] = {
        center: [center.lng, center.lat],
46
        zoom: [map.getZoom()]
47
48
49
50
51
      };
    }
  }

  openPopup(campusInfo) {
52
    this.setState({ popup: campusInfo });
53
54
55
  }

  closePopup() {
56
    this.setState({ popup: undefined });
57
58
  }

59
60
61
  toggleHover(mapInstance, cursor) {
    // eslint-disable-next-line no-param-reassign
    mapInstance.getCanvas().style.cursor = cursor;
62
63
  }

Florent Chehab's avatar
Florent Chehab committed
64
  renderLayer(selected) {
65
    const { campuses, theme } = this.props;
Florent Chehab's avatar
Florent Chehab committed
66
67
68
69
70
    return (
      <Layer
        type="circle"
        id={`campuses${selected ? "selected" : "notSelected"}`}
        paint={{
71
72
73
          "circle-color": selected
            ? theme.palette.primary.main
            : theme.palette.action.disabled,
Florent Chehab's avatar
Florent Chehab committed
74
75
          "circle-opacity": selected ? 0.8 : 0.5,
          "circle-radius": 8
76
77
78
79
80
81
82
83
84
85
86
87
88
89
        }}
      >
        {campuses
          .filter(c => c.selected === selected)
          .map((campusInfo, idx) => (
            <Feature
              // eslint-disable-next-line react/no-array-index-key
              key={idx}
              coordinates={[campusInfo.lon, campusInfo.lat]}
              onClick={() => this.openPopup(campusInfo)}
              onMouseEnter={({ map }) => this.toggleHover(map, "pointer")}
              onMouseLeave={({ map }) => this.toggleHover(map, "")}
            />
          ))}
Florent Chehab's avatar
Florent Chehab committed
90
91
92
93
      </Layer>
    );
  }

94
  render() {
Florent Chehab's avatar
Florent Chehab committed
95
    const style = this.props.theme.palette.type === "light" ? "light" : "dark";
96

97
    const mapStatus = { ...BaseMap.DEFAULTS };
98
99
100
101
102
103
104
105

    if (typeof this.props.id !== "undefined") {
      const previousData = BaseMap.allMaps[this.props.id];
      if (typeof previousData !== "undefined") {
        Object.assign(mapStatus, previousData);
      }
    }

106
107
    const { popup } = this.state;
    const { campuses, classes } = this.props;
108
109

    return (
110
111
112
113
114
115
      <Map
        style={`/map-server/styles/${style}/style.json`}
        className={classes.mapContainer}
        zoom={mapStatus.zoom}
        center={mapStatus.center}
        onMoveEnd={map => this.saveStatus(map)}
116
      >
117
118
119
120
121
122
123
124
        {campuses ? (
          <>
            {this.renderLayer(true)}
            {this.renderLayer(false)}
          </>
        ) : (
          <></>
        )}
125
126
        {popup && (
          <Popup key={popup.univId} coordinates={[popup.lon, popup.lat]}>
127
            <UnivMapPopup {...popup} handleClose={() => this.closePopup()} />
128
129
130
131
132
133
134
135
136
137
138
          </Popup>
        )}
      </Map>
    );
  }
}

BaseMap.propTypes = {
  classes: PropTypes.object.isRequired,
  theme: PropTypes.object.isRequired,
  id: PropTypes.string,
139
  campuses: PropTypes.array
140
141
};

142
143
144
145
146
BaseMap.defaultProps = {
  id: undefined,
  campuses: undefined
};

147
// eslint-disable-next-line no-unused-vars
Florent Chehab's avatar
Florent Chehab committed
148
149
150
151
152
153
154
155
const styles = theme => ({
  mapContainer: {
    width: "100%",
    [theme.breakpoints.up("lg")]: {
      height: "45vh",
      minHeight: 600
    },
    [theme.breakpoints.down("md")]: {
156
      height: "60vh"
Florent Chehab's avatar
Florent Chehab committed
157
158
159
    }
  }
});
160

161
export default compose(withStyles(styles, { withTheme: true }))(BaseMap);