@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,69 @@
|
|
|
1
|
+
import type { RealtimeStop } from "mobility-toolbox-js/types";
|
|
2
|
+
import type { JSX, PreactDOMAttributes } from "preact";
|
|
3
|
+
|
|
4
|
+
import { memo } from "preact/compat";
|
|
5
|
+
import { useEffect, useRef } from "preact/hooks";
|
|
6
|
+
|
|
7
|
+
import RouteScheduleFooter from "../RouteScheduleFooter";
|
|
8
|
+
import RouteScheduleHeader from "../RouteScheduleHeader";
|
|
9
|
+
import RouteStop from "../RouteStop";
|
|
10
|
+
import useMapContext from "../utils/hooks/useMapContext";
|
|
11
|
+
|
|
12
|
+
export type RouteScheduleProps = JSX.HTMLAttributes<HTMLDivElement> &
|
|
13
|
+
PreactDOMAttributes;
|
|
14
|
+
|
|
15
|
+
function RouteSchedule(props: RouteScheduleProps) {
|
|
16
|
+
const { stopSequence } = useMapContext();
|
|
17
|
+
const ref = useRef();
|
|
18
|
+
|
|
19
|
+
useEffect(() => {
|
|
20
|
+
const interval = window.setInterval(() => {
|
|
21
|
+
const elt = ref.current as HTMLDivElement;
|
|
22
|
+
if (!elt) {
|
|
23
|
+
return;
|
|
24
|
+
}
|
|
25
|
+
const nextStation = elt.querySelector("[data-station-passed=false]");
|
|
26
|
+
if (nextStation) {
|
|
27
|
+
nextStation.scrollIntoView({
|
|
28
|
+
behavior: "smooth",
|
|
29
|
+
});
|
|
30
|
+
}
|
|
31
|
+
clearInterval(interval);
|
|
32
|
+
}, 300);
|
|
33
|
+
return () => {
|
|
34
|
+
clearTimeout(interval);
|
|
35
|
+
};
|
|
36
|
+
// Scroll automatically when a new scroll infos is set.
|
|
37
|
+
}, [stopSequence]);
|
|
38
|
+
|
|
39
|
+
if (!stopSequence) {
|
|
40
|
+
return null;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
const { className } = props;
|
|
44
|
+
|
|
45
|
+
return (
|
|
46
|
+
<>
|
|
47
|
+
<RouteScheduleHeader />
|
|
48
|
+
<div className={className} ref={ref}>
|
|
49
|
+
{stopSequence.stations.map((stop: RealtimeStop, index: number) => {
|
|
50
|
+
const { arrivalTime, departureTime, stationId, stationName } = stop;
|
|
51
|
+
return (
|
|
52
|
+
<RouteStop
|
|
53
|
+
// Train line can go in circle so begin and end have the same id,
|
|
54
|
+
index={index}
|
|
55
|
+
// using the time in the key should fix the issue.
|
|
56
|
+
key={
|
|
57
|
+
(`${stationId}` || stationName) + arrivalTime + departureTime
|
|
58
|
+
}
|
|
59
|
+
stop={stop}
|
|
60
|
+
/>
|
|
61
|
+
);
|
|
62
|
+
})}
|
|
63
|
+
<RouteScheduleFooter />
|
|
64
|
+
</div>
|
|
65
|
+
</>
|
|
66
|
+
);
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
export default memo(RouteSchedule);
|
|
Binary file
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { default } from "./RouteSchedule";
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import { memo } from "preact/compat";
|
|
2
|
+
|
|
3
|
+
import useMapContext from "../utils/hooks/useMapContext";
|
|
4
|
+
|
|
5
|
+
const defaultRenderLink = (text: string, url: string) => {
|
|
6
|
+
return url ? (
|
|
7
|
+
<a
|
|
8
|
+
className="whitespace-normal underline"
|
|
9
|
+
href={url}
|
|
10
|
+
rel="noreferrer"
|
|
11
|
+
target="_blank"
|
|
12
|
+
>
|
|
13
|
+
{text}
|
|
14
|
+
</a>
|
|
15
|
+
) : (
|
|
16
|
+
text
|
|
17
|
+
);
|
|
18
|
+
};
|
|
19
|
+
|
|
20
|
+
function RouteScheduleFooter() {
|
|
21
|
+
const { stopSequence } = useMapContext();
|
|
22
|
+
if (!stopSequence.operator && !stopSequence.publisher) {
|
|
23
|
+
return null;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
return (
|
|
27
|
+
<>
|
|
28
|
+
<div className="m-4 mb-0 flex flex-wrap text-sm text-gray-500 ">
|
|
29
|
+
{stopSequence.operator &&
|
|
30
|
+
defaultRenderLink(stopSequence.operator, stopSequence.operatorUrl)}
|
|
31
|
+
{stopSequence.operator && stopSequence.publisher && <span> - </span>}
|
|
32
|
+
{stopSequence.publisher &&
|
|
33
|
+
defaultRenderLink(stopSequence.publisher, stopSequence.publisherUrl)}
|
|
34
|
+
{stopSequence.license && <span> (</span>}
|
|
35
|
+
{stopSequence.license &&
|
|
36
|
+
defaultRenderLink(stopSequence.license, stopSequence.licenseUrl)}
|
|
37
|
+
{stopSequence.license && ")"}
|
|
38
|
+
</div>
|
|
39
|
+
<div className="pointer-events-none sticky bottom-0 h-8 w-full bg-gradient-to-b from-transparent to-white" />
|
|
40
|
+
</>
|
|
41
|
+
);
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
export default memo(RouteScheduleFooter);
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { default } from "./RouteScheduleFooter";
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
import { memo } from "preact/compat";
|
|
2
|
+
|
|
3
|
+
import RouteIcon from "../RouteIcon";
|
|
4
|
+
import RouteInfos from "../RouteInfos";
|
|
5
|
+
import getBgColor from "../utils/getBgColor";
|
|
6
|
+
import useMapContext from "../utils/hooks/useMapContext";
|
|
7
|
+
|
|
8
|
+
function RouteScheduleHeader() {
|
|
9
|
+
const { isFollowing, setIsFollowing, stopSequence } = useMapContext();
|
|
10
|
+
const { stroke, text_color: textColor, type, vehicleType } = stopSequence;
|
|
11
|
+
const backgroundColor = stroke || getBgColor(type || vehicleType);
|
|
12
|
+
const color = textColor || "black";
|
|
13
|
+
return (
|
|
14
|
+
<div className="flex items-center gap-x-4 bg-slate-100 p-4">
|
|
15
|
+
<RouteIcon stopSequence={stopSequence} />
|
|
16
|
+
<RouteInfos className="flex grow flex-col" stopSequence={stopSequence} />
|
|
17
|
+
<button
|
|
18
|
+
className={`flex size-[38px] flex-none items-center justify-center rounded-full bg-white p-1.5 shadow-lg ${
|
|
19
|
+
isFollowing ? "animate-pulse" : ""
|
|
20
|
+
}`}
|
|
21
|
+
onClick={() => {
|
|
22
|
+
setIsFollowing(!isFollowing);
|
|
23
|
+
}}
|
|
24
|
+
style={{
|
|
25
|
+
/* stylelint-disable-next-line value-keyword-case */
|
|
26
|
+
backgroundColor: isFollowing ? backgroundColor : "white",
|
|
27
|
+
color: isFollowing ? color : "black",
|
|
28
|
+
}}
|
|
29
|
+
type="button"
|
|
30
|
+
>
|
|
31
|
+
<svg
|
|
32
|
+
fill="none"
|
|
33
|
+
height="24"
|
|
34
|
+
part="svg"
|
|
35
|
+
viewBox="0 0 14 14"
|
|
36
|
+
width="24"
|
|
37
|
+
xmlns="http://www.w3.org/2000/svg"
|
|
38
|
+
>
|
|
39
|
+
<path
|
|
40
|
+
clipRule="evenodd"
|
|
41
|
+
d="M7 0.333344C7.375 0.333344 7.66667 0.62501 7.66667 0.97921V2.35414C9.7292 2.66668 11.3333 4.27081 11.625 6.33334H13C13.375 6.33334 13.6667 6.62501 13.6667 7.00001C13.6667 7.37501 13.375 7.66668 13 7.66668H11.625C11.3333 9.70834 9.70833 11.3333 7.66667 11.625V13C7.66667 13.375 7.375 13.6667 7 13.6667C6.64587 13.6667 6.33333 13.375 6.33333 13V11.625C4.29167 11.3333 2.68747 9.70834 2.39587 7.66668H1C0.625 7.66668 0.333333 7.37501 0.333333 7.00001C0.333333 6.62501 0.625 6.33334 1 6.33334H2.39587C2.68747 4.27081 4.29167 2.66668 6.33333 2.35414V0.97921C6.33333 0.62501 6.64587 0.333344 7 0.333344ZM7 3.66668C5.16667 3.66668 3.66667 5.16668 3.66667 7.00001C3.66667 8.79168 5.08333 10.3125 7 10.3125C8.89587 10.3125 10.3333 8.81254 10.3333 7.00001C10.3333 5.16668 8.83333 3.66668 7 3.66668Z"
|
|
42
|
+
fill="currentColor"
|
|
43
|
+
fillRule="evenodd"
|
|
44
|
+
/>
|
|
45
|
+
<path
|
|
46
|
+
clipRule="evenodd"
|
|
47
|
+
d="M5.66667 7.00001C5.66667 6.27081 6.2708 5.66668 7 5.66668C7.7292 5.66668 8.33333 6.27081 8.33333 7.00001C8.33333 7.72921 7.7292 8.33334 7 8.33334C6.2708 8.33334 5.66667 7.72921 5.66667 7.00001Z"
|
|
48
|
+
fill="currentColor"
|
|
49
|
+
fillRule="evenodd"
|
|
50
|
+
part="circle"
|
|
51
|
+
/>
|
|
52
|
+
</svg>
|
|
53
|
+
</button>
|
|
54
|
+
</div>
|
|
55
|
+
);
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
export default memo(RouteScheduleHeader);
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { default } from "./RouteScheduleHeader";
|
|
@@ -0,0 +1,121 @@
|
|
|
1
|
+
import type { RealtimeStation, RealtimeStop } from "mobility-toolbox-js/types";
|
|
2
|
+
|
|
3
|
+
import { JSX, PreactDOMAttributes } from "preact";
|
|
4
|
+
import { memo } from "preact/compat";
|
|
5
|
+
import { useEffect, useMemo, useState } from "preact/hooks";
|
|
6
|
+
|
|
7
|
+
import DebugStop from "../DebugStop/DebugStop";
|
|
8
|
+
import RouteStopDelay from "../RouteStopDelay";
|
|
9
|
+
import RouteStopProgress from "../RouteStopProgress";
|
|
10
|
+
import RouteStopStation from "../RouteStopStation";
|
|
11
|
+
import RouteStopTime from "../RouteStopTime";
|
|
12
|
+
import getStopStatus from "../utils/getStopStatus";
|
|
13
|
+
import useMapContext from "../utils/hooks/useMapContext";
|
|
14
|
+
import { RouteStopContext } from "../utils/hooks/useRouteStop";
|
|
15
|
+
|
|
16
|
+
export type RouteScheduleStopProps = {
|
|
17
|
+
classNameGreyOut?: string;
|
|
18
|
+
index?: number;
|
|
19
|
+
invertColor?: boolean;
|
|
20
|
+
stop?: {
|
|
21
|
+
platform?: string;
|
|
22
|
+
} & RealtimeStop;
|
|
23
|
+
} & JSX.HTMLAttributes<HTMLButtonElement> &
|
|
24
|
+
PreactDOMAttributes;
|
|
25
|
+
|
|
26
|
+
function RouteStop({
|
|
27
|
+
children,
|
|
28
|
+
classNameGreyOut = "text-gray-600",
|
|
29
|
+
index,
|
|
30
|
+
invertColor = false,
|
|
31
|
+
stop,
|
|
32
|
+
...props
|
|
33
|
+
}: RouteScheduleStopProps) {
|
|
34
|
+
const { map, realtimeLayer, stopSequence } = useMapContext();
|
|
35
|
+
const {
|
|
36
|
+
// @ts-expect-error bad type definition
|
|
37
|
+
stopUID,
|
|
38
|
+
} = stop;
|
|
39
|
+
const [station, setStation] = useState<RealtimeStation>();
|
|
40
|
+
const [status, setStatus] = useState(getStopStatus(stopSequence, index));
|
|
41
|
+
|
|
42
|
+
useEffect(() => {
|
|
43
|
+
let interval = null;
|
|
44
|
+
setStatus(getStopStatus(stopSequence, index));
|
|
45
|
+
|
|
46
|
+
// To see the progress bar we have to update the status of the vehicle until we get the new stopSeqeunce.
|
|
47
|
+
if (status.isInBetween) {
|
|
48
|
+
interval = setInterval(() => {
|
|
49
|
+
setStatus(getStopStatus(stopSequence, index));
|
|
50
|
+
}, 1000);
|
|
51
|
+
}
|
|
52
|
+
return () => {
|
|
53
|
+
clearInterval(interval);
|
|
54
|
+
};
|
|
55
|
+
}, [index, status.isInBetween, stopSequence]);
|
|
56
|
+
|
|
57
|
+
useEffect(() => {
|
|
58
|
+
if (!stopUID || !realtimeLayer?.api) {
|
|
59
|
+
return;
|
|
60
|
+
}
|
|
61
|
+
const subscribe = async () => {
|
|
62
|
+
realtimeLayer?.api?.subscribe(`station ${stopUID}`, ({ content }) => {
|
|
63
|
+
if (content) {
|
|
64
|
+
setStation(content);
|
|
65
|
+
}
|
|
66
|
+
});
|
|
67
|
+
};
|
|
68
|
+
subscribe();
|
|
69
|
+
|
|
70
|
+
return () => {
|
|
71
|
+
setStation(null);
|
|
72
|
+
if (stopUID) {
|
|
73
|
+
realtimeLayer?.api?.unsubscribe(`station ${stopUID}`);
|
|
74
|
+
}
|
|
75
|
+
};
|
|
76
|
+
}, [stopUID, realtimeLayer?.api]);
|
|
77
|
+
|
|
78
|
+
const routeStopState = useMemo(() => {
|
|
79
|
+
return { index, invertColor, station, status, stop };
|
|
80
|
+
}, [stop, status, index, invertColor, station]);
|
|
81
|
+
|
|
82
|
+
let colorScheme = status.isPassed || status.isLeft ? classNameGreyOut : "";
|
|
83
|
+
|
|
84
|
+
if (invertColor) {
|
|
85
|
+
colorScheme =
|
|
86
|
+
status.isPassed || status.isLeft || status.isBoarding
|
|
87
|
+
? ""
|
|
88
|
+
: classNameGreyOut;
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
return (
|
|
92
|
+
<RouteStopContext.Provider value={routeStopState}>
|
|
93
|
+
<button
|
|
94
|
+
// max-h-[58px] because the svg showing the progress is 58px height.
|
|
95
|
+
className={`flex max-h-[58px] w-full scroll-mt-[50px] items-stretch rounded text-left hover:bg-slate-100 ${colorScheme}`}
|
|
96
|
+
data-station-passed={status.isPassed} // Use for auto scroll
|
|
97
|
+
onClick={() => {
|
|
98
|
+
if (stop.coordinate) {
|
|
99
|
+
map.getView().animate({
|
|
100
|
+
center: [stop.coordinate[0], stop.coordinate[1]],
|
|
101
|
+
zoom: map.getView().getZoom(),
|
|
102
|
+
});
|
|
103
|
+
}
|
|
104
|
+
}}
|
|
105
|
+
type="button"
|
|
106
|
+
{...props}
|
|
107
|
+
>
|
|
108
|
+
{children || (
|
|
109
|
+
<>
|
|
110
|
+
<RouteStopTime className="ml-4 flex w-10 shrink-0 flex-col justify-center text-xs" />
|
|
111
|
+
<RouteStopDelay className="flex w-8 shrink-0 flex-col justify-center text-[0.6rem]" />
|
|
112
|
+
<RouteStopProgress className="relative flex w-8 shrink-0 items-center" />
|
|
113
|
+
<RouteStopStation className="flex grow flex-col items-start justify-center pr-2 text-sm font-medium" />
|
|
114
|
+
</>
|
|
115
|
+
)}
|
|
116
|
+
</button>
|
|
117
|
+
<DebugStop />
|
|
118
|
+
</RouteStopContext.Provider>
|
|
119
|
+
);
|
|
120
|
+
}
|
|
121
|
+
export default memo(RouteStop);
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { default } from "./RouteStop";
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import { JSX, PreactDOMAttributes } from "preact";
|
|
2
|
+
import { memo } from "preact/compat";
|
|
3
|
+
|
|
4
|
+
import getDelayColor from "../utils/getDelayColor";
|
|
5
|
+
import getDelayString from "../utils/getDelayString";
|
|
6
|
+
import useRouteStop from "../utils/hooks/useRouteStop";
|
|
7
|
+
|
|
8
|
+
export type RouteStopDelayProps = JSX.HTMLAttributes<HTMLDivElement> &
|
|
9
|
+
PreactDOMAttributes;
|
|
10
|
+
|
|
11
|
+
function RouteStopDelay(props: RouteStopDelayProps) {
|
|
12
|
+
const { status, stop } = useRouteStop();
|
|
13
|
+
const { arrivalDelay, departureDelay } = stop;
|
|
14
|
+
|
|
15
|
+
const hideDelay =
|
|
16
|
+
status.isNotRealtime ||
|
|
17
|
+
status.isCancelled ||
|
|
18
|
+
status.isNotStop ||
|
|
19
|
+
status.isPassed;
|
|
20
|
+
|
|
21
|
+
return (
|
|
22
|
+
<div {...props}>
|
|
23
|
+
{hideDelay || arrivalDelay === null || status.isFirst ? null : (
|
|
24
|
+
<span style={{ color: getDelayColor(arrivalDelay) }}>
|
|
25
|
+
{`+${getDelayString(arrivalDelay)}`}
|
|
26
|
+
</span>
|
|
27
|
+
)}
|
|
28
|
+
{hideDelay || departureDelay === null || status.isLast ? null : (
|
|
29
|
+
<span style={{ color: getDelayColor(arrivalDelay) }}>
|
|
30
|
+
{`+${getDelayString(departureDelay)}`}
|
|
31
|
+
</span>
|
|
32
|
+
)}
|
|
33
|
+
</div>
|
|
34
|
+
);
|
|
35
|
+
}
|
|
36
|
+
export default memo(RouteStopDelay);
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { default } from "./RouteStopDelay";
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import type { JSX, PreactDOMAttributes } from "preact";
|
|
2
|
+
|
|
3
|
+
import { memo } from "preact/compat";
|
|
4
|
+
|
|
5
|
+
import RouteStopPlatform from "../RouteStopPlatform";
|
|
6
|
+
import useRouteStop from "../utils/hooks/useRouteStop";
|
|
7
|
+
|
|
8
|
+
export type RouteStopNameProps = JSX.HTMLAttributes<HTMLDivElement> &
|
|
9
|
+
PreactDOMAttributes;
|
|
10
|
+
|
|
11
|
+
function RouteStopName({ children, ...props }: RouteStopNameProps) {
|
|
12
|
+
const { stop } = useRouteStop();
|
|
13
|
+
const { stationName } = stop || {};
|
|
14
|
+
return (
|
|
15
|
+
<div {...props}>
|
|
16
|
+
{stationName}
|
|
17
|
+
<br />
|
|
18
|
+
<RouteStopPlatform className="rounded-sm bg-slate-100 px-0.5 py-px text-xs group-hover:bg-slate-50" />
|
|
19
|
+
{children}
|
|
20
|
+
</div>
|
|
21
|
+
);
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
export default memo(RouteStopName);
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { default } from "./RouteStopName";
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import type { JSX, PreactDOMAttributes } from "preact";
|
|
2
|
+
|
|
3
|
+
import { memo } from "preact/compat";
|
|
4
|
+
|
|
5
|
+
import useI18n from "../utils/hooks/useI18n";
|
|
6
|
+
import useMapContext from "../utils/hooks/useMapContext";
|
|
7
|
+
import useRouteStop from "../utils/hooks/useRouteStop";
|
|
8
|
+
|
|
9
|
+
export type RouteStopPlatformProps = JSX.HTMLAttributes<HTMLSpanElement> &
|
|
10
|
+
PreactDOMAttributes;
|
|
11
|
+
|
|
12
|
+
function RouteStopPlatform({ ...props }: RouteStopPlatformProps) {
|
|
13
|
+
const { stop } = useRouteStop();
|
|
14
|
+
const { stopSequence } = useMapContext();
|
|
15
|
+
const { type } = stopSequence;
|
|
16
|
+
const { t } = useI18n();
|
|
17
|
+
const { platform } = stop || {};
|
|
18
|
+
if (!platform) {
|
|
19
|
+
return null;
|
|
20
|
+
}
|
|
21
|
+
const translated = t(`platform_${type || "rail"}`);
|
|
22
|
+
return (
|
|
23
|
+
<span {...props}>
|
|
24
|
+
{translated || t(`platform_rail`)} {platform}
|
|
25
|
+
</span>
|
|
26
|
+
);
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
export default memo(RouteStopPlatform);
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { default } from "./RouteStopPlatform";
|
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
import { JSX, PreactDOMAttributes } from "preact";
|
|
2
|
+
import { memo } from "preact/compat";
|
|
3
|
+
|
|
4
|
+
import getMainColorForVehicle from "../utils/getMainColorForVehicle";
|
|
5
|
+
import useMapContext from "../utils/hooks/useMapContext";
|
|
6
|
+
import useRouteStop from "../utils/hooks/useRouteStop";
|
|
7
|
+
|
|
8
|
+
export type RouteStopProgressProps = {
|
|
9
|
+
svgProps?: JSX.HTMLAttributes<SVGElement> & PreactDOMAttributes;
|
|
10
|
+
} & JSX.HTMLAttributes<HTMLDivElement> &
|
|
11
|
+
PreactDOMAttributes;
|
|
12
|
+
|
|
13
|
+
function RouteStopProgress({ svgProps, ...props }: RouteStopProgressProps) {
|
|
14
|
+
const { stopSequence } = useMapContext();
|
|
15
|
+
const { invertColor, status } = useRouteStop();
|
|
16
|
+
const { isBoarding, isFirst, isLast, isLeft, isPassed, progress } = status;
|
|
17
|
+
const y1 = isFirst ? "50%" : "-100%";
|
|
18
|
+
const y2 = isLast ? "50%" : "100%";
|
|
19
|
+
const yDone = `${progress}%`;
|
|
20
|
+
|
|
21
|
+
const greyColor = "rgb(156, 163, 175)";
|
|
22
|
+
const lineColor = getMainColorForVehicle(stopSequence);
|
|
23
|
+
|
|
24
|
+
let colorScheme = isPassed ? greyColor : lineColor;
|
|
25
|
+
let invertColorScheme = isPassed ? lineColor : greyColor;
|
|
26
|
+
let progressDoneColor = greyColor;
|
|
27
|
+
|
|
28
|
+
if (invertColor) {
|
|
29
|
+
colorScheme = isPassed ? lineColor : greyColor;
|
|
30
|
+
invertColorScheme = isPassed ? greyColor : lineColor;
|
|
31
|
+
progressDoneColor = lineColor;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
const circleColor =
|
|
35
|
+
!isPassed && (isLeft || isBoarding) ? invertColorScheme : colorScheme;
|
|
36
|
+
|
|
37
|
+
return (
|
|
38
|
+
<div {...props}>
|
|
39
|
+
<svg
|
|
40
|
+
fill="none"
|
|
41
|
+
height="100%"
|
|
42
|
+
stroke={colorScheme}
|
|
43
|
+
width="16"
|
|
44
|
+
xmlns="http://www.w3.org/2000/svg"
|
|
45
|
+
{...svgProps}
|
|
46
|
+
>
|
|
47
|
+
{/* Circle used to display a black border */}
|
|
48
|
+
<circle cx="50%" cy="50%" r="5" stroke="black" strokeWidth="6" />
|
|
49
|
+
|
|
50
|
+
{/* Black line used to display a black border */}
|
|
51
|
+
<line
|
|
52
|
+
stroke="black"
|
|
53
|
+
strokeWidth="6"
|
|
54
|
+
x1="50%"
|
|
55
|
+
x2="50%"
|
|
56
|
+
y1={y1}
|
|
57
|
+
y2={y2}
|
|
58
|
+
/>
|
|
59
|
+
|
|
60
|
+
{/* Colored line (grey or color) */}
|
|
61
|
+
<line
|
|
62
|
+
stroke={progressDoneColor}
|
|
63
|
+
strokeWidth="4"
|
|
64
|
+
x1="50%"
|
|
65
|
+
x2="50%"
|
|
66
|
+
y1={y1}
|
|
67
|
+
y2={yDone}
|
|
68
|
+
/>
|
|
69
|
+
<line
|
|
70
|
+
strokeWidth="4"
|
|
71
|
+
x1="50%"
|
|
72
|
+
x2="50%"
|
|
73
|
+
y1={`${progress - 2}%`} // we use -2 because sometimes it could be a small gap between the previousline and this one
|
|
74
|
+
y2={y2}
|
|
75
|
+
/>
|
|
76
|
+
|
|
77
|
+
{/* Colored circle (grey or color) */}
|
|
78
|
+
<circle cx="50%" cy="50%" r="5" strokeWidth="4" />
|
|
79
|
+
<circle
|
|
80
|
+
className={isBoarding ? "animate-pulse" : ""}
|
|
81
|
+
cx="50%"
|
|
82
|
+
cy="50%"
|
|
83
|
+
r="5"
|
|
84
|
+
stroke={circleColor}
|
|
85
|
+
strokeWidth="4"
|
|
86
|
+
/>
|
|
87
|
+
|
|
88
|
+
{/* white circle with black border */}
|
|
89
|
+
<circle
|
|
90
|
+
cx="50%"
|
|
91
|
+
cy="50%"
|
|
92
|
+
fill="white"
|
|
93
|
+
r="3"
|
|
94
|
+
stroke="black"
|
|
95
|
+
strokeWidth="1"
|
|
96
|
+
/>
|
|
97
|
+
</svg>
|
|
98
|
+
</div>
|
|
99
|
+
);
|
|
100
|
+
}
|
|
101
|
+
export default memo(RouteStopProgress);
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { default } from "./RouteStopProgress";
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import type { JSX, PreactDOMAttributes } from "preact";
|
|
2
|
+
|
|
3
|
+
import { memo } from "preact/compat";
|
|
4
|
+
|
|
5
|
+
import StationServices from "../StationServices";
|
|
6
|
+
import useRouteStop from "../utils/hooks/useRouteStop";
|
|
7
|
+
|
|
8
|
+
export type RouteStopNameProps = JSX.HTMLAttributes<HTMLDivElement> &
|
|
9
|
+
PreactDOMAttributes;
|
|
10
|
+
|
|
11
|
+
function RouteStopServices(props: RouteStopNameProps) {
|
|
12
|
+
const { station } = useRouteStop();
|
|
13
|
+
if (!station) {
|
|
14
|
+
return null;
|
|
15
|
+
}
|
|
16
|
+
return (
|
|
17
|
+
<StationServices
|
|
18
|
+
accessibility
|
|
19
|
+
station={station}
|
|
20
|
+
{...props}
|
|
21
|
+
iconProps={{ width: 20 }}
|
|
22
|
+
/>
|
|
23
|
+
);
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
export default memo(RouteStopServices);
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { default } from "./RouteStopServices";
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import { JSX, PreactDOMAttributes } from "preact";
|
|
2
|
+
import { memo } from "preact/compat";
|
|
3
|
+
|
|
4
|
+
import RouteStopName from "../RouteStopName";
|
|
5
|
+
import RouteStopServices from "../RouteStopServices";
|
|
6
|
+
import useRouteStop from "../utils/hooks/useRouteStop";
|
|
7
|
+
|
|
8
|
+
export type RouteStopStationProps = {
|
|
9
|
+
classNameCancelled?: string;
|
|
10
|
+
} & JSX.HTMLAttributes<HTMLDivElement> &
|
|
11
|
+
PreactDOMAttributes;
|
|
12
|
+
|
|
13
|
+
function RouteStopStation({
|
|
14
|
+
children,
|
|
15
|
+
className = "flex items-center gap-2",
|
|
16
|
+
classNameCancelled = "text-red-600 line-through",
|
|
17
|
+
...props
|
|
18
|
+
}: RouteStopStationProps) {
|
|
19
|
+
const { status } = useRouteStop();
|
|
20
|
+
|
|
21
|
+
return (
|
|
22
|
+
<div
|
|
23
|
+
{...props}
|
|
24
|
+
className={`${className} ${status.isCancelled ? classNameCancelled : ""}`}
|
|
25
|
+
>
|
|
26
|
+
<RouteStopName />
|
|
27
|
+
<RouteStopServices className="flex flex-wrap gap-1" />
|
|
28
|
+
{children}
|
|
29
|
+
</div>
|
|
30
|
+
);
|
|
31
|
+
}
|
|
32
|
+
export default memo(RouteStopStation);
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { default } from "./RouteStopStation";
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import { JSX, PreactDOMAttributes } from "preact";
|
|
2
|
+
import { memo } from "preact/compat";
|
|
3
|
+
|
|
4
|
+
import getHoursAndMinutes from "../utils/getHoursAndMinutes";
|
|
5
|
+
import useRouteStop from "../utils/hooks/useRouteStop";
|
|
6
|
+
|
|
7
|
+
export type RouteStopTimeProps = JSX.HTMLAttributes<HTMLDivElement> &
|
|
8
|
+
PreactDOMAttributes;
|
|
9
|
+
|
|
10
|
+
function RouteStopTime(props: RouteStopTimeProps) {
|
|
11
|
+
const { status, stop } = useRouteStop();
|
|
12
|
+
const { isCancelled, isFirst, isLast } = status;
|
|
13
|
+
const { aimedArrivalTime, aimedDepartureTime } = stop;
|
|
14
|
+
|
|
15
|
+
return (
|
|
16
|
+
<div {...props}>
|
|
17
|
+
<span
|
|
18
|
+
className={`${isCancelled ? "text-red-600 line-through" : ""} ${
|
|
19
|
+
isFirst ? "hidden" : ""
|
|
20
|
+
}`}
|
|
21
|
+
>
|
|
22
|
+
{getHoursAndMinutes(aimedArrivalTime)}
|
|
23
|
+
</span>
|
|
24
|
+
<span
|
|
25
|
+
className={`${status.isCancelled ? "text-red-600 line-through" : ""} ${
|
|
26
|
+
isLast ? "hidden" : ""
|
|
27
|
+
}`}
|
|
28
|
+
>
|
|
29
|
+
{getHoursAndMinutes(aimedDepartureTime)}
|
|
30
|
+
</span>
|
|
31
|
+
</div>
|
|
32
|
+
);
|
|
33
|
+
}
|
|
34
|
+
export default memo(RouteStopTime);
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { default } from "./RouteStopTime";
|