@geops/rvf-mobility-web-component 0.1.61 → 0.1.62

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 (72) hide show
  1. package/CHANGELOG.md +45 -0
  2. package/README.md +1 -0
  3. package/docutils.js +103 -4
  4. package/fonts/source-sans-3/source-sans-3-v15-latin-500.ttf +0 -0
  5. package/fonts/source-sans-3/source-sans-3-v15-latin-500.woff2 +0 -0
  6. package/fonts/source-sans-3/source-sans-3-v15-latin-600.ttf +0 -0
  7. package/fonts/source-sans-3/source-sans-3-v15-latin-600.woff2 +0 -0
  8. package/fonts/source-sans-3/source-sans-3-v15-latin-700.ttf +0 -0
  9. package/fonts/source-sans-3/source-sans-3-v15-latin-700.woff2 +0 -0
  10. package/fonts/source-sans-3/source-sans-3-v15-latin-regular.ttf +0 -0
  11. package/fonts/source-sans-3/source-sans-3-v15-latin-regular.woff2 +0 -0
  12. package/index.html +27 -97
  13. package/index.js +235 -224
  14. package/notifications.html +144 -0
  15. package/package.json +2 -2
  16. package/search.html +24 -65
  17. package/src/FeatureDetails/FeatureDetails.tsx +20 -6
  18. package/src/FeaturesInfosListener/FeaturesInfosListener.tsx +2 -0
  19. package/src/LayerTree/TreeItem/TreeItem.tsx +2 -2
  20. package/src/LayerTreeMenu/LayerTreeMenu.tsx +19 -3
  21. package/src/LayoutState/LayoutState.tsx +17 -0
  22. package/src/LinesNetworkPlanDetails/LinesNetworkPlanDetails.tsx +34 -21
  23. package/src/LinesNetworkPlanLayer/LinesNetworkPlanLayer.tsx +3 -6
  24. package/src/LinesNetworkPlanLayerHighlight/LinesNetworkPlanLayerHighlight.tsx +88 -0
  25. package/src/LinesNetworkPlanLayerHighlight/index.tsx +1 -0
  26. package/src/MapDispatchEvents/MapDispatchEvents.tsx +6 -4
  27. package/src/MapLayout/MapLayout.tsx +2 -2
  28. package/src/MapsetLayer/MapsetLayer.tsx +116 -0
  29. package/src/MapsetLayer/index.tsx +1 -0
  30. package/src/MobilityMap/MobilityMap.tsx +27 -5
  31. package/src/MobilityMap/MobilityMapAttributes.test.ts +38 -0
  32. package/src/MobilityMap/MobilityMapAttributes.ts +99 -22
  33. package/src/MobilityMap/MobilityMapEvents.ts +53 -0
  34. package/src/MobilityNotifications/MobilityNotifications.tsx +93 -0
  35. package/src/MobilityNotifications/MobilityNotificationsAttributes.test.ts +21 -0
  36. package/src/MobilityNotifications/MobilityNotificationsAttributes.ts +46 -0
  37. package/src/MobilityNotifications/index.ts +2 -0
  38. package/src/MobilitySearch/MobilitySearchEvents.ts +21 -0
  39. package/src/NotificationDetails/NotificationDetails.tsx +74 -251
  40. package/src/OverlayContent/OverlayContent.tsx +1 -1
  41. package/src/OverlayDetails/OverlayDetails.tsx +4 -2
  42. package/src/OverlayFooter/OverlayFooter.tsx +3 -2
  43. package/src/RealtimeLayer/RealtimeLayer.tsx +36 -7
  44. package/src/RouteScheduleFooter/RouteScheduleFooter.tsx +1 -2
  45. package/src/RvfFeatureDetails/RvfFeatureDetails.tsx +2 -2
  46. package/src/RvfFeatureDetails/RvfLineNetworkDetails/RvfLineNetworkDetails.tsx +2 -0
  47. package/src/RvfFeatureDetails/RvfNotificationDetails/RvfNotificationDetails.tsx +12 -453
  48. package/src/RvfFeatureDetails/RvfSellingPointDetails/RvfSellingPointDetails.tsx +20 -17
  49. package/src/RvfFeatureDetails/RvfSharedMobilityDetail/RvfSharedMobilityDetails.tsx +93 -36
  50. package/src/RvfLink/RvfLink.tsx +5 -2
  51. package/src/RvfMobilityMap/RvfMobilityMap.tsx +27 -9
  52. package/src/RvfSelectedFeatureHighlightLayer/RvfSelectedFeatureHighlightLayer.tsx +9 -5
  53. package/src/RvfSingleClickListener/RvfSingleClickListener.tsx +3 -1
  54. package/src/SituationDetails/SituationDetails.tsx +324 -0
  55. package/src/SituationDetails/index.ts +1 -0
  56. package/src/index.tsx +16 -0
  57. package/src/indexDoc.ts +25 -3
  58. package/src/ui/Checkbox/Checkbox.tsx +2 -2
  59. package/src/ui/Link/Link.tsx +49 -0
  60. package/src/ui/Link/index.tsx +1 -0
  61. package/src/ui/Select/Select.tsx +2 -2
  62. package/src/utils/constants.ts +37 -0
  63. package/src/utils/exportPdf.ts +3 -1
  64. package/src/utils/highlightLinesNetworkPlan.ts +25 -0
  65. package/src/utils/hooks/useI18n.tsx +6 -4
  66. package/src/utils/hooks/useMapContext.tsx +9 -0
  67. package/src/utils/sharingGraphqlUtils.ts +1 -1
  68. package/src/utils/translations.ts +12 -1
  69. package/tailwind.config.mjs +3 -1
  70. package/src/ShareMenu/PermalinkButton/PermalinkButton.tsx +0 -62
  71. package/src/ShareMenu/PermalinkButton/index.tsx +0 -1
  72. package/src/icons/Geolocation/airport-14-svgrepo-com.svg +0 -41
@@ -1,37 +1,92 @@
1
+ import { GeoJSON } from "ol/format";
2
+ import { memo } from "preact/compat";
1
3
  import { useEffect, useMemo, useState } from "preact/hooks";
2
4
  import { Fragment } from "preact/jsx-runtime";
5
+ import { twMerge } from "tailwind-merge";
3
6
 
7
+ import ShadowOverflow from "../../ShadowOverflow";
4
8
  import { PROVIDER_LOGOS_BY_FEED_ID } from "../../utils/constants";
9
+ import useMapContext from "../../utils/hooks/useMapContext";
10
+ import { fetchSharingStation } from "../../utils/sharingGraphqlUtils";
5
11
 
6
12
  import FloatingVehiclesDetails from "./FloatingVehiclesDetails";
7
13
  import StationDetails from "./StationDetails";
8
14
 
15
+ import type { GeoJSONSource } from "maplibre-gl";
9
16
  import type { Feature } from "ol";
17
+ import type { HTMLAttributes, PreactDOMAttributes } from "preact";
18
+ const geojson = new GeoJSON();
10
19
 
11
- export interface RvfSharedMobilityDetailsProps {
20
+ export type RvfSharedMobilityDetailsProps = {
21
+ className?: string;
12
22
  feature: Feature;
13
- }
23
+ } & HTMLAttributes<HTMLDivElement> &
24
+ PreactDOMAttributes;
14
25
 
15
- function RvfSharedMobilityDetails({ feature }: RvfSharedMobilityDetailsProps) {
26
+ function RvfSharedMobilityDetails({
27
+ className,
28
+ feature,
29
+ ...props
30
+ }: RvfSharedMobilityDetailsProps) {
31
+ const { baseLayer } = useMapContext();
16
32
  const [features, setFeatures] = useState([]);
17
33
  const isStationDetails = !!feature.get("station_id");
18
34
  const isFloatingVehicle = !!feature.get("vehicle_id");
19
35
  const isCluster = !!feature.get("cluster_id");
20
36
 
37
+ // Get station infos from MobiDataBW graphql API
38
+ useEffect(() => {
39
+ if (isStationDetails) {
40
+ // Sharing station
41
+ const sharingStationId = feature?.get("station_id");
42
+ const feedId = feature?.get("feed_id");
43
+ if (sharingStationId) {
44
+ fetchSharingStation(sharingStationId)
45
+ .then((sharingStationInfo) => {
46
+ feature.setProperties(sharingStationInfo);
47
+ feature.set("provider_logo", PROVIDER_LOGOS_BY_FEED_ID[feedId]);
48
+ })
49
+ .catch((error) => {
50
+ console.warn("Error fetching sharing station info:", error);
51
+ });
52
+ }
53
+ }
54
+ }, [feature, isStationDetails]);
55
+
21
56
  useEffect(() => {
22
57
  if (isCluster) {
23
- setFeatures(feature.get("features"));
58
+ const clusterId = feature.get("cluster_id");
59
+ if (clusterId) {
60
+ // Append leaves infos about the cluster
61
+ const vtFeat = feature.get("vectorTileFeature");
62
+ const sourceId = vtFeat.layer.source;
63
+ baseLayer.mapLibreMap
64
+ .getSource<GeoJSONSource>(sourceId)
65
+ ?.getClusterLeaves(clusterId, 1000, 0)
66
+ .then((leaves) => {
67
+ const featuresLeaves = leaves.map((l) => {
68
+ return geojson.readFeature(l);
69
+ });
70
+ setFeatures(featuresLeaves || []);
71
+ })
72
+ .catch(() => {
73
+ // eslint-disable-next-line no-console
74
+ console.warn(
75
+ `Impossible to get cluster ${clusterId} leaves from source ${sourceId}`,
76
+ );
77
+ });
78
+ }
24
79
  } else {
25
- setFeatures([feature]);
80
+ setFeatures(feature ? [feature] : []);
26
81
  }
27
- }, [feature, isFloatingVehicle, isCluster]);
82
+ }, [feature, isFloatingVehicle, isCluster, baseLayer?.mapLibreMap]);
28
83
 
29
84
  const featuresByFeedId: Record<string, Feature[]> = useMemo(() => {
30
85
  const featuresByFeedIdd = {};
31
86
  if (isStationDetails) {
32
87
  return null;
33
88
  }
34
- features.forEach((featuree) => {
89
+ (features || []).forEach((featuree) => {
35
90
  const feedId = featuree.get("feed_id");
36
91
  if (featuresByFeedIdd[feedId]) {
37
92
  featuresByFeedIdd[feedId].push(featuree);
@@ -43,35 +98,37 @@ function RvfSharedMobilityDetails({ feature }: RvfSharedMobilityDetailsProps) {
43
98
  }, [features, isStationDetails]);
44
99
 
45
100
  return (
46
- <div className={"space-y-4"}>
47
- {isStationDetails && (
48
- <>
49
- <img
50
- alt="logo"
51
- className="max-w-24"
52
- src={PROVIDER_LOGOS_BY_FEED_ID[features[0]?.get("feed_id")]}
53
- />
54
- {features.length && isStationDetails && (
55
- <StationDetails feature={features[0]} />
56
- )}
57
- </>
58
- )}
59
- {(isFloatingVehicle || isCluster) &&
60
- featuresByFeedId &&
61
- Object.entries(featuresByFeedId).map(([key, feats]) => {
62
- return (
63
- <Fragment key={key}>
64
- <img
65
- alt="logo"
66
- className="max-w-24"
67
- src={PROVIDER_LOGOS_BY_FEED_ID[feats[0]?.get("feed_id")]}
68
- />
69
- <FloatingVehiclesDetails features={feats} />
70
- </Fragment>
71
- );
72
- })}
73
- </div>
101
+ <ShadowOverflow {...props} className={twMerge("px-4 text-base", className)}>
102
+ <div className={"space-y-4"}>
103
+ {isStationDetails && (
104
+ <>
105
+ <img
106
+ alt="logo"
107
+ className="max-w-24"
108
+ src={PROVIDER_LOGOS_BY_FEED_ID[features[0]?.get("feed_id")]}
109
+ />
110
+ {features.length && isStationDetails && (
111
+ <StationDetails feature={features[0]} />
112
+ )}
113
+ </>
114
+ )}
115
+ {(isFloatingVehicle || isCluster) &&
116
+ featuresByFeedId &&
117
+ Object.entries(featuresByFeedId).map(([key, feats]) => {
118
+ return (
119
+ <Fragment key={key}>
120
+ <img
121
+ alt="logo"
122
+ className="max-w-24"
123
+ src={PROVIDER_LOGOS_BY_FEED_ID[feats[0]?.get("feed_id")]}
124
+ />
125
+ <FloatingVehiclesDetails features={feats} />
126
+ </Fragment>
127
+ );
128
+ })}
129
+ </div>
130
+ </ShadowOverflow>
74
131
  );
75
132
  }
76
133
 
77
- export default RvfSharedMobilityDetails;
134
+ export default memo(RvfSharedMobilityDetails);
@@ -11,7 +11,7 @@ export type RvfLinkProps = {
11
11
  PreactDOMAttributes;
12
12
 
13
13
  const baseClasses =
14
- "my-1 flex items-center leading-[1.1] underline text-[14px] @sm/main:text-[16px] @md/main:text-[18px] items-center justify-left font-semibold";
14
+ "my-1 flex items-center leading-[1.1] underline text-base items-center justify-left font-semibold gap-1";
15
15
 
16
16
  export const themes = {
17
17
  primary: {
@@ -40,8 +40,11 @@ function RvfLink({
40
40
 
41
41
  return (
42
42
  <a className={classes} rel="noreferrer" target="_blank" {...props}>
43
+ {(!(props.href as string)?.includes(window.location.hostname) ||
44
+ !(props.href as string)?.startsWith("(https?|ftp)")) && (
45
+ <ArrowUpRight height="20" width="20" />
46
+ )}
43
47
  {children}
44
- <ArrowUpRight />
45
48
  </a>
46
49
  );
47
50
  }
@@ -1,3 +1,10 @@
1
+ import {
2
+ type MaplibreLayer,
3
+ type MaplibreStyleLayer,
4
+ type MapsetLayer as MbtMapsetLayer,
5
+ type RealtimeLayer as MbtRealtimeLayer,
6
+ type MocoLayer,
7
+ } from "mobility-toolbox-js/ol";
1
8
  import { memo } from "preact/compat";
2
9
  import { useEffect, useMemo, useRef, useState } from "preact/hooks";
3
10
  import { twMerge } from "tailwind-merge";
@@ -13,6 +20,7 @@ import LayoutState from "../LayoutState";
13
20
  import LinesNetworkPlanLayer from "../LinesNetworkPlanLayer";
14
21
  import Map from "../Map";
15
22
  import MapDispatchEvents from "../MapDispatchEvents";
23
+ import MapsetLayer from "../MapsetLayer";
16
24
  import MobilityMapAttributes from "../MobilityMap/MobilityMapAttributes";
17
25
  import NotificationsLayer from "../NotificationsLayer";
18
26
  import Overlay from "../Overlay";
@@ -50,12 +58,6 @@ import tailwind from "../style.css";
50
58
  // @ts-expect-error bad type definition
51
59
  import style from "./index.css";
52
60
 
53
- import type {
54
- MaplibreLayer,
55
- MaplibreStyleLayer,
56
- RealtimeLayer as MbtRealtimeLayer,
57
- MocoLayer,
58
- } from "mobility-toolbox-js/ol";
59
61
  import type {
60
62
  LayerGetFeatureInfoResponse,
61
63
  RealtimeStation,
@@ -154,6 +156,7 @@ function RvfMobilityMap(props: RvfMobilityMapProps) {
154
156
  const [hasGeolocation, setHasGeolocation] = useState<boolean>(false);
155
157
  const [hasLnp, setHasLnp] = useState<boolean>(false);
156
158
  const [hasDetails, setHasDetails] = useState<boolean>(false);
159
+ const [hasMapset, setHasMapset] = useState<boolean>(false);
157
160
  const [hasNotification, setHasNotification] = useState<boolean>(false);
158
161
  const [hasPermalink, setHasPermalink] = useState<boolean>(false);
159
162
  const [hasPrint, setHasPrint] = useState<boolean>(false);
@@ -175,9 +178,12 @@ function RvfMobilityMap(props: RvfMobilityMapProps) {
175
178
  const [notificationsLayer, setNotificationsLayer] = useState<MocoLayer>();
176
179
  const [linesNetworkPlanLayer, setLinesNetworkPlanLayer] =
177
180
  useState<MaplibreStyleLayer>();
181
+ const [mapsetLayer, setMapsetLayer] = useState<MbtMapsetLayer>();
182
+
178
183
  const [map, setMap] = useState<OlMap>();
179
184
  const [stationId, setStationId] = useState<RealtimeStationId>();
180
185
  const [trainId, setTrainId] = useState<RealtimeTrainId>();
186
+ const [linesIds, setLinesIds] = useState<string[]>();
181
187
  const [featuresInfos, setFeaturesInfos] = useState<
182
188
  LayerGetFeatureInfoResponse[]
183
189
  >([]);
@@ -216,6 +222,7 @@ function RvfMobilityMap(props: RvfMobilityMapProps) {
216
222
  hasGeolocation,
217
223
  hasLayerTree,
218
224
  hasLnp,
225
+ hasMapset,
219
226
  hasNotification,
220
227
  hasPermalink,
221
228
  hasPrint,
@@ -232,8 +239,10 @@ function RvfMobilityMap(props: RvfMobilityMapProps) {
232
239
  isSearchOpen,
233
240
  isShareMenuOpen,
234
241
  isTracking,
242
+ linesIds,
235
243
  linesNetworkPlanLayer,
236
244
  map,
245
+ mapsetLayer,
237
246
  notificationsLayer,
238
247
  permalinkUrlSearchParams,
239
248
  previewNotifications,
@@ -247,6 +256,7 @@ function RvfMobilityMap(props: RvfMobilityMapProps) {
247
256
  setHasGeolocation,
248
257
  setHasLayerTree,
249
258
  setHasLnp,
259
+ setHasMapset,
250
260
  setHasNotification,
251
261
  setHasPermalink,
252
262
  setHasPrint,
@@ -263,8 +273,10 @@ function RvfMobilityMap(props: RvfMobilityMapProps) {
263
273
  setIsSearchOpen,
264
274
  setIsShareMenuOpen,
265
275
  setIsTracking,
276
+ setLinesIds,
266
277
  setLinesNetworkPlanLayer,
267
278
  setMap,
279
+ setMapsetLayer,
268
280
  setNotificationsLayer,
269
281
  setPermalinkUrlSearchParams,
270
282
  setPreviewNotifications,
@@ -291,6 +303,7 @@ function RvfMobilityMap(props: RvfMobilityMapProps) {
291
303
  hasGeolocation,
292
304
  hasLayerTree,
293
305
  hasLnp,
306
+ hasMapset,
294
307
  hasNotification,
295
308
  hasPermalink,
296
309
  hasPrint,
@@ -307,8 +320,10 @@ function RvfMobilityMap(props: RvfMobilityMapProps) {
307
320
  isSearchOpen,
308
321
  isShareMenuOpen,
309
322
  isTracking,
323
+ linesIds,
310
324
  linesNetworkPlanLayer,
311
325
  map,
326
+ mapsetLayer,
312
327
  notificationsLayer,
313
328
  permalinkUrlSearchParams,
314
329
  previewNotifications,
@@ -456,6 +471,9 @@ function RvfMobilityMap(props: RvfMobilityMapProps) {
456
471
  <RvfTarifZonenLayer />
457
472
  <RvfSellingPointsLayer />
458
473
  {hasLnp && <LinesNetworkPlanLayer />}
474
+
475
+ {hasMapset && <MapsetLayer />}
476
+
459
477
  <RvfPoisLayer />
460
478
  <RvfSharedMobilityLayerGroup />
461
479
  {mainlink && (
@@ -490,11 +508,11 @@ function RvfMobilityMap(props: RvfMobilityMapProps) {
490
508
  )}
491
509
  </Map>
492
510
 
493
- <div className="pointer-events-none absolute top-2 bottom-2 left-2 z-10 flex flex-col gap-2 *:pointer-events-auto">
511
+ <div className="pointer-events-none absolute top-2 bottom-2 left-2 z-10 flex flex-col gap-2">
494
512
  {hasToolbar && (
495
513
  <div
496
514
  className={
497
- "relative z-10 w-fit rounded-2xl bg-black/10 p-0 backdrop-blur-sm"
515
+ "pointer-events-none relative z-10 w-fit rounded-2xl bg-black/10 p-0 backdrop-blur-sm *:pointer-events-auto"
498
516
  }
499
517
  // className="w-fit rounded-2xl bg-black/10 p-1 backdrop-blur-sm">
500
518
  >
@@ -540,7 +558,7 @@ function RvfMobilityMap(props: RvfMobilityMapProps) {
540
558
  {/* Desktop (>= lg) */}
541
559
  <div
542
560
  className={twMerge(
543
- "flex w-0 flex-1 flex-col overflow-hidden rounded-2xl",
561
+ "pointer-events-none flex w-0 flex-1 flex-col overflow-hidden rounded-2xl",
544
562
  isOverlayOpen ? "@lg:min-w-64" : "p-0",
545
563
  )}
546
564
  style={{ containerType: "normal" }}
@@ -3,16 +3,15 @@ import { useEffect, useMemo } from "preact/hooks";
3
3
 
4
4
  import { EMPTY_FEATURE_COLLECTION, SOURCE_SELECT } from "../utils/constants";
5
5
  import useMapContext from "../utils/hooks/useMapContext";
6
- import useRvfContext from "../utils/hooks/useRvfContext";
7
6
 
8
7
  const geojson = new GeoJSON({
9
8
  dataProjection: "EPSG:4326",
10
9
  featureProjection: "EPSG:3857",
11
10
  });
12
11
 
13
- function SingleClickListener() {
12
+ function RvfSelectedFeatureHighlightLayer() {
14
13
  const { baseLayer } = useMapContext();
15
- const { selectedFeature } = useRvfContext();
14
+ const { selectedFeature } = useMapContext();
16
15
 
17
16
  // const layer = useMemo(() => {
18
17
  // const layer = new VectorLayer({
@@ -55,7 +54,12 @@ function SingleClickListener() {
55
54
  hover: true,
56
55
  });
57
56
  if (feature.get("cluster_id")) {
58
- feature.set("mot", feature.get("features")[0].get("form_factor"));
57
+ // Maybe not the most robust solutions, so I set scooter by default in case something changes in the style.
58
+ feature.set(
59
+ "mot",
60
+ feature.get("vectorTileFeature")?.source?.split("_")?.pop() ||
61
+ "scooter",
62
+ );
59
63
  feature.set("provider_name", "");
60
64
  feature.set("num_vehicles_available", feature.get("point_count"));
61
65
  }
@@ -93,4 +97,4 @@ function SingleClickListener() {
93
97
 
94
98
  return null;
95
99
  }
96
- export default SingleClickListener;
100
+ export default RvfSelectedFeatureHighlightLayer;
@@ -154,6 +154,7 @@ function SingleClickListener({ eventNode }: { eventNode: HTMLElement }) {
154
154
  return f.features;
155
155
  });
156
156
 
157
+ console.log(features);
157
158
  // Append more infos about the features
158
159
  for (const feature of features) {
159
160
  const clusterId = feature.get("cluster_id");
@@ -165,8 +166,9 @@ function SingleClickListener({ eventNode }: { eventNode: HTMLElement }) {
165
166
  .getSource<GeoJSONSource>(sourceId)
166
167
  ?.getClusterLeaves(clusterId, 1000, 0)) || [];
167
168
 
169
+ console.log(leaves);
168
170
  feature.set(
169
- "features",
171
+ "clusterLeaves",
170
172
  leaves.map((l) => {
171
173
  return geojson.readFeature(l);
172
174
  }),