@geops/rvf-mobility-web-component 0.1.46 → 0.1.47

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 (204) hide show
  1. package/.prettierrc.js +3 -1
  2. package/CHANGELOG.md +12 -0
  3. package/README.md +1 -1
  4. package/doc/package.json +4 -3
  5. package/doc/postcss.config.mjs +1 -1
  6. package/doc/src/app/components/GeopsMobilityDoc.tsx +13 -224
  7. package/doc/src/app/components/GeopsMobilitySearchDoc.tsx +11 -107
  8. package/doc/src/app/components/WebComponentDoc.tsx +45 -56
  9. package/doc/src/app/geops-mobility/page.tsx +7 -2
  10. package/doc/src/app/geops-mobility-search/page.tsx +6 -2
  11. package/doc/src/app/globals.css +47 -27
  12. package/doc/src/app/layout.tsx +4 -2
  13. package/docutils.js +33 -17
  14. package/eslint.config.mjs +28 -34
  15. package/iframe.html +181 -207
  16. package/index.html +108 -88
  17. package/index.js +2345 -1976
  18. package/input.css +21 -3
  19. package/package.json +39 -41
  20. package/scripts/build.mjs +2 -2
  21. package/scripts/dev.mjs +3 -3
  22. package/search.html +70 -23
  23. package/src/BaseLayer/BaseLayer.tsx +2 -1
  24. package/src/Copyright/Copyright.tsx +4 -2
  25. package/src/DebugDeparture/DebugDeparture.tsx +16 -12
  26. package/src/DebugStop/DebugStop.tsx +2 -2
  27. package/src/Departure/Departure.tsx +2 -3
  28. package/src/EmbedNavigation/DragPanWarning.ts +125 -0
  29. package/src/EmbedNavigation/EmbedNavigation.tsx +52 -0
  30. package/src/EmbedNavigation/index.js +1 -0
  31. package/src/EmbedNavigation/index.tsx +1 -0
  32. package/src/GeolocationButton/GeolocationButton.tsx +11 -35
  33. package/src/GeolocationButton/index.tsx +1 -1
  34. package/src/Map/Map.tsx +5 -3
  35. package/src/MapDispatchEvents/MapDispatchEvents.tsx +78 -0
  36. package/src/MapDispatchEvents/index.tsx +1 -0
  37. package/src/MobilityMap/MobilityMap.tsx +117 -162
  38. package/src/MobilityMap/MobilityMapAttributes.test.ts +21 -0
  39. package/src/MobilityMap/MobilityMapAttributes.ts +252 -0
  40. package/src/MobilityMap/index.tsx +1 -0
  41. package/src/MobilitySearch/MobilitySearch.tsx +35 -0
  42. package/src/MobilitySearch/MobilitySearchAttributes.test.ts +21 -0
  43. package/src/MobilitySearch/MobilitySearchAttributes.ts +68 -0
  44. package/src/MobilitySearch/index.ts +2 -0
  45. package/src/NotificationLayer/NotificationLayer.tsx +36 -5
  46. package/src/Overlay/Overlay.tsx +24 -11
  47. package/src/Permalink/Permalink.tsx +77 -0
  48. package/src/Permalink/index.tsx +1 -0
  49. package/src/RealtimeLayer/RealtimeLayer.tsx +72 -33
  50. package/src/RouteDestination/RouteDestination.tsx +3 -3
  51. package/src/RouteIcon/RouteIcon.tsx +33 -25
  52. package/src/RouteIcon/index.tsx +1 -1
  53. package/src/RouteIdentifier/RouteIdentifer.tsx +6 -5
  54. package/src/RouteInfos/RouteInfos.tsx +7 -3
  55. package/src/RouteSchedule/RouteSchedule.tsx +3 -3
  56. package/src/RouteScheduleFooter/RouteScheduleFooter.tsx +1 -1
  57. package/src/RouteScheduleHeader/RouteScheduleHeader.tsx +7 -29
  58. package/src/RouteStop/RouteStop.tsx +8 -11
  59. package/src/RouteStopDelay/RouteStopDelay.tsx +2 -1
  60. package/src/RouteStopName/RouteStopName.tsx +2 -2
  61. package/src/RouteStopPlatform/RouteStopPlatform.tsx +2 -2
  62. package/src/RouteStopProgress/RouteStopProgress.tsx +2 -1
  63. package/src/RouteStopServices/RouteStopServices.tsx +2 -2
  64. package/src/RouteStopStation/RouteStopStation.tsx +8 -2
  65. package/src/RouteStopTime/RouteStopTime.tsx +2 -1
  66. package/src/RvfButton/RvfButton.tsx +14 -5
  67. package/src/RvfCheckbox/RvfCheckbox.tsx +8 -8
  68. package/src/RvfEmbedNavigation/DragPanWarning.ts +5 -5
  69. package/src/RvfEmbedNavigation/RvfEmbedNavigation.tsx +1 -0
  70. package/src/RvfExportMenu/RvfExportMenu.tsx +14 -12
  71. package/src/RvfExportMenuButton/RvfExportMenuButton.tsx +6 -7
  72. package/src/RvfFeatureDetails/RvfFeatureDetails.tsx +25 -21
  73. package/src/RvfFeatureDetails/RvfLineNetworkDetails/RvfLineNetworkDetails.tsx +131 -127
  74. package/src/RvfFeatureDetails/RvfNotificationDetails/RvfNotificationDetails.tsx +309 -111
  75. package/src/RvfFeatureDetails/RvfSellingPointDetails/RvfSellingPointDetails.tsx +2 -2
  76. package/src/RvfFeatureDetails/RvfSharedMobilityDetail/FloatingVehiclesDetails/FloatingVehiclesDetails.tsx +3 -3
  77. package/src/RvfFeatureDetails/RvfSharedMobilityDetail/RvfSharedMobilityDetails.tsx +8 -6
  78. package/src/RvfFeatureDetails/RvfSharedMobilityDetail/StationDetails/StationDetails.tsx +5 -4
  79. package/src/RvfFeatureDetailsFooter/RvfFeatureDetailsFooter.tsx +43 -0
  80. package/src/RvfFeatureDetailsFooter/index.tsx +1 -0
  81. package/src/RvfFeatureDetailsTitle/RvfFeatureDetailsTitle.tsx +81 -0
  82. package/src/RvfFeatureDetailsTitle/index.tsx +1 -0
  83. package/src/RvfFloatingMenu/RvfFloatingMenu.tsx +4 -4
  84. package/src/RvfGeolocationButton/GeolocationButton.tsx +98 -0
  85. package/src/RvfGeolocationButton/index.tsx +1 -0
  86. package/src/RvfIconButton/RvfIconButton.tsx +20 -9
  87. package/src/RvfInputCopy/RvfInputCopy.tsx +8 -8
  88. package/src/RvfLayerTree/RvfLayerTree.tsx +5 -2
  89. package/src/RvfLayerTree/TreeItem/TreeItem.tsx +13 -16
  90. package/src/RvfLayerTree/layersTreeReducer.ts +23 -18
  91. package/src/RvfLayerTreeButton/RvfLayerTreeButton.tsx +6 -6
  92. package/src/RvfLineNetworkPlanLayer/RvfLineNetworkPlanLayer.tsx +2 -1
  93. package/src/RvfLink/RvfLink.tsx +4 -3
  94. package/src/RvfMobilityMap/RvfMobilityMap.tsx +324 -320
  95. package/src/RvfModal/RvfModal.tsx +4 -3
  96. package/src/RvfOverlayContent/RvfOverlayContent.tsx +128 -0
  97. package/src/RvfOverlayContent/index.ts +0 -0
  98. package/src/RvfOverlayHeader/RvfOverlayHeader.tsx +13 -10
  99. package/src/RvfPermalink/RvfPermalink.tsx +2 -2
  100. package/src/RvfPoisLayer/RvfPoisLayer.tsx +2 -1
  101. package/src/RvfRadioButton/RvfRadioButton.tsx +1 -1
  102. package/src/RvfRouteIcon/RvfRouteIcon.tsx +10 -0
  103. package/src/RvfRouteIcon/index.tsx +1 -0
  104. package/src/RvfSearch/RvfSearch.tsx +4 -1
  105. package/src/RvfSearchButton/RvfSearchButton.tsx +27 -0
  106. package/src/RvfSearchButton/index.tsx +1 -0
  107. package/src/RvfSelect/RvfSelect.tsx +7 -5
  108. package/src/RvfSelectedFeatureHighlightLayer/RvfSelectedFeatureHighlightLayer.tsx +1 -2
  109. package/src/RvfSellingPointsLayer/RvfSellingPointsLayer.tsx +2 -1
  110. package/src/RvfShare/RvfPermalinkButton/RvfPermalinkButton.tsx +13 -12
  111. package/src/RvfShare/RvfShare.tsx +11 -10
  112. package/src/RvfShareMenuButton/RvfShareMenuButton.tsx +5 -5
  113. package/src/RvfSharedMobilityLayerGroup/RvfSharedMobilityLayerGroup.tsx +25 -22
  114. package/src/RvfSingleClickListener/RvfSingleClickListener.tsx +102 -67
  115. package/src/RvfTarifZonenLayer/RvfTarifZonenLayer.tsx +2 -1
  116. package/src/RvfTopics/RvfTopics.tsx +6 -5
  117. package/src/RvfZoomButtons/RvfZoomButtons.tsx +3 -3
  118. package/src/ScaleLine/ScaleLine.tsx +5 -4
  119. package/src/ScrollableHandler/ScrollableHandler.tsx +2 -1
  120. package/src/ScrollableHandler/index.tsx +1 -1
  121. package/src/SingleClickListener/SingleClickListener.tsx +47 -4
  122. package/src/Station/Station.tsx +5 -5
  123. package/src/StationName/StationName.tsx +3 -3
  124. package/src/StationServices/StationServices.tsx +3 -3
  125. package/src/StationsLayer/StationsLayer.tsx +5 -4
  126. package/src/StopsSearch/StopsSearch.tsx +143 -88
  127. package/src/WindowMessageListener/WindowMessageListener.tsx +68 -0
  128. package/src/WindowMessageListener/index.tsx +1 -0
  129. package/src/icons/Airport/Airport.tsx +4 -4
  130. package/src/icons/ArrowDown/ArrowDown.tsx +1 -1
  131. package/src/icons/ArrowRight/ArrowRight.tsx +19 -0
  132. package/src/icons/ArrowRight/arrow-right.svg +16 -0
  133. package/src/icons/ArrowRight/index.tsx +1 -0
  134. package/src/icons/ArrowUp/ArrowUp.tsx +1 -1
  135. package/src/icons/ArrowUpRight/ArrowUpRight.tsx +1 -1
  136. package/src/icons/BarAndRestaurants/BarAndRestaurants.tsx +2 -2
  137. package/src/icons/Bathroom/Bathroom.tsx +1 -1
  138. package/src/icons/Copy/Copy.tsx +1 -1
  139. package/src/icons/Doc/Doc.tsx +1 -1
  140. package/src/icons/Email/Email.tsx +1 -1
  141. package/src/icons/FilePdf/FilePdf.tsx +1 -1
  142. package/src/icons/Geolocation/Geolocation.tsx +3 -5
  143. package/src/icons/Image/Image.tsx +1 -1
  144. package/src/icons/Menu/Menu.tsx +1 -1
  145. package/src/icons/Minus/Minus.tsx +1 -1
  146. package/src/icons/NoRealtime/NoRealtime.tsx +1 -1
  147. package/src/icons/Plus/Plus.tsx +1 -1
  148. package/src/icons/Police/Police.tsx +3 -3
  149. package/src/icons/Search/Search.tsx +0 -1
  150. package/src/icons/Share/Share.tsx +1 -1
  151. package/src/icons/Stack/Stack.tsx +1 -1
  152. package/src/icons/Tracking/Tracking.tsx +29 -0
  153. package/src/icons/Tracking/airport-14-svgrepo-com.svg +41 -0
  154. package/src/icons/Tracking/index.tsx +1 -0
  155. package/src/icons/WaitingAreas/WaitingAreas.tsx +1 -1
  156. package/src/icons/Warning/Warning.tsx +56 -0
  157. package/src/icons/Warning/index.tsx +1 -0
  158. package/src/icons/Warning/info-achtung-kreisrot-rot.svg +28 -0
  159. package/src/icons/WheelChair/WheelChair.tsx +1 -1
  160. package/src/index.tsx +8 -46
  161. package/src/indexDoc.ts +13 -0
  162. package/src/ui/Button/Button.tsx +57 -0
  163. package/src/ui/Button/index.tsx +1 -0
  164. package/src/ui/IconButton/IconButton.tsx +44 -0
  165. package/src/ui/IconButton/index.tsx +1 -0
  166. package/src/utils/MobilityEvent.ts +4 -3
  167. package/src/utils/applyInitialLayerVisibility.ts +3 -3
  168. package/src/utils/centerOnStation.ts +3 -2
  169. package/src/utils/centerOnVehicle.ts +5 -4
  170. package/src/utils/constants.ts +27 -3
  171. package/src/utils/exportPdf.ts +26 -20
  172. package/src/utils/fullTrajectoryStyle.ts +2 -2
  173. package/src/utils/getAllLayers.ts +4 -3
  174. package/src/utils/getDelayColor.test.ts +1 -0
  175. package/src/utils/getDelayColorForVehicle.test.ts +2 -0
  176. package/src/utils/getDelayString.test.ts +3 -0
  177. package/src/utils/getDelayTextForVehicle.test.ts +4 -0
  178. package/src/utils/getFullTrajectoryAndFit.ts +4 -3
  179. package/src/utils/getHoursAndMinutes.test.ts +1 -0
  180. package/src/utils/getLayersAsFlatArray.ts +2 -2
  181. package/src/utils/getLinkByDevice.ts +1 -1
  182. package/src/utils/getMainColorForVehicle.ts +3 -3
  183. package/src/utils/getPermalinkParameters.ts +2 -2
  184. package/src/utils/getStopStatus.test.ts +2 -1
  185. package/src/utils/getStopStatus.ts +1 -1
  186. package/src/utils/getTextForVehicle.ts +1 -1
  187. package/src/utils/hooks/useDeparture.tsx +6 -5
  188. package/src/utils/hooks/useI18n.tsx +6 -4
  189. package/src/utils/hooks/useInitialLayersVisiblity.tsx +2 -1
  190. package/src/utils/hooks/useLayerConfig.tsx +40 -0
  191. package/src/utils/hooks/useMapContext.tsx +30 -18
  192. package/src/utils/hooks/useRouteStop.tsx +3 -2
  193. package/src/utils/hooks/useRvfContext.tsx +11 -3
  194. package/src/utils/hooks/useStation.tsx +2 -1
  195. package/src/utils/hooks/useUpdatePermalink.tsx +25 -24
  196. package/src/utils/hooks/useZoom.tsx +4 -4
  197. package/src/utils/realtimeRVFStyle.ts +5 -4
  198. package/src/utils/sharingGraphqlUtils.ts +3 -2
  199. package/src/utils/sharingStylesUtils.ts +7 -7
  200. package/src/utils/sharingWFSUtils.ts +9 -15
  201. package/tailwind.config.mjs +1 -0
  202. package/tsconfig.json +1 -1
  203. package/doc/tailwind.config.ts +0 -20
  204. package/src/utils/getFeatureInformationTitle.tsx +0 -54
@@ -1,31 +1,14 @@
1
- import type { JSX, PreactDOMAttributes } from "preact";
2
-
3
- import { Feature, Geolocation } from "ol";
4
- import { Point } from "ol/geom";
5
- import VectorLayer from "ol/layer/Vector";
1
+ import { Geolocation } from "ol";
6
2
  import { unByKey } from "ol/Observable";
7
3
  import { fromLonLat } from "ol/proj";
8
- import VectorSource from "ol/source/Vector";
9
- import { Icon, Style } from "ol/style";
4
+ import { memo } from "preact/compat";
10
5
  import { useEffect, useMemo } from "preact/hooks";
11
6
 
12
7
  import GeolocationIcon from "../icons/Geolocation";
13
- // @ts-expect-error - svg file not handled by vite
14
- import locationSvg from "../icons/Geolocation/location.svg";
15
- import RvfIconButton from "../RvfIconButton";
8
+ import IconButton from "../ui/IconButton";
16
9
  import useMapContext from "../utils/hooks/useMapContext";
17
10
 
18
- const point = new Point([0, 0]);
19
- const feature = new Feature(point);
20
- const layer = new VectorLayer({
21
- source: new VectorSource({ features: [feature] }),
22
- style: new Style({
23
- image: new Icon({
24
- anchor: [0.5, 1],
25
- src: locationSvg,
26
- }),
27
- }),
28
- });
11
+ import type { JSX, PreactDOMAttributes } from "preact";
29
12
 
30
13
  export type GeolocationButtonProps = JSX.HTMLAttributes<HTMLButtonElement> &
31
14
  PreactDOMAttributes;
@@ -53,7 +36,6 @@ function GeolocationButton({ ...props }: GeolocationButtonProps) {
53
36
  const coord = fromLonLat(position, "EPSG:3857");
54
37
  map.getView().setZoom(TRACKING_ZOOM);
55
38
  map.getView().setCenter(coord);
56
- point.setCoordinates(coord);
57
39
  }
58
40
  }),
59
41
  // then we only center the map.
@@ -62,7 +44,6 @@ function GeolocationButton({ ...props }: GeolocationButtonProps) {
62
44
  if (evt.target.getPosition()) {
63
45
  const coord = fromLonLat(position, "EPSG:3857");
64
46
  map.getView().setCenter(coord);
65
- point.setCoordinates(coord);
66
47
  }
67
48
  }),
68
49
  ];
@@ -74,25 +55,20 @@ function GeolocationButton({ ...props }: GeolocationButtonProps) {
74
55
 
75
56
  useEffect(() => {
76
57
  geolocation.setTracking(isTracking);
77
- if (isTracking) {
78
- layer.setMap(map);
79
- }
80
- return () => {
81
- layer.setMap(null);
82
- };
83
58
  }, [map, geolocation, isTracking]);
84
59
 
85
60
  return (
86
- <RvfIconButton
87
- className={isTracking ? "animate-pulse" : ""}
61
+ <IconButton
62
+ // @ts-expect-error - onClick exists
88
63
  onClick={() => {
89
- setIsTracking(!isTracking);
64
+ return setIsTracking(!isTracking);
90
65
  }}
91
66
  {...props}
67
+ selected={isTracking}
92
68
  >
93
- <GeolocationIcon />
94
- </RvfIconButton>
69
+ <GeolocationIcon className={isTracking ? "animate-pulse" : ""} />
70
+ </IconButton>
95
71
  );
96
72
  }
97
73
 
98
- export default GeolocationButton;
74
+ export default memo(GeolocationButton);
@@ -1 +1 @@
1
- export { default } from "./GeolocationButton";
1
+ export { default } from "../RvfGeolocationButton";
package/src/Map/Map.tsx CHANGED
@@ -1,13 +1,15 @@
1
1
  import { Map as OlMap, View } from "ol";
2
2
  import { unByKey } from "ol/Observable";
3
- // @ts-expect-error bad type definition
4
- import olStyle from "ol/ol.css";
5
- import { JSX, PreactDOMAttributes } from "preact";
6
3
  import { memo } from "preact/compat";
7
4
  import { useEffect, useMemo, useRef } from "preact/hooks";
8
5
 
9
6
  import useMapContext from "../utils/hooks/useMapContext";
10
7
 
8
+ // @ts-expect-error bad type definition
9
+ import olStyle from "ol/ol.css";
10
+
11
+ import type { JSX, PreactDOMAttributes } from "preact";
12
+
11
13
  export type RealtimeMapProps = JSX.HTMLAttributes<HTMLDivElement> &
12
14
  PreactDOMAttributes;
13
15
 
@@ -0,0 +1,78 @@
1
+ import { GeoJSON } from "ol/format";
2
+ import { memo } from "preact/compat";
3
+ import { useEffect } from "preact/hooks";
4
+
5
+ import useMapContext from "../utils/hooks/useMapContext";
6
+ import MobilityEvent from "../utils/MobilityEvent";
7
+
8
+ import type { MapBrowserEvent } from "ol";
9
+
10
+ import type { MobilityMapProps } from "../MobilityMap/MobilityMap";
11
+ import type { MobilityEventType } from "../utils/MobilityEvent";
12
+
13
+ const dispatchEvent = (
14
+ node: HTMLElement | null,
15
+ eventType: MobilityEventType,
16
+ eventData: unknown,
17
+ ) => {
18
+ node?.dispatchEvent(
19
+ new MobilityEvent(eventType, eventData, { bubbles: true }),
20
+ );
21
+ };
22
+
23
+ const geojson = new GeoJSON();
24
+
25
+ /**
26
+ * This component is responsible to dispatch events listenable for the window outside the web component.
27
+ *
28
+ * @example
29
+ * const wc = document.getElementById("my-web-component");
30
+ * wc.addEventListener("mwc:singleclick", (event) => {
31
+ * console.log(event.data);
32
+ * });
33
+ *
34
+ * @param param0 - Props for the component
35
+ * @returns JSX.Element | null
36
+ */
37
+
38
+ function MapDispatchEvents({
39
+ node,
40
+ wcAttributes,
41
+ }: {
42
+ node: HTMLDivElement | null;
43
+ wcAttributes: MobilityMapProps;
44
+ }) {
45
+ const { map, permalinkUrlSearchParams, selectedFeature } = useMapContext();
46
+
47
+ // Send events when one of the web component attributes changes
48
+ useEffect(() => {
49
+ dispatchEvent(node, "mwc:attribute", wcAttributes);
50
+ }, [node, wcAttributes]);
51
+
52
+ // Send events when we click on the map
53
+ useEffect(() => {
54
+ map?.on("singleclick", (event: MapBrowserEvent) => {
55
+ dispatchEvent(node, "mwc:singleclick", {
56
+ coordinate: event.coordinate,
57
+ pixel: event.pixel,
58
+ });
59
+ });
60
+ }, [node, map]);
61
+
62
+ // Send events when the selected feature changes
63
+ useEffect(() => {
64
+ const json = selectedFeature
65
+ ? geojson.writeFeatureObject(selectedFeature)
66
+ : null;
67
+ dispatchEvent(node, "mwc:selectedfeature", json);
68
+ }, [node, selectedFeature]);
69
+
70
+ // Send current map state using URL Search parameters
71
+ useEffect(() => {
72
+ dispatchEvent(node, "mwc:permalink", permalinkUrlSearchParams?.toString());
73
+ }, [node, permalinkUrlSearchParams]);
74
+
75
+ return null;
76
+ }
77
+
78
+ export default memo(MapDispatchEvents);
@@ -0,0 +1 @@
1
+ export { default } from "./MapDispatchEvents";
@@ -1,24 +1,15 @@
1
- import {
2
- MaplibreLayer,
3
- MaplibreStyleLayer,
4
- RealtimeLayer as MbtRealtimeLayer,
5
- } from "mobility-toolbox-js/ol";
6
- import {
7
- RealtimeStation,
8
- RealtimeStationId,
9
- RealtimeStopSequence,
10
- RealtimeTrainId,
11
- } from "mobility-toolbox-js/types";
12
- import { Map as OlMap } from "ol";
13
1
  import { memo } from "preact/compat";
14
- import { useEffect, useMemo, useRef, useState } from "preact/hooks";
2
+ import { useMemo, useRef, useState } from "preact/hooks";
15
3
 
16
4
  import BaseLayer from "../BaseLayer";
17
5
  import Copyright from "../Copyright";
6
+ import EmbedNavigation from "../EmbedNavigation";
18
7
  import GeolocationButton from "../GeolocationButton";
19
8
  import Map from "../Map";
9
+ import MapDispatchEvents from "../MapDispatchEvents";
20
10
  import NotificationLayer from "../NotificationLayer";
21
11
  import Overlay from "../Overlay";
12
+ import Permalink from "../Permalink";
22
13
  import RealtimeLayer from "../RealtimeLayer";
23
14
  import RouteSchedule from "../RouteSchedule";
24
15
  import ScaleLine from "../ScaleLine";
@@ -26,64 +17,40 @@ import Search from "../Search";
26
17
  import SingleClickListener from "../SingleClickListener/SingleClickListener";
27
18
  import Station from "../Station";
28
19
  import StationsLayer from "../StationsLayer";
29
- // @ts-expect-error bad type definition
30
- import tailwind from "../style.css";
31
20
  import { I18nContext } from "../utils/hooks/useI18n";
32
21
  import { MapContext } from "../utils/hooks/useMapContext";
33
- import useUpdatePermalink from "../utils/hooks/useUpdatePermalink";
34
22
  import i18n from "../utils/i18n";
35
- import MobilityEvent from "../utils/MobilityEvent";
23
+ import WindowMessageListener from "../WindowMessageListener";
24
+
25
+ import MobilityMapAttributes from "./MobilityMapAttributes";
26
+
27
+ // @ts-expect-error bad type definition
28
+ import tailwind from "../style.css";
36
29
  // @ts-expect-error bad type definition
37
30
  import style from "./index.css";
38
- // Notificationurl example: https://mobility-web-component-tmp.vercel.app/geops-mobility?notificationurl=https%3A%2F%2Fmoco.geops.io%2Fapi%2Fv1%2Fexport%2Fnotification%2F%3Fsso_config%3Dsob&geolocation=false&realtime=false&search=false&notificationat=2024-01-25T22%3A59%3A00Z
39
31
 
40
- export interface MobilityMapProps {
41
- apikey?: string;
42
- baselayer?: string;
43
- center?: string;
44
- extent?: string;
45
- geolocation?: string;
46
- mapsurl?: string;
47
- maxextent?: string;
48
- maxzoom?: string;
49
- minzoom?: string;
50
- mots?: string;
51
- notification?: string;
52
- notificationat?: string; // 2024-01-25T22:59:00Z
53
- notificationbeforelayerid?: string;
54
- notificationurl?: string; // https://moco.geops.io/api/v1/
55
- permalink?: string;
56
- realtime?: string;
57
- realtimeurl?: string;
58
- search?: string;
59
- stopsurl?: string;
60
- tenant?: string;
61
- zoom?: string;
62
- }
32
+ import type {
33
+ MaplibreLayer,
34
+ MaplibreStyleLayer,
35
+ RealtimeLayer as MbtRealtimeLayer,
36
+ } from "mobility-toolbox-js/ol";
37
+ import type {
38
+ RealtimeStation,
39
+ RealtimeStationId,
40
+ RealtimeStopSequence,
41
+ RealtimeTrainId,
42
+ SituationType,
43
+ } from "mobility-toolbox-js/types";
44
+ import type { Feature, Map as OlMap } from "ol";
63
45
 
64
- function MobilityMap({
65
- apikey = null,
66
- baselayer = "travic_v2",
67
- center = "831634,5933959",
68
- extent = null,
69
- geolocation = "true",
70
- mapsurl = "https://maps.geops.io",
71
- maxextent = null,
72
- maxzoom = null,
73
- minzoom = null,
74
- mots = null,
75
- notification = "false",
76
- notificationat = null,
77
- notificationbeforelayerid = null,
78
- notificationurl = "https://moco.geops.io/api/v1/",
79
- permalink = "false",
80
- realtime = "true",
81
- realtimeurl = "wss://api.geops.io/tracker-ws/v1/ws",
82
- search = "true",
83
- stopsurl = "https://api.geops.io/stops/v1/",
84
- tenant = null,
85
- zoom = "13",
86
- }: MobilityMapProps) {
46
+ import type { MobilityMapAttributeName } from "./MobilityMapAttributes";
47
+
48
+ export type MobilityMapProps = Record<
49
+ MobilityMapAttributeName,
50
+ null | string | undefined
51
+ >;
52
+
53
+ function MobilityMap(props: MobilityMapProps) {
87
54
  const eventNodeRef = useRef<HTMLDivElement>();
88
55
  const [baseLayer, setBaseLayer] = useState<MaplibreLayer>();
89
56
  const [isFollowing, setIsFollowing] = useState(false);
@@ -95,40 +62,73 @@ function MobilityMap({
95
62
  const [map, setMap] = useState<OlMap>();
96
63
  const [stationId, setStationId] = useState<RealtimeStationId>();
97
64
  const [trainId, setTrainId] = useState<RealtimeTrainId>();
65
+ const [selectedFeature, setSelectedFeature] = useState<Feature>(null);
66
+ const [selectedFeatures, setSelectedFeatures] = useState<Feature[]>([]);
67
+ const [permalinkUrlSearchParams, setPermalinkUrlSearchParams] =
68
+ useState<URLSearchParams>();
69
+
70
+ const [previewNotifications, setPreviewNotifications] =
71
+ useState<SituationType[]>();
98
72
 
99
- // TODO: this should be removed. The parent application should be responsible to do this
100
- // or we should find something that fit more usecases
101
- useUpdatePermalink(map, permalink === "true");
73
+ const {
74
+ embed,
75
+ geolocation,
76
+ notification,
77
+ permalink,
78
+ realtime,
79
+ search,
80
+ tenant,
81
+ } = props;
82
+
83
+ const hasRealtime = useMemo(() => {
84
+ return realtime === "true";
85
+ }, [realtime]);
102
86
 
87
+ const hasNotification = useMemo(() => {
88
+ return notification === "true" || !!previewNotifications;
89
+ }, [notification, previewNotifications]);
90
+
91
+ const hasGeolocation = useMemo(() => {
92
+ return geolocation === "true";
93
+ }, [geolocation]);
94
+
95
+ const hasPermalink = useMemo(() => {
96
+ return permalink === "true";
97
+ }, [permalink]);
98
+
99
+ const hasSearch = useMemo(() => {
100
+ return search === "true";
101
+ }, [search]);
102
+
103
+ const isEmbed = useMemo(() => {
104
+ return embed === "true";
105
+ }, [embed]);
106
+
107
+ // Object representing all the web-component attributes and the map context values.
103
108
  const mapContextValue = useMemo(() => {
104
109
  return {
105
- // MobilityMapProps && MapContextProps
106
- apikey,
107
- baselayer,
110
+ // MobilityMapProps
111
+ ...props,
112
+ // MapContextProps
108
113
  baseLayer,
109
- center,
110
- extent,
111
- geolocation,
114
+ isEmbed,
112
115
  isFollowing,
113
116
  isTracking,
114
117
  map,
115
- mapsurl,
116
- maxextent,
117
- maxzoom,
118
- minzoom,
119
- mots,
120
- notification,
121
- notificationat,
122
- notificationbeforelayerid,
123
- notificationurl,
124
- permalink,
118
+ permalinkUrlSearchParams,
119
+ previewNotifications,
125
120
  realtimeLayer,
126
- realtimeurl,
121
+ selectedFeature,
122
+ selectedFeatures,
127
123
  setBaseLayer,
128
124
  setIsFollowing,
129
125
  setIsTracking,
130
126
  setMap,
127
+ setPermalinkUrlSearchParams,
128
+ setPreviewNotifications,
131
129
  setRealtimeLayer,
130
+ setSelectedFeature,
131
+ setSelectedFeatures,
132
132
  setStation,
133
133
  setStationId,
134
134
  setStationsLayer,
@@ -138,85 +138,25 @@ function MobilityMap({
138
138
  stationId,
139
139
  stationsLayer,
140
140
  stopSequence,
141
- stopsurl,
142
- tenant,
143
141
  trainId,
144
- zoom,
145
142
  };
146
143
  }, [
147
- apikey,
148
- baselayer,
144
+ props,
149
145
  baseLayer,
150
- center,
151
- extent,
152
- geolocation,
146
+ isEmbed,
153
147
  isFollowing,
154
148
  isTracking,
155
149
  map,
156
- mapsurl,
157
- maxextent,
158
- maxzoom,
159
- minzoom,
160
- mots,
161
- notification,
162
- notificationat,
163
- notificationbeforelayerid,
164
- notificationurl,
165
- permalink,
150
+ permalinkUrlSearchParams,
151
+ previewNotifications,
166
152
  realtimeLayer,
167
- realtimeurl,
153
+ selectedFeature,
154
+ selectedFeatures,
168
155
  station,
169
156
  stationId,
170
157
  stationsLayer,
171
158
  stopSequence,
172
- stopsurl,
173
- tenant,
174
159
  trainId,
175
- zoom,
176
- ]);
177
-
178
- useEffect(() => {
179
- eventNodeRef.current?.dispatchEvent(
180
- new MobilityEvent<MobilityMapProps>("mwc:attribute", {
181
- baselayer,
182
- center,
183
- extent,
184
- geolocation,
185
- mapsurl,
186
- maxextent,
187
- maxzoom,
188
- minzoom,
189
- mots,
190
- notification,
191
- notificationat,
192
- notificationbeforelayerid,
193
- notificationurl,
194
- realtime,
195
- realtimeurl,
196
- search,
197
- tenant,
198
- zoom,
199
- }),
200
- );
201
- }, [
202
- baselayer,
203
- center,
204
- extent,
205
- geolocation,
206
- mapsurl,
207
- maxextent,
208
- maxzoom,
209
- minzoom,
210
- mots,
211
- notification,
212
- notificationat,
213
- notificationurl,
214
- notificationbeforelayerid,
215
- realtime,
216
- realtimeurl,
217
- search,
218
- tenant,
219
- zoom,
220
160
  ]);
221
161
 
222
162
  return (
@@ -224,26 +164,31 @@ function MobilityMap({
224
164
  <style>{tailwind}</style>
225
165
  <style>{style}</style>
226
166
  <MapContext.Provider value={mapContextValue}>
167
+ <Permalink replaceState={hasPermalink} />
168
+ <MapDispatchEvents node={eventNodeRef.current} wcAttributes={props} />
169
+ <WindowMessageListener eventNode={eventNodeRef.current} />
227
170
  <div
228
- className="relative size-full border font-sans @container/main"
171
+ className="@container/main relative size-full border font-sans"
229
172
  ref={eventNodeRef}
230
173
  >
231
174
  <div className="relative flex size-full flex-col @lg/main:flex-row-reverse">
232
- <Map className="relative flex-1 overflow-visible ">
175
+ <Map className="relative flex-1 overflow-visible">
233
176
  <BaseLayer />
234
177
  <SingleClickListener />
235
- {realtime === "true" && <RealtimeLayer />}
178
+ <EmbedNavigation />
179
+
180
+ {hasNotification && <NotificationLayer />}
181
+ {hasRealtime && <RealtimeLayer />}
236
182
  {tenant && <StationsLayer />}
237
- {notification === "true" && <NotificationLayer />}
238
183
  <div className="absolute inset-x-2 bottom-2 z-10 flex items-end justify-between gap-2 text-[10px]">
239
184
  <ScaleLine className="bg-slate-50/70" />
240
185
  <Copyright className="bg-slate-50/70" />
241
186
  </div>
242
- <div className="absolute right-2 top-2 z-10 flex flex-col gap-2">
243
- {geolocation === "true" && <GeolocationButton />}
187
+ <div className="absolute top-2 right-2 z-10 flex">
188
+ {hasGeolocation && <GeolocationButton />}
244
189
  </div>
245
- {search === "true" && (
246
- <div className="absolute left-2 right-12 top-2 z-10 flex max-h-[90%] min-w-64 max-w-96 flex-col">
190
+ {hasSearch && (
191
+ <div className="absolute top-2 right-12 left-2 z-10 flex max-h-[90%] max-w-96 min-w-64 flex-col">
247
192
  <Search />
248
193
  </div>
249
194
  )}
@@ -255,11 +200,11 @@ function MobilityMap({
255
200
  style: { width: "calc(100% - 60px)" },
256
201
  }}
257
202
  >
258
- {realtime === "true" && trainId && (
259
- <RouteSchedule className="relative overflow-y-auto overflow-x-hidden" />
203
+ {hasRealtime && trainId && (
204
+ <RouteSchedule className="relative overflow-x-hidden overflow-y-auto" />
260
205
  )}
261
206
  {tenant && stationId && (
262
- <Station className="relative overflow-y-auto overflow-x-hidden" />
207
+ <Station className="relative overflow-x-hidden overflow-y-auto" />
263
208
  )}
264
209
  </Overlay>
265
210
  </div>
@@ -269,4 +214,14 @@ function MobilityMap({
269
214
  );
270
215
  }
271
216
 
272
- export default memo(MobilityMap);
217
+ // We creates a wrapper to inject the default props values from MobilityMapAttributes.
218
+ const defaultProps = {};
219
+ Object.entries(MobilityMapAttributes).forEach(([key]) => {
220
+ defaultProps[key] = MobilityMapAttributes[key].defaultValue || null;
221
+ });
222
+
223
+ function MobilityMapWithDefaultProps(props: MobilityMapProps) {
224
+ return <MobilityMap {...defaultProps} {...props} />;
225
+ }
226
+
227
+ export default memo(MobilityMapWithDefaultProps);
@@ -0,0 +1,21 @@
1
+ import attributes from "./MobilityMapAttributes";
2
+
3
+ describe("MobilityMapAttributes", () => {
4
+ Object.entries(attributes).forEach(([key, value]) => {
5
+ test(`${key} has a description`, () => {
6
+ expect(value.description).toBeDefined();
7
+ });
8
+
9
+ if (value.type === "boolean") {
10
+ test(`${key} has a defaultValue for boolean`, () => {
11
+ expect(value.defaultValue).toMatch(/^(true|false)$/);
12
+ });
13
+ }
14
+
15
+ if (value.defaultValue === "true" || value.defaultValue === "false") {
16
+ test(`${key} is boolean when defaultValue is true or false`, () => {
17
+ expect(value.type).toBe("boolean");
18
+ });
19
+ }
20
+ });
21
+ });