@geops/rvf-mobility-web-component 0.1.83 → 0.1.84

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 (97) hide show
  1. package/CHANGELOG.md +33 -0
  2. package/index.js +206 -187
  3. package/package.json +2 -2
  4. package/src/Departure/Departure.tsx +6 -3
  5. package/src/FeatureDetails/FeatureDetails.tsx +4 -4
  6. package/src/FeaturesInfosListener/FeaturesInfosListener.tsx +22 -0
  7. package/src/LayerTreeMenu/LayerTreeMenu.tsx +12 -4
  8. package/src/LayoutState/LayoutState.tsx +144 -63
  9. package/src/LinesNetworkPlanDetails/LinesNetworkPlanDetails.tsx +2 -2
  10. package/src/LinesNetworkPlanLayerHighlight/LinesNetworkPlanLayerHighlight.tsx +2 -40
  11. package/src/MapLayout/MapLayout.tsx +2 -7
  12. package/src/MobilityMap/MobilityMap.tsx +1 -6
  13. package/src/MobilityMap/MobilityMapAttributes.ts +28 -7
  14. package/src/NotificationDetails/NotificationDetails.tsx +21 -22
  15. package/src/NotificationsLayer/NotificationsLayer.tsx +47 -3
  16. package/src/OverlayDetails/OverlayDetails.tsx +5 -0
  17. package/src/RealtimeLayer/RealtimeLayer.tsx +44 -50
  18. package/src/RouteIcon/RouteIcon.tsx +25 -14
  19. package/src/RouteSchedule/RouteSchedule.tsx +14 -11
  20. package/src/RouteScheduleFooter/RouteScheduleFooter.tsx +6 -3
  21. package/src/RouteScheduleHeader/RouteScheduleHeader.tsx +8 -2
  22. package/src/RouteStop/RouteStop.tsx +14 -29
  23. package/src/RouteStopPlatform/RouteStopPlatform.tsx +1 -3
  24. package/src/RouteStopProgress/RouteStopProgress.tsx +11 -18
  25. package/src/RouteStopStation/RouteStopStation.tsx +3 -3
  26. package/src/RvfRealtimeLayer/RvfRealtimeLayer.tsx +16 -3
  27. package/src/RvfSearch/RvfSearch.tsx +5 -4
  28. package/src/Search/Search.tsx +41 -20
  29. package/src/Search/SearchBase.tsx +169 -0
  30. package/src/Search/SearchHeadless.tsx +79 -0
  31. package/src/Search/index.tsx +2 -0
  32. package/src/SearchLinesResult/SearchLinesResult.tsx +43 -0
  33. package/src/SearchLinesResult/index.tsx +1 -0
  34. package/src/SearchLinesResults/SearchLinesResults.tsx +106 -0
  35. package/src/SearchLinesResults/index.tsx +1 -0
  36. package/src/SearchLnpStopsResult/SearchLnpStopsResult.tsx +42 -0
  37. package/src/SearchLnpStopsResult/index.tsx +1 -0
  38. package/src/SearchLnpStopsResults/SearchLnpStopsResults.tsx +106 -0
  39. package/src/SearchLnpStopsResults/index.tsx +1 -0
  40. package/src/SearchResult/SearchResult.tsx +25 -0
  41. package/src/SearchResult/index.tsx +1 -0
  42. package/src/SearchResults/SearchResults.tsx +81 -0
  43. package/src/SearchResults/index.tsx +1 -0
  44. package/src/SearchResultsHeader/SearchResultsHeader.tsx +36 -0
  45. package/src/SearchResultsHeader/index.tsx +1 -0
  46. package/src/SearchStopsResult/SearchStopsResult.tsx +43 -0
  47. package/src/SearchStopsResult/index.tsx +1 -0
  48. package/src/SearchStopsResults/SearchStopsResults.tsx +101 -0
  49. package/src/SearchStopsResults/index.tsx +1 -0
  50. package/src/SearchTrainsResult/SearchTrainsResult.tsx +42 -0
  51. package/src/SearchTrainsResult/index.tsx +1 -0
  52. package/src/SearchTrainsResults/SearchTrainsResults.tsx +109 -0
  53. package/src/SearchTrainsResults/index.tsx +1 -0
  54. package/src/SingleClickListener/SingleClickListener.tsx +38 -2
  55. package/src/Station/Station.tsx +23 -48
  56. package/src/StationHeader/StationHeader.tsx +3 -3
  57. package/src/StationsLayer/StationsLayer.tsx +9 -1
  58. package/src/StopsSearch/StopsSearch.tsx +2 -2
  59. package/src/ui/InputSearch/InputSearch.tsx +105 -0
  60. package/src/ui/InputSearch/index.tsx +1 -0
  61. package/src/utils/centerOnVehicle.ts +9 -2
  62. package/src/utils/constants.ts +8 -1
  63. package/src/utils/fullTrajectoryStyle.ts +4 -7
  64. package/src/utils/getBgColor.ts +4 -2
  65. package/src/utils/getDelayColorForVehicle.test.ts +21 -11
  66. package/src/utils/getDelayColorForVehicle.ts +7 -5
  67. package/src/utils/getDelayTextForVehicle.test.ts +12 -12
  68. package/src/utils/getDelayTextForVehicle.ts +4 -0
  69. package/src/utils/getMainColorForVehicle.ts +11 -4
  70. package/src/utils/getRadius.ts +9 -3
  71. package/src/utils/getTextColor.ts +1 -1
  72. package/src/utils/getTextColorForVehicle.ts +29 -0
  73. package/src/utils/getTextFontForVehicle.test.ts +1 -1
  74. package/src/utils/getTextFontForVehicle.tsx +11 -3
  75. package/src/utils/getTextForVehicle.ts +7 -1
  76. package/src/utils/hooks/useFit.tsx +69 -0
  77. package/src/utils/hooks/useFitOnFeatures.tsx +77 -0
  78. package/src/utils/hooks/useLayersConfig.tsx +3 -0
  79. package/src/utils/hooks/useLnp.tsx +39 -5
  80. package/src/utils/hooks/useMapContext.tsx +2 -5
  81. package/src/utils/hooks/useRealtimeDepartures.tsx +45 -0
  82. package/src/utils/hooks/useRealtimeRenderedTrajectory.tsx +42 -0
  83. package/src/utils/hooks/useRealtimeStation.tsx +39 -0
  84. package/src/utils/hooks/useRealtimeStopSequences.tsx +43 -0
  85. package/src/utils/hooks/useRealtimeTrainsByRouteIdentifier.tsx +71 -0
  86. package/src/utils/hooks/useRouteStop.tsx +7 -1
  87. package/src/utils/hooks/useSearchLines.tsx +34 -0
  88. package/src/utils/hooks/useSearchLnpStops.tsx +38 -0
  89. package/src/utils/hooks/useSearchStops.tsx +85 -0
  90. package/src/utils/hooks/useSearchTrains.tsx +83 -0
  91. package/src/utils/realtimeRVFStyle.ts +38 -30
  92. package/src/utils/translations.ts +17 -0
  93. package/tash +58 -0
  94. package/src/utils/centerOnStation.ts +0 -18
  95. package/src/utils/getDelayFontForVehicle.test.ts +0 -7
  96. package/src/utils/getDelayFontForVehicle.tsx +0 -8
  97. package/src/utils/hooks/useStation.tsx +0 -22
@@ -0,0 +1,109 @@
1
+ import { cloneElement, toChildArray } from "preact";
2
+ import { memo } from "preact/compat";
3
+ import { useCallback, useContext, useMemo } from "preact/hooks";
4
+
5
+ import { SearchContext } from "../Search/SearchBase";
6
+ import SearchResult from "../SearchResult";
7
+ import SearchResults from "../SearchResults";
8
+ import SearchResultsHeader from "../SearchResultsHeader";
9
+ import useI18n from "../utils/hooks/useI18n";
10
+ import useSearchTrains from "../utils/hooks/useSearchTrains";
11
+
12
+ import type { RealtimeRouteIdentifierMatch } from "mobility-toolbox-js/types";
13
+ import type { ReactElement } from "preact/compat";
14
+
15
+ import type {
16
+ SearchResultsChildProps,
17
+ SearchResultsProps,
18
+ } from "../SearchResults/SearchResults";
19
+
20
+ function SearchTrainsResults({
21
+ children,
22
+ filter,
23
+ resultClassName,
24
+ resultsClassName,
25
+ resultsContainerClassName,
26
+ sort,
27
+ }: SearchResultsProps<RealtimeRouteIdentifierMatch>) {
28
+ const { open, query, setOpen, setSelectedQuery } = useContext(SearchContext);
29
+ const searchResponse = useSearchTrains(query);
30
+
31
+ const { t } = useI18n();
32
+
33
+ const onSelectResult = useCallback(
34
+ (item: RealtimeRouteIdentifierMatch) => {
35
+ setSelectedQuery(item.route_identifier);
36
+ setOpen(false);
37
+ },
38
+ [setOpen, setSelectedQuery],
39
+ );
40
+
41
+ const results = useMemo(() => {
42
+ let rs = [...(searchResponse?.results || [])];
43
+
44
+ if (filter) {
45
+ rs = rs.filter(filter);
46
+ }
47
+
48
+ if (sort) {
49
+ rs = rs.sort(sort);
50
+ }
51
+ return rs;
52
+ }, [searchResponse, filter, sort]);
53
+
54
+ const searchResponseFiltered = useMemo(() => {
55
+ return {
56
+ ...searchResponse,
57
+ results,
58
+ };
59
+ }, [results, searchResponse]);
60
+
61
+ const showResults = useMemo(() => {
62
+ return open && !!results?.length;
63
+ }, [open, results]);
64
+
65
+ if (!showResults) {
66
+ return null;
67
+ }
68
+
69
+ return (
70
+ <>
71
+ <SearchResultsHeader>{t("search_trains_results")}</SearchResultsHeader>
72
+ <SearchResults
73
+ className={resultsContainerClassName}
74
+ resultsClassName={resultsClassName}
75
+ searchResponse={searchResponseFiltered}
76
+ >
77
+ {results.map((item: RealtimeRouteIdentifierMatch) => {
78
+ return (
79
+ <SearchResult
80
+ className={resultClassName}
81
+ key={item.trains[0].train_id}
82
+ >
83
+ {toChildArray(children).map(
84
+ (
85
+ child: ReactElement<
86
+ SearchResultsChildProps<RealtimeRouteIdentifierMatch>
87
+ >,
88
+ ) => {
89
+ const onSelectItem = (
90
+ itemm: RealtimeRouteIdentifierMatch,
91
+ evt: Event,
92
+ ) => {
93
+ onSelectResult(itemm);
94
+ child.props?.onSelectItem?.(itemm, evt);
95
+ };
96
+ return cloneElement(child, {
97
+ item: item,
98
+ onSelectItem,
99
+ });
100
+ },
101
+ )}
102
+ </SearchResult>
103
+ );
104
+ })}
105
+ </SearchResults>
106
+ </>
107
+ );
108
+ }
109
+ export default memo(SearchTrainsResults);
@@ -0,0 +1 @@
1
+ export { default } from "./SearchTrainsResults";
@@ -25,6 +25,10 @@ function SingleClickListener({
25
25
  queryablelayers,
26
26
  setFeaturesInfos,
27
27
  setFeaturesInfosHovered,
28
+ setLinesIds,
29
+ setNotificationId,
30
+ setStationId,
31
+ setTrainId,
28
32
  stationsLayer,
29
33
  tenant,
30
34
  } = useMapContext();
@@ -50,7 +54,19 @@ function SingleClickListener({
50
54
  const stationsFeatures = featuresInfoStations?.features || [];
51
55
 
52
56
  const [stationFeature] = stationsFeatures.filter((feat) => {
53
- return feat.get("tralis_network")?.includes(tenant);
57
+ // TODO: think how to do better. LNP stations should have a tralis_network property?
58
+ // travic stations has a tralis_network property
59
+ if (feat.get("tralis_network")) {
60
+ return feat.get("tralis_network").includes(tenant);
61
+ }
62
+
63
+ // We move the external_id to uid to be consistent across all stations (lnp and others)
64
+ if (!feat.get("uid") && feat.get("external_id")) {
65
+ feat.set("uid", feat.get("external_id"));
66
+ }
67
+
68
+ // LNP stations have no tralis_network property
69
+ return true;
54
70
  });
55
71
 
56
72
  // Replace the features clicked in the stations layer by the filtered one
@@ -85,8 +101,28 @@ function SingleClickListener({
85
101
  async (evt: MapBrowserEvent<PointerEvent>) => {
86
102
  const featuresInfos = await getFeaturesInfosAtEvt(evt);
87
103
  setFeaturesInfos(featuresInfos);
104
+ // When user click we close the overlay
105
+ if (
106
+ featuresInfos?.flatMap((fi) => {
107
+ return fi.features;
108
+ }).length === 0
109
+ ) {
110
+ // It means no feature selectable were clicked so we set all ids to null
111
+ // to close the overlay
112
+ setTrainId(null);
113
+ setStationId(null);
114
+ setNotificationId(null);
115
+ setLinesIds(null);
116
+ }
88
117
  },
89
- [getFeaturesInfosAtEvt, setFeaturesInfos],
118
+ [
119
+ getFeaturesInfosAtEvt,
120
+ setFeaturesInfos,
121
+ setLinesIds,
122
+ setNotificationId,
123
+ setStationId,
124
+ setTrainId,
125
+ ],
90
126
  );
91
127
 
92
128
  useEffect(() => {
@@ -1,11 +1,12 @@
1
- import { debounceDeparturesMessages } from "mobility-toolbox-js/ol";
2
1
  import { memo } from "preact/compat";
3
- import { useEffect, useRef, useState } from "preact/hooks";
4
2
  import { twMerge } from "tailwind-merge";
5
3
 
6
4
  import Departure from "../Departure";
5
+ import ShadowOverflow from "../ShadowOverflow";
7
6
  import StationHeader from "../StationHeader";
8
7
  import useMapContext from "../utils/hooks/useMapContext";
8
+ import useRealtimeDepartures from "../utils/hooks/useRealtimeDepartures";
9
+ import useRealtimeStation from "../utils/hooks/useRealtimeStation";
9
10
 
10
11
  import type { RealtimeDeparture } from "mobility-toolbox-js/types";
11
12
  import type { HTMLAttributes, PreactDOMAttributes } from "preact";
@@ -15,34 +16,10 @@ export type StationProps = {
15
16
  } & HTMLAttributes<HTMLDivElement> &
16
17
  PreactDOMAttributes;
17
18
 
18
- function Station(props: StationProps) {
19
- const { realtimeLayer, station } = useMapContext();
20
- const [departures, setDepartures] = useState<RealtimeDeparture[]>();
21
- const ref = useRef();
22
- const { className } = props;
23
-
24
- useEffect(() => {
25
- if (!station || !realtimeLayer?.api) {
26
- return;
27
- }
28
-
29
- const onMessage = debounceDeparturesMessages(
30
- (newDepartures: RealtimeDeparture[]) => {
31
- setDepartures(newDepartures);
32
- return null;
33
- },
34
- false,
35
- 180,
36
- );
37
- // @ts-expect-error bad type definition
38
- realtimeLayer.api.subscribeDepartures(station?.properties?.uid, onMessage);
39
-
40
- return () => {
41
- setDepartures(null);
42
- // @ts-expect-error bad type definition
43
- realtimeLayer?.api?.unsubscribeDepartures(station?.properties.uid);
44
- };
45
- }, [station, realtimeLayer?.api]);
19
+ function Station({ className, ...props }: StationProps) {
20
+ const { stationId } = useMapContext();
21
+ const station = useRealtimeStation(stationId);
22
+ const departures = useRealtimeDepartures(stationId);
46
23
 
47
24
  if (!station) {
48
25
  return null;
@@ -50,24 +27,22 @@ function Station(props: StationProps) {
50
27
 
51
28
  return (
52
29
  <>
53
- <StationHeader />
54
- <div
55
- className={twMerge("flex flex-col p-2", className)}
56
- ref={ref}
57
- {...props}
58
- >
59
- {(departures || [])
60
- // .filter(hideDepartures)
61
- .map((departure: RealtimeDeparture, index: number) => {
62
- return (
63
- <Departure
64
- departure={departure}
65
- index={index}
66
- key={departure.call_id}
67
- />
68
- );
69
- })}
70
- </div>
30
+ <StationHeader station={station} />
31
+ <ShadowOverflow>
32
+ <div className={twMerge("flex flex-col p-2", className)} {...props}>
33
+ {(departures || [])
34
+ // .filter(hideDepartures)
35
+ .map((departure: RealtimeDeparture, index: number) => {
36
+ return (
37
+ <Departure
38
+ departure={departure}
39
+ index={index}
40
+ key={departure.call_id}
41
+ />
42
+ );
43
+ })}
44
+ </div>
45
+ </ShadowOverflow>
71
46
  </>
72
47
  );
73
48
  }
@@ -2,10 +2,10 @@ import { memo } from "preact/compat";
2
2
 
3
3
  import StationName from "../StationName";
4
4
  import StationServices from "../StationServices";
5
- import useMapContext from "../utils/hooks/useMapContext";
6
5
 
7
- function StationHeader() {
8
- const { station } = useMapContext();
6
+ import type { RealtimeStation } from "mobility-toolbox-js/types";
7
+
8
+ function StationHeader({ station }: { station: RealtimeStation }) {
9
9
  return (
10
10
  <div className="flex items-center gap-x-4 bg-slate-100 p-4">
11
11
  <div className="flex grow flex-col">
@@ -18,11 +18,19 @@ function StationsLayer(props: Partial<MaplibreStyleLayerOptions>) {
18
18
  layersFilter: ({ metadata }) => {
19
19
  return (
20
20
  metadata?.["tralis.variable"] === "station" ||
21
- metadata?.["general.filter"] === "stations"
21
+ metadata?.["general.filter"] === "stations" ||
22
+ metadata?.["geops.filter"] === "netzplan_stops"
22
23
  );
23
24
  },
24
25
  maplibreLayer: baseLayer,
25
26
  name: LAYER_NAME_STATIONS,
27
+ // queryRenderedLayersFilter: ({ metadata }) => {
28
+ // return (
29
+ // metadata?.["tralis.variable"] === "station" ||
30
+ // metadata?.["general.filter"] === "stations" ||
31
+ // metadata?.["geops.filter"] === "netzplan_stops" // we include lnp stations only on click
32
+ // );
33
+ // },
26
34
  ...(props || {}),
27
35
  });
28
36
  }, [baseLayer, props]);
@@ -24,7 +24,7 @@ import type {
24
24
  TargetedKeyboardEvent,
25
25
  } from "preact";
26
26
 
27
- export type StopsFeature = StopsResponse["features"][0];
27
+ import type { StopsFeature } from "../utils/hooks/useSearchStops";
28
28
 
29
29
  export type StopsSearchProps = {
30
30
  apikey: string;
@@ -235,7 +235,7 @@ function StopsSearch({
235
235
  searchIconContainerClassName,
236
236
  )}
237
237
  >
238
- <Search className="size-4" />
238
+ <Search />
239
239
  </div>
240
240
  <div
241
241
  className={twMerge(
@@ -0,0 +1,105 @@
1
+ import { memo } from "preact/compat";
2
+ import { useRef } from "preact/hooks";
3
+ import { twMerge } from "tailwind-merge";
4
+
5
+ import Cancel from "../../icons/Cancel";
6
+ import Search from "../../icons/Search";
7
+ import useI18n from "../../utils/hooks/useI18n";
8
+ import IconButton from "../IconButton";
9
+ import Input from "../Input";
10
+
11
+ import type { PreactDOMAttributes } from "preact";
12
+ import type { HTMLAttributes, ReactNode } from "preact/compat";
13
+
14
+ import type { IconButtonProps } from "../IconButton/IconButton";
15
+ import type { InputProps } from "../Input/Input";
16
+
17
+ export type InputSearchProps = {
18
+ cancelButtonClassName?: string;
19
+ cancelButtonProps?: IconButtonProps;
20
+ cancelIcon?: ReactNode;
21
+ className?: string;
22
+ inputClassName?: string;
23
+ inputContainerClassName?: string;
24
+ inputProps?: InputProps;
25
+ resultClassName?: string;
26
+ resultsClassName?: string;
27
+ resultsContainerClassName?: string;
28
+ searchIcon?: ReactNode;
29
+ searchIconContainerClassName?: string;
30
+ withResultsClassName?: string;
31
+ } & HTMLAttributes<HTMLDivElement> &
32
+ PreactDOMAttributes;
33
+
34
+ /**
35
+ * Rich search input component.
36
+ */
37
+ function InputSearch({
38
+ cancelButtonClassName,
39
+ cancelButtonProps,
40
+ cancelIcon,
41
+ children,
42
+ className,
43
+ inputClassName,
44
+ inputContainerClassName,
45
+ inputProps,
46
+ searchIcon,
47
+ searchIconContainerClassName,
48
+ withResultsClassName,
49
+ }: InputSearchProps) {
50
+ const { t } = useI18n();
51
+ const myRef = useRef<HTMLDivElement>();
52
+
53
+ return (
54
+ <>
55
+ <div
56
+ className={twMerge(
57
+ "flex h-16 items-center gap-4 rounded-md bg-white p-4 pt-3.5 shadow",
58
+ className,
59
+ withResultsClassName,
60
+ )}
61
+ ref={myRef}
62
+ >
63
+ <div
64
+ className={twMerge(
65
+ "text-grey flex items-center",
66
+ searchIconContainerClassName,
67
+ )}
68
+ >
69
+ {searchIcon || <Search />}
70
+ </div>
71
+ <div
72
+ className={twMerge(
73
+ "@container/inputsearch flex grow items-center gap-2 border-b-2 border-solid",
74
+ inputContainerClassName,
75
+ )}
76
+ >
77
+ <Input
78
+ autoComplete="off"
79
+ className={twMerge(
80
+ "h-8 w-1 grow overflow-hidden text-ellipsis placeholder:text-zinc-400",
81
+ inputClassName,
82
+ )}
83
+ type="text"
84
+ {...(inputProps || {})}
85
+ />
86
+ {!!inputProps.value && (
87
+ <IconButton
88
+ className={twMerge(
89
+ "flex size-4 items-center rounded-none border-none bg-transparent p-0 shadow-none",
90
+ cancelButtonClassName,
91
+ )}
92
+ title={t("search_input_cancel")}
93
+ {...(cancelButtonProps || {})}
94
+ >
95
+ {cancelIcon || <Cancel />}
96
+ </IconButton>
97
+ )}
98
+ </div>
99
+ </div>
100
+ {children}
101
+ </>
102
+ );
103
+ }
104
+
105
+ export default memo(InputSearch);
@@ -0,0 +1 @@
1
+ export { default } from "./InputSearch";
@@ -8,6 +8,7 @@ const centerOnVehicle = async (
8
8
  vehicle: RealtimeTrajectory,
9
9
  map: Map,
10
10
  targetZoom = 0,
11
+ isOverlayOpen = true,
11
12
  ) => {
12
13
  if (!vehicle) {
13
14
  return Promise.reject(new Error("No vehicle provided"));
@@ -21,15 +22,21 @@ const centerOnVehicle = async (
21
22
  const zoom = targetZoom || view.getZoom();
22
23
  const resolution = zoom > 0 ? view.getResolutionForZoom(zoom) : undefined;
23
24
 
24
- let center = coordinate;
25
+ let center = coordinate ? [...coordinate] : null;
25
26
  if (!center && geometry) {
26
27
  const { coord } = getVehiclePosition(Date.now(), vehicle, true);
27
- center = coord as [number, number];
28
+ center = coord ? [...coord] : null;
28
29
  }
29
30
  if (!center) {
30
31
  return Promise.reject(new Error("No center found"));
31
32
  }
33
+ if (isOverlayOpen) {
34
+ // Adjust center to take in account the opened overlay.
35
+ center[0] -= (320 / 2) * resolution; // shift right by 400px
36
+ // console.log(center);
37
+ }
32
38
 
39
+ // Shift of 150px to the left and 50px to the top.
33
40
  view.cancelAnimations();
34
41
 
35
42
  const promise = new Promise((resolve) => {
@@ -224,7 +224,7 @@ export const LNP_LINE_ID_PROP = "original_line_id";
224
224
  // LNP data source id in the style
225
225
  export const LNP_SOURCE_ID = "network_plans";
226
226
 
227
- // Metadata key in the lnp data source
227
+ // LNP metadata key in the lnp data source
228
228
  export const LNP_MD_LINES = "geops.lnp.lines";
229
229
  export const LNP_MD_STOPS = "geops.lnp.stops";
230
230
 
@@ -233,3 +233,10 @@ export const LNP_GEOPS_FILTER_HIGHLIGHT = "highlightnetzplan";
233
233
 
234
234
  // LNP style layer id where the dynamic filtering will apply
235
235
  export const LNP_LAYER_ID_HIGHLIGHT = "netzplan_highlight_trip";
236
+
237
+ // Layer props used by layer and/or layerConfig
238
+ export const LAYER_TREE_HIDE_PROP = "layerTreeHidden";
239
+ export const LAYER_TREE_TITLE_FUNC_PROP = "layerTreeTitleRenderFunc";
240
+
241
+ /** FIT ON FEATURES */
242
+ export const FIT_ON_FEATURES_MAX_ZOOM_POINT = 16;
@@ -1,5 +1,7 @@
1
1
  import { Circle, Fill, Stroke, Style } from "ol/style";
2
2
 
3
+ import getBgColor from "./getBgColor";
4
+
3
5
  import type { FeatureLike } from "ol/Feature";
4
6
 
5
7
  const borderStyle = new Style({
@@ -16,11 +18,7 @@ const borderStyle = new Style({
16
18
  zIndex: 2,
17
19
  });
18
20
 
19
- const fullTrajectoryStyle = (
20
- feature: FeatureLike,
21
- resolution: number,
22
- options: { getBgColor: (type: string, line: { name: string }) => string },
23
- ): Style[] => {
21
+ const fullTrajectoryStyle = (feature: FeatureLike): Style[] => {
24
22
  let lineColor = "#ffffff"; // white
25
23
 
26
24
  const type = feature.get("type");
@@ -29,8 +27,7 @@ const fullTrajectoryStyle = (
29
27
  if (stroke && stroke[0] !== "#") {
30
28
  stroke = `#${stroke}`;
31
29
  }
32
- lineColor =
33
- stroke || options?.getBgColor(type, { name: feature.get("line_name") });
30
+ lineColor = stroke || getBgColor(type, { name: feature.get("line_name") });
34
31
 
35
32
  // Don't allow white lines, use red instead.
36
33
  lineColor = /#ffffff/i.test(lineColor) ? "#ff0000" : lineColor;
@@ -2,7 +2,9 @@ import { realtimeConfig } from "mobility-toolbox-js/ol";
2
2
 
3
3
  import { LINE_COLOR_BY_NAME } from "./constants";
4
4
 
5
- const getBgColor = (type, line) => {
5
+ import type { RealtimeLine, RealtimeMot } from "mobility-toolbox-js/types";
6
+
7
+ const getBgColor = (type: RealtimeMot, line: Partial<RealtimeLine>) => {
6
8
  if (type === "bus") {
7
9
  return "#646363";
8
10
  }
@@ -12,7 +14,7 @@ const getBgColor = (type, line) => {
12
14
  return lineColor;
13
15
  }
14
16
  }
15
- return realtimeConfig.getBgColor(type);
17
+ return realtimeConfig.getColorForType(type);
16
18
  };
17
19
 
18
20
  export default getBgColor;
@@ -2,8 +2,8 @@ import getDelayColorForVehicle from "./getDelayColorForVehicle";
2
2
 
3
3
  describe("getDelayColorForVehicle", () => {
4
4
  it("returns cancelled color", () => {
5
- expect(getDelayColorForVehicle(0, true, true)).toBe("#dc2626");
6
- expect(getDelayColorForVehicle(0, true, false)).toBe("#a0a0a0");
5
+ expect(getDelayColorForVehicle(null, null, 0, true, true)).toBe("#dc2626");
6
+ expect(getDelayColorForVehicle(null, null, 0, true, false)).toBe("#a0a0a0");
7
7
  });
8
8
 
9
9
  it("returns null delay (no realtime train) color", () => {
@@ -11,9 +11,13 @@ describe("getDelayColorForVehicle", () => {
11
11
  });
12
12
 
13
13
  it("returns transparent", () => {
14
- expect(getDelayColorForVehicle(0)).toBe("transparent");
15
- expect(getDelayColorForVehicle(1 * 60 * 1000)).toBe("transparent");
16
- expect(getDelayColorForVehicle(0.4 * 60 * 1000)).toBe("transparent");
14
+ expect(getDelayColorForVehicle(null, null, 0)).toBe("transparent");
15
+ expect(getDelayColorForVehicle(null, null, 1 * 60 * 1000)).toBe(
16
+ "transparent",
17
+ );
18
+ expect(getDelayColorForVehicle(null, null, 0.4 * 60 * 1000)).toBe(
19
+ "transparent",
20
+ );
17
21
  });
18
22
  // it("returns green", () => {
19
23
  // expect(getDelayColorForVehicle(0)).toBe("#16a34a");
@@ -24,14 +28,20 @@ describe("getDelayColorForVehicle", () => {
24
28
  // expect(getDelayColorForVehicle(4.49 * 60 * 1000 - 1)).toBe("#ca8a04");
25
29
  // });
26
30
  it("returns orange", () => {
27
- expect(getDelayColorForVehicle(3 * 60 * 1000)).toBe("#ea580c");
28
- expect(getDelayColorForVehicle(4.49 * 60 * 1000 - 1)).toBe("#ea580c");
29
- expect(getDelayColorForVehicle(5 * 60 * 1000)).toBe("#ea580c");
31
+ expect(getDelayColorForVehicle(null, null, 3 * 60 * 1000)).toBe("#ea580c");
32
+ expect(getDelayColorForVehicle(null, null, 4.49 * 60 * 1000 - 1)).toBe(
33
+ "#ea580c",
34
+ );
35
+ expect(getDelayColorForVehicle(null, null, 5 * 60 * 1000)).toBe("#ea580c");
30
36
  });
31
37
 
32
38
  it("returns red", () => {
33
- expect(getDelayColorForVehicle(9.49 * 60 * 1000 - 1)).toBe("#dc2626");
34
- expect(getDelayColorForVehicle(10 * 60 * 1000)).toBe("#dc2626");
35
- expect(getDelayColorForVehicle(180 * 60 * 1000)).toBe("#dc2626");
39
+ expect(getDelayColorForVehicle(null, null, 9.49 * 60 * 1000 - 1)).toBe(
40
+ "#dc2626",
41
+ );
42
+ expect(getDelayColorForVehicle(null, null, 10 * 60 * 1000)).toBe("#dc2626");
43
+ expect(getDelayColorForVehicle(null, null, 180 * 60 * 1000)).toBe(
44
+ "#dc2626",
45
+ );
36
46
  });
37
47
  });
@@ -1,13 +1,15 @@
1
1
  import getDelayColor from "./getDelayColor";
2
2
 
3
+ import type { ViewState } from "mobility-toolbox-js/types";
4
+
3
5
  /**
4
- * @private
5
- * @param {number} delayInMs Delay in milliseconds.
6
- * @param {boolean} cancelled true if the journey is cancelled.
7
- * @param {boolean} isDelayText true if the color is used for delay text of the symbol.
6
+ * Return the delay color depending on an object representing a vehicle or a line.
7
+ * This function is used to have the same color on the map and on other components.
8
8
  */
9
9
  const getDelayColorForVehicle = (
10
- delayInMs: null | number,
10
+ object?: unknown,
11
+ viewState?: ViewState,
12
+ delayInMs?: number,
11
13
  cancelled?: boolean,
12
14
  isDelayText?: boolean,
13
15
  ): string => {
@@ -2,31 +2,31 @@ import getDelayTextForVehicle from "./getDelayTextForVehicle";
2
2
 
3
3
  describe("getDelayTextForVehicle", () => {
4
4
  it("returns cancelled character", () => {
5
- expect(getDelayTextForVehicle(7200000, true)).toBe(
5
+ expect(getDelayTextForVehicle(null, null, 7200000, true)).toBe(
6
6
  String.fromCodePoint(0x00d7),
7
7
  );
8
8
  });
9
9
 
10
10
  it("returns hours (floor)", () => {
11
- expect(getDelayTextForVehicle(7200000)).toBe("+2h");
12
- expect(getDelayTextForVehicle(7255555)).toBe("+2h1m");
11
+ expect(getDelayTextForVehicle(null, null, 7200000)).toBe("+2h");
12
+ expect(getDelayTextForVehicle(null, null, 7255555)).toBe("+2h1m");
13
13
  });
14
14
 
15
15
  it("returns minutes (round)", () => {
16
- expect(getDelayTextForVehicle(120000)).toBe("+2m");
17
- expect(getDelayTextForVehicle(151000)).toBe("+3m");
16
+ expect(getDelayTextForVehicle(null, null, 120000)).toBe("+2m");
17
+ expect(getDelayTextForVehicle(null, null, 151000)).toBe("+3m");
18
18
  });
19
19
 
20
20
  it("doesn't display seconds", () => {
21
- expect(getDelayTextForVehicle(1000)).toBe("");
22
- expect(getDelayTextForVehicle(30000)).toBe("+1m");
23
- expect(getDelayTextForVehicle(7255555)).toBe("+2h1m");
21
+ expect(getDelayTextForVehicle(null, null, 1000)).toBe("");
22
+ expect(getDelayTextForVehicle(null, null, 30000)).toBe("+1m");
23
+ expect(getDelayTextForVehicle(null, null, 7255555)).toBe("+2h1m");
24
24
  });
25
25
 
26
26
  it("returns empty value", () => {
27
- expect(getDelayTextForVehicle(1000)).toBe("");
28
- expect(getDelayTextForVehicle(null)).toBe("");
29
- expect(getDelayTextForVehicle(undefined)).toBe("");
30
- expect(getDelayTextForVehicle(0)).toBe("");
27
+ expect(getDelayTextForVehicle(null, null, 1000)).toBe("");
28
+ expect(getDelayTextForVehicle(null, null, null)).toBe("");
29
+ expect(getDelayTextForVehicle(null, null, undefined)).toBe("");
30
+ expect(getDelayTextForVehicle(null, null, 0)).toBe("");
31
31
  });
32
32
  });
@@ -1,11 +1,15 @@
1
1
  import getDelayString from "./getDelayString";
2
2
 
3
+ import type { RealtimeTrajectory, ViewState } from "mobility-toolbox-js/types";
4
+
3
5
  /**
4
6
  * This function returns the text displays near the vehicle.
5
7
  * We use getDelayString inside it to make sure that RouteSchedule and
6
8
  * the map have the same values.
7
9
  */
8
10
  const getDelayTextForVehicle = (
11
+ trajectory: RealtimeTrajectory,
12
+ viewState: ViewState,
9
13
  delayInMs: number,
10
14
  cancelled = false,
11
15
  ): string => {