@geops/rvf-mobility-web-component 0.1.8
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/.fixpackrc +21 -0
- package/.husky/commit-msg +1 -0
- package/.husky/post-checkout +1 -0
- package/.husky/post-merge +1 -0
- package/.husky/post-rebase +1 -0
- package/.husky/pre-commit +1 -0
- package/.lintstagedrc.js +12 -0
- package/.nvmrc +1 -0
- package/.prettierrc.js +1 -0
- package/CHANGELOG.md +183 -0
- package/Logo.svg +10 -0
- package/README.md +179 -0
- package/__mocks__/dataurl.js +11 -0
- package/__mocks__/mapbox-gl.js +19 -0
- package/commitlint.config.cjs +1 -0
- package/doc/.eslintrc.json +3 -0
- package/doc/.fixpackrc +21 -0
- package/doc/.prettierrc +1 -0
- package/doc/README.md +14 -0
- package/doc/declarations.d.ts +6 -0
- package/doc/next.config.mjs +4 -0
- package/doc/package.json +43 -0
- package/doc/postcss.config.mjs +8 -0
- package/doc/public/README.md +1 -0
- package/doc/src/app/components/GeopsAPIKeyLink.tsx +6 -0
- package/doc/src/app/components/GeopsAPIsLink.tsx +6 -0
- package/doc/src/app/components/GeopsMapsAPILink.tsx +8 -0
- package/doc/src/app/components/GeopsMobility.tsx +9 -0
- package/doc/src/app/components/GeopsMobilityDoc.tsx +231 -0
- package/doc/src/app/components/GeopsMobilitySearch.tsx +10 -0
- package/doc/src/app/components/GeopsMobilitySearchDoc.tsx +129 -0
- package/doc/src/app/components/GeopsRealtimeAPILink.tsx +10 -0
- package/doc/src/app/components/GeopsStopsAPILink.tsx +8 -0
- package/doc/src/app/components/Link.tsx +9 -0
- package/doc/src/app/components/WebComponentDoc.tsx +296 -0
- package/doc/src/app/favicon.ico +0 -0
- package/doc/src/app/geops-mobility/page.tsx +6 -0
- package/doc/src/app/geops-mobility-search/page.tsx +7 -0
- package/doc/src/app/globals.css +38 -0
- package/doc/src/app/hooks/useAttrFromUrlParams.ts +21 -0
- package/doc/src/app/hooks/useIsFullScreen.ts +14 -0
- package/doc/src/app/hooks/usePublicKey.ts +21 -0
- package/doc/src/app/layout.tsx +51 -0
- package/doc/src/app/page.tsx +86 -0
- package/doc/src/geops-ui.ts +3 -0
- package/doc/tailwind.config.ts +20 -0
- package/doc/tsconfig.json +40 -0
- package/eslint.config.mjs +40 -0
- package/favicon.ico +0 -0
- package/global.d.ts +4 -0
- package/iframe.html +34 -0
- package/index.html +276 -0
- package/index.js +2162 -0
- package/input.css +34 -0
- package/jest-setup.js +4 -0
- package/jest.config.js +17 -0
- package/package.json +80 -0
- package/scripts/build.mjs +16 -0
- package/scripts/dev.mjs +26 -0
- package/search.html +144 -0
- package/src/BaseLayer/BaseLayer.tsx +36 -0
- package/src/BaseLayer/index.tsx +1 -0
- package/src/Copyright/Copyright.tsx +54 -0
- package/src/Copyright/index.css +3 -0
- package/src/Copyright/index.tsx +1 -0
- package/src/DebugDeparture/DebugDeparture.tsx +116 -0
- package/src/DebugDeparture/index.tsx +1 -0
- package/src/DebugStop/DebugStop.tsx +47 -0
- package/src/DebugStop/index.tsx +1 -0
- package/src/Departure/Departure.tsx +55 -0
- package/src/Departure/index.tsx +1 -0
- package/src/GeolocationButton/GeolocationButton.tsx +81 -0
- package/src/GeolocationButton/index.tsx +1 -0
- package/src/Map/Map.tsx +89 -0
- package/src/Map/index.tsx +1 -0
- package/src/MobilityMap/MobilityMap.tsx +259 -0
- package/src/MobilityMap/index.css +13 -0
- package/src/MobilityMap/index.tsx +1 -0
- package/src/NotificationLayer/NotificationLayer.tsx +156 -0
- package/src/NotificationLayer/index.tsx +1 -0
- package/src/NotificationLayer/notificationUtils.ts +191 -0
- package/src/Overlay/Overlay.tsx +57 -0
- package/src/Overlay/index.tsx +1 -0
- package/src/RealtimeLayer/RealtimeLayer.tsx +230 -0
- package/src/RealtimeLayer/index.tsx +1 -0
- package/src/RouteDestination/RouteDestination.test.tsx +13 -0
- package/src/RouteDestination/RouteDestination.tsx +15 -0
- package/src/RouteDestination/index.tsx +1 -0
- package/src/RouteIcon/RouteIcon.tsx +66 -0
- package/src/RouteIcon/index.tsx +1 -0
- package/src/RouteIdentifier/RouteIdentifer.tsx +35 -0
- package/src/RouteIdentifier/index.tsx +1 -0
- package/src/RouteInfos/RouteInfos.tsx +22 -0
- package/src/RouteInfos/index.tsx +1 -0
- package/src/RouteSchedule/RouteSchedule.tsx +69 -0
- package/src/RouteSchedule/firstStation.png +0 -0
- package/src/RouteSchedule/index.tsx +1 -0
- package/src/RouteSchedule/lastStation.png +0 -0
- package/src/RouteSchedule/line.png +0 -0
- package/src/RouteSchedule/station.png +0 -0
- package/src/RouteScheduleFooter/RouteScheduleFooter.tsx +44 -0
- package/src/RouteScheduleFooter/index.tsx +1 -0
- package/src/RouteScheduleHeader/RouteScheduleHeader.tsx +58 -0
- package/src/RouteScheduleHeader/index.tsx +1 -0
- package/src/RouteStop/RouteStop.tsx +121 -0
- package/src/RouteStop/index.tsx +1 -0
- package/src/RouteStopDelay/RouteStopDelay.tsx +36 -0
- package/src/RouteStopDelay/index.tsx +1 -0
- package/src/RouteStopName/RouteStopName.tsx +24 -0
- package/src/RouteStopName/index.tsx +1 -0
- package/src/RouteStopPlatform/RouteStopPlatform.tsx +29 -0
- package/src/RouteStopPlatform/index.tsx +1 -0
- package/src/RouteStopProgress/RouteStopProgress.tsx +101 -0
- package/src/RouteStopProgress/index.tsx +1 -0
- package/src/RouteStopServices/RouteStopServices.tsx +26 -0
- package/src/RouteStopServices/index.tsx +1 -0
- package/src/RouteStopStation/RouteStopStation.tsx +32 -0
- package/src/RouteStopStation/index.tsx +1 -0
- package/src/RouteStopTime/RouteStopTime.tsx +34 -0
- package/src/RouteStopTime/index.tsx +1 -0
- package/src/RvfMobilityMap/RvfMobilityMap.tsx +245 -0
- package/src/RvfMobilityMap/index.css +13 -0
- package/src/RvfMobilityMap/index.tsx +1 -0
- package/src/ScaleLine/ScaleLine.tsx +51 -0
- package/src/ScaleLine/index.css +6 -0
- package/src/ScaleLine/index.tsx +1 -0
- package/src/ScrollableHandler/ScrollableHandler.tsx +65 -0
- package/src/ScrollableHandler/index.tsx +1 -0
- package/src/Search/Search.tsx +18 -0
- package/src/Search/index.tsx +1 -0
- package/src/SingleClickListener/SingleClickListener.tsx +103 -0
- package/src/SingleClickListener/index.tsx +1 -0
- package/src/Station/Station.tsx +68 -0
- package/src/Station/index.tsx +1 -0
- package/src/StationHeader/StationHeader.tsx +32 -0
- package/src/StationHeader/index.tsx +1 -0
- package/src/StationName/StationName.tsx +21 -0
- package/src/StationName/index.tsx +1 -0
- package/src/StationServices/StationServices.tsx +80 -0
- package/src/StationServices/index.tsx +1 -0
- package/src/StationsLayer/StationsLayer.tsx +41 -0
- package/src/StationsLayer/index.tsx +1 -0
- package/src/StopsSearch/StopsSearch.tsx +254 -0
- package/src/StopsSearch/index.tsx +1 -0
- package/src/icons/Airport/Airport.tsx +17 -0
- package/src/icons/Airport/airport-14-svgrepo-com.svg +41 -0
- package/src/icons/Airport/index.tsx +1 -0
- package/src/icons/BarAndRestaurants/BarAndRestaurants.tsx +17 -0
- package/src/icons/BarAndRestaurants/food-restaurant-svgrepo-com.svg +12 -0
- package/src/icons/BarAndRestaurants/index.tsx +1 -0
- package/src/icons/Bathroom/Bathroom.tsx +59 -0
- package/src/icons/Bathroom/bathroom-restroom-svgrepo-com.svg +38 -0
- package/src/icons/Bathroom/index.tsx +1 -0
- package/src/icons/BikeStorage/BikeStorage.tsx +17 -0
- package/src/icons/BikeStorage/index.tsx +1 -0
- package/src/icons/BikeStorage/parking-bicycle-14-svgrepo-com.svg +41 -0
- package/src/icons/Elevator/Elevator.tsx +16 -0
- package/src/icons/Elevator/elevator-svgrepo-com.svg +2 -0
- package/src/icons/Elevator/index.tsx +1 -0
- package/src/icons/Police/Police.tsx +20 -0
- package/src/icons/Police/index.tsx +1 -0
- package/src/icons/Police/polizia.png +0 -0
- package/src/icons/README.md +52 -0
- package/src/icons/WaitingAreas/WaitingAreas.tsx +16 -0
- package/src/icons/WaitingAreas/highway-rest-area-svgrepo-com.svg +5 -0
- package/src/icons/WaitingAreas/index.tsx +1 -0
- package/src/icons/WaitingAreas/wheelchair-svgrepo-com.svg +2 -0
- package/src/icons/WheelChair/WheelChair.tsx +16 -0
- package/src/icons/WheelChair/disabili.png +0 -0
- package/src/icons/WheelChair/index.tsx +1 -0
- package/src/icons/WheelChair/wheelchair-svgrepo-com.svg +2 -0
- package/src/index.tsx +50 -0
- package/src/utils/MobilityEvent.ts +21 -0
- package/src/utils/addSourceAndLayers.ts +62 -0
- package/src/utils/centerOnStation.ts +17 -0
- package/src/utils/centerOnVehicle.ts +50 -0
- package/src/utils/getBgColor.ts +3 -0
- package/src/utils/getDelayColor.test.ts +20 -0
- package/src/utils/getDelayColor.ts +23 -0
- package/src/utils/getDelayColorForVehicle.test.ts +28 -0
- package/src/utils/getDelayColorForVehicle.ts +25 -0
- package/src/utils/getDelayFontForVehicle.test.ts +7 -0
- package/src/utils/getDelayFontForVehicle.tsx +8 -0
- package/src/utils/getDelayString.test.ts +22 -0
- package/src/utils/getDelayString.ts +28 -0
- package/src/utils/getDelayTextForVehicle.test.ts +28 -0
- package/src/utils/getDelayTextForVehicle.ts +21 -0
- package/src/utils/getFullTrajectoryAndFit.ts +40 -0
- package/src/utils/getHoursAndMinutes.test.ts +14 -0
- package/src/utils/getHoursAndMinutes.ts +22 -0
- package/src/utils/getMainColorForVehicle.test.ts +27 -0
- package/src/utils/getMainColorForVehicle.ts +46 -0
- package/src/utils/getStopStatus.test.ts +104 -0
- package/src/utils/getStopStatus.ts +171 -0
- package/src/utils/getTextFontForVehicle.test.ts +7 -0
- package/src/utils/getTextFontForVehicle.tsx +9 -0
- package/src/utils/getTextForVehicle.test.ts +17 -0
- package/src/utils/getTextForVehicle.ts +19 -0
- package/src/utils/hooks/useDebug.tsx +11 -0
- package/src/utils/hooks/useDeparture.tsx +23 -0
- package/src/utils/hooks/useI18n.tsx +20 -0
- package/src/utils/hooks/useMapContext.tsx +74 -0
- package/src/utils/hooks/useParams.ts +5 -0
- package/src/utils/hooks/useRouteStop.tsx +33 -0
- package/src/utils/hooks/useStation.tsx +21 -0
- package/src/utils/hooks/useUpdatePermalink.tsx +33 -0
- package/src/utils/hooks/useZoom.tsx +32 -0
- package/src/utils/i18n.ts +16 -0
- package/src/utils/translations.ts +31 -0
- package/tailwind.config.mjs +56 -0
- package/testNotification.json +50653 -0
- package/tsconfig.json +12 -0
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import getMainColorForVehicle from "./getMainColorForVehicle";
|
|
2
|
+
|
|
3
|
+
describe("getTextForVehicle", () => {
|
|
4
|
+
it("returns default rail color", () => {
|
|
5
|
+
expect(getMainColorForVehicle()).toBe("#ff8080");
|
|
6
|
+
expect(getMainColorForVehicle(null)).toBe("#ff8080");
|
|
7
|
+
expect(getMainColorForVehicle({ train_type: 178 })).toBe("#ff8080");
|
|
8
|
+
expect(getMainColorForVehicle({ vehicleType: 178 })).toBe("#ff8080");
|
|
9
|
+
});
|
|
10
|
+
|
|
11
|
+
it("returns color", () => {
|
|
12
|
+
expect(getMainColorForVehicle({ color: "foo" })).toBe("#foo");
|
|
13
|
+
expect(getMainColorForVehicle({ line: { color: "#foo" } })).toBe("#foo");
|
|
14
|
+
expect(
|
|
15
|
+
getMainColorForVehicle({ properties: { line: { color: "foo" } } }),
|
|
16
|
+
).toBe("#foo");
|
|
17
|
+
});
|
|
18
|
+
|
|
19
|
+
it("returns type color", () => {
|
|
20
|
+
expect(getMainColorForVehicle({ type: "tram" })).toBe("#ffb400");
|
|
21
|
+
expect(getMainColorForVehicle({ properties: { type: "tram" } })).toBe(
|
|
22
|
+
"#ffb400",
|
|
23
|
+
);
|
|
24
|
+
expect(getMainColorForVehicle({ vehicleType: 0 })).toBe("#ffb400");
|
|
25
|
+
expect(getMainColorForVehicle({ train_type: 0 })).toBe("#ffb400");
|
|
26
|
+
});
|
|
27
|
+
});
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
import {
|
|
2
|
+
RealtimeDeparture,
|
|
3
|
+
RealtimeLine,
|
|
4
|
+
RealtimeMot,
|
|
5
|
+
RealtimeStopSequence,
|
|
6
|
+
RealtimeTrajectory,
|
|
7
|
+
} from "mobility-toolbox-js/types";
|
|
8
|
+
|
|
9
|
+
import getBgColor from "./getBgColor";
|
|
10
|
+
|
|
11
|
+
// This function returns the main color of a line using a line, trajectory, stopsequence or departure object.
|
|
12
|
+
const getMainColorForVehicle = (object: unknown = null): string => {
|
|
13
|
+
let color =
|
|
14
|
+
(object as RealtimeTrajectory)?.properties?.line?.color ||
|
|
15
|
+
// @ts-expect-error bad type definition
|
|
16
|
+
(object as RealtimeStopSequence)?.line?.color ||
|
|
17
|
+
(object as RealtimeLine)?.color;
|
|
18
|
+
|
|
19
|
+
if (!color) {
|
|
20
|
+
let type: RealtimeMot =
|
|
21
|
+
(object as RealtimeTrajectory)?.properties?.type ||
|
|
22
|
+
(object as RealtimeStopSequence)?.type;
|
|
23
|
+
|
|
24
|
+
if (!type) {
|
|
25
|
+
let typeNumber: number = (object as RealtimeStopSequence)?.vehicleType;
|
|
26
|
+
|
|
27
|
+
if (!Number.isFinite(typeNumber)) {
|
|
28
|
+
typeNumber = (object as RealtimeDeparture)?.train_type;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
if (Number.isFinite(typeNumber)) {
|
|
32
|
+
type = typeNumber as unknown as RealtimeMot;
|
|
33
|
+
} else {
|
|
34
|
+
type = "rail";
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
color = getBgColor(type) || getBgColor("rail");
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
if (color && color[0] !== "#") {
|
|
41
|
+
color = `#${color}`;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
return color;
|
|
45
|
+
};
|
|
46
|
+
export default getMainColorForVehicle;
|
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
import getStopStatus from "./getStopStatus";
|
|
2
|
+
|
|
3
|
+
describe("getStopStatus", () => {
|
|
4
|
+
describe("when the station is empty", () => {
|
|
5
|
+
it("returns a correct status", () => {
|
|
6
|
+
const stopSequence = {
|
|
7
|
+
stations: [{}],
|
|
8
|
+
};
|
|
9
|
+
const {
|
|
10
|
+
// progress,
|
|
11
|
+
// isInBetween,
|
|
12
|
+
// isCancelled,
|
|
13
|
+
// isFirst,
|
|
14
|
+
// isLast,
|
|
15
|
+
// isPassed,
|
|
16
|
+
// isLeft,
|
|
17
|
+
// isNotRealtime,
|
|
18
|
+
isNotStop,
|
|
19
|
+
// isBoarding,
|
|
20
|
+
// isNextStop,
|
|
21
|
+
// isCloseToNextStop,
|
|
22
|
+
// @ts-expect-error bad type definition
|
|
23
|
+
} = getStopStatus(stopSequence, 0);
|
|
24
|
+
expect(isNotStop).toBe(true);
|
|
25
|
+
});
|
|
26
|
+
|
|
27
|
+
it("returns a correct isNotRealtime status", () => {
|
|
28
|
+
const stopSequence = {
|
|
29
|
+
stations: [
|
|
30
|
+
{ arrivalDelay: null, departureDelay: null, state: "PENDING" },
|
|
31
|
+
{ arrivalDelay: null, departureDelay: null, state: "TIME_BASED" },
|
|
32
|
+
{ arrivalDelay: 0, departureDelay: 0, state: "PENDING" },
|
|
33
|
+
{ arrivalDelay: 0, departureDelay: 0, state: "TIME_BASED" },
|
|
34
|
+
],
|
|
35
|
+
};
|
|
36
|
+
let {
|
|
37
|
+
isNotRealtime,
|
|
38
|
+
// @ts-expect-error bad type definition
|
|
39
|
+
} = getStopStatus(stopSequence, 0);
|
|
40
|
+
expect(isNotRealtime).toBe(true);
|
|
41
|
+
// @ts-expect-error bad type definition
|
|
42
|
+
isNotRealtime = getStopStatus(stopSequence, 1).isNotRealtime;
|
|
43
|
+
expect(isNotRealtime).toBe(true);
|
|
44
|
+
// @ts-expect-error bad type definition
|
|
45
|
+
isNotRealtime = getStopStatus(stopSequence, 2).isNotRealtime;
|
|
46
|
+
expect(isNotRealtime).toBe(false);
|
|
47
|
+
// @ts-expect-error bad type definition
|
|
48
|
+
isNotRealtime = getStopStatus(stopSequence, 3).isNotRealtime;
|
|
49
|
+
expect(isNotRealtime).toBe(true);
|
|
50
|
+
});
|
|
51
|
+
});
|
|
52
|
+
|
|
53
|
+
describe("when it's the first station", () => {
|
|
54
|
+
it("returns a correct status", () => {
|
|
55
|
+
const stopSequence = {
|
|
56
|
+
stations: [{}],
|
|
57
|
+
};
|
|
58
|
+
const {
|
|
59
|
+
// isInBetween,
|
|
60
|
+
// isCancelled,
|
|
61
|
+
isFirst,
|
|
62
|
+
// isLast,
|
|
63
|
+
// isPassed,
|
|
64
|
+
// isLeft,
|
|
65
|
+
// isNotRealtime,
|
|
66
|
+
// isNotStop,
|
|
67
|
+
// isBoarding,
|
|
68
|
+
isNextStop,
|
|
69
|
+
progress,
|
|
70
|
+
// isCloseToNextStop,
|
|
71
|
+
// @ts-expect-error bad type definition
|
|
72
|
+
} = getStopStatus(stopSequence, 0);
|
|
73
|
+
expect(progress).toBe(50);
|
|
74
|
+
expect(isFirst).toBe(true);
|
|
75
|
+
expect(isNextStop).toBe(true);
|
|
76
|
+
});
|
|
77
|
+
});
|
|
78
|
+
describe("when it's the first station", () => {
|
|
79
|
+
it("returns a correct status", () => {
|
|
80
|
+
const stopSequence = {
|
|
81
|
+
stations: [{}],
|
|
82
|
+
};
|
|
83
|
+
const {
|
|
84
|
+
// isInBetween,
|
|
85
|
+
// isCancelled,
|
|
86
|
+
isFirst,
|
|
87
|
+
// isBoarding,
|
|
88
|
+
isNextStop,
|
|
89
|
+
// isLast,
|
|
90
|
+
// isPassed,
|
|
91
|
+
// isLeft,
|
|
92
|
+
// isNotRealtime,
|
|
93
|
+
isNotStop,
|
|
94
|
+
progress,
|
|
95
|
+
// isCloseToNextStop,
|
|
96
|
+
// @ts-expect-error bad type definition
|
|
97
|
+
} = getStopStatus(stopSequence, 0);
|
|
98
|
+
expect(progress).toBe(50);
|
|
99
|
+
expect(isFirst).toBe(true);
|
|
100
|
+
expect(isNextStop).toBe(true);
|
|
101
|
+
expect(isNotStop).toBe(true);
|
|
102
|
+
});
|
|
103
|
+
});
|
|
104
|
+
});
|
|
@@ -0,0 +1,171 @@
|
|
|
1
|
+
import { RealtimeStopSequence } from "mobility-toolbox-js/types";
|
|
2
|
+
|
|
3
|
+
export interface StopStatus {
|
|
4
|
+
isBoarding?: boolean;
|
|
5
|
+
isCancelled?: boolean;
|
|
6
|
+
isCloseToNextStop?: boolean;
|
|
7
|
+
isFirst?: boolean;
|
|
8
|
+
isInBetween?: boolean;
|
|
9
|
+
isLast?: boolean;
|
|
10
|
+
isLeft?: boolean;
|
|
11
|
+
isNextStop?: boolean;
|
|
12
|
+
isNotRealtime?: boolean;
|
|
13
|
+
isNotStop?: boolean;
|
|
14
|
+
isPassed?: boolean;
|
|
15
|
+
progress?: number;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
const getBasicStatus = (stop, currTime, previousStop, nextStop) => {
|
|
19
|
+
let topBoundary = stop.arrivalTime;
|
|
20
|
+
let bottomBoundary = stop.arrivalTime;
|
|
21
|
+
|
|
22
|
+
if (previousStop) {
|
|
23
|
+
topBoundary =
|
|
24
|
+
stop.arrivalTime - (stop.arrivalTime - previousStop.arrivalTime) / 2;
|
|
25
|
+
}
|
|
26
|
+
if (nextStop) {
|
|
27
|
+
bottomBoundary =
|
|
28
|
+
stop.arrivalTime + (nextStop.arrivalTime - stop.arrivalTime) / 2;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
const isNotStop = !stop.arrivalTime && !stop.departureTime;
|
|
32
|
+
const hasNoDelays =
|
|
33
|
+
stop.arrivalDelay === null && stop.departureDelay === null;
|
|
34
|
+
const isNotRealtime = stop.state === "TIME_BASED" || hasNoDelays;
|
|
35
|
+
const isCancelled =
|
|
36
|
+
stop.state === "JOURNEY_CANCELLED" || stop.state === "STOP_CANCELLED";
|
|
37
|
+
const isBoarding = stop.state === "BOARDING";
|
|
38
|
+
|
|
39
|
+
const isPassedBottom = currTime > bottomBoundary;
|
|
40
|
+
|
|
41
|
+
const isLeft =
|
|
42
|
+
stop.state === "LEAVING" ||
|
|
43
|
+
(isNotRealtime && isPassedBottom) ||
|
|
44
|
+
(isCancelled && isPassedBottom);
|
|
45
|
+
|
|
46
|
+
return {
|
|
47
|
+
bottomBoundary,
|
|
48
|
+
isBoarding,
|
|
49
|
+
isCancelled,
|
|
50
|
+
isLeft,
|
|
51
|
+
isNotRealtime,
|
|
52
|
+
isNotStop,
|
|
53
|
+
isPassedBottom,
|
|
54
|
+
topBoundary,
|
|
55
|
+
};
|
|
56
|
+
};
|
|
57
|
+
|
|
58
|
+
/**
|
|
59
|
+
* This function provide an object with some informations about the status of the vehicle for this station.
|
|
60
|
+
* If the vehicul has already passed the sattion how far is it from the station, ...
|
|
61
|
+
*/
|
|
62
|
+
const getStopStatus = (
|
|
63
|
+
stopSequence: RealtimeStopSequence,
|
|
64
|
+
index: number,
|
|
65
|
+
): StopStatus => {
|
|
66
|
+
const { stations: stops } = stopSequence;
|
|
67
|
+
let progress = 0;
|
|
68
|
+
const previousStop = stops[index - 1];
|
|
69
|
+
const stop = stops[index];
|
|
70
|
+
const nextStop = stops[index + 1];
|
|
71
|
+
|
|
72
|
+
if (!stop) {
|
|
73
|
+
return {};
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
const currTime = Date.now();
|
|
77
|
+
const previousStopStatus =
|
|
78
|
+
previousStop &&
|
|
79
|
+
getBasicStatus(previousStop, currTime, stops[index - 2], stop);
|
|
80
|
+
const basicStatus = getBasicStatus(stop, currTime, previousStop, nextStop);
|
|
81
|
+
let { isLeft } = basicStatus;
|
|
82
|
+
const {
|
|
83
|
+
bottomBoundary,
|
|
84
|
+
isBoarding,
|
|
85
|
+
isCancelled,
|
|
86
|
+
isNotRealtime,
|
|
87
|
+
isNotStop,
|
|
88
|
+
isPassedBottom,
|
|
89
|
+
topBoundary,
|
|
90
|
+
} = basicStatus;
|
|
91
|
+
|
|
92
|
+
const isNextStop = !isLeft && (!previousStop || previousStopStatus.isLeft);
|
|
93
|
+
|
|
94
|
+
// The future stop is the stop after a stop with state === "BOARDING"
|
|
95
|
+
const isFutureStop = !isLeft && (!previousStop || !previousStopStatus.isLeft);
|
|
96
|
+
|
|
97
|
+
let isCloseToNextStop = false;
|
|
98
|
+
if (isNextStop) {
|
|
99
|
+
const timeGap = (bottomBoundary - topBoundary) * 0.2; // 20% of the duration between 2 stops
|
|
100
|
+
isCloseToNextStop = currTime > stop.arrivalTime - timeGap;
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
// Check if the stop has been passed
|
|
104
|
+
// For SBAHNMW-298
|
|
105
|
+
// Considerate the state ("LEAVING")
|
|
106
|
+
// which is important if the data has not been updated for a long time
|
|
107
|
+
let isPassed = isPassedBottom && !isNextStop && !isFutureStop;
|
|
108
|
+
|
|
109
|
+
if (topBoundary < currTime && currTime < bottomBoundary && !isFutureStop) {
|
|
110
|
+
progress = Math.round(
|
|
111
|
+
((currTime - topBoundary) * 100) / (bottomBoundary - topBoundary),
|
|
112
|
+
);
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
// For SBAHNMW-298
|
|
116
|
+
// Set the progress manually
|
|
117
|
+
// if the data has not been updated for a long time.
|
|
118
|
+
// These stops would have be passed if the data were up-to-date
|
|
119
|
+
if (isPassedBottom && (isNextStop || isFutureStop)) {
|
|
120
|
+
progress = 0;
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
// The first station is a special case because the top boundary starts at 50% of the div element.
|
|
124
|
+
const isFirstStation = index === 0;
|
|
125
|
+
|
|
126
|
+
if (isFirstStation) {
|
|
127
|
+
progress = 50;
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
if (isBoarding) {
|
|
131
|
+
// When the train has not left the first station the progress is 0.
|
|
132
|
+
progress = 50;
|
|
133
|
+
} else if (
|
|
134
|
+
!isFirstStation &&
|
|
135
|
+
((isLeft && progress < 50) || (!isLeft && progress > 50))
|
|
136
|
+
) {
|
|
137
|
+
// Here we ensure that isLeft and progress values are properly synced
|
|
138
|
+
// It doesn't apply to the the first station.
|
|
139
|
+
progress = 50;
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
// For SBAHNMW-210
|
|
143
|
+
// If some data (arrivalTime or state) from backend are wrong, 2 stops have the progress bar.
|
|
144
|
+
// So to avoid double progress display, we force the isPassed value to true.
|
|
145
|
+
if (progress > 50 && getStopStatus(stopSequence, index + 1).progress > 0) {
|
|
146
|
+
progress = 100;
|
|
147
|
+
isLeft = true;
|
|
148
|
+
isPassed = true;
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
if (isPassed) {
|
|
152
|
+
progress = 100;
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
return {
|
|
156
|
+
isBoarding,
|
|
157
|
+
isCancelled,
|
|
158
|
+
isCloseToNextStop,
|
|
159
|
+
isFirst: !previousStop,
|
|
160
|
+
isInBetween: !isPassed && progress > 0,
|
|
161
|
+
isLast: !nextStop,
|
|
162
|
+
isLeft,
|
|
163
|
+
isNextStop,
|
|
164
|
+
isNotRealtime,
|
|
165
|
+
isNotStop,
|
|
166
|
+
isPassed,
|
|
167
|
+
progress,
|
|
168
|
+
};
|
|
169
|
+
};
|
|
170
|
+
|
|
171
|
+
export default getStopStatus;
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Return the font for the delay text in the map.
|
|
3
|
+
*/
|
|
4
|
+
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
5
|
+
const getTextFontForVehicle = (fontSize: number, text?: string) => {
|
|
6
|
+
return `bold ${fontSize}px arial`;
|
|
7
|
+
};
|
|
8
|
+
|
|
9
|
+
export default getTextFontForVehicle;
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import getTextForVehicle from "./getTextForVehicle";
|
|
2
|
+
|
|
3
|
+
describe("getTextForVehicle", () => {
|
|
4
|
+
it("returns empty", () => {
|
|
5
|
+
expect(getTextForVehicle()).toBe("");
|
|
6
|
+
expect(getTextForVehicle(null)).toBe("");
|
|
7
|
+
});
|
|
8
|
+
|
|
9
|
+
it("returns name", () => {
|
|
10
|
+
expect(getTextForVehicle("name")).toBe("name");
|
|
11
|
+
expect(getTextForVehicle({ name: "name" })).toBe("name");
|
|
12
|
+
expect(getTextForVehicle({ line: { name: "name" } })).toBe("name");
|
|
13
|
+
expect(getTextForVehicle({ properties: { line: { name: "name" } } })).toBe(
|
|
14
|
+
"name",
|
|
15
|
+
);
|
|
16
|
+
});
|
|
17
|
+
});
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import {
|
|
2
|
+
RealtimeLine,
|
|
3
|
+
RealtimeStopSequence,
|
|
4
|
+
RealtimeTrajectory,
|
|
5
|
+
} from "mobility-toolbox-js/types";
|
|
6
|
+
|
|
7
|
+
const getTextForVehicle = (object: unknown = ""): string => {
|
|
8
|
+
const name =
|
|
9
|
+
(object as RealtimeTrajectory)?.properties?.line?.name ||
|
|
10
|
+
// @ts-expect-error bad type definition
|
|
11
|
+
(object as RealtimeStopSequence)?.line?.name ||
|
|
12
|
+
(object as RealtimeLine)?.name ||
|
|
13
|
+
(object as string) ||
|
|
14
|
+
"";
|
|
15
|
+
|
|
16
|
+
return name;
|
|
17
|
+
};
|
|
18
|
+
|
|
19
|
+
export default getTextForVehicle;
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { useMemo } from "preact/hooks";
|
|
2
|
+
|
|
3
|
+
const useDebug = () => {
|
|
4
|
+
const debug = useMemo(() => {
|
|
5
|
+
const value = new URLSearchParams(window.location.search).get("debug");
|
|
6
|
+
return !value || value === "false" ? false : value;
|
|
7
|
+
}, []);
|
|
8
|
+
return debug;
|
|
9
|
+
};
|
|
10
|
+
|
|
11
|
+
export default useDebug;
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import { RealtimeDeparture } from "mobility-toolbox-js/types";
|
|
2
|
+
import { createContext } from "preact";
|
|
3
|
+
import { useContext } from "preact/hooks";
|
|
4
|
+
|
|
5
|
+
export type DepartureContextType = {
|
|
6
|
+
index?: number;
|
|
7
|
+
departure?: RealtimeDeparture;
|
|
8
|
+
};
|
|
9
|
+
|
|
10
|
+
export const DepartureContext = createContext({
|
|
11
|
+
index: null,
|
|
12
|
+
departure: null,
|
|
13
|
+
} as DepartureContextType);
|
|
14
|
+
|
|
15
|
+
const useDeparture = (): DepartureContextType => {
|
|
16
|
+
const context = useContext<DepartureContextType>(DepartureContext);
|
|
17
|
+
if (!context) {
|
|
18
|
+
throw new Error("useDeparture must be used within a ContextProvider");
|
|
19
|
+
}
|
|
20
|
+
return context;
|
|
21
|
+
};
|
|
22
|
+
|
|
23
|
+
export default useDeparture;
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import { createContext } from "preact";
|
|
2
|
+
import { useContext } from "preact/hooks";
|
|
3
|
+
|
|
4
|
+
export type I18NContextType = {
|
|
5
|
+
t: (id: string, templateValues?: { [key: string]: string }) => string;
|
|
6
|
+
};
|
|
7
|
+
|
|
8
|
+
export const I18nContext = createContext({
|
|
9
|
+
t: (id, templateValues) => `${id} ${JSON.stringify(templateValues)}`,
|
|
10
|
+
} as I18NContextType);
|
|
11
|
+
|
|
12
|
+
const useI18n = (): I18NContextType => {
|
|
13
|
+
const context = useContext<I18NContextType>(I18nContext);
|
|
14
|
+
if (!context) {
|
|
15
|
+
throw new Error("useI18n must be used within a ContextProvider");
|
|
16
|
+
}
|
|
17
|
+
return context;
|
|
18
|
+
};
|
|
19
|
+
|
|
20
|
+
export default useI18n;
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
/* eslint-disable @typescript-eslint/no-empty-function */
|
|
2
|
+
import {
|
|
3
|
+
MaplibreLayer,
|
|
4
|
+
MaplibreStyleLayer,
|
|
5
|
+
RealtimeLayer,
|
|
6
|
+
} from "mobility-toolbox-js/ol";
|
|
7
|
+
import {
|
|
8
|
+
RealtimeStation,
|
|
9
|
+
RealtimeStationId,
|
|
10
|
+
RealtimeStopSequence,
|
|
11
|
+
RealtimeTrainId,
|
|
12
|
+
} from "mobility-toolbox-js/types";
|
|
13
|
+
import { Map } from "ol";
|
|
14
|
+
import { createContext } from "preact";
|
|
15
|
+
/* eslint-disable @typescript-eslint/no-unused-vars */
|
|
16
|
+
import { useContext } from "preact/hooks";
|
|
17
|
+
|
|
18
|
+
import type { MobilityMapProps } from "../../MobilityMap/MobilityMap";
|
|
19
|
+
|
|
20
|
+
export type MapContextType = {
|
|
21
|
+
baseLayer: MaplibreLayer;
|
|
22
|
+
isFollowing: boolean;
|
|
23
|
+
isTracking: boolean;
|
|
24
|
+
map: Map;
|
|
25
|
+
realtimeLayer: RealtimeLayer;
|
|
26
|
+
setBaseLayer: (baseLayer: MaplibreLayer) => void;
|
|
27
|
+
setIsFollowing: (isFollowing: boolean) => void;
|
|
28
|
+
setIsTracking: (isTracking: boolean) => void;
|
|
29
|
+
setMap: (map?: Map) => void;
|
|
30
|
+
setRealtimeLayer: (realtimeLayer?: RealtimeLayer) => void;
|
|
31
|
+
setStation: (station?: RealtimeStation) => void;
|
|
32
|
+
setStationId: (stationId?: RealtimeStationId) => void;
|
|
33
|
+
setStationsLayer: (stationsLayer?: MaplibreStyleLayer) => void;
|
|
34
|
+
setStopSequence: (stopSequence?: RealtimeStopSequence) => void;
|
|
35
|
+
setTrainId: (trainId?: RealtimeTrainId) => void;
|
|
36
|
+
station: RealtimeStation;
|
|
37
|
+
stationId: RealtimeStationId;
|
|
38
|
+
stationsLayer: MaplibreStyleLayer;
|
|
39
|
+
stopSequence: RealtimeStopSequence;
|
|
40
|
+
trainId: RealtimeTrainId;
|
|
41
|
+
} & MobilityMapProps;
|
|
42
|
+
|
|
43
|
+
export const MapContext = createContext<MapContextType>({
|
|
44
|
+
baseLayer: null,
|
|
45
|
+
isFollowing: false,
|
|
46
|
+
isTracking: false,
|
|
47
|
+
map: null,
|
|
48
|
+
realtimeLayer: null,
|
|
49
|
+
setBaseLayer: (baseLayer?: MaplibreLayer) => {},
|
|
50
|
+
setIsFollowing: (isFollowing: boolean) => {},
|
|
51
|
+
setIsTracking: (isTracking: boolean) => {},
|
|
52
|
+
setMap: (map?: Map) => {},
|
|
53
|
+
setRealtimeLayer: (realtimeLayer?: RealtimeLayer) => {},
|
|
54
|
+
setStation: (station?: RealtimeStation) => {},
|
|
55
|
+
setStationId: (stationId?: RealtimeStationId) => {},
|
|
56
|
+
setStationsLayer: (stationsLayer?: MaplibreStyleLayer) => {},
|
|
57
|
+
setStopSequence: (stopSequence?: RealtimeStopSequence) => {},
|
|
58
|
+
setTrainId: (trainId?: RealtimeTrainId) => {},
|
|
59
|
+
station: null,
|
|
60
|
+
stationId: null,
|
|
61
|
+
stationsLayer: null,
|
|
62
|
+
stopSequence: null,
|
|
63
|
+
trainId: null,
|
|
64
|
+
} as MapContextType);
|
|
65
|
+
|
|
66
|
+
const useMapContext = (): MapContextType => {
|
|
67
|
+
const context = useContext<MapContextType>(MapContext);
|
|
68
|
+
if (!context) {
|
|
69
|
+
throw new Error("useMapContext must be used within a ContextProvider");
|
|
70
|
+
}
|
|
71
|
+
return context;
|
|
72
|
+
};
|
|
73
|
+
|
|
74
|
+
export default useMapContext;
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import { RealtimeStation, RealtimeStop } from "mobility-toolbox-js/types";
|
|
2
|
+
import { createContext } from "preact";
|
|
3
|
+
import { useContext } from "preact/hooks";
|
|
4
|
+
|
|
5
|
+
import { StopStatus } from "../getStopStatus";
|
|
6
|
+
|
|
7
|
+
export interface RouteStopContextType {
|
|
8
|
+
index?: number;
|
|
9
|
+
invertColor?: boolean;
|
|
10
|
+
station?: RealtimeStation;
|
|
11
|
+
status?: StopStatus;
|
|
12
|
+
stop?: {
|
|
13
|
+
platform?: string;
|
|
14
|
+
} & RealtimeStop;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
export const RouteStopContext = createContext({
|
|
18
|
+
index: null,
|
|
19
|
+
invertColor: null,
|
|
20
|
+
station: null,
|
|
21
|
+
status: null,
|
|
22
|
+
stop: null,
|
|
23
|
+
} as RouteStopContextType);
|
|
24
|
+
|
|
25
|
+
const useRouteStop = (): RouteStopContextType => {
|
|
26
|
+
const context = useContext<RouteStopContextType>(RouteStopContext);
|
|
27
|
+
if (!context) {
|
|
28
|
+
throw new Error("useRouteStop must be used within a ContextProvider");
|
|
29
|
+
}
|
|
30
|
+
return context;
|
|
31
|
+
};
|
|
32
|
+
|
|
33
|
+
export default useRouteStop;
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import { RealtimeStation } from "mobility-toolbox-js/types";
|
|
2
|
+
import { createContext } from "preact";
|
|
3
|
+
import { useContext } from "preact/hooks";
|
|
4
|
+
|
|
5
|
+
export interface StationContextType {
|
|
6
|
+
station?: RealtimeStation;
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
export const StationContext = createContext({
|
|
10
|
+
station: null,
|
|
11
|
+
} as StationContextType);
|
|
12
|
+
|
|
13
|
+
const useRouteStop = (): StationContextType => {
|
|
14
|
+
const context = useContext<StationContextType>(StationContext);
|
|
15
|
+
if (!context) {
|
|
16
|
+
throw new Error("useRouteStop must be used within a ContextProvider");
|
|
17
|
+
}
|
|
18
|
+
return context;
|
|
19
|
+
};
|
|
20
|
+
|
|
21
|
+
export default useRouteStop;
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import { Map } from "ol";
|
|
2
|
+
import { unByKey } from "ol/Observable";
|
|
3
|
+
import { useEffect, useState } from "preact/hooks";
|
|
4
|
+
|
|
5
|
+
const useUpdatePermalink = (map: Map, permalink: boolean) => {
|
|
6
|
+
const [x, setX] = useState<string>(null);
|
|
7
|
+
const [y, setY] = useState<string>(null);
|
|
8
|
+
const [z, setZ] = useState<string>(null);
|
|
9
|
+
|
|
10
|
+
useEffect(() => {
|
|
11
|
+
let listener;
|
|
12
|
+
if (map && permalink) {
|
|
13
|
+
listener = map.on("moveend", () => {
|
|
14
|
+
const urlParams = new URLSearchParams(window.location.search);
|
|
15
|
+
const newX = map.getView().getCenter()[0].toFixed(2);
|
|
16
|
+
const newY = map.getView().getCenter()[1].toFixed(2);
|
|
17
|
+
const newZ = map.getView().getZoom().toFixed(1);
|
|
18
|
+
setX(newX);
|
|
19
|
+
urlParams.set("x", newX);
|
|
20
|
+
setY(newY);
|
|
21
|
+
urlParams.set("y", newY);
|
|
22
|
+
setZ(newZ);
|
|
23
|
+
urlParams.set("z", newZ);
|
|
24
|
+
window.history.replaceState(null, null, `?${urlParams.toString()}`);
|
|
25
|
+
});
|
|
26
|
+
}
|
|
27
|
+
return () => {
|
|
28
|
+
unByKey(listener);
|
|
29
|
+
};
|
|
30
|
+
}, [map, permalink]);
|
|
31
|
+
return { x, y, z };
|
|
32
|
+
};
|
|
33
|
+
export default useUpdatePermalink;
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import { unByKey } from "ol/Observable";
|
|
2
|
+
import { useEffect, useState } from "preact/hooks";
|
|
3
|
+
|
|
4
|
+
import useMapContext from "./useMapContext";
|
|
5
|
+
|
|
6
|
+
const useZoom = () => {
|
|
7
|
+
const { map } = useMapContext();
|
|
8
|
+
const [zoom, setZoom] = useState(map?.getView()?.getZoom());
|
|
9
|
+
useEffect(() => {
|
|
10
|
+
if (!map) {
|
|
11
|
+
return;
|
|
12
|
+
}
|
|
13
|
+
let timeout;
|
|
14
|
+
const view = map.getView();
|
|
15
|
+
if (view) {
|
|
16
|
+
setZoom(view.getZoom());
|
|
17
|
+
}
|
|
18
|
+
const zoomListener = view.on("change:resolution", () => {
|
|
19
|
+
clearTimeout(timeout);
|
|
20
|
+
timeout = setTimeout(() => {
|
|
21
|
+
return setZoom(view.getZoom());
|
|
22
|
+
}, 150);
|
|
23
|
+
});
|
|
24
|
+
return () => {
|
|
25
|
+
clearTimeout(timeout);
|
|
26
|
+
unByKey(zoomListener);
|
|
27
|
+
};
|
|
28
|
+
}, [map]);
|
|
29
|
+
return zoom;
|
|
30
|
+
};
|
|
31
|
+
|
|
32
|
+
export default useZoom;
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import rosetta from "rosetta";
|
|
2
|
+
|
|
3
|
+
import translations from "./translations";
|
|
4
|
+
|
|
5
|
+
const i18n = rosetta(translations);
|
|
6
|
+
|
|
7
|
+
// Set current language to preferred browser language with fallback to english
|
|
8
|
+
i18n.locale(
|
|
9
|
+
navigator.languages // @ts-expect-error bad type definition
|
|
10
|
+
.find((l) => {
|
|
11
|
+
return i18n.table(l.split("-")[0]) !== undefined;
|
|
12
|
+
})
|
|
13
|
+
?.split("-")[0] || "en",
|
|
14
|
+
);
|
|
15
|
+
|
|
16
|
+
export default i18n;
|