@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.
- package/.prettierrc.js +3 -1
- package/CHANGELOG.md +12 -0
- package/README.md +1 -1
- package/doc/package.json +4 -3
- package/doc/postcss.config.mjs +1 -1
- package/doc/src/app/components/GeopsMobilityDoc.tsx +13 -224
- package/doc/src/app/components/GeopsMobilitySearchDoc.tsx +11 -107
- package/doc/src/app/components/WebComponentDoc.tsx +45 -56
- package/doc/src/app/geops-mobility/page.tsx +7 -2
- package/doc/src/app/geops-mobility-search/page.tsx +6 -2
- package/doc/src/app/globals.css +47 -27
- package/doc/src/app/layout.tsx +4 -2
- package/docutils.js +33 -17
- package/eslint.config.mjs +28 -34
- package/iframe.html +181 -207
- package/index.html +108 -88
- package/index.js +2345 -1976
- package/input.css +21 -3
- package/package.json +39 -41
- package/scripts/build.mjs +2 -2
- package/scripts/dev.mjs +3 -3
- package/search.html +70 -23
- package/src/BaseLayer/BaseLayer.tsx +2 -1
- package/src/Copyright/Copyright.tsx +4 -2
- package/src/DebugDeparture/DebugDeparture.tsx +16 -12
- package/src/DebugStop/DebugStop.tsx +2 -2
- package/src/Departure/Departure.tsx +2 -3
- package/src/EmbedNavigation/DragPanWarning.ts +125 -0
- package/src/EmbedNavigation/EmbedNavigation.tsx +52 -0
- package/src/EmbedNavigation/index.js +1 -0
- package/src/EmbedNavigation/index.tsx +1 -0
- package/src/GeolocationButton/GeolocationButton.tsx +11 -35
- package/src/GeolocationButton/index.tsx +1 -1
- package/src/Map/Map.tsx +5 -3
- package/src/MapDispatchEvents/MapDispatchEvents.tsx +78 -0
- package/src/MapDispatchEvents/index.tsx +1 -0
- package/src/MobilityMap/MobilityMap.tsx +117 -162
- package/src/MobilityMap/MobilityMapAttributes.test.ts +21 -0
- package/src/MobilityMap/MobilityMapAttributes.ts +252 -0
- package/src/MobilityMap/index.tsx +1 -0
- package/src/MobilitySearch/MobilitySearch.tsx +35 -0
- package/src/MobilitySearch/MobilitySearchAttributes.test.ts +21 -0
- package/src/MobilitySearch/MobilitySearchAttributes.ts +68 -0
- package/src/MobilitySearch/index.ts +2 -0
- package/src/NotificationLayer/NotificationLayer.tsx +36 -5
- package/src/Overlay/Overlay.tsx +24 -11
- package/src/Permalink/Permalink.tsx +77 -0
- package/src/Permalink/index.tsx +1 -0
- package/src/RealtimeLayer/RealtimeLayer.tsx +72 -33
- package/src/RouteDestination/RouteDestination.tsx +3 -3
- package/src/RouteIcon/RouteIcon.tsx +33 -25
- package/src/RouteIcon/index.tsx +1 -1
- package/src/RouteIdentifier/RouteIdentifer.tsx +6 -5
- package/src/RouteInfos/RouteInfos.tsx +7 -3
- package/src/RouteSchedule/RouteSchedule.tsx +3 -3
- package/src/RouteScheduleFooter/RouteScheduleFooter.tsx +1 -1
- package/src/RouteScheduleHeader/RouteScheduleHeader.tsx +7 -29
- package/src/RouteStop/RouteStop.tsx +8 -11
- package/src/RouteStopDelay/RouteStopDelay.tsx +2 -1
- package/src/RouteStopName/RouteStopName.tsx +2 -2
- package/src/RouteStopPlatform/RouteStopPlatform.tsx +2 -2
- package/src/RouteStopProgress/RouteStopProgress.tsx +2 -1
- package/src/RouteStopServices/RouteStopServices.tsx +2 -2
- package/src/RouteStopStation/RouteStopStation.tsx +8 -2
- package/src/RouteStopTime/RouteStopTime.tsx +2 -1
- package/src/RvfButton/RvfButton.tsx +14 -5
- package/src/RvfCheckbox/RvfCheckbox.tsx +8 -8
- package/src/RvfEmbedNavigation/DragPanWarning.ts +5 -5
- package/src/RvfEmbedNavigation/RvfEmbedNavigation.tsx +1 -0
- package/src/RvfExportMenu/RvfExportMenu.tsx +14 -12
- package/src/RvfExportMenuButton/RvfExportMenuButton.tsx +6 -7
- package/src/RvfFeatureDetails/RvfFeatureDetails.tsx +25 -21
- package/src/RvfFeatureDetails/RvfLineNetworkDetails/RvfLineNetworkDetails.tsx +131 -127
- package/src/RvfFeatureDetails/RvfNotificationDetails/RvfNotificationDetails.tsx +309 -111
- package/src/RvfFeatureDetails/RvfSellingPointDetails/RvfSellingPointDetails.tsx +2 -2
- package/src/RvfFeatureDetails/RvfSharedMobilityDetail/FloatingVehiclesDetails/FloatingVehiclesDetails.tsx +3 -3
- package/src/RvfFeatureDetails/RvfSharedMobilityDetail/RvfSharedMobilityDetails.tsx +8 -6
- package/src/RvfFeatureDetails/RvfSharedMobilityDetail/StationDetails/StationDetails.tsx +5 -4
- package/src/RvfFeatureDetailsFooter/RvfFeatureDetailsFooter.tsx +43 -0
- package/src/RvfFeatureDetailsFooter/index.tsx +1 -0
- package/src/RvfFeatureDetailsTitle/RvfFeatureDetailsTitle.tsx +81 -0
- package/src/RvfFeatureDetailsTitle/index.tsx +1 -0
- package/src/RvfFloatingMenu/RvfFloatingMenu.tsx +4 -4
- package/src/RvfGeolocationButton/GeolocationButton.tsx +98 -0
- package/src/RvfGeolocationButton/index.tsx +1 -0
- package/src/RvfIconButton/RvfIconButton.tsx +20 -9
- package/src/RvfInputCopy/RvfInputCopy.tsx +8 -8
- package/src/RvfLayerTree/RvfLayerTree.tsx +5 -2
- package/src/RvfLayerTree/TreeItem/TreeItem.tsx +13 -16
- package/src/RvfLayerTree/layersTreeReducer.ts +23 -18
- package/src/RvfLayerTreeButton/RvfLayerTreeButton.tsx +6 -6
- package/src/RvfLineNetworkPlanLayer/RvfLineNetworkPlanLayer.tsx +2 -1
- package/src/RvfLink/RvfLink.tsx +4 -3
- package/src/RvfMobilityMap/RvfMobilityMap.tsx +324 -320
- package/src/RvfModal/RvfModal.tsx +4 -3
- package/src/RvfOverlayContent/RvfOverlayContent.tsx +128 -0
- package/src/RvfOverlayContent/index.ts +0 -0
- package/src/RvfOverlayHeader/RvfOverlayHeader.tsx +13 -10
- package/src/RvfPermalink/RvfPermalink.tsx +2 -2
- package/src/RvfPoisLayer/RvfPoisLayer.tsx +2 -1
- package/src/RvfRadioButton/RvfRadioButton.tsx +1 -1
- package/src/RvfRouteIcon/RvfRouteIcon.tsx +10 -0
- package/src/RvfRouteIcon/index.tsx +1 -0
- package/src/RvfSearch/RvfSearch.tsx +4 -1
- package/src/RvfSearchButton/RvfSearchButton.tsx +27 -0
- package/src/RvfSearchButton/index.tsx +1 -0
- package/src/RvfSelect/RvfSelect.tsx +7 -5
- package/src/RvfSelectedFeatureHighlightLayer/RvfSelectedFeatureHighlightLayer.tsx +1 -2
- package/src/RvfSellingPointsLayer/RvfSellingPointsLayer.tsx +2 -1
- package/src/RvfShare/RvfPermalinkButton/RvfPermalinkButton.tsx +13 -12
- package/src/RvfShare/RvfShare.tsx +11 -10
- package/src/RvfShareMenuButton/RvfShareMenuButton.tsx +5 -5
- package/src/RvfSharedMobilityLayerGroup/RvfSharedMobilityLayerGroup.tsx +25 -22
- package/src/RvfSingleClickListener/RvfSingleClickListener.tsx +102 -67
- package/src/RvfTarifZonenLayer/RvfTarifZonenLayer.tsx +2 -1
- package/src/RvfTopics/RvfTopics.tsx +6 -5
- package/src/RvfZoomButtons/RvfZoomButtons.tsx +3 -3
- package/src/ScaleLine/ScaleLine.tsx +5 -4
- package/src/ScrollableHandler/ScrollableHandler.tsx +2 -1
- package/src/ScrollableHandler/index.tsx +1 -1
- package/src/SingleClickListener/SingleClickListener.tsx +47 -4
- package/src/Station/Station.tsx +5 -5
- package/src/StationName/StationName.tsx +3 -3
- package/src/StationServices/StationServices.tsx +3 -3
- package/src/StationsLayer/StationsLayer.tsx +5 -4
- package/src/StopsSearch/StopsSearch.tsx +143 -88
- package/src/WindowMessageListener/WindowMessageListener.tsx +68 -0
- package/src/WindowMessageListener/index.tsx +1 -0
- package/src/icons/Airport/Airport.tsx +4 -4
- package/src/icons/ArrowDown/ArrowDown.tsx +1 -1
- package/src/icons/ArrowRight/ArrowRight.tsx +19 -0
- package/src/icons/ArrowRight/arrow-right.svg +16 -0
- package/src/icons/ArrowRight/index.tsx +1 -0
- package/src/icons/ArrowUp/ArrowUp.tsx +1 -1
- package/src/icons/ArrowUpRight/ArrowUpRight.tsx +1 -1
- package/src/icons/BarAndRestaurants/BarAndRestaurants.tsx +2 -2
- package/src/icons/Bathroom/Bathroom.tsx +1 -1
- package/src/icons/Copy/Copy.tsx +1 -1
- package/src/icons/Doc/Doc.tsx +1 -1
- package/src/icons/Email/Email.tsx +1 -1
- package/src/icons/FilePdf/FilePdf.tsx +1 -1
- package/src/icons/Geolocation/Geolocation.tsx +3 -5
- package/src/icons/Image/Image.tsx +1 -1
- package/src/icons/Menu/Menu.tsx +1 -1
- package/src/icons/Minus/Minus.tsx +1 -1
- package/src/icons/NoRealtime/NoRealtime.tsx +1 -1
- package/src/icons/Plus/Plus.tsx +1 -1
- package/src/icons/Police/Police.tsx +3 -3
- package/src/icons/Search/Search.tsx +0 -1
- package/src/icons/Share/Share.tsx +1 -1
- package/src/icons/Stack/Stack.tsx +1 -1
- package/src/icons/Tracking/Tracking.tsx +29 -0
- package/src/icons/Tracking/airport-14-svgrepo-com.svg +41 -0
- package/src/icons/Tracking/index.tsx +1 -0
- package/src/icons/WaitingAreas/WaitingAreas.tsx +1 -1
- package/src/icons/Warning/Warning.tsx +56 -0
- package/src/icons/Warning/index.tsx +1 -0
- package/src/icons/Warning/info-achtung-kreisrot-rot.svg +28 -0
- package/src/icons/WheelChair/WheelChair.tsx +1 -1
- package/src/index.tsx +8 -46
- package/src/indexDoc.ts +13 -0
- package/src/ui/Button/Button.tsx +57 -0
- package/src/ui/Button/index.tsx +1 -0
- package/src/ui/IconButton/IconButton.tsx +44 -0
- package/src/ui/IconButton/index.tsx +1 -0
- package/src/utils/MobilityEvent.ts +4 -3
- package/src/utils/applyInitialLayerVisibility.ts +3 -3
- package/src/utils/centerOnStation.ts +3 -2
- package/src/utils/centerOnVehicle.ts +5 -4
- package/src/utils/constants.ts +27 -3
- package/src/utils/exportPdf.ts +26 -20
- package/src/utils/fullTrajectoryStyle.ts +2 -2
- package/src/utils/getAllLayers.ts +4 -3
- package/src/utils/getDelayColor.test.ts +1 -0
- package/src/utils/getDelayColorForVehicle.test.ts +2 -0
- package/src/utils/getDelayString.test.ts +3 -0
- package/src/utils/getDelayTextForVehicle.test.ts +4 -0
- package/src/utils/getFullTrajectoryAndFit.ts +4 -3
- package/src/utils/getHoursAndMinutes.test.ts +1 -0
- package/src/utils/getLayersAsFlatArray.ts +2 -2
- package/src/utils/getLinkByDevice.ts +1 -1
- package/src/utils/getMainColorForVehicle.ts +3 -3
- package/src/utils/getPermalinkParameters.ts +2 -2
- package/src/utils/getStopStatus.test.ts +2 -1
- package/src/utils/getStopStatus.ts +1 -1
- package/src/utils/getTextForVehicle.ts +1 -1
- package/src/utils/hooks/useDeparture.tsx +6 -5
- package/src/utils/hooks/useI18n.tsx +6 -4
- package/src/utils/hooks/useInitialLayersVisiblity.tsx +2 -1
- package/src/utils/hooks/useLayerConfig.tsx +40 -0
- package/src/utils/hooks/useMapContext.tsx +30 -18
- package/src/utils/hooks/useRouteStop.tsx +3 -2
- package/src/utils/hooks/useRvfContext.tsx +11 -3
- package/src/utils/hooks/useStation.tsx +2 -1
- package/src/utils/hooks/useUpdatePermalink.tsx +25 -24
- package/src/utils/hooks/useZoom.tsx +4 -4
- package/src/utils/realtimeRVFStyle.ts +5 -4
- package/src/utils/sharingGraphqlUtils.ts +3 -2
- package/src/utils/sharingStylesUtils.ts +7 -7
- package/src/utils/sharingWFSUtils.ts +9 -15
- package/tailwind.config.mjs +1 -0
- package/tsconfig.json +1 -1
- package/doc/tailwind.config.ts +0 -20
- package/src/utils/getFeatureInformationTitle.tsx +0 -54
|
@@ -1,31 +1,14 @@
|
|
|
1
|
-
import
|
|
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
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
<
|
|
87
|
-
|
|
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
|
-
</
|
|
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 "
|
|
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 {
|
|
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
|
|
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¬ificationat=2024-01-25T22%3A59%3A00Z
|
|
39
31
|
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
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
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
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
|
-
|
|
100
|
-
|
|
101
|
-
|
|
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
|
|
106
|
-
|
|
107
|
-
|
|
110
|
+
// MobilityMapProps
|
|
111
|
+
...props,
|
|
112
|
+
// MapContextProps
|
|
108
113
|
baseLayer,
|
|
109
|
-
|
|
110
|
-
extent,
|
|
111
|
-
geolocation,
|
|
114
|
+
isEmbed,
|
|
112
115
|
isFollowing,
|
|
113
116
|
isTracking,
|
|
114
117
|
map,
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
maxzoom,
|
|
118
|
-
minzoom,
|
|
119
|
-
mots,
|
|
120
|
-
notification,
|
|
121
|
-
notificationat,
|
|
122
|
-
notificationbeforelayerid,
|
|
123
|
-
notificationurl,
|
|
124
|
-
permalink,
|
|
118
|
+
permalinkUrlSearchParams,
|
|
119
|
+
previewNotifications,
|
|
125
120
|
realtimeLayer,
|
|
126
|
-
|
|
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
|
-
|
|
148
|
-
baselayer,
|
|
144
|
+
props,
|
|
149
145
|
baseLayer,
|
|
150
|
-
|
|
151
|
-
extent,
|
|
152
|
-
geolocation,
|
|
146
|
+
isEmbed,
|
|
153
147
|
isFollowing,
|
|
154
148
|
isTracking,
|
|
155
149
|
map,
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
maxzoom,
|
|
159
|
-
minzoom,
|
|
160
|
-
mots,
|
|
161
|
-
notification,
|
|
162
|
-
notificationat,
|
|
163
|
-
notificationbeforelayerid,
|
|
164
|
-
notificationurl,
|
|
165
|
-
permalink,
|
|
150
|
+
permalinkUrlSearchParams,
|
|
151
|
+
previewNotifications,
|
|
166
152
|
realtimeLayer,
|
|
167
|
-
|
|
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
|
|
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
|
-
|
|
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
|
|
243
|
-
{
|
|
187
|
+
<div className="absolute top-2 right-2 z-10 flex">
|
|
188
|
+
{hasGeolocation && <GeolocationButton />}
|
|
244
189
|
</div>
|
|
245
|
-
{
|
|
246
|
-
<div className="absolute
|
|
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
|
-
{
|
|
259
|
-
<RouteSchedule className="relative overflow-
|
|
203
|
+
{hasRealtime && trainId && (
|
|
204
|
+
<RouteSchedule className="relative overflow-x-hidden overflow-y-auto" />
|
|
260
205
|
)}
|
|
261
206
|
{tenant && stationId && (
|
|
262
|
-
<Station className="relative overflow-
|
|
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
|
-
|
|
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
|
+
});
|