@opentripplanner/vehicle-rental-overlay 1.3.0 → 1.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/lib/index.js CHANGED
@@ -7,6 +7,8 @@ Object.defineProperty(exports, "__esModule", {
7
7
  });
8
8
  exports.default = void 0;
9
9
 
10
+ var _flat = _interopRequireDefault(require("flat"));
11
+
10
12
  var _baseMap = require("@opentripplanner/base-map");
11
13
 
12
14
  var _coreUtils = _interopRequireDefault(require("@opentripplanner/core-utils"));
@@ -19,18 +21,29 @@ var _propTypes = _interopRequireDefault(require("prop-types"));
19
21
 
20
22
  var _react = _interopRequireDefault(require("react"));
21
23
 
24
+ var _reactIntl = require("react-intl");
25
+
22
26
  var _reactLeaflet = require("react-leaflet");
23
27
 
24
28
  var _DefaultMarkers = require("./DefaultMarkers");
25
29
 
30
+ var _enUS = _interopRequireDefault(require("../i18n/en-US.yml"));
31
+
32
+ // Load the default messages.
33
+ // HACK: We should flatten the messages loaded above because
34
+ // the YAML loaders behave differently between webpack and our version of jest:
35
+ // - the yaml loader for webpack returns a nested object,
36
+ // - the yaml loader for jest returns messages with flattened ids.
37
+ const defaultMessages = (0, _flat.default)(_enUS.default);
26
38
  /**
27
39
  * This vehicle rental overlay can be used to render vehicle rentals of various
28
40
  * types. This layer can be configured to show different styles of markers at
29
41
  * different zoom levels.
30
42
  */
43
+
31
44
  class VehicleRentalOverlay extends _reactLeaflet.MapLayer {
32
- constructor(...args) {
33
- super(...args);
45
+ constructor(props) {
46
+ super(props);
34
47
 
35
48
  this.renderSymbolWithPopup = Symbol => {
36
49
  const SymbolWrapper = ({
@@ -76,6 +89,26 @@ class VehicleRentalOverlay extends _reactLeaflet.MapLayer {
76
89
  };
77
90
  });
78
91
 
92
+ this.onOverlayAdded = () => {
93
+ this.startRefreshing();
94
+ };
95
+
96
+ this.onOverlayRemoved = () => {
97
+ this.stopRefreshing();
98
+ };
99
+
100
+ this.onViewportChanged = viewport => {
101
+ const {
102
+ zoom: newZoom
103
+ } = viewport;
104
+
105
+ if (this.state.zoom !== newZoom) {
106
+ this.setState({
107
+ zoom: newZoom
108
+ });
109
+ }
110
+ };
111
+
79
112
  this.renderPopupForStation = (station, stationIsHub = false) => {
80
113
  const {
81
114
  configCompanies,
@@ -88,12 +121,37 @@ class VehicleRentalOverlay extends _reactLeaflet.MapLayer {
88
121
  lon: station.x,
89
122
  name: stationName
90
123
  };
91
- return /*#__PURE__*/_react.default.createElement(_reactLeaflet.Popup, null, /*#__PURE__*/_react.default.createElement(_baseMap.Styled.MapOverlayPopup, null, /*#__PURE__*/_react.default.createElement(_baseMap.Styled.PopupTitle, null, stationName), stationIsHub && /*#__PURE__*/_react.default.createElement(_baseMap.Styled.PopupRow, null, /*#__PURE__*/_react.default.createElement("div", null, "Available bikes: ", station.bikesAvailable), /*#__PURE__*/_react.default.createElement("div", null, "Available docks: ", station.spacesAvailable)), /*#__PURE__*/_react.default.createElement(_baseMap.Styled.PopupRow, null, /*#__PURE__*/_react.default.createElement("b", null, "Plan a trip:"), /*#__PURE__*/_react.default.createElement(_fromToLocationPicker.default, {
124
+ return /*#__PURE__*/_react.default.createElement(_reactLeaflet.Popup, null, /*#__PURE__*/_react.default.createElement(_baseMap.Styled.MapOverlayPopup, null, /*#__PURE__*/_react.default.createElement(_baseMap.Styled.PopupTitle, null, stationName), stationIsHub && /*#__PURE__*/_react.default.createElement(_baseMap.Styled.PopupRow, null, /*#__PURE__*/_react.default.createElement("div", null, /*#__PURE__*/_react.default.createElement(_reactIntl.FormattedMessage, {
125
+ defaultMessage: defaultMessages["otpUi.VehicleRentalOverlay.availableBikes"],
126
+ description: "Label text for the number of bikes available",
127
+ id: "otpUi.VehicleRentalOverlay.availableBikes",
128
+ values: {
129
+ value: station.bikesAvailable
130
+ }
131
+ })), /*#__PURE__*/_react.default.createElement("div", null, /*#__PURE__*/_react.default.createElement(_reactIntl.FormattedMessage, {
132
+ defaultMessage: defaultMessages["otpUi.VehicleRentalOverlay.availableDocks"],
133
+ description: "Label text for the number of docks available",
134
+ id: "otpUi.VehicleRentalOverlay.availableDocks",
135
+ values: {
136
+ value: station.spacesAvailable
137
+ }
138
+ }))), /*#__PURE__*/_react.default.createElement(_baseMap.Styled.PopupRow, null, /*#__PURE__*/_react.default.createElement(_fromToLocationPicker.default, {
139
+ label: true,
92
140
  location: location,
93
141
  setLocation: setLocation
94
142
  }))));
95
143
  };
144
+
145
+ this.state = {
146
+ zoom: props.leaflet.map.getZoom()
147
+ };
96
148
  }
149
+ /**
150
+ * This helper method will be passed to the ZoomBasedMarkers symbolTransform prop.
151
+ * It creates a component that inserts a popup
152
+ * as a child of the specified symbol from the mapSymbols prop.
153
+ */
154
+
97
155
 
98
156
  createLeafletElement() {}
99
157
 
@@ -117,25 +175,32 @@ class VehicleRentalOverlay extends _reactLeaflet.MapLayer {
117
175
  stopRefreshing() {
118
176
  if (this.refreshTimer) clearInterval(this.refreshTimer);
119
177
  }
178
+ /**
179
+ * When the layer is added (or toggled on, or its visibility becomes true),
180
+ * start refreshing vehicle positions.
181
+ */
182
+
120
183
 
184
+ /**
185
+ * Upon mounting, see whether the vehicles should be fetched,
186
+ * and also call the register overlay prop that the
187
+ * @opentripplanner/base-map package has injected to listen to zoom/position changes.
188
+ */
121
189
  componentDidMount() {
122
190
  const {
191
+ registerOverlay,
123
192
  visible
124
193
  } = this.props;
125
194
  if (visible) this.startRefreshing();
195
+
196
+ if (typeof registerOverlay === "function") {
197
+ registerOverlay(this);
198
+ }
126
199
  }
127
200
 
128
201
  componentWillUnmount() {
129
202
  this.stopRefreshing();
130
203
  }
131
-
132
- componentDidUpdate(prevProps) {
133
- if (!prevProps.visible && this.props.visible) {
134
- this.startRefreshing();
135
- } else if (prevProps.visible && !this.props.visible) {
136
- this.stopRefreshing();
137
- }
138
- }
139
204
  /**
140
205
  * Render some popup html for a station. This contains custom logic for
141
206
  * displaying rental vehicles in the TriMet MOD website that might not be
@@ -147,17 +212,15 @@ class VehicleRentalOverlay extends _reactLeaflet.MapLayer {
147
212
  const {
148
213
  companies,
149
214
  mapSymbols,
150
- stations,
151
- visible
152
- } = this.props; // Render an empty FeatureGroup if the rental vehicles should not be visible
215
+ stations
216
+ } = this.props;
217
+ const {
218
+ zoom
219
+ } = this.state; // Render an empty FeatureGroup if the rental vehicles should not be visible
153
220
  // on the map. Otherwise previous stations may still be shown due to some
154
221
  // react-leaflet internals, maybe? Also, do not return null because that will
155
222
  // prevent the overlay from appearing in the layer controls.
156
223
 
157
- if (!visible) {
158
- return /*#__PURE__*/_react.default.createElement(_reactLeaflet.FeatureGroup, null);
159
- }
160
-
161
224
  let filteredStations = stations;
162
225
 
163
226
  if (companies) {
@@ -166,10 +229,8 @@ class VehicleRentalOverlay extends _reactLeaflet.MapLayer {
166
229
 
167
230
  if (!filteredStations || filteredStations.length === 0) {
168
231
  return /*#__PURE__*/_react.default.createElement(_reactLeaflet.FeatureGroup, null);
169
- } // get zoom to check which symbol to render
170
-
232
+ } // Convert map symbols for this overlay to zoomBasedSymbolType.
171
233
 
172
- const zoom = this.props.leaflet.map.getZoom(); // Convert map symbols for this overlay to zoomBasedSymbolType.
173
234
 
174
235
  const symbols = this.convertToZoomMarkerSymbols(mapSymbols);
175
236
  return /*#__PURE__*/_react.default.createElement(_reactLeaflet.FeatureGroup, null, /*#__PURE__*/_react.default.createElement(_zoomBasedMarkers.default, {
package/lib/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/index.js"],"names":["VehicleRentalOverlay","MapLayer","renderSymbolWithPopup","Symbol","SymbolWrapper","entity","station","zoom","renderPopupForStation","bikesAvailable","undefined","isFloatingBike","propTypes","coreUtils","types","stationType","isRequired","PropTypes","number","convertToZoomMarkerSymbols","mapSymbols","map","mapSymbol","symbol","type","HubAndFloatingBike","minZoom","stationIsHub","configCompanies","getStationName","setLocation","props","stationName","location","lat","y","lon","x","name","spacesAvailable","createLeafletElement","updateLeafletElement","startRefreshing","refreshVehicles","refreshTimer","setInterval","stopRefreshing","clearInterval","componentDidMount","visible","componentWillUnmount","componentDidUpdate","prevProps","render","companies","stations","filteredStations","filter","networks","value","includes","length","leaflet","getZoom","symbols","arrayOf","companyType","string","func","vehicleRentalMapOverlaySymbolsType","bool","defaultProps","stationNetworks","itinerary","getCompaniesLabelFromNetworks","id","isFloatingCar","isFloatingVehicle","GenericMarker"],"mappings":"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","sourcesContent":["import { Styled as BaseMapStyled } from \"@opentripplanner/base-map\";\nimport coreUtils from \"@opentripplanner/core-utils\";\nimport FromToLocationPicker from \"@opentripplanner/from-to-location-picker\";\nimport ZoomBasedMarkers from \"@opentripplanner/zoom-based-markers\";\nimport PropTypes from \"prop-types\";\nimport React from \"react\";\nimport { FeatureGroup, MapLayer, Popup, withLeaflet } from \"react-leaflet\";\n\nimport {\n GenericMarker,\n HubAndFloatingBike,\n SharedBikeCircle\n} from \"./DefaultMarkers\";\n\n/**\n * This vehicle rental overlay can be used to render vehicle rentals of various\n * types. This layer can be configured to show different styles of markers at\n * different zoom levels.\n */\nclass VehicleRentalOverlay extends MapLayer {\n /**\n * This helper method will be passed to the ZoomBasedMarkers symbolTransform prop.\n * It creates a component that inserts a popup\n * as a child of the specified symbol from the mapSymbols prop.\n */\n renderSymbolWithPopup = Symbol => {\n const SymbolWrapper = ({ entity: station, zoom }) => (\n <Symbol entity={station} zoom={zoom}>\n {this.renderPopupForStation(\n station,\n station.bikesAvailable !== undefined && !station.isFloatingBike\n )}\n </Symbol>\n );\n SymbolWrapper.propTypes = {\n entity: coreUtils.types.stationType.isRequired,\n zoom: PropTypes.number.isRequired\n };\n\n return SymbolWrapper;\n };\n\n /**\n * Convert map symbols to zoomBasedSymbolType.\n */\n convertToZoomMarkerSymbols = mapSymbols =>\n mapSymbols.map(mapSymbol => {\n // If mapSymbol uses zoomBasedSymbolType, use it as is.\n if (mapSymbol.symbol) {\n return mapSymbol;\n }\n\n // Otherwise, convert into zoomBasedType (no support for symbols by type).\n let symbol;\n switch (mapSymbol.type) {\n case \"circle\":\n symbol = SharedBikeCircle(mapSymbol);\n break;\n case \"hubAndFloatingBike\":\n symbol = HubAndFloatingBike;\n break;\n default:\n symbol = GenericMarker(mapSymbol);\n }\n\n return {\n minZoom: mapSymbol.minZoom,\n symbol\n };\n });\n\n createLeafletElement() {}\n\n updateLeafletElement() {}\n\n startRefreshing() {\n const { refreshVehicles } = this.props;\n\n // Create the timer only if refreshVehicles is a valid function.\n if (typeof refreshVehicles === \"function\") {\n // initial station retrieval\n refreshVehicles();\n\n // set up timer to refresh stations periodically\n this.refreshTimer = setInterval(() => {\n refreshVehicles();\n }, 30000); // defaults to every 30 sec. TODO: make this configurable?\n }\n }\n\n stopRefreshing() {\n if (this.refreshTimer) clearInterval(this.refreshTimer);\n }\n\n componentDidMount() {\n const { visible } = this.props;\n if (visible) this.startRefreshing();\n }\n\n componentWillUnmount() {\n this.stopRefreshing();\n }\n\n componentDidUpdate(prevProps) {\n if (!prevProps.visible && this.props.visible) {\n this.startRefreshing();\n } else if (prevProps.visible && !this.props.visible) {\n this.stopRefreshing();\n }\n }\n\n /**\n * Render some popup html for a station. This contains custom logic for\n * displaying rental vehicles in the TriMet MOD website that might not be\n * applicable to other regions.\n */\n renderPopupForStation = (station, stationIsHub = false) => {\n const { configCompanies, getStationName, setLocation } = this.props;\n const stationName = getStationName(configCompanies, station);\n const location = {\n lat: station.y,\n lon: station.x,\n name: stationName\n };\n return (\n <Popup>\n <BaseMapStyled.MapOverlayPopup>\n <BaseMapStyled.PopupTitle>{stationName}</BaseMapStyled.PopupTitle>\n\n {/* render dock info if it is available */}\n {stationIsHub && (\n <BaseMapStyled.PopupRow>\n <div>Available bikes: {station.bikesAvailable}</div>\n <div>Available docks: {station.spacesAvailable}</div>\n </BaseMapStyled.PopupRow>\n )}\n\n {/* Set as from/to toolbar */}\n <BaseMapStyled.PopupRow>\n <b>Plan a trip:</b>\n <FromToLocationPicker\n location={location}\n setLocation={setLocation}\n />\n </BaseMapStyled.PopupRow>\n </BaseMapStyled.MapOverlayPopup>\n </Popup>\n );\n };\n\n render() {\n const { companies, mapSymbols, stations, visible } = this.props;\n // Render an empty FeatureGroup if the rental vehicles should not be visible\n // on the map. Otherwise previous stations may still be shown due to some\n // react-leaflet internals, maybe? Also, do not return null because that will\n // prevent the overlay from appearing in the layer controls.\n if (!visible) {\n return <FeatureGroup />;\n }\n\n let filteredStations = stations;\n if (companies) {\n filteredStations = stations.filter(\n station =>\n station.networks.filter(value => companies.includes(value)).length > 0\n );\n }\n\n if (!filteredStations || filteredStations.length === 0) {\n return <FeatureGroup />;\n }\n\n // get zoom to check which symbol to render\n const zoom = this.props.leaflet.map.getZoom();\n\n // Convert map symbols for this overlay to zoomBasedSymbolType.\n const symbols = this.convertToZoomMarkerSymbols(mapSymbols);\n\n return (\n <FeatureGroup>\n <ZoomBasedMarkers\n entities={filteredStations}\n symbols={symbols}\n symbolTransform={this.renderSymbolWithPopup}\n zoom={zoom}\n />\n </FeatureGroup>\n );\n }\n}\n\nVehicleRentalOverlay.props = {\n /**\n * The entire companies config array.\n */\n configCompanies: PropTypes.arrayOf(coreUtils.types.companyType.isRequired)\n .isRequired,\n /**\n * A list of companies that are applicable to just this instance of the\n * overlay.\n */\n companies: PropTypes.arrayOf(PropTypes.string.isRequired),\n /**\n * An optional custom function to create a string name of a particular vehicle\n * rental station. This function takes two arguments of the configCompanies\n * prop and a vehicle rental station. The function must return a string.\n */\n getStationName: PropTypes.func,\n /**\n * A configuration of what map markers or symbols to show at various\n * zoom levels.\n */\n mapSymbols: coreUtils.types.vehicleRentalMapOverlaySymbolsType,\n /**\n * If specified, a function that will be triggered every 30 seconds whenever this layer is\n * visible.\n */\n refreshVehicles: PropTypes.func,\n /**\n * A callback for when a user clicks on setting this stop as either the from\n * or to location of a new search.\n *\n * This will be dispatched with the following argument:\n *\n * ```js\n * {\n * location: {\n * lat: number,\n * lon: number,\n * name: string\n * },\n * locationType: \"from\" or \"to\"\n * }\n * ```\n */\n setLocation: PropTypes.func.isRequired,\n /**\n * A list of the vehicle rental stations specific to this overlay instance.\n */\n stations: PropTypes.arrayOf(coreUtils.types.stationType),\n /**\n * Whether the overlay is currently visible.\n */\n visible: PropTypes.bool\n};\n\nVehicleRentalOverlay.defaultProps = {\n getStationName: (configCompanies, station) => {\n const stationNetworks = coreUtils.itinerary.getCompaniesLabelFromNetworks(\n station.networks,\n configCompanies\n );\n let stationName = station.name || station.id;\n if (station.isFloatingBike) {\n stationName = `Free-floating bike: ${stationName}`;\n } else if (station.isFloatingCar) {\n stationName = `${stationNetworks} ${stationName}`;\n } else if (station.isFloatingVehicle) {\n // assumes that all floating vehicles are E-scooters\n stationName = `${stationNetworks} E-scooter`;\n }\n return stationName;\n },\n mapSymbols: [\n {\n zoom: 0,\n symbol: GenericMarker\n }\n ],\n refreshVehicles: null,\n stations: [],\n visible: false\n};\n\nexport default withLeaflet(VehicleRentalOverlay);\n"],"file":"index.js"}
1
+ {"version":3,"sources":["../src/index.js"],"names":["defaultMessages","defaultEnglishMessages","VehicleRentalOverlay","MapLayer","constructor","props","renderSymbolWithPopup","Symbol","SymbolWrapper","entity","station","zoom","renderPopupForStation","bikesAvailable","undefined","isFloatingBike","propTypes","coreUtils","types","stationType","isRequired","PropTypes","number","convertToZoomMarkerSymbols","mapSymbols","map","mapSymbol","symbol","type","HubAndFloatingBike","minZoom","onOverlayAdded","startRefreshing","onOverlayRemoved","stopRefreshing","onViewportChanged","viewport","newZoom","state","setState","stationIsHub","configCompanies","getStationName","setLocation","stationName","location","lat","y","lon","x","name","value","spacesAvailable","leaflet","getZoom","createLeafletElement","updateLeafletElement","refreshVehicles","refreshTimer","setInterval","clearInterval","componentDidMount","registerOverlay","visible","componentWillUnmount","render","companies","stations","filteredStations","filter","networks","includes","length","symbols","arrayOf","companyType","string","func","vehicleRentalMapOverlaySymbolsType","bool","defaultProps","stationNetworks","itinerary","getCompaniesLabelFromNetworks","id","isFloatingCar","isFloatingVehicle","GenericMarker"],"mappings":"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","sourcesContent":["import flatten from \"flat\";\nimport { Styled as BaseMapStyled } from \"@opentripplanner/base-map\";\nimport coreUtils from \"@opentripplanner/core-utils\";\nimport FromToLocationPicker from \"@opentripplanner/from-to-location-picker\";\nimport ZoomBasedMarkers from \"@opentripplanner/zoom-based-markers\";\nimport PropTypes from \"prop-types\";\nimport React from \"react\";\nimport { FormattedMessage } from \"react-intl\";\nimport { FeatureGroup, MapLayer, Popup, withLeaflet } from \"react-leaflet\";\n\nimport {\n GenericMarker,\n HubAndFloatingBike,\n SharedBikeCircle\n} from \"./DefaultMarkers\";\n\n// Load the default messages.\nimport defaultEnglishMessages from \"../i18n/en-US.yml\";\n\n// HACK: We should flatten the messages loaded above because\n// the YAML loaders behave differently between webpack and our version of jest:\n// - the yaml loader for webpack returns a nested object,\n// - the yaml loader for jest returns messages with flattened ids.\nconst defaultMessages = flatten(defaultEnglishMessages);\n\n/**\n * This vehicle rental overlay can be used to render vehicle rentals of various\n * types. This layer can be configured to show different styles of markers at\n * different zoom levels.\n */\nclass VehicleRentalOverlay extends MapLayer {\n constructor(props) {\n super(props);\n this.state = {\n zoom: props.leaflet.map.getZoom()\n };\n }\n\n /**\n * This helper method will be passed to the ZoomBasedMarkers symbolTransform prop.\n * It creates a component that inserts a popup\n * as a child of the specified symbol from the mapSymbols prop.\n */\n renderSymbolWithPopup = Symbol => {\n const SymbolWrapper = ({ entity: station, zoom }) => (\n <Symbol entity={station} zoom={zoom}>\n {this.renderPopupForStation(\n station,\n station.bikesAvailable !== undefined && !station.isFloatingBike\n )}\n </Symbol>\n );\n SymbolWrapper.propTypes = {\n entity: coreUtils.types.stationType.isRequired,\n zoom: PropTypes.number.isRequired\n };\n\n return SymbolWrapper;\n };\n\n /**\n * Convert map symbols to zoomBasedSymbolType.\n */\n convertToZoomMarkerSymbols = mapSymbols =>\n mapSymbols.map(mapSymbol => {\n // If mapSymbol uses zoomBasedSymbolType, use it as is.\n if (mapSymbol.symbol) {\n return mapSymbol;\n }\n\n // Otherwise, convert into zoomBasedType (no support for symbols by type).\n let symbol;\n switch (mapSymbol.type) {\n case \"circle\":\n symbol = SharedBikeCircle(mapSymbol);\n break;\n case \"hubAndFloatingBike\":\n symbol = HubAndFloatingBike;\n break;\n default:\n symbol = GenericMarker(mapSymbol);\n }\n\n return {\n minZoom: mapSymbol.minZoom,\n symbol\n };\n });\n\n createLeafletElement() {}\n\n updateLeafletElement() {}\n\n startRefreshing() {\n const { refreshVehicles } = this.props;\n\n // Create the timer only if refreshVehicles is a valid function.\n if (typeof refreshVehicles === \"function\") {\n // initial station retrieval\n refreshVehicles();\n\n // set up timer to refresh stations periodically\n this.refreshTimer = setInterval(() => {\n refreshVehicles();\n }, 30000); // defaults to every 30 sec. TODO: make this configurable?\n }\n }\n\n stopRefreshing() {\n if (this.refreshTimer) clearInterval(this.refreshTimer);\n }\n\n /**\n * When the layer is added (or toggled on, or its visibility becomes true),\n * start refreshing vehicle positions.\n */\n onOverlayAdded = () => {\n this.startRefreshing();\n };\n\n /**\n * When the layer is removed (or toggled off, or its visibility becomes false),\n * stop refreshing vehicle positions.\n */\n onOverlayRemoved = () => {\n this.stopRefreshing();\n };\n\n /**\n * Listen to changes on the BaseMap's center or zoom.\n * @param viewport The viewport data. See https://github.com/PaulLeCam/react-leaflet/blob/master/example/components/viewport.js for details.\n */\n onViewportChanged = viewport => {\n const { zoom: newZoom } = viewport;\n if (this.state.zoom !== newZoom) {\n this.setState({ zoom: newZoom });\n }\n };\n\n /**\n * Upon mounting, see whether the vehicles should be fetched,\n * and also call the register overlay prop that the\n * @opentripplanner/base-map package has injected to listen to zoom/position changes.\n */\n componentDidMount() {\n const { registerOverlay, visible } = this.props;\n if (visible) this.startRefreshing();\n if (typeof registerOverlay === \"function\") {\n registerOverlay(this);\n }\n }\n\n componentWillUnmount() {\n this.stopRefreshing();\n }\n\n /**\n * Render some popup html for a station. This contains custom logic for\n * displaying rental vehicles in the TriMet MOD website that might not be\n * applicable to other regions.\n */\n renderPopupForStation = (station, stationIsHub = false) => {\n const { configCompanies, getStationName, setLocation } = this.props;\n const stationName = getStationName(configCompanies, station);\n const location = {\n lat: station.y,\n lon: station.x,\n name: stationName\n };\n return (\n <Popup>\n <BaseMapStyled.MapOverlayPopup>\n <BaseMapStyled.PopupTitle>{stationName}</BaseMapStyled.PopupTitle>\n\n {/* render dock info if it is available */}\n {stationIsHub && (\n <BaseMapStyled.PopupRow>\n <div>\n <FormattedMessage\n defaultMessage={\n defaultMessages[\"otpUi.VehicleRentalOverlay.availableBikes\"]\n }\n description=\"Label text for the number of bikes available\"\n id=\"otpUi.VehicleRentalOverlay.availableBikes\"\n values={{ value: station.bikesAvailable }}\n />\n </div>\n <div>\n <FormattedMessage\n defaultMessage={\n defaultMessages[\"otpUi.VehicleRentalOverlay.availableDocks\"]\n }\n description=\"Label text for the number of docks available\"\n id=\"otpUi.VehicleRentalOverlay.availableDocks\"\n values={{ value: station.spacesAvailable }}\n />\n </div>\n </BaseMapStyled.PopupRow>\n )}\n\n {/* Set as from/to toolbar */}\n <BaseMapStyled.PopupRow>\n <FromToLocationPicker\n label\n location={location}\n setLocation={setLocation}\n />\n </BaseMapStyled.PopupRow>\n </BaseMapStyled.MapOverlayPopup>\n </Popup>\n );\n };\n\n render() {\n const { companies, mapSymbols, stations } = this.props;\n const { zoom } = this.state;\n // Render an empty FeatureGroup if the rental vehicles should not be visible\n // on the map. Otherwise previous stations may still be shown due to some\n // react-leaflet internals, maybe? Also, do not return null because that will\n // prevent the overlay from appearing in the layer controls.\n\n let filteredStations = stations;\n if (companies) {\n filteredStations = stations.filter(\n station =>\n station.networks.filter(value => companies.includes(value)).length > 0\n );\n }\n\n if (!filteredStations || filteredStations.length === 0) {\n return <FeatureGroup />;\n }\n\n // Convert map symbols for this overlay to zoomBasedSymbolType.\n const symbols = this.convertToZoomMarkerSymbols(mapSymbols);\n\n return (\n <FeatureGroup>\n <ZoomBasedMarkers\n entities={filteredStations}\n symbols={symbols}\n symbolTransform={this.renderSymbolWithPopup}\n zoom={zoom}\n />\n </FeatureGroup>\n );\n }\n}\n\nVehicleRentalOverlay.props = {\n /**\n * The entire companies config array.\n */\n configCompanies: PropTypes.arrayOf(coreUtils.types.companyType.isRequired)\n .isRequired,\n /**\n * A list of companies that are applicable to just this instance of the\n * overlay.\n */\n companies: PropTypes.arrayOf(PropTypes.string.isRequired),\n /**\n * An optional custom function to create a string name of a particular vehicle\n * rental station. This function takes two arguments of the configCompanies\n * prop and a vehicle rental station. The function must return a string.\n */\n getStationName: PropTypes.func,\n /**\n * A configuration of what map markers or symbols to show at various\n * zoom levels.\n */\n mapSymbols: coreUtils.types.vehicleRentalMapOverlaySymbolsType,\n /**\n * If specified, a function that will be triggered every 30 seconds whenever this layer is\n * visible.\n */\n refreshVehicles: PropTypes.func,\n /**\n * A callback for when a user clicks on setting this stop as either the from\n * or to location of a new search.\n *\n * This will be dispatched with the following argument:\n *\n * ```js\n * {\n * location: {\n * lat: number,\n * lon: number,\n * name: string\n * },\n * locationType: \"from\" or \"to\"\n * }\n * ```\n */\n setLocation: PropTypes.func.isRequired,\n /**\n * A list of the vehicle rental stations specific to this overlay instance.\n */\n stations: PropTypes.arrayOf(coreUtils.types.stationType),\n /**\n * Whether the overlay is currently visible.\n */\n visible: PropTypes.bool\n};\n\nVehicleRentalOverlay.defaultProps = {\n getStationName: (configCompanies, station) => {\n const stationNetworks = coreUtils.itinerary.getCompaniesLabelFromNetworks(\n station.networks,\n configCompanies\n );\n let stationName = station.name || station.id;\n if (station.isFloatingBike) {\n stationName = `Free-floating bike: ${stationName}`;\n } else if (station.isFloatingCar) {\n stationName = `${stationNetworks} ${stationName}`;\n } else if (station.isFloatingVehicle) {\n // assumes that all floating vehicles are E-scooters\n stationName = `${stationNetworks} E-scooter`;\n }\n return stationName;\n },\n mapSymbols: [\n {\n zoom: 0,\n symbol: GenericMarker\n }\n ],\n refreshVehicles: null,\n stations: [],\n visible: false\n};\n\nexport default withLeaflet(VehicleRentalOverlay);\n"],"file":"index.js"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@opentripplanner/vehicle-rental-overlay",
3
- "version": "1.3.0",
3
+ "version": "1.4.0",
4
4
  "description": "A map overlay to show vehicle rentals from a specific company",
5
5
  "main": "lib/index.js",
6
6
  "module": "esm/index.js",
@@ -18,17 +18,19 @@
18
18
  "url": "git+https://github.com/opentripplanner/otp-ui.git"
19
19
  },
20
20
  "dependencies": {
21
- "@opentripplanner/from-to-location-picker": "^1.2.1",
22
- "@opentripplanner/zoom-based-markers": "^1.2.0",
23
- "@opentripplanner/core-utils": "^4.1.0",
21
+ "@opentripplanner/core-utils": "^4.5.0",
22
+ "@opentripplanner/from-to-location-picker": "^2.1.0",
23
+ "@opentripplanner/zoom-based-markers": "^1.2.1",
24
+ "@styled-icons/fa-solid": "^10.34.0",
25
+ "flat": "^5.0.2",
24
26
  "lodash.memoize": "^4.1.2",
25
- "prop-types": "^15.7.2",
26
- "@styled-icons/fa-solid": "^10.34.0"
27
+ "prop-types": "^15.7.2"
27
28
  },
28
29
  "peerDependencies": {
29
- "@opentripplanner/base-map": "^1.2.0",
30
+ "@opentripplanner/base-map": "^2.0.0",
30
31
  "react": "^16.14.0",
31
32
  "react-dom": "^16.8.6",
33
+ "react-intl": "^5.24.6",
32
34
  "react-leaflet": "^2.6.1",
33
35
  "styled-components": "^5.3.0"
34
36
  }
@@ -22,10 +22,13 @@ const templatePropTypes = {
22
22
  /** The children of the component. */
23
23
  children: PropTypes.node,
24
24
  /** The rental vehicle or station to render. */
25
- entity: coreUtils.types.stationType.isRequired
25
+ entity: coreUtils.types.stationType.isRequired,
26
+ /** leaflet attribute to control tabindex value for keyboaryd-only / SR users */
27
+ keyboard: PropTypes.bool
26
28
  };
27
29
  const templateDefaultProps = {
28
- children: null
30
+ children: null,
31
+ keyboard: false
29
32
  };
30
33
 
31
34
  /**
@@ -38,7 +41,7 @@ export const SharedBikeCircle = ({
38
41
  pixels,
39
42
  strokeColor
40
43
  }) => {
41
- const GeneratedMarker = ({ children, entity: station }) => {
44
+ const GeneratedMarker = ({ children, keyboard, entity: station }) => {
42
45
  let newStrokeColor = strokeColor || fillColor;
43
46
 
44
47
  if (!station.isFloatingBike) {
@@ -51,6 +54,7 @@ export const SharedBikeCircle = ({
51
54
  color={newStrokeColor}
52
55
  fillColor={fillColor}
53
56
  fillOpacity={1}
57
+ keyboard={keyboard}
54
58
  radius={pixels - (station.isFloatingBike ? 1 : 0)}
55
59
  weight={1}
56
60
  >
@@ -68,7 +72,7 @@ export const SharedBikeCircle = ({
68
72
  * A component that renders rental bike entities
69
73
  * either as a bike or a bike dock (or hub, showing spaces available).
70
74
  */
71
- export const HubAndFloatingBike = ({ children, entity: station }) => {
75
+ export const HubAndFloatingBike = ({ children, keyboard, entity: station }) => {
72
76
  let icon;
73
77
  if (station.isFloatingBike) {
74
78
  icon = floatingBikeIcon;
@@ -80,7 +84,7 @@ export const HubAndFloatingBike = ({ children, entity: station }) => {
80
84
  icon = hubIcons[i];
81
85
  }
82
86
  return (
83
- <Marker icon={icon} position={[station.y, station.x]}>
87
+ <Marker icon={icon} keyboard={keyboard} position={[station.y, station.x]}>
84
88
  {children}
85
89
  </Marker>
86
90
  );
@@ -110,8 +114,12 @@ const getStationMarkerByColor = memoize(color =>
110
114
  export const GenericMarker = ({ fillColor = "gray" }) => {
111
115
  const markerIcon = getStationMarkerByColor(fillColor);
112
116
 
113
- const GeneratedMarker = ({ children, entity: station }) => (
114
- <Marker icon={markerIcon} position={[station.y, station.x]}>
117
+ const GeneratedMarker = ({ children, keyboard, entity: station }) => (
118
+ <Marker
119
+ icon={markerIcon}
120
+ keyboard={keyboard}
121
+ position={[station.y, station.x]}
122
+ >
115
123
  {children}
116
124
  </Marker>
117
125
  );
@@ -1,7 +1,7 @@
1
1
  import BaseMap from "@opentripplanner/base-map";
2
2
  import coreUtils from "@opentripplanner/core-utils";
3
3
  import PropTypes from "prop-types";
4
- import React, { Component } from "react";
4
+ import React from "react";
5
5
  import { CircleMarker } from "react-leaflet";
6
6
  import { action } from "@storybook/addon-actions";
7
7
  import { boolean } from "@storybook/addon-knobs";
@@ -11,7 +11,6 @@ import bikeRentalStations from "../__mocks__/bike-rental-stations.json";
11
11
  import carRentalStations from "../__mocks__/car-rental-stations.json";
12
12
  import eScooterStations from "../__mocks__/e-scooter-rental-stations.json";
13
13
  import { HubAndFloatingBike } from "./DefaultMarkers";
14
- import LeafletLayerControlInterface from "./leaflet-layer-control-interface";
15
14
 
16
15
  import "../../../node_modules/leaflet/dist/leaflet.css";
17
16
 
@@ -165,51 +164,32 @@ const EScooterMapSymbols = [
165
164
  ];
166
165
  const setLocation = action("setLocation");
167
166
 
168
- class ZoomControlledMapWithVehicleRentalOverlay extends Component {
169
- constructor() {
170
- super();
171
- this.state = { zoom: 13 };
172
- }
173
-
174
- onViewportChanged = ({ zoom }) => {
175
- const { zoom: stateZoom } = this.state;
176
- if (zoom !== stateZoom) {
177
- this.setState({ zoom });
178
- }
179
- };
167
+ const INITIAL_ZOOM = 13;
180
168
 
181
- render() {
182
- const {
183
- companies,
184
- getStationName,
185
- mapSymbols,
186
- refreshVehicles,
187
- stations,
188
- visible
189
- } = this.props;
190
- const { zoom } = this.state;
191
- return (
192
- <BaseMap
193
- center={center}
194
- onViewportChanged={this.onViewportChanged}
195
- zoom={zoom}
196
- >
197
- <LeafletLayerControlInterface
198
- configCompanies={configCompanies}
199
- companies={companies}
200
- getStationName={getStationName}
201
- setLocation={setLocation}
202
- mapSymbols={mapSymbols}
203
- name="Rentals"
204
- refreshVehicles={refreshVehicles}
205
- stations={stations}
206
- visible={visible}
207
- zoom={zoom}
208
- />
209
- </BaseMap>
210
- );
211
- }
212
- }
169
+ const ZoomControlledMapWithVehicleRentalOverlay = ({
170
+ companies,
171
+ getStationName,
172
+ mapSymbols,
173
+ refreshVehicles,
174
+ stations,
175
+ visible
176
+ }) => (
177
+ // Caution, <BaseMap> must be a direct parent of <VehicleRentalOverlay>.
178
+ // Therefore, do not place <BaseMap> in a decorator at this time.
179
+ <BaseMap center={center} zoom={INITIAL_ZOOM}>
180
+ <VehicleRentalOverlay
181
+ configCompanies={configCompanies}
182
+ companies={companies}
183
+ getStationName={getStationName}
184
+ setLocation={setLocation}
185
+ mapSymbols={mapSymbols}
186
+ name="Rentals"
187
+ refreshVehicles={refreshVehicles}
188
+ stations={stations}
189
+ visible={visible}
190
+ />
191
+ </BaseMap>
192
+ );
213
193
 
214
194
  ZoomControlledMapWithVehicleRentalOverlay.propTypes = {
215
195
  companies: PropTypes.arrayOf(PropTypes.string.isRequired),
@@ -225,7 +205,7 @@ ZoomControlledMapWithVehicleRentalOverlay.defaultProps = {
225
205
  companies: null,
226
206
  getStationName: undefined,
227
207
  mapSymbols: null,
228
- visible: undefined
208
+ visible: true
229
209
  };
230
210
 
231
211
  function customStationName(_, station) {
@@ -249,7 +229,7 @@ export const RentalBicycles = () => (
249
229
  export const RentalBicyclesVisibilityControlledByKnob = () => {
250
230
  const isOverlayVisible = boolean(
251
231
  "Toggle visibility of vehicle rental overlay",
252
- true
232
+ false
253
233
  );
254
234
  return (
255
235
  <ZoomControlledMapWithVehicleRentalOverlay
package/src/index.js CHANGED
@@ -1,9 +1,11 @@
1
+ import flatten from "flat";
1
2
  import { Styled as BaseMapStyled } from "@opentripplanner/base-map";
2
3
  import coreUtils from "@opentripplanner/core-utils";
3
4
  import FromToLocationPicker from "@opentripplanner/from-to-location-picker";
4
5
  import ZoomBasedMarkers from "@opentripplanner/zoom-based-markers";
5
6
  import PropTypes from "prop-types";
6
7
  import React from "react";
8
+ import { FormattedMessage } from "react-intl";
7
9
  import { FeatureGroup, MapLayer, Popup, withLeaflet } from "react-leaflet";
8
10
 
9
11
  import {
@@ -12,12 +14,28 @@ import {
12
14
  SharedBikeCircle
13
15
  } from "./DefaultMarkers";
14
16
 
17
+ // Load the default messages.
18
+ import defaultEnglishMessages from "../i18n/en-US.yml";
19
+
20
+ // HACK: We should flatten the messages loaded above because
21
+ // the YAML loaders behave differently between webpack and our version of jest:
22
+ // - the yaml loader for webpack returns a nested object,
23
+ // - the yaml loader for jest returns messages with flattened ids.
24
+ const defaultMessages = flatten(defaultEnglishMessages);
25
+
15
26
  /**
16
27
  * This vehicle rental overlay can be used to render vehicle rentals of various
17
28
  * types. This layer can be configured to show different styles of markers at
18
29
  * different zoom levels.
19
30
  */
20
31
  class VehicleRentalOverlay extends MapLayer {
32
+ constructor(props) {
33
+ super(props);
34
+ this.state = {
35
+ zoom: props.leaflet.map.getZoom()
36
+ };
37
+ }
38
+
21
39
  /**
22
40
  * This helper method will be passed to the ZoomBasedMarkers symbolTransform prop.
23
41
  * It creates a component that inserts a popup
@@ -92,23 +110,50 @@ class VehicleRentalOverlay extends MapLayer {
92
110
  if (this.refreshTimer) clearInterval(this.refreshTimer);
93
111
  }
94
112
 
113
+ /**
114
+ * When the layer is added (or toggled on, or its visibility becomes true),
115
+ * start refreshing vehicle positions.
116
+ */
117
+ onOverlayAdded = () => {
118
+ this.startRefreshing();
119
+ };
120
+
121
+ /**
122
+ * When the layer is removed (or toggled off, or its visibility becomes false),
123
+ * stop refreshing vehicle positions.
124
+ */
125
+ onOverlayRemoved = () => {
126
+ this.stopRefreshing();
127
+ };
128
+
129
+ /**
130
+ * Listen to changes on the BaseMap's center or zoom.
131
+ * @param viewport The viewport data. See https://github.com/PaulLeCam/react-leaflet/blob/master/example/components/viewport.js for details.
132
+ */
133
+ onViewportChanged = viewport => {
134
+ const { zoom: newZoom } = viewport;
135
+ if (this.state.zoom !== newZoom) {
136
+ this.setState({ zoom: newZoom });
137
+ }
138
+ };
139
+
140
+ /**
141
+ * Upon mounting, see whether the vehicles should be fetched,
142
+ * and also call the register overlay prop that the
143
+ * @opentripplanner/base-map package has injected to listen to zoom/position changes.
144
+ */
95
145
  componentDidMount() {
96
- const { visible } = this.props;
146
+ const { registerOverlay, visible } = this.props;
97
147
  if (visible) this.startRefreshing();
148
+ if (typeof registerOverlay === "function") {
149
+ registerOverlay(this);
150
+ }
98
151
  }
99
152
 
100
153
  componentWillUnmount() {
101
154
  this.stopRefreshing();
102
155
  }
103
156
 
104
- componentDidUpdate(prevProps) {
105
- if (!prevProps.visible && this.props.visible) {
106
- this.startRefreshing();
107
- } else if (prevProps.visible && !this.props.visible) {
108
- this.stopRefreshing();
109
- }
110
- }
111
-
112
157
  /**
113
158
  * Render some popup html for a station. This contains custom logic for
114
159
  * displaying rental vehicles in the TriMet MOD website that might not be
@@ -130,15 +175,33 @@ class VehicleRentalOverlay extends MapLayer {
130
175
  {/* render dock info if it is available */}
131
176
  {stationIsHub && (
132
177
  <BaseMapStyled.PopupRow>
133
- <div>Available bikes: {station.bikesAvailable}</div>
134
- <div>Available docks: {station.spacesAvailable}</div>
178
+ <div>
179
+ <FormattedMessage
180
+ defaultMessage={
181
+ defaultMessages["otpUi.VehicleRentalOverlay.availableBikes"]
182
+ }
183
+ description="Label text for the number of bikes available"
184
+ id="otpUi.VehicleRentalOverlay.availableBikes"
185
+ values={{ value: station.bikesAvailable }}
186
+ />
187
+ </div>
188
+ <div>
189
+ <FormattedMessage
190
+ defaultMessage={
191
+ defaultMessages["otpUi.VehicleRentalOverlay.availableDocks"]
192
+ }
193
+ description="Label text for the number of docks available"
194
+ id="otpUi.VehicleRentalOverlay.availableDocks"
195
+ values={{ value: station.spacesAvailable }}
196
+ />
197
+ </div>
135
198
  </BaseMapStyled.PopupRow>
136
199
  )}
137
200
 
138
201
  {/* Set as from/to toolbar */}
139
202
  <BaseMapStyled.PopupRow>
140
- <b>Plan a trip:</b>
141
203
  <FromToLocationPicker
204
+ label
142
205
  location={location}
143
206
  setLocation={setLocation}
144
207
  />
@@ -149,14 +212,12 @@ class VehicleRentalOverlay extends MapLayer {
149
212
  };
150
213
 
151
214
  render() {
152
- const { companies, mapSymbols, stations, visible } = this.props;
215
+ const { companies, mapSymbols, stations } = this.props;
216
+ const { zoom } = this.state;
153
217
  // Render an empty FeatureGroup if the rental vehicles should not be visible
154
218
  // on the map. Otherwise previous stations may still be shown due to some
155
219
  // react-leaflet internals, maybe? Also, do not return null because that will
156
220
  // prevent the overlay from appearing in the layer controls.
157
- if (!visible) {
158
- return <FeatureGroup />;
159
- }
160
221
 
161
222
  let filteredStations = stations;
162
223
  if (companies) {
@@ -170,9 +231,6 @@ class VehicleRentalOverlay extends MapLayer {
170
231
  return <FeatureGroup />;
171
232
  }
172
233
 
173
- // get zoom to check which symbol to render
174
- const zoom = this.props.leaflet.map.getZoom();
175
-
176
234
  // Convert map symbols for this overlay to zoomBasedSymbolType.
177
235
  const symbols = this.convertToZoomMarkerSymbols(mapSymbols);
178
236