@geops/rvf-mobility-web-component 0.1.15 → 0.1.16

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.
Files changed (50) hide show
  1. package/CHANGELOG.md +29 -0
  2. package/docutils.js +8 -2
  3. package/index.html +14 -2
  4. package/index.js +162 -75
  5. package/package.json +2 -1
  6. package/src/RealtimeLayer/RealtimeLayer.tsx +2 -0
  7. package/src/RvfExportMenu/RvfExportMenu.tsx +12 -1
  8. package/src/RvfFeatureDetails/RvfFeatureDetails.tsx +25 -4
  9. package/src/RvfFeatureDetails/RvfSharedMobilityDetail/FloatingVehiclesDetails/FloatingVehiclesDetails.tsx +53 -0
  10. package/src/RvfFeatureDetails/RvfSharedMobilityDetail/FloatingVehiclesDetails/index.tsx +1 -0
  11. package/src/RvfFeatureDetails/RvfSharedMobilityDetail/RvfSharedMobilityDetails.tsx +129 -0
  12. package/src/RvfFeatureDetails/RvfSharedMobilityDetail/StationDetails/StationDetails.tsx +24 -0
  13. package/src/RvfFeatureDetails/RvfSharedMobilityDetail/StationDetails/index.tsx +1 -0
  14. package/src/RvfFeatureDetails/RvfSharedMobilityDetail/index.tsx +1 -0
  15. package/src/RvfLineNetworkPlanLayer/RvfLineNetworkPlanLayer.tsx +2 -0
  16. package/src/RvfMobilityMap/RvfMobilityMap.tsx +107 -26
  17. package/src/RvfOverlayHeader/RvfOverlayHeader.tsx +1 -1
  18. package/src/RvfPoisLayer/RvfPoisLayer.tsx +2 -0
  19. package/src/RvfSelectedFeatureHighlightLayer/RvfSelectedFeatureHighlightLayer.tsx +64 -0
  20. package/src/RvfSelectedFeatureHighlightLayer/index.tsx +1 -0
  21. package/src/RvfSellingPointsLayer/RvfSellingPointsLayer.tsx +2 -0
  22. package/src/RvfShare/RvfShare.tsx +1 -1
  23. package/src/RvfSharedMobilityLayerGroup/RvfSharedMobilityLayerGroup.tsx +50 -18
  24. package/src/RvfSingleClickListener/RvfSingleClickListener.tsx +2 -10
  25. package/src/RvfTarifZonenLayer/RvfTarifZonenLayer.tsx +2 -0
  26. package/src/RvfTopics/RvfTopics.tsx +40 -8
  27. package/src/StationsLayer/StationsLayer.tsx +2 -0
  28. package/src/icons/Bike/rvf_shared_bike.svg +2 -2
  29. package/src/icons/Car/rvf_shared_car.svg +3 -3
  30. package/src/icons/CargoBike/rvf_shared_cargo_bike.svg +3 -3
  31. package/src/icons/Scooter/rvf_shared_scooter.svg +2 -2
  32. package/src/index.tsx +4 -0
  33. package/src/logos/callabike_logo.png +0 -0
  34. package/src/logos/flinkster_logo.png +0 -0
  35. package/src/logos/gruene_flotte_logo.png +0 -0
  36. package/src/logos/logo_frelo_web_rgb.png +0 -0
  37. package/src/logos/natur_energie_logo.png +0 -0
  38. package/src/logos/yoio_logo.png +0 -0
  39. package/src/logos/zeus_logo.png +0 -0
  40. package/src/utils/applyInitialLayerVisibility.ts +41 -0
  41. package/src/utils/constants.ts +30 -0
  42. package/src/utils/{createSharedMobilityLayer.ts → createFreeFloatMobilityLayer.ts} +41 -69
  43. package/src/utils/createMobiDataBwWfsLayer.ts +108 -67
  44. package/src/utils/exportPdf.ts +1 -4
  45. package/src/utils/getLayersAsFlatArray.ts +22 -0
  46. package/src/utils/getLinkByDevice.ts +28 -0
  47. package/src/utils/getPermalinkParameters.ts +27 -0
  48. package/src/utils/hooks/useInitialLayersVisiblity.tsx +28 -0
  49. package/src/utils/hooks/useMapContext.tsx +2 -0
  50. package/src/utils/hooks/useUpdatePermalink.tsx +44 -11
package/package.json CHANGED
@@ -2,12 +2,13 @@
2
2
  "name": "@geops/rvf-mobility-web-component",
3
3
  "license": "UNLICENSED",
4
4
  "description": "Web components for rvf in the domains of mobility and logistics.",
5
- "version": "0.1.15",
5
+ "version": "0.1.16",
6
6
  "homepage": "https://rvf-mobility-web-component-geops.vercel.app/",
7
7
  "type": "module",
8
8
  "main": "index.js",
9
9
  "dependencies": {
10
10
  "jspdf": "^2.5.2",
11
+ "lodash.debounce": "^4.0.8",
11
12
  "maplibre-gl": "^4.7.1",
12
13
  "mobility-toolbox-js": "3.1.1-beta.0",
13
14
  "ol": "^10.3.1",
@@ -7,6 +7,7 @@ import { memo } from "preact/compat";
7
7
  import { useEffect, useMemo } from "preact/hooks";
8
8
 
9
9
  import centerOnVehicle from "../utils/centerOnVehicle";
10
+ import { RVF_LAYERS_NAMES } from "../utils/constants";
10
11
  import getDelayColorForVehicle from "../utils/getDelayColorForVehicle";
11
12
  import getDelayFontForVehicle from "../utils/getDelayFontForVehicle";
12
13
  import getDelayTextForVehicle from "../utils/getDelayTextForVehicle";
@@ -48,6 +49,7 @@ function RealtimeLayer(props: RealtimeLayerProps) {
48
49
  return mots.split(",") as RealtimeMot[];
49
50
  }
50
51
  : undefined,
52
+ name: RVF_LAYERS_NAMES.echtzeit,
51
53
  tenant,
52
54
  url: realtimeurl,
53
55
  zIndex: 1,
@@ -15,8 +15,10 @@ export type RvfExportMenuButtonProps = JSX.HTMLAttributes<HTMLDivElement> &
15
15
 
16
16
  const formats = ["A4", "A3", "A1", "A0"];
17
17
 
18
+ let prevRealtimeLayerVisibility = false;
19
+
18
20
  function RvfExportMenu({ ...props }: RvfExportMenuButtonProps) {
19
- const { map } = useMapContext();
21
+ const { map, realtimeLayer } = useMapContext();
20
22
  const [useMaxExtent, setUseMaxExtent] = useState(false);
21
23
  const [format, setFormat] = useState<string>(formats[0]);
22
24
  const checkboxId = useId();
@@ -64,12 +66,21 @@ function RvfExportMenu({ ...props }: RvfExportMenuButtonProps) {
64
66
  { format },
65
67
  {
66
68
  onAfter: (map, layers) => {
69
+ if (
70
+ prevRealtimeLayerVisibility !== realtimeLayer.getVisible()
71
+ ) {
72
+ realtimeLayer.setVisible(prevRealtimeLayerVisibility);
73
+ }
67
74
  getAllLayers(layers).forEach((layer) => {
68
75
  layer.set(LAYER_PROP_IS_EXPORTING, false);
69
76
  });
70
77
  },
71
78
 
72
79
  onBefore: (map, layers) => {
80
+ prevRealtimeLayerVisibility = realtimeLayer.getVisible();
81
+ if (realtimeLayer.getVisible()) {
82
+ realtimeLayer.setVisible(false);
83
+ }
73
84
  getAllLayers(layers).forEach((layer) => {
74
85
  layer.set(LAYER_PROP_IS_EXPORTING, true);
75
86
  });
@@ -3,16 +3,28 @@ import type { JSX, PreactDOMAttributes } from "preact";
3
3
  import { memo } from "preact/compat";
4
4
 
5
5
  import useRvfContext from "../utils/hooks/useRvfContext";
6
+ import RvfSharedMobilityDetails from "./RvfSharedMobilityDetail";
6
7
 
7
8
  export type RvfFeatureDetailsProps = JSX.HTMLAttributes<HTMLDivElement> &
8
9
  PreactDOMAttributes;
9
10
 
11
+ const getIsSharedMobility = (selectedFeature): boolean => {
12
+ const id =
13
+ selectedFeature.getId() || selectedFeature.get("features")[0].getId();
14
+ if (typeof id === "number") {
15
+ return !!selectedFeature.get("category");
16
+ } else if (typeof id === "string") {
17
+ return id.includes("sharing_stations") || id.includes("Vehicle");
18
+ }
19
+ };
20
+
10
21
  function RvfFeatureDetails(props: RvfFeatureDetailsProps) {
11
22
  const { selectedFeature } = useRvfContext();
23
+ const isSharedMobility = getIsSharedMobility(selectedFeature);
12
24
 
13
- return (
14
- <div {...props}>
15
- {Object.entries(selectedFeature.getProperties()).map(([key, value]) => {
25
+ const showDefaultData = () => {
26
+ return Object.entries(selectedFeature.getProperties()).map(
27
+ ([key, value]) => {
16
28
  return (
17
29
  <div className="flex gap-2" key={key}>
18
30
  <span>
@@ -21,7 +33,16 @@ function RvfFeatureDetails(props: RvfFeatureDetailsProps) {
21
33
  <div>{value}</div>
22
34
  </div>
23
35
  );
24
- })}
36
+ },
37
+ );
38
+ };
39
+
40
+ return (
41
+ <div {...props}>
42
+ {isSharedMobility && (
43
+ <RvfSharedMobilityDetails selectedFeature={selectedFeature} />
44
+ )}
45
+ {!isSharedMobility && showDefaultData()}
25
46
  </div>
26
47
  );
27
48
  }
@@ -0,0 +1,53 @@
1
+ import { Feature } from "ol";
2
+
3
+ import getLinkByDevice from "../../../utils/getLinkByDevice";
4
+
5
+ export interface FloatingVehiclesDetailsProps {
6
+ features: Feature[];
7
+ }
8
+
9
+ function FloatingVehiclesDetails({ features }: FloatingVehiclesDetailsProps) {
10
+ const renderedDetails = features.map((feature, idx) => {
11
+ let vehicleNumber;
12
+ if (feature.get("feed_id") === "yoio_freiburg") {
13
+ vehicleNumber = feature.get("rental_uris_android").split("/").pop();
14
+ } else if (feature.get("feed_id") === "zeus_freiburg") {
15
+ const url = new URL(feature.get("rental_uris_web"));
16
+ vehicleNumber = new URLSearchParams(url.search).get("vehicle");
17
+ }
18
+
19
+ const scooterLink = getLinkByDevice(feature);
20
+ const availableDistanceString = feature
21
+ .get("current_range_meters")
22
+ .toLocaleString()
23
+ .replace(",", "'");
24
+
25
+ return (
26
+ <li className="p-1" key={idx}>
27
+ <a href={scooterLink} key={idx} rel="noreferrer" target="_blank">
28
+ {`${vehicleNumber} (${availableDistanceString} m)`}
29
+ </a>
30
+ </li>
31
+ );
32
+ });
33
+
34
+ return (
35
+ <div className="flex flex-col overflow-scroll">
36
+ {features.length} Fahrzeuge
37
+ <span className="text-lg text-grey">Fahrzeug mieten</span>
38
+ <ul className="h-screen list-disc overflow-scroll pl-4 underline underline-offset-2">
39
+ {renderedDetails}
40
+ </ul>
41
+ <a
42
+ className="mx-auto text-lg text-grey hover:text-red"
43
+ href={getLinkByDevice(features[0])}
44
+ rel="noreferrer"
45
+ target="_blank"
46
+ >
47
+ Über {features[0].get("feed_id").split("_")[0].toUpperCase()}
48
+ </a>
49
+ </div>
50
+ );
51
+ }
52
+
53
+ export default FloatingVehiclesDetails;
@@ -0,0 +1 @@
1
+ export { default } from "./FloatingVehiclesDetails";
@@ -0,0 +1,129 @@
1
+ import { Feature } from "ol";
2
+ import { useEffect, useState } from "preact/hooks";
3
+
4
+ import callBike from "../../logos/callabike_logo.png";
5
+ import flinkster from "../../logos/flinkster_logo.png";
6
+ import grueneFlotteLogo from "../../logos/gruene_flotte_logo.png";
7
+ import lastenVeloLogo from "../../logos/lasten_velo_freiburg.png";
8
+ import freloLogo from "../../logos/logo_frelo_web_rgb.png";
9
+ import naturEnergieLogo from "../../logos/natur_energie_logo.png";
10
+ import yoioLogo from "../../logos/yoio_logo.png";
11
+ import zeusLogo from "../../logos/zeus_logo.png";
12
+ import { API_REQUEST_FEATURE_TYPE } from "../../utils/constants";
13
+ import FloatingVehiclesDetails from "./FloatingVehiclesDetails";
14
+ import StationDetails from "./StationDetails";
15
+
16
+ export interface RvfSharedMobilityDetailsProps {
17
+ selectedFeature: Feature;
18
+ }
19
+
20
+ const logos = {
21
+ callabike_ice: callBike,
22
+ flinkster_carsharing: flinkster,
23
+ "gruene-flotte_freiburg": grueneFlotteLogo,
24
+ lastenvelo_fr: lastenVeloLogo,
25
+ naturenergie_sharing: naturEnergieLogo,
26
+ nextbike_df: freloLogo,
27
+ yoio_freiburg: yoioLogo,
28
+ zeus_freiburg: zeusLogo,
29
+ };
30
+
31
+ const categories = {
32
+ callabike_ice: "Bike Sharing",
33
+ flinkster_carsharing: "Car Sharing",
34
+ "gruene-flotte_freiburg": "Car Sharing",
35
+ lastenvelo_fr: "Cargo Bike Sharing",
36
+ naturenergie_sharing: "Car Sharing",
37
+ nextbike_df: "Bike Sharing",
38
+ yoio_freiburg: "E-Scooter",
39
+ zeus_freiburg: "E-Scooter",
40
+ };
41
+
42
+ function RvfSharedMobilityDetails({
43
+ selectedFeature,
44
+ }: RvfSharedMobilityDetailsProps) {
45
+ const [features, setFeatures] = useState([]);
46
+ const isStationDetails =
47
+ !!selectedFeature.get("categories") ||
48
+ !selectedFeature.get("features")?.length;
49
+ const isFloatingVehicle = !!selectedFeature.get("features")?.length;
50
+
51
+ useEffect(() => {
52
+ const extent = selectedFeature.getGeometry().getExtent();
53
+ extent[0] = extent[0] - 2;
54
+ extent[1] = extent[1] - 2;
55
+ extent[2] = extent[2] + 2;
56
+ extent[3] = extent[3] + 2;
57
+ const categoryName = selectedFeature.get("category")?.toLowerCase();
58
+ let featureType;
59
+
60
+ if (categoryName) {
61
+ if (categoryName === "bike sharing") {
62
+ featureType = API_REQUEST_FEATURE_TYPE.stations.bike;
63
+ } else if (categoryName === "car sharing") {
64
+ featureType = API_REQUEST_FEATURE_TYPE.stations.car;
65
+ } else if (categoryName === "cargo bike sharing") {
66
+ featureType = API_REQUEST_FEATURE_TYPE.stations.cargoBike;
67
+ }
68
+
69
+ fetch(createRequestLink(featureType, extent))
70
+ .then((res) => {
71
+ return res.json();
72
+ })
73
+ .then((data) => {
74
+ selectedFeature.setProperties(data.features[0].properties);
75
+ setFeatures([selectedFeature]);
76
+ })
77
+ .catch((err) => {
78
+ console.error(err);
79
+ });
80
+ } else {
81
+ if (isFloatingVehicle) {
82
+ setFeatures(selectedFeature.get("features"));
83
+ } else {
84
+ setFeatures([selectedFeature]);
85
+ }
86
+ }
87
+ }, [selectedFeature, isFloatingVehicle]);
88
+
89
+ const createRequestLink = (name, extent) => {
90
+ return (
91
+ "https://api.mobidata-bw.de/geoserver/MobiData-BW/" +
92
+ name +
93
+ "/ows" +
94
+ "?service=WFS&" +
95
+ "version=1.1.0&request=GetFeature&typename=" +
96
+ "MobiData-BW:" +
97
+ name +
98
+ "&" +
99
+ "outputFormat=application/json&srsname=EPSG:3857&" +
100
+ "bbox=" +
101
+ extent.join(",") +
102
+ ",EPSG:3857"
103
+ );
104
+ };
105
+
106
+ return (
107
+ <div className="flex flex-col overflow-scroll px-2 pt-2">
108
+ <div className="flex items-center text-xl text-grey">
109
+ <img
110
+ alt="logo"
111
+ className="max-w-24"
112
+ src={logos[features[0]?.get("feed_id")]}
113
+ />
114
+ <span className="ml-4">
115
+ {selectedFeature.get("category") ||
116
+ categories[features[0]?.get("feed_id")]}
117
+ </span>
118
+ </div>
119
+ {features.length && isStationDetails && (
120
+ <StationDetails feature={features[0]} />
121
+ )}
122
+ {!!features[0]?.get("form_factor") && isFloatingVehicle && (
123
+ <FloatingVehiclesDetails features={features} />
124
+ )}
125
+ </div>
126
+ );
127
+ }
128
+
129
+ export default RvfSharedMobilityDetails;
@@ -0,0 +1,24 @@
1
+ import { Feature } from "ol";
2
+
3
+ import getLinkByDevice from "../../../utils/getLinkByDevice";
4
+ export interface StationDetailsProps {
5
+ feature: Feature;
6
+ }
7
+
8
+ function StationDetails({ feature }: StationDetailsProps) {
9
+ return (
10
+ <div className="flex flex-col">
11
+ {feature.get("num_vehicles_available")} Fahrzeuge
12
+ <a
13
+ className="mx-auto mt-[40%] rounded-xl border border-grey px-2 py-1 text-xl text-grey hover:border-red hover:text-red"
14
+ href={getLinkByDevice(feature)}
15
+ rel="noreferrer"
16
+ target="_blank"
17
+ >
18
+ Jetzt buchen
19
+ </a>
20
+ </div>
21
+ );
22
+ }
23
+
24
+ export default StationDetails;
@@ -0,0 +1 @@
1
+ export { default } from "./StationDetails";
@@ -0,0 +1 @@
1
+ export { default } from "./RvfSharedMobilityDetails";
@@ -3,6 +3,7 @@ import { MaplibreStyleLayerOptions } from "mobility-toolbox-js/ol/layers/Maplibr
3
3
  import { memo } from "preact/compat";
4
4
  import { useEffect, useMemo } from "preact/hooks";
5
5
 
6
+ import { RVF_LAYERS_NAMES } from "../utils/constants";
6
7
  import useMapContext from "../utils/hooks/useMapContext";
7
8
  import useRvfContext from "../utils/hooks/useRvfContext";
8
9
 
@@ -21,6 +22,7 @@ function RvfLineNetworkPlanLayer(props: MaplibreStyleLayerOptions) {
21
22
  },
22
23
  maplibreLayer: baseLayer,
23
24
  minZoom: 10,
25
+ name: RVF_LAYERS_NAMES.liniennetz,
24
26
  ...(props || {}),
25
27
  });
26
28
  }, [baseLayer, props]);
@@ -39,6 +39,7 @@ import RvfLineNetworkPlanLayer from "../RvfLineNetworkPlanLayer";
39
39
  import Modal from "../RvfModal";
40
40
  import RvfOverlayHeader from "../RvfOverlayHeader";
41
41
  import RvfPoisLayer from "../RvfPoisLayer";
42
+ import RvfSelectedFeatureHighlightLayer from "../RvfSelectedFeatureHighlightLayer";
42
43
  import RvfSellingPointsLayer from "../RvfSellingPointsLayer";
43
44
  import RvfShare from "../RvfShare";
44
45
  import RvfSharedMobilityLayerGroup from "../RvfSharedMobilityLayerGroup";
@@ -55,6 +56,7 @@ import StationsLayer from "../StationsLayer";
55
56
  import tailwind from "../style.css";
56
57
  import { RVF_EXTENT_3857 } from "../utils/constants";
57
58
  import { I18nContext } from "../utils/hooks/useI18n";
59
+ import useInitialLayersVisiblity from "../utils/hooks/useInitialLayersVisiblity";
58
60
  import { MapContext } from "../utils/hooks/useMapContext";
59
61
  import { RvfContext } from "../utils/hooks/useRvfContext";
60
62
  import useUpdatePermalink from "../utils/hooks/useUpdatePermalink";
@@ -63,7 +65,13 @@ import MobilityEvent from "../utils/MobilityEvent";
63
65
  // @ts-expect-error bad type definition
64
66
  import style from "./index.css";
65
67
 
66
- export type RvfMobilityMapProps = { toolbar: string } & MobilityMapProps;
68
+ export type RvfMobilityMapProps = {
69
+ layers: string; // list of visible layers on load
70
+ layertree: string;
71
+ print: string;
72
+ share: string;
73
+ toolbar: string;
74
+ } & MobilityMapProps;
67
75
 
68
76
  const bbox = RVF_EXTENT_3857.join(",");
69
77
 
@@ -87,6 +95,8 @@ function RvfMobilityMap({
87
95
  center = null,
88
96
  extent = bbox,
89
97
  geolocation = "true",
98
+ layers = null,
99
+ layertree = "true",
90
100
  mapsurl = "https://maps.geops.io",
91
101
  maxextent = bbox,
92
102
  maxzoom = "20",
@@ -97,9 +107,11 @@ function RvfMobilityMap({
97
107
  notificationbeforelayerid = null,
98
108
  notificationurl = null,
99
109
  permalink = "false",
110
+ print = "true",
100
111
  realtime = "true",
101
112
  realtimeurl = "wss://api.geops.io/tracker-ws/v1/ws",
102
113
  search = "false",
114
+ share = "true",
103
115
  stopsurl = "https://api.geops.io/stops/v1/",
104
116
  tenant = null,
105
117
  toolbar = "true",
@@ -129,7 +141,42 @@ function RvfMobilityMap({
129
141
  useState<MaplibreStyleLayer>();
130
142
  const [sharedMobilityLayerGroup, setSharedMobilityLayerGroup] =
131
143
  useState<Group>();
132
- const [hasToolbar] = useState<boolean>(toolbar === "true");
144
+
145
+ // Convert string boolean to boolean
146
+ const hasToolbar = useMemo(() => {
147
+ return toolbar === "true";
148
+ }, [toolbar]);
149
+
150
+ const hasLayerTree = useMemo(() => {
151
+ return layertree === "true";
152
+ }, [layertree]);
153
+
154
+ const hasRealtime = useMemo(() => {
155
+ return realtime === "true";
156
+ }, [realtime]);
157
+
158
+ const hasNotification = useMemo(() => {
159
+ return notification === "true";
160
+ }, [notification]);
161
+
162
+ const hasGeolocation = useMemo(() => {
163
+ return geolocation === "true";
164
+ }, [geolocation]);
165
+
166
+ const hasSearch = useMemo(() => {
167
+ return search === "true";
168
+ }, [search]);
169
+
170
+ const hasShare = useMemo(() => {
171
+ return share === "true";
172
+ }, [share]);
173
+
174
+ const hasPrint = useMemo(() => {
175
+ return print === "true";
176
+ }, [print]);
177
+
178
+ // Apply initial visibility of layers
179
+ useInitialLayersVisiblity(map, layers);
133
180
 
134
181
  // TODO: this should be removed. The parent application should be responsible to do this
135
182
  // or we should find something that fit more usecases
@@ -146,6 +193,7 @@ function RvfMobilityMap({
146
193
  geolocation,
147
194
  isFollowing,
148
195
  isTracking,
196
+ layers,
149
197
  map,
150
198
  mapsurl,
151
199
  maxextent,
@@ -190,6 +238,7 @@ function RvfMobilityMap({
190
238
  map,
191
239
  mapsurl,
192
240
  maxextent,
241
+ layers,
193
242
  maxzoom,
194
243
  minzoom,
195
244
  mots,
@@ -217,6 +266,8 @@ function RvfMobilityMap({
217
266
  center,
218
267
  extent,
219
268
  geolocation,
269
+ layers,
270
+ layertree,
220
271
  mapsurl,
221
272
  maxextent,
222
273
  maxzoom,
@@ -226,9 +277,11 @@ function RvfMobilityMap({
226
277
  notificationat,
227
278
  notificationbeforelayerid,
228
279
  notificationurl,
280
+ print,
229
281
  realtime,
230
282
  realtimeurl,
231
283
  search,
284
+ share,
232
285
  tenant,
233
286
  toolbar,
234
287
  zoom,
@@ -236,8 +289,10 @@ function RvfMobilityMap({
236
289
  );
237
290
  }, [
238
291
  baselayer,
292
+ layers,
239
293
  center,
240
294
  geolocation,
295
+ layertree,
241
296
  toolbar,
242
297
  mapsurl,
243
298
  maxzoom,
@@ -254,6 +309,8 @@ function RvfMobilityMap({
254
309
  zoom,
255
310
  extent,
256
311
  maxextent,
312
+ print,
313
+ share,
257
314
  ]);
258
315
 
259
316
  const rvfContextValue = useMemo(() => {
@@ -261,6 +318,7 @@ function RvfMobilityMap({
261
318
  isExportMenuOpen,
262
319
  isLayerTreeOpen,
263
320
  isShareMenuOpen,
321
+ layertree,
264
322
  lineNetworkPlanLayer,
265
323
  poisLayer,
266
324
  selectedFeature,
@@ -281,6 +339,7 @@ function RvfMobilityMap({
281
339
  };
282
340
  }, [
283
341
  isExportMenuOpen,
342
+ layertree,
284
343
  isLayerTreeOpen,
285
344
  isShareMenuOpen,
286
345
  lineNetworkPlanLayer,
@@ -399,8 +458,9 @@ function RvfMobilityMap({
399
458
  <Map className="relative flex-1 overflow-visible ">
400
459
  <BaseLayer {...baseLayerProps} />
401
460
  <SingleClickListener />
461
+ <RvfSelectedFeatureHighlightLayer />
402
462
 
403
- {realtime === "true" && <RealtimeLayer title="Echtzeit" />}
463
+ {hasRealtime && <RealtimeLayer title="Echtzeit" />}
404
464
  {
405
465
  <StationsLayer
406
466
  minZoom={10}
@@ -408,14 +468,14 @@ function RvfMobilityMap({
408
468
  {...stationsLayerProps}
409
469
  />
410
470
  }
411
- {notification === "true" && <NotificationLayer />}
471
+ {hasNotification && <NotificationLayer />}
472
+ <RvfTarifZonenLayer title="Tarifzonen" />
412
473
  <RvfSellingPointsLayer isQueryable title="Verkaufsstellen" />
413
474
  <RvfLineNetworkPlanLayer
414
475
  isQueryable
415
476
  minZoom={10}
416
477
  title="Liniennetz"
417
478
  />
418
- <RvfTarifZonenLayer isQueryable title="Tarifzonen" />
419
479
  <RvfPoisLayer title="POIs" />
420
480
  <RvfSharedMobilityLayerGroup title="Shared Mobility" />
421
481
 
@@ -428,11 +488,11 @@ function RvfMobilityMap({
428
488
  </div>
429
489
 
430
490
  <div className="absolute right-2 top-2 z-10 flex flex-col gap-2">
431
- {geolocation === "true" && <GeolocationButton />}
432
- {!hasToolbar && <RvfExportMenuButton />}
491
+ {hasGeolocation && <GeolocationButton />}
492
+ {!hasToolbar && hasPrint && <RvfExportMenuButton />}
433
493
  </div>
434
494
 
435
- {search === "true" && (
495
+ {hasSearch && (
436
496
  <div className="absolute left-2 right-12 top-2 z-10 flex max-h-[90%] min-w-64 max-w-96 flex-col">
437
497
  <Search />
438
498
  </div>
@@ -442,7 +502,7 @@ function RvfMobilityMap({
442
502
  <RvfZoomButtons />
443
503
  </div>
444
504
 
445
- {!hasToolbar && (
505
+ {!hasToolbar && hasLayerTree && (
446
506
  <RvfFloatingMenu
447
507
  isOpen={isLayerTreeOpen}
448
508
  onClick={onLayerTreeMenuClick}
@@ -453,10 +513,10 @@ function RvfMobilityMap({
453
513
  )}
454
514
  </Map>
455
515
  <Overlay
456
- className={"z-50"}
516
+ className={"z-50 bg-white"}
457
517
  ScrollableHandlerProps={scrollableHandlerProps}
458
518
  >
459
- {realtime === "true" && trainId && (
519
+ {hasRealtime && trainId && (
460
520
  <>
461
521
  <RvfOverlayHeader
462
522
  onClose={() => {
@@ -486,21 +546,21 @@ function RvfMobilityMap({
486
546
  }}
487
547
  title="Informations"
488
548
  ></RvfOverlayHeader>
489
- <RvfFeatureDetails className="relative flex flex-col gap-2 overflow-y-auto overflow-x-hidden p-2" />
549
+ <RvfFeatureDetails className="relative flex flex-col gap-2 overflow-y-auto overflow-x-hidden px-2 pt-2" />
490
550
  </>
491
551
  )}
492
- {hasToolbar && isExportMenuOpen && (
552
+ {hasToolbar && hasPrint && isExportMenuOpen && (
493
553
  <>
494
554
  <RvfOverlayHeader
495
555
  onClose={() => {
496
556
  setIsExportMenuOpen(false);
497
557
  }}
498
- title="Export"
558
+ title="Drücken"
499
559
  ></RvfOverlayHeader>
500
- <RvfExportMenu className="relative flex flex-col gap-2 overflow-y-auto overflow-x-hidden p-2" />
560
+ <RvfExportMenu className="relative flex flex-col gap-8 overflow-y-auto overflow-x-hidden px-4 py-6" />
501
561
  </>
502
562
  )}
503
- {hasToolbar && isLayerTreeOpen && (
563
+ {hasToolbar && hasLayerTree && isLayerTreeOpen && (
504
564
  <>
505
565
  <RvfOverlayHeader
506
566
  onClose={() => {
@@ -508,33 +568,54 @@ function RvfMobilityMap({
508
568
  }}
509
569
  title="Layers"
510
570
  ></RvfOverlayHeader>
511
- <Topics className="relative flex h-full flex-col overflow-y-auto overflow-x-hidden p-2" />
571
+ <Topics className=" relative flex h-full flex-col overflow-y-auto overflow-x-hidden p-2" />
512
572
  </>
513
573
  )}
514
- {hasToolbar && isShareMenuOpen && (
574
+ {hasToolbar && hasShare && isShareMenuOpen && (
515
575
  <>
516
576
  <RvfOverlayHeader
517
577
  onClose={() => {
518
- setIsLayerTreeOpen(false);
578
+ setIsShareMenuOpen(false);
519
579
  }}
520
580
  title="Share"
521
581
  ></RvfOverlayHeader>
522
- <RvfShare className="relative flex h-full flex-col overflow-y-auto overflow-x-hidden p-2" />
582
+ <RvfShare className="relative flex h-full flex-col gap-8 overflow-y-auto overflow-x-hidden px-4 py-6" />
523
583
  </>
524
584
  )}
525
585
  </Overlay>
526
586
 
527
587
  {hasToolbar && (
528
- <div className={"overflow-x-hidden border-r"}>
529
- <RvfLayerTreeButton className={"border-none"} />
530
- <RvfExportMenuButton className={"border-none"} />
531
- <RvfShareMenuButton className={"border-none"} />
588
+ <div
589
+ className={
590
+ "z-[100] flex justify-around overflow-x-hidden border-t bg-white p-1 @lg/main:block @lg/main:border-r @lg/main:p-0 "
591
+ }
592
+ >
593
+ {hasLayerTree && (
594
+ <RvfLayerTreeButton
595
+ className={"border-none"}
596
+ title="Layers"
597
+ />
598
+ )}
599
+
600
+ {hasPrint && (
601
+ <RvfExportMenuButton
602
+ className={"border-none"}
603
+ title="Drücken"
604
+ />
605
+ )}
606
+
607
+ {hasShare && (
608
+ <RvfShareMenuButton
609
+ className={"border-none"}
610
+ title="Share"
611
+ />
612
+ )}
532
613
  </div>
533
614
  )}
534
615
 
535
- {!hasToolbar && isExportMenuOpen && (
616
+ {!hasToolbar && hasPrint && isExportMenuOpen && (
536
617
  <Modal onClose={onExportMenuClose}>
537
- <RvfExportMenu className="relative flex h-full flex-col overflow-y-auto overflow-x-hidden" />
618
+ <RvfExportMenu className="relative flex h-full flex-col overflow-y-auto overflow-x-hidden p-2" />
538
619
  </Modal>
539
620
  )}
540
621
  </div>
@@ -22,7 +22,7 @@ function RvfOverlayHeader({
22
22
  return (
23
23
  <div
24
24
  className={twMerge(
25
- "flex flex-row items-center justify-between gap-2 p-2 border-b" +
25
+ "flex flex-row items-center justify-between gap-2 p-2 border-b pl-4" +
26
26
  (className || ""),
27
27
  )}
28
28
  {...props}