@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.
Files changed (212) hide show
  1. package/.fixpackrc +21 -0
  2. package/.husky/commit-msg +1 -0
  3. package/.husky/post-checkout +1 -0
  4. package/.husky/post-merge +1 -0
  5. package/.husky/post-rebase +1 -0
  6. package/.husky/pre-commit +1 -0
  7. package/.lintstagedrc.js +12 -0
  8. package/.nvmrc +1 -0
  9. package/.prettierrc.js +1 -0
  10. package/CHANGELOG.md +183 -0
  11. package/Logo.svg +10 -0
  12. package/README.md +179 -0
  13. package/__mocks__/dataurl.js +11 -0
  14. package/__mocks__/mapbox-gl.js +19 -0
  15. package/commitlint.config.cjs +1 -0
  16. package/doc/.eslintrc.json +3 -0
  17. package/doc/.fixpackrc +21 -0
  18. package/doc/.prettierrc +1 -0
  19. package/doc/README.md +14 -0
  20. package/doc/declarations.d.ts +6 -0
  21. package/doc/next.config.mjs +4 -0
  22. package/doc/package.json +43 -0
  23. package/doc/postcss.config.mjs +8 -0
  24. package/doc/public/README.md +1 -0
  25. package/doc/src/app/components/GeopsAPIKeyLink.tsx +6 -0
  26. package/doc/src/app/components/GeopsAPIsLink.tsx +6 -0
  27. package/doc/src/app/components/GeopsMapsAPILink.tsx +8 -0
  28. package/doc/src/app/components/GeopsMobility.tsx +9 -0
  29. package/doc/src/app/components/GeopsMobilityDoc.tsx +231 -0
  30. package/doc/src/app/components/GeopsMobilitySearch.tsx +10 -0
  31. package/doc/src/app/components/GeopsMobilitySearchDoc.tsx +129 -0
  32. package/doc/src/app/components/GeopsRealtimeAPILink.tsx +10 -0
  33. package/doc/src/app/components/GeopsStopsAPILink.tsx +8 -0
  34. package/doc/src/app/components/Link.tsx +9 -0
  35. package/doc/src/app/components/WebComponentDoc.tsx +296 -0
  36. package/doc/src/app/favicon.ico +0 -0
  37. package/doc/src/app/geops-mobility/page.tsx +6 -0
  38. package/doc/src/app/geops-mobility-search/page.tsx +7 -0
  39. package/doc/src/app/globals.css +38 -0
  40. package/doc/src/app/hooks/useAttrFromUrlParams.ts +21 -0
  41. package/doc/src/app/hooks/useIsFullScreen.ts +14 -0
  42. package/doc/src/app/hooks/usePublicKey.ts +21 -0
  43. package/doc/src/app/layout.tsx +51 -0
  44. package/doc/src/app/page.tsx +86 -0
  45. package/doc/src/geops-ui.ts +3 -0
  46. package/doc/tailwind.config.ts +20 -0
  47. package/doc/tsconfig.json +40 -0
  48. package/eslint.config.mjs +40 -0
  49. package/favicon.ico +0 -0
  50. package/global.d.ts +4 -0
  51. package/iframe.html +34 -0
  52. package/index.html +276 -0
  53. package/index.js +2162 -0
  54. package/input.css +34 -0
  55. package/jest-setup.js +4 -0
  56. package/jest.config.js +17 -0
  57. package/package.json +80 -0
  58. package/scripts/build.mjs +16 -0
  59. package/scripts/dev.mjs +26 -0
  60. package/search.html +144 -0
  61. package/src/BaseLayer/BaseLayer.tsx +36 -0
  62. package/src/BaseLayer/index.tsx +1 -0
  63. package/src/Copyright/Copyright.tsx +54 -0
  64. package/src/Copyright/index.css +3 -0
  65. package/src/Copyright/index.tsx +1 -0
  66. package/src/DebugDeparture/DebugDeparture.tsx +116 -0
  67. package/src/DebugDeparture/index.tsx +1 -0
  68. package/src/DebugStop/DebugStop.tsx +47 -0
  69. package/src/DebugStop/index.tsx +1 -0
  70. package/src/Departure/Departure.tsx +55 -0
  71. package/src/Departure/index.tsx +1 -0
  72. package/src/GeolocationButton/GeolocationButton.tsx +81 -0
  73. package/src/GeolocationButton/index.tsx +1 -0
  74. package/src/Map/Map.tsx +89 -0
  75. package/src/Map/index.tsx +1 -0
  76. package/src/MobilityMap/MobilityMap.tsx +259 -0
  77. package/src/MobilityMap/index.css +13 -0
  78. package/src/MobilityMap/index.tsx +1 -0
  79. package/src/NotificationLayer/NotificationLayer.tsx +156 -0
  80. package/src/NotificationLayer/index.tsx +1 -0
  81. package/src/NotificationLayer/notificationUtils.ts +191 -0
  82. package/src/Overlay/Overlay.tsx +57 -0
  83. package/src/Overlay/index.tsx +1 -0
  84. package/src/RealtimeLayer/RealtimeLayer.tsx +230 -0
  85. package/src/RealtimeLayer/index.tsx +1 -0
  86. package/src/RouteDestination/RouteDestination.test.tsx +13 -0
  87. package/src/RouteDestination/RouteDestination.tsx +15 -0
  88. package/src/RouteDestination/index.tsx +1 -0
  89. package/src/RouteIcon/RouteIcon.tsx +66 -0
  90. package/src/RouteIcon/index.tsx +1 -0
  91. package/src/RouteIdentifier/RouteIdentifer.tsx +35 -0
  92. package/src/RouteIdentifier/index.tsx +1 -0
  93. package/src/RouteInfos/RouteInfos.tsx +22 -0
  94. package/src/RouteInfos/index.tsx +1 -0
  95. package/src/RouteSchedule/RouteSchedule.tsx +69 -0
  96. package/src/RouteSchedule/firstStation.png +0 -0
  97. package/src/RouteSchedule/index.tsx +1 -0
  98. package/src/RouteSchedule/lastStation.png +0 -0
  99. package/src/RouteSchedule/line.png +0 -0
  100. package/src/RouteSchedule/station.png +0 -0
  101. package/src/RouteScheduleFooter/RouteScheduleFooter.tsx +44 -0
  102. package/src/RouteScheduleFooter/index.tsx +1 -0
  103. package/src/RouteScheduleHeader/RouteScheduleHeader.tsx +58 -0
  104. package/src/RouteScheduleHeader/index.tsx +1 -0
  105. package/src/RouteStop/RouteStop.tsx +121 -0
  106. package/src/RouteStop/index.tsx +1 -0
  107. package/src/RouteStopDelay/RouteStopDelay.tsx +36 -0
  108. package/src/RouteStopDelay/index.tsx +1 -0
  109. package/src/RouteStopName/RouteStopName.tsx +24 -0
  110. package/src/RouteStopName/index.tsx +1 -0
  111. package/src/RouteStopPlatform/RouteStopPlatform.tsx +29 -0
  112. package/src/RouteStopPlatform/index.tsx +1 -0
  113. package/src/RouteStopProgress/RouteStopProgress.tsx +101 -0
  114. package/src/RouteStopProgress/index.tsx +1 -0
  115. package/src/RouteStopServices/RouteStopServices.tsx +26 -0
  116. package/src/RouteStopServices/index.tsx +1 -0
  117. package/src/RouteStopStation/RouteStopStation.tsx +32 -0
  118. package/src/RouteStopStation/index.tsx +1 -0
  119. package/src/RouteStopTime/RouteStopTime.tsx +34 -0
  120. package/src/RouteStopTime/index.tsx +1 -0
  121. package/src/RvfMobilityMap/RvfMobilityMap.tsx +245 -0
  122. package/src/RvfMobilityMap/index.css +13 -0
  123. package/src/RvfMobilityMap/index.tsx +1 -0
  124. package/src/ScaleLine/ScaleLine.tsx +51 -0
  125. package/src/ScaleLine/index.css +6 -0
  126. package/src/ScaleLine/index.tsx +1 -0
  127. package/src/ScrollableHandler/ScrollableHandler.tsx +65 -0
  128. package/src/ScrollableHandler/index.tsx +1 -0
  129. package/src/Search/Search.tsx +18 -0
  130. package/src/Search/index.tsx +1 -0
  131. package/src/SingleClickListener/SingleClickListener.tsx +103 -0
  132. package/src/SingleClickListener/index.tsx +1 -0
  133. package/src/Station/Station.tsx +68 -0
  134. package/src/Station/index.tsx +1 -0
  135. package/src/StationHeader/StationHeader.tsx +32 -0
  136. package/src/StationHeader/index.tsx +1 -0
  137. package/src/StationName/StationName.tsx +21 -0
  138. package/src/StationName/index.tsx +1 -0
  139. package/src/StationServices/StationServices.tsx +80 -0
  140. package/src/StationServices/index.tsx +1 -0
  141. package/src/StationsLayer/StationsLayer.tsx +41 -0
  142. package/src/StationsLayer/index.tsx +1 -0
  143. package/src/StopsSearch/StopsSearch.tsx +254 -0
  144. package/src/StopsSearch/index.tsx +1 -0
  145. package/src/icons/Airport/Airport.tsx +17 -0
  146. package/src/icons/Airport/airport-14-svgrepo-com.svg +41 -0
  147. package/src/icons/Airport/index.tsx +1 -0
  148. package/src/icons/BarAndRestaurants/BarAndRestaurants.tsx +17 -0
  149. package/src/icons/BarAndRestaurants/food-restaurant-svgrepo-com.svg +12 -0
  150. package/src/icons/BarAndRestaurants/index.tsx +1 -0
  151. package/src/icons/Bathroom/Bathroom.tsx +59 -0
  152. package/src/icons/Bathroom/bathroom-restroom-svgrepo-com.svg +38 -0
  153. package/src/icons/Bathroom/index.tsx +1 -0
  154. package/src/icons/BikeStorage/BikeStorage.tsx +17 -0
  155. package/src/icons/BikeStorage/index.tsx +1 -0
  156. package/src/icons/BikeStorage/parking-bicycle-14-svgrepo-com.svg +41 -0
  157. package/src/icons/Elevator/Elevator.tsx +16 -0
  158. package/src/icons/Elevator/elevator-svgrepo-com.svg +2 -0
  159. package/src/icons/Elevator/index.tsx +1 -0
  160. package/src/icons/Police/Police.tsx +20 -0
  161. package/src/icons/Police/index.tsx +1 -0
  162. package/src/icons/Police/polizia.png +0 -0
  163. package/src/icons/README.md +52 -0
  164. package/src/icons/WaitingAreas/WaitingAreas.tsx +16 -0
  165. package/src/icons/WaitingAreas/highway-rest-area-svgrepo-com.svg +5 -0
  166. package/src/icons/WaitingAreas/index.tsx +1 -0
  167. package/src/icons/WaitingAreas/wheelchair-svgrepo-com.svg +2 -0
  168. package/src/icons/WheelChair/WheelChair.tsx +16 -0
  169. package/src/icons/WheelChair/disabili.png +0 -0
  170. package/src/icons/WheelChair/index.tsx +1 -0
  171. package/src/icons/WheelChair/wheelchair-svgrepo-com.svg +2 -0
  172. package/src/index.tsx +50 -0
  173. package/src/utils/MobilityEvent.ts +21 -0
  174. package/src/utils/addSourceAndLayers.ts +62 -0
  175. package/src/utils/centerOnStation.ts +17 -0
  176. package/src/utils/centerOnVehicle.ts +50 -0
  177. package/src/utils/getBgColor.ts +3 -0
  178. package/src/utils/getDelayColor.test.ts +20 -0
  179. package/src/utils/getDelayColor.ts +23 -0
  180. package/src/utils/getDelayColorForVehicle.test.ts +28 -0
  181. package/src/utils/getDelayColorForVehicle.ts +25 -0
  182. package/src/utils/getDelayFontForVehicle.test.ts +7 -0
  183. package/src/utils/getDelayFontForVehicle.tsx +8 -0
  184. package/src/utils/getDelayString.test.ts +22 -0
  185. package/src/utils/getDelayString.ts +28 -0
  186. package/src/utils/getDelayTextForVehicle.test.ts +28 -0
  187. package/src/utils/getDelayTextForVehicle.ts +21 -0
  188. package/src/utils/getFullTrajectoryAndFit.ts +40 -0
  189. package/src/utils/getHoursAndMinutes.test.ts +14 -0
  190. package/src/utils/getHoursAndMinutes.ts +22 -0
  191. package/src/utils/getMainColorForVehicle.test.ts +27 -0
  192. package/src/utils/getMainColorForVehicle.ts +46 -0
  193. package/src/utils/getStopStatus.test.ts +104 -0
  194. package/src/utils/getStopStatus.ts +171 -0
  195. package/src/utils/getTextFontForVehicle.test.ts +7 -0
  196. package/src/utils/getTextFontForVehicle.tsx +9 -0
  197. package/src/utils/getTextForVehicle.test.ts +17 -0
  198. package/src/utils/getTextForVehicle.ts +19 -0
  199. package/src/utils/hooks/useDebug.tsx +11 -0
  200. package/src/utils/hooks/useDeparture.tsx +23 -0
  201. package/src/utils/hooks/useI18n.tsx +20 -0
  202. package/src/utils/hooks/useMapContext.tsx +74 -0
  203. package/src/utils/hooks/useParams.ts +5 -0
  204. package/src/utils/hooks/useRouteStop.tsx +33 -0
  205. package/src/utils/hooks/useStation.tsx +21 -0
  206. package/src/utils/hooks/useUpdatePermalink.tsx +33 -0
  207. package/src/utils/hooks/useZoom.tsx +32 -0
  208. package/src/utils/i18n.ts +16 -0
  209. package/src/utils/translations.ts +31 -0
  210. package/tailwind.config.mjs +56 -0
  211. package/testNotification.json +50653 -0
  212. package/tsconfig.json +12 -0
@@ -0,0 +1,80 @@
1
+ import type { JSX, PreactDOMAttributes } from "preact";
2
+
3
+ import { RealtimeStation } from "mobility-toolbox-js/types";
4
+ import { memo } from "preact/compat";
5
+
6
+ import Airport from "../icons/Airport";
7
+ import BarAndRestaurants from "../icons/BarAndRestaurants";
8
+ import Bathroom from "../icons/Bathroom";
9
+ import BikeStorage from "../icons/BikeStorage";
10
+ import Elevator from "../icons/Elevator";
11
+ import Police from "../icons/Police";
12
+ import WaitingAreas from "../icons/WaitingAreas";
13
+ import WheelChair from "../icons/WheelChair";
14
+
15
+ export type StationServicesProps = {
16
+ accessibility?: boolean;
17
+ airport?: boolean;
18
+ barAndRestaurants?: boolean;
19
+ bathroom?: boolean;
20
+ bikeStorage?: boolean;
21
+ elevator?: boolean;
22
+ iconProps?: JSX.HTMLAttributes<SVGElement> & PreactDOMAttributes;
23
+ police?: boolean;
24
+ station: RealtimeStation;
25
+ waitingAreas?: boolean;
26
+ } & JSX.HTMLAttributes<HTMLDivElement> &
27
+ PreactDOMAttributes;
28
+
29
+ function StationServices({
30
+ accessibility = false,
31
+ airport = false,
32
+ barAndRestaurants = false,
33
+ bathroom = false,
34
+ bikeStorage = false,
35
+ elevator = false,
36
+ iconProps = {},
37
+ police = false,
38
+ station,
39
+ waitingAreas = false,
40
+ ...props
41
+ }: StationServicesProps) {
42
+ if (!station) {
43
+ return null;
44
+ }
45
+
46
+ const {
47
+ hasAccessibility,
48
+ hasAirport,
49
+ // @ts-expect-error bad type definition
50
+ hasBarAndRestaurants,
51
+ // @ts-expect-error bad type definition
52
+ hasBathroom,
53
+ // @ts-expect-error bad type definition
54
+ hasBikeStorage,
55
+ hasElevator,
56
+ // @ts-expect-error bad type definition
57
+ hasPolice,
58
+ // @ts-expect-error bad type definition
59
+ hasWaitingAreas,
60
+ // hasZOB,
61
+ // isRailReplacement,
62
+ } = station.properties;
63
+
64
+ return (
65
+ <div {...props}>
66
+ {accessibility && hasAccessibility && <WheelChair {...iconProps} />}
67
+ {airport && hasAirport && <Airport {...iconProps} />}
68
+ {barAndRestaurants && hasBarAndRestaurants && (
69
+ <BarAndRestaurants {...iconProps} />
70
+ )}
71
+ {bathroom && hasBathroom && <Bathroom {...iconProps} />}
72
+ {bikeStorage && hasBikeStorage && <BikeStorage {...iconProps} />}
73
+ {elevator && hasElevator && <Elevator {...iconProps} />}
74
+ {police && hasPolice && <Police {...iconProps} />}
75
+ {waitingAreas && hasWaitingAreas && <WaitingAreas {...iconProps} />}
76
+ </div>
77
+ );
78
+ }
79
+
80
+ export default memo(StationServices);
@@ -0,0 +1 @@
1
+ export { default } from "./StationServices";
@@ -0,0 +1,41 @@
1
+ import { MaplibreStyleLayer } from "mobility-toolbox-js/ol";
2
+ import { MaplibreStyleLayerOptions } from "mobility-toolbox-js/ol/layers/MaplibreStyleLayer";
3
+ import { memo } from "preact/compat";
4
+ import { useEffect, useMemo } from "preact/hooks";
5
+
6
+ import useMapContext from "../utils/hooks/useMapContext";
7
+
8
+ function StationsLayer(props: MaplibreStyleLayerOptions) {
9
+ const { baseLayer, map, setStationsLayer } = useMapContext();
10
+
11
+ const layer = useMemo(() => {
12
+ if (!baseLayer) {
13
+ return null;
14
+ }
15
+ return new MaplibreStyleLayer({
16
+ layersFilter: ({ metadata }) => {
17
+ return metadata?.["tralis.variable"] === "station";
18
+ },
19
+ maplibreLayer: baseLayer,
20
+ ...(props || {}),
21
+ });
22
+ }, [baseLayer, props]);
23
+
24
+ useEffect(() => {
25
+ if (!map || !layer) {
26
+ return;
27
+ }
28
+
29
+ map.addLayer(layer);
30
+ setStationsLayer(layer);
31
+
32
+ return () => {
33
+ map.removeLayer(layer);
34
+ layer.detachFromMap();
35
+ };
36
+ }, [map, setStationsLayer, layer]);
37
+
38
+ return null; // <RegisterForSelectFeaturesOnClick />;
39
+ }
40
+
41
+ export default memo(StationsLayer);
@@ -0,0 +1 @@
1
+ export { default } from "./StationsLayer";
@@ -0,0 +1,254 @@
1
+ import debounce from "lodash.debounce";
2
+ import { StopsAPI } from "mobility-toolbox-js/api";
3
+ import { StopsResponse } from "mobility-toolbox-js/types";
4
+ import { JSX, PreactDOMAttributes } from "preact";
5
+ import { memo } from "preact/compat";
6
+ import {
7
+ useCallback,
8
+ useEffect,
9
+ useMemo,
10
+ useRef,
11
+ useState,
12
+ } from "preact/hooks";
13
+ import { FaSearch } from "react-icons/fa";
14
+ import { MdClose } from "react-icons/md";
15
+
16
+ // @ts-expect-error tailwind must be added for the search web component
17
+ import tailwind from "../style.css";
18
+ import i18n from "../utils/i18n";
19
+ import MobilityEvent from "../utils/MobilityEvent";
20
+
21
+ export type SearchProps = {
22
+ apikey: string;
23
+ bbox?: string;
24
+ countrycode?: string;
25
+ event?: string;
26
+ field?: string;
27
+ limit?: number;
28
+ mots?: string;
29
+ onselect?: (arg: StationFeature) => void;
30
+ params?: string; // JSONstring
31
+ prefagencies?: string;
32
+ reflocation?: string;
33
+ url: string;
34
+ } & JSX.HTMLAttributes<HTMLDivElement> &
35
+ PreactDOMAttributes;
36
+
37
+ export type StationFeature = StopsResponse["features"][0];
38
+
39
+ const getQueryForSelectedStation = (selectedStation: StationFeature) => {
40
+ return selectedStation.properties.name;
41
+ };
42
+
43
+ /**
44
+ * Input field to search for stations
45
+ *
46
+ * @fires stationselect
47
+ */
48
+ function StopsSearch({
49
+ apikey,
50
+ bbox,
51
+ countrycode,
52
+ event,
53
+ field,
54
+ limit,
55
+ mots,
56
+ onselect,
57
+ params,
58
+ prefagencies,
59
+ reflocation,
60
+ url = "https://api.geops.io/stops/v1/",
61
+ }: SearchProps) {
62
+ const { t } = i18n;
63
+ const [query, setQuery] = useState("");
64
+ const [selectedStation, setSelectedStation] = useState<StationFeature>();
65
+ const [results, setResults] = useState<StopsResponse["features"]>(undefined);
66
+ const myRef = useRef<HTMLDivElement>();
67
+
68
+ const api: StopsAPI = useMemo(() => {
69
+ return new StopsAPI({ apiKey: apikey, url: url });
70
+ }, [apikey, url]);
71
+
72
+ const dispatchEvent = useCallback(
73
+ (station?: StationFeature) => {
74
+ const customEvt = new MobilityEvent<StationFeature>(
75
+ event || "mwc:stopssearchselect",
76
+ station,
77
+ {
78
+ bubbles: true,
79
+ },
80
+ );
81
+ if (myRef.current) {
82
+ myRef.current.dispatchEvent(customEvt);
83
+ }
84
+ if (onselect && typeof onselect === "function") {
85
+ onselect(station);
86
+ }
87
+ },
88
+ [event, onselect],
89
+ );
90
+
91
+ const debouncedSearch = useMemo(() => {
92
+ let abortCtrl: AbortController;
93
+ return debounce(async (q) => {
94
+ abortCtrl?.abort();
95
+ abortCtrl = new AbortController();
96
+ api
97
+ .search(
98
+ {
99
+ bbox,
100
+ field,
101
+ limit,
102
+ mots,
103
+ prefagencies,
104
+ q,
105
+ ref_location: reflocation,
106
+ ...JSON.parse(params || "{}"),
107
+ },
108
+ { signal: abortCtrl.signal },
109
+ )
110
+ .then((res: StopsResponse) => {
111
+ setResults(
112
+ res.features.filter((f) => {
113
+ return !countrycode || f.properties?.country_code === countrycode;
114
+ }),
115
+ );
116
+ })
117
+ .catch((e) => {
118
+ // AbortError is expected
119
+ if (e.code !== 20) {
120
+ console.error("Failed to fetch stations", e);
121
+ return;
122
+ }
123
+ });
124
+ }, 150);
125
+ }, [
126
+ api,
127
+ bbox,
128
+ countrycode,
129
+ field,
130
+ limit,
131
+ mots,
132
+ params,
133
+ prefagencies,
134
+ reflocation,
135
+ ]);
136
+
137
+ useEffect(() => {
138
+ if (
139
+ selectedStation &&
140
+ query === getQueryForSelectedStation(selectedStation)
141
+ ) {
142
+ return;
143
+ }
144
+ if (!query) {
145
+ setSelectedStation(undefined);
146
+ return;
147
+ }
148
+ debouncedSearch(query);
149
+ return () => {
150
+ debouncedSearch.cancel();
151
+ };
152
+ }, [query, selectedStation, debouncedSearch]);
153
+
154
+ useEffect(() => {
155
+ setResults(undefined);
156
+
157
+ if (selectedStation) {
158
+ setQuery(getQueryForSelectedStation(selectedStation));
159
+ }
160
+
161
+ dispatchEvent(selectedStation);
162
+ }, [dispatchEvent, selectedStation]);
163
+
164
+ return (
165
+ <>
166
+ <style>{tailwind}</style>
167
+ {/* <div className="relative z-0 rounded-md bg-white" > */}
168
+ <div
169
+ className={
170
+ "flex h-16 items-center gap-4 rounded-md bg-white p-4 pt-3.5 shadow"
171
+ }
172
+ ref={myRef}
173
+ >
174
+ <div className={"flex items-center "}>
175
+ <FaSearch className="size-4" />
176
+ </div>
177
+ <div className={"flex grow overflow-hidden border-b-2 border-solid"}>
178
+ <input
179
+ autoComplete="off"
180
+ className="h-8 flex-1 outline-0 placeholder:text-zinc-400"
181
+ id="searchfield"
182
+ onChange={(event) => {
183
+ // @ts-expect-error target is missing
184
+ setQuery(event.target.value);
185
+ }}
186
+ onKeyUp={(event: JSX.TargetedKeyboardEvent<HTMLInputElement>) => {
187
+ if (event.key === "Enter") {
188
+ if (results?.length > 0) {
189
+ setSelectedStation(results[0]);
190
+ }
191
+ }
192
+ }}
193
+ placeholder={t("stops_search_placeholder")}
194
+ type="text"
195
+ value={query}
196
+ />
197
+ {query.length > 0 && (
198
+ <button
199
+ className="flex items-center"
200
+ onClick={() => {
201
+ setQuery("");
202
+ setResults(undefined);
203
+ }}
204
+ >
205
+ <MdClose className="size-4" />
206
+ </button>
207
+ )}
208
+ </div>
209
+ </div>
210
+
211
+ <div className="mt-[-4px] flex grow overflow-auto rounded-md rounded-t-none bg-white shadow">
212
+ {results && results.length === 0 && (
213
+ <div
214
+ className={
215
+ "flex grow gap-3 border border-solid p-3 pt-2 text-zinc-400"
216
+ }
217
+ style={{ border: 1 }}
218
+ >
219
+ <div className="size-6"></div>
220
+ <div>{t("no_stops_found")}</div>
221
+ </div>
222
+ )}
223
+ {results && results.length > 0 && (
224
+ <ul
225
+ className="grow rounded-md rounded-t-none border border-solid bg-white p-0 "
226
+ style={{ border: 1 }} // without this th ul is displayed 1 px on the right
227
+ >
228
+ {results?.map((station) => {
229
+ return (
230
+ <li
231
+ className="border-b border-dashed border-slate-300 p-3 last:border-0"
232
+ key={station.properties.uid}
233
+ >
234
+ <button
235
+ className="flex w-full items-center gap-3 text-left"
236
+ onClick={() => {
237
+ setSelectedStation(station);
238
+ }}
239
+ >
240
+ <div className="size-6"></div>
241
+ <div className="grow">{station.properties.name}</div>
242
+ </button>
243
+ </li>
244
+ );
245
+ })}
246
+ </ul>
247
+ )}
248
+ </div>
249
+ {/* </div> */}
250
+ </>
251
+ );
252
+ }
253
+
254
+ export default memo(StopsSearch);
@@ -0,0 +1 @@
1
+ export { default, SearchProps, StationFeature } from "./StopsSearch";
@@ -0,0 +1,17 @@
1
+ function Airport({ ...props }) {
2
+ return (
3
+ <svg
4
+ xmlns="http://www.w3.org/2000/svg"
5
+ version="1.1"
6
+ width="24"
7
+ height="24"
8
+ fill="currentColor"
9
+ viewBox="0 0 14 14"
10
+ {...props}
11
+ >
12
+ <path d="m 14,8 0,1 -6,-1 0,3 2,2 0,1 L 7,13 4,14 4,13 6,11 6,8 0,9 0,8 6,5 6,2 C 6,1 6.22222,0 7,0 7.77777,0 8,1 8,2 l 0,3 z" />
13
+ </svg>
14
+ );
15
+ }
16
+
17
+ export default Airport;
@@ -0,0 +1,41 @@
1
+ <?xml version="1.0" encoding="UTF-8" standalone="no"?>
2
+
3
+
4
+ <!-- Uploaded to: SVG Repo, www.svgrepo.com, Generator: SVG Repo Mixer Tools -->
5
+ <svg
6
+ xmlns:dc="http://purl.org/dc/elements/1.1/"
7
+ xmlns:cc="http://creativecommons.org/ns#"
8
+ xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
9
+ xmlns:svg="http://www.w3.org/2000/svg"
10
+ xmlns="http://www.w3.org/2000/svg"
11
+ version="1.1"
12
+ width="100%"
13
+ height="100%"
14
+ viewBox="0 0 14 14"
15
+ id="svg2">
16
+ <metadata
17
+ id="metadata8">
18
+ <rdf:RDF>
19
+ <cc:Work
20
+ rdf:about="">
21
+ <dc:format>image/svg+xml</dc:format>
22
+ <dc:type
23
+ rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
24
+ <dc:title></dc:title>
25
+ </cc:Work>
26
+ </rdf:RDF>
27
+ </metadata>
28
+ <defs
29
+ id="defs6" />
30
+ <rect
31
+ width="14"
32
+ height="14"
33
+ x="0"
34
+ y="0"
35
+ id="canvas"
36
+ style="fill:none;stroke:none;visibility:hidden" />
37
+ <path
38
+ d="m 14,8 0,1 -6,-1 0,3 2,2 0,1 L 7,13 4,14 4,13 6,11 6,8 0,9 0,8 6,5 6,2 C 6,1 6.22222,0 7,0 7.77777,0 8,1 8,2 l 0,3 z"
39
+ id="airport"
40
+ style="fill:#000000;fill-opacity:1;stroke:none" />
41
+ </svg>
@@ -0,0 +1 @@
1
+ export { default } from "./Airport";
@@ -0,0 +1,17 @@
1
+ function BarAndRestaurants({ ...props }) {
2
+ return (
3
+ <svg
4
+ fill="currentColor"
5
+ width="24px"
6
+ height="24px"
7
+ viewBox="0 -3.84 122.88 122.88"
8
+ version="1.1"
9
+ xmlns="http://www.w3.org/2000/svg"
10
+ {...props}
11
+ >
12
+ <path d="M29.03,100.46l20.79-25.21l9.51,12.13L41,110.69C33.98,119.61,20.99,110.21,29.03,100.46L29.03,100.46z M53.31,43.05 c1.98-6.46,1.07-11.98-6.37-20.18L28.76,1c-2.58-3.03-8.66,1.42-6.12,5.09L37.18,24c2.75,3.34-2.36,7.76-5.2,4.32L16.94,9.8 c-2.8-3.21-8.59,1.03-5.66,4.7c4.24,5.1,10.8,13.43,15.04,18.53c2.94,2.99-1.53,7.42-4.43,3.69L6.96,18.32 c-2.19-2.38-5.77-0.9-6.72,1.88c-1.02,2.97,1.49,5.14,3.2,7.34L20.1,49.06c5.17,5.99,10.95,9.54,17.67,7.53 c1.03-0.31,2.29-0.94,3.64-1.77l44.76,57.78c2.41,3.11,7.06,3.44,10.08,0.93l0.69-0.57c3.4-2.83,3.95-8,1.04-11.34L50.58,47.16 C51.96,45.62,52.97,44.16,53.31,43.05L53.31,43.05z M65.98,55.65l7.37-8.94C63.87,23.21,99-8.11,116.03,6.29 C136.72,23.8,105.97,66,84.36,55.57l-8.73,11.09L65.98,55.65L65.98,55.65z" />
13
+ </svg>
14
+ );
15
+ }
16
+
17
+ export default BarAndRestaurants;
@@ -0,0 +1,12 @@
1
+ <?xml version="1.0" encoding="utf-8"?>
2
+
3
+ <!-- Uploaded to: SVG Repo, www.svgrepo.com, Generator: SVG Repo Mixer Tools -->
4
+ <svg fill="#000000" width="800px" height="800px" viewBox="0 -3.84 122.88 122.88" version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" style="enable-background:new 0 0 122.88 115.21" xml:space="preserve">
5
+
6
+ <g>
7
+
8
+ <path d="M29.03,100.46l20.79-25.21l9.51,12.13L41,110.69C33.98,119.61,20.99,110.21,29.03,100.46L29.03,100.46z M53.31,43.05 c1.98-6.46,1.07-11.98-6.37-20.18L28.76,1c-2.58-3.03-8.66,1.42-6.12,5.09L37.18,24c2.75,3.34-2.36,7.76-5.2,4.32L16.94,9.8 c-2.8-3.21-8.59,1.03-5.66,4.7c4.24,5.1,10.8,13.43,15.04,18.53c2.94,2.99-1.53,7.42-4.43,3.69L6.96,18.32 c-2.19-2.38-5.77-0.9-6.72,1.88c-1.02,2.97,1.49,5.14,3.2,7.34L20.1,49.06c5.17,5.99,10.95,9.54,17.67,7.53 c1.03-0.31,2.29-0.94,3.64-1.77l44.76,57.78c2.41,3.11,7.06,3.44,10.08,0.93l0.69-0.57c3.4-2.83,3.95-8,1.04-11.34L50.58,47.16 C51.96,45.62,52.97,44.16,53.31,43.05L53.31,43.05z M65.98,55.65l7.37-8.94C63.87,23.21,99-8.11,116.03,6.29 C136.72,23.8,105.97,66,84.36,55.57l-8.73,11.09L65.98,55.65L65.98,55.65z"/>
9
+
10
+ </g>
11
+
12
+ </svg>
@@ -0,0 +1 @@
1
+ export { default } from "./BarAndRestaurants";
@@ -0,0 +1,59 @@
1
+ function Bathroom({ ...props }) {
2
+ return (
3
+ <svg
4
+ fill="currentColor"
5
+ height="24px"
6
+ width="24px"
7
+ version="1.1"
8
+ viewBox="0 0 512 512"
9
+ xmlns="http://www.w3.org/2000/svg"
10
+ {...props}
11
+ >
12
+ <g>
13
+ <g>
14
+ <path
15
+ d="M256,0c-9.217,0-16.696,7.473-16.696,16.696v478.609c0,9.223,7.479,16.696,16.696,16.696
16
+ c9.217,0,16.696-7.473,16.696-16.696V16.696C272.696,7.473,265.217,0,256,0z"
17
+ />
18
+ </g>
19
+ </g>
20
+ <g>
21
+ <g>
22
+ <path
23
+ d="M105.739,44.522c-36.826,0-66.783,29.956-66.783,66.783c0,36.826,29.956,66.783,66.783,66.783
24
+ s66.783-29.956,66.783-66.783C172.522,74.478,142.565,44.522,105.739,44.522z"
25
+ />
26
+ </g>
27
+ </g>
28
+ <g>
29
+ <g>
30
+ <path
31
+ d="M406.261,44.522c-36.826,0-66.783,29.956-66.783,66.783c0,36.826,29.956,66.783,66.783,66.783
32
+ s66.783-29.956,66.783-66.783C473.043,74.478,443.087,44.522,406.261,44.522z"
33
+ />
34
+ </g>
35
+ </g>
36
+ <g>
37
+ <g>
38
+ <path
39
+ d="M202.935,218.664c-3.109-4.501-8.239-7.186-13.717-7.186H22.261c-5.478,0-10.608,2.685-13.717,7.185
40
+ c-3.12,4.5-3.837,10.244-1.913,15.374l49.022,130.729v63.755c0,27.619,22.468,50.087,50.087,50.087s50.087-22.468,50.087-50.087
41
+ v-63.755l49.022-130.728C206.772,228.909,206.054,223.163,202.935,218.664z"
42
+ />
43
+ </g>
44
+ </g>
45
+ <g>
46
+ <g>
47
+ <path
48
+ d="M505.37,355.875l-50.087-133.565c-2.445-6.516-8.674-10.831-15.63-10.831H372.87c-6.957,0-13.185,4.315-15.63,10.831
49
+ l-50.087,133.565c-1.924,5.13-1.207,10.876,1.913,15.374c3.109,4.501,8.239,7.186,13.717,7.186h33.391v50.087
50
+ c0,27.619,22.468,50.087,50.087,50.087s50.087-22.468,50.087-50.087v-50.087h33.391c5.478,0,10.608-2.685,13.717-7.185
51
+ C506.576,366.75,507.293,361.006,505.37,355.875z"
52
+ />
53
+ </g>
54
+ </g>
55
+ </svg>
56
+ );
57
+ }
58
+
59
+ export default Bathroom;
@@ -0,0 +1,38 @@
1
+ <?xml version="1.0" encoding="iso-8859-1"?>
2
+ <!-- Uploaded to: SVG Repo, www.svgrepo.com, Generator: SVG Repo Mixer Tools -->
3
+ <svg fill="#000000" height="800px" width="800px" version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink"
4
+ viewBox="0 0 512 512" xml:space="preserve">
5
+ <g>
6
+ <g>
7
+ <path d="M256,0c-9.217,0-16.696,7.473-16.696,16.696v478.609c0,9.223,7.479,16.696,16.696,16.696
8
+ c9.217,0,16.696-7.473,16.696-16.696V16.696C272.696,7.473,265.217,0,256,0z"/>
9
+ </g>
10
+ </g>
11
+ <g>
12
+ <g>
13
+ <path d="M105.739,44.522c-36.826,0-66.783,29.956-66.783,66.783c0,36.826,29.956,66.783,66.783,66.783
14
+ s66.783-29.956,66.783-66.783C172.522,74.478,142.565,44.522,105.739,44.522z"/>
15
+ </g>
16
+ </g>
17
+ <g>
18
+ <g>
19
+ <path d="M406.261,44.522c-36.826,0-66.783,29.956-66.783,66.783c0,36.826,29.956,66.783,66.783,66.783
20
+ s66.783-29.956,66.783-66.783C473.043,74.478,443.087,44.522,406.261,44.522z"/>
21
+ </g>
22
+ </g>
23
+ <g>
24
+ <g>
25
+ <path d="M202.935,218.664c-3.109-4.501-8.239-7.186-13.717-7.186H22.261c-5.478,0-10.608,2.685-13.717,7.185
26
+ c-3.12,4.5-3.837,10.244-1.913,15.374l49.022,130.729v63.755c0,27.619,22.468,50.087,50.087,50.087s50.087-22.468,50.087-50.087
27
+ v-63.755l49.022-130.728C206.772,228.909,206.054,223.163,202.935,218.664z"/>
28
+ </g>
29
+ </g>
30
+ <g>
31
+ <g>
32
+ <path d="M505.37,355.875l-50.087-133.565c-2.445-6.516-8.674-10.831-15.63-10.831H372.87c-6.957,0-13.185,4.315-15.63,10.831
33
+ l-50.087,133.565c-1.924,5.13-1.207,10.876,1.913,15.374c3.109,4.501,8.239,7.186,13.717,7.186h33.391v50.087
34
+ c0,27.619,22.468,50.087,50.087,50.087s50.087-22.468,50.087-50.087v-50.087h33.391c5.478,0,10.608-2.685,13.717-7.185
35
+ C506.576,366.75,507.293,361.006,505.37,355.875z"/>
36
+ </g>
37
+ </g>
38
+ </svg>
@@ -0,0 +1 @@
1
+ export { default } from "./Bathroom";
@@ -0,0 +1,17 @@
1
+ function BikeStorage({ ...props }) {
2
+ return (
3
+ <svg
4
+ fill="currentColor"
5
+ height="24px"
6
+ version="1.1"
7
+ viewBox="0 0 14 14"
8
+ width="24px"
9
+ xmlns="http://www.w3.org/2000/svg"
10
+ {...props}
11
+ >
12
+ <path d="m 0,0 0,10 2.000077,0 0,-3.5 2.500097,0 c 1.976154,3e-7 3.500135,-1 3.500135,-3.25 C 8.000309,1 6.4871,2e-7 4.968942,0 z m 2.000077,2 2.500097,0 c 0.963608,0 1.500058,0.5254308 1.500058,1.25 0,0.7245692 -0.631804,1.25 -1.500058,1.25 l -2.500097,0 z M 9,7 9,7.5938559 9.500367,8 l 0,0.75 -3.000116,0 0,-0.25 0.50002,0 c 0.479412,0 0.455442,-0.5 0,-0.5 L 5.500213,8 c -0.50002,0 -0.50002,0.452061 0,0.5 l 0.500019,0 0,0.25 -0.562522,1.28125 C 5.296384,10.001436 5.147956,10 5.000193,10 c -1.02182,0 -2.000077,0.793929 -2.000077,2 0,1.206071 0.978257,2 2.000077,2 1.02182,0 1.937575,-0.856429 1.937575,-2.0625 0,-0.150759 -0.0039,-0.302075 -0.03125,-0.4375 l 0.843783,0 1.821945,-2 0.365639,0.78125 C 9.383285,10.615837 9.000348,11.229684 9.000348,12 c 0,1.206071 0.978257,2 2.000077,2 C 12.022246,14 12.938,13.143571 12.938,11.9375 12.938,10.731429 12.022246,10 11.000425,10 c -0.15008,0 -0.292278,0.02946 -0.437517,0.0625 L 10.000386,8.75 10,7.8 z m -2.593502,2.5 2.193834,0 -1.193796,1.25 -0.843782,0 C 6.422636,10.56342 6.258266,10.401358 6.062734,10.28125 z M 5.000193,11 c 0.635098,0 1.000039,0.549217 1.000039,1 0,0.450783 -0.364941,1 -1.000039,1 -0.635098,0 -1.000038,-0.549217 -1.000038,-1 0,-0.450783 0.36494,-1 1.000038,-1 z m 6.000232,0 c 0.635099,0 1.000039,0.549217 1.000039,1 0,0.450783 -0.36494,1 -1.000039,1 -0.635098,0 -1.000039,-0.549217 -1.000039,-1 0,-0.450783 0.364941,-1 1.000039,-1 z" />
13
+ </svg>
14
+ );
15
+ }
16
+
17
+ export default BikeStorage;
@@ -0,0 +1 @@
1
+ export { default } from "./BikeStorage";
@@ -0,0 +1,41 @@
1
+ <?xml version="1.0" encoding="UTF-8" standalone="no"?>
2
+
3
+
4
+ <!-- Uploaded to: SVG Repo, www.svgrepo.com, Generator: SVG Repo Mixer Tools -->
5
+ <svg
6
+ xmlns:dc="http://purl.org/dc/elements/1.1/"
7
+ xmlns:cc="http://creativecommons.org/ns#"
8
+ xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
9
+ xmlns:svg="http://www.w3.org/2000/svg"
10
+ xmlns="http://www.w3.org/2000/svg"
11
+ version="1.1"
12
+ width="100%"
13
+ height="100%"
14
+ viewBox="0 0 14 14"
15
+ id="svg2">
16
+ <metadata
17
+ id="metadata8">
18
+ <rdf:RDF>
19
+ <cc:Work
20
+ rdf:about="">
21
+ <dc:format>image/svg+xml</dc:format>
22
+ <dc:type
23
+ rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
24
+ <dc:title></dc:title>
25
+ </cc:Work>
26
+ </rdf:RDF>
27
+ </metadata>
28
+ <defs
29
+ id="defs6" />
30
+ <rect
31
+ width="14"
32
+ height="14"
33
+ x="0"
34
+ y="0"
35
+ id="canvas"
36
+ style="fill:none;stroke:none;visibility:hidden" />
37
+ <path
38
+ d="m 0,0 0,10 2.000077,0 0,-3.5 2.500097,0 c 1.976154,3e-7 3.500135,-1 3.500135,-3.25 C 8.000309,1 6.4871,2e-7 4.968942,0 z m 2.000077,2 2.500097,0 c 0.963608,0 1.500058,0.5254308 1.500058,1.25 0,0.7245692 -0.631804,1.25 -1.500058,1.25 l -2.500097,0 z M 9,7 9,7.5938559 9.500367,8 l 0,0.75 -3.000116,0 0,-0.25 0.50002,0 c 0.479412,0 0.455442,-0.5 0,-0.5 L 5.500213,8 c -0.50002,0 -0.50002,0.452061 0,0.5 l 0.500019,0 0,0.25 -0.562522,1.28125 C 5.296384,10.001436 5.147956,10 5.000193,10 c -1.02182,0 -2.000077,0.793929 -2.000077,2 0,1.206071 0.978257,2 2.000077,2 1.02182,0 1.937575,-0.856429 1.937575,-2.0625 0,-0.150759 -0.0039,-0.302075 -0.03125,-0.4375 l 0.843783,0 1.821945,-2 0.365639,0.78125 C 9.383285,10.615837 9.000348,11.229684 9.000348,12 c 0,1.206071 0.978257,2 2.000077,2 C 12.022246,14 12.938,13.143571 12.938,11.9375 12.938,10.731429 12.022246,10 11.000425,10 c -0.15008,0 -0.292278,0.02946 -0.437517,0.0625 L 10.000386,8.75 10,7.8 z m -2.593502,2.5 2.193834,0 -1.193796,1.25 -0.843782,0 C 6.422636,10.56342 6.258266,10.401358 6.062734,10.28125 z M 5.000193,11 c 0.635098,0 1.000039,0.549217 1.000039,1 0,0.450783 -0.364941,1 -1.000039,1 -0.635098,0 -1.000038,-0.549217 -1.000038,-1 0,-0.450783 0.36494,-1 1.000038,-1 z m 6.000232,0 c 0.635099,0 1.000039,0.549217 1.000039,1 0,0.450783 -0.36494,1 -1.000039,1 -0.635098,0 -1.000039,-0.549217 -1.000039,-1 0,-0.450783 0.364941,-1 1.000039,-1 z"
39
+ id="parking-bicycle"
40
+ style="fill:#000000;fill-rule:evenodd" />
41
+ </svg>
@@ -0,0 +1,16 @@
1
+ function Elevator({ ...props }) {
2
+ return (
3
+ <svg
4
+ fill="currentColor"
5
+ width="24px"
6
+ height="24px"
7
+ viewBox="0 0 24 24"
8
+ xmlns="http://www.w3.org/2000/svg"
9
+ {...props}
10
+ >
11
+ <path d="M2 22h15V2H2zm14-1h-6V3h6zM3 3h6v18H3zm15.625 10h3.75L20.5 16zm3.75-2h-3.75L20.5 8z" />
12
+ </svg>
13
+ );
14
+ }
15
+
16
+ export default Elevator;
@@ -0,0 +1,2 @@
1
+ <?xml version="1.0" encoding="utf-8"?><!-- Uploaded to: SVG Repo, www.svgrepo.com, Generator: SVG Repo Mixer Tools -->
2
+ <svg width="800px" height="800px" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg"><path d="M2 22h15V2H2zm14-1h-6V3h6zM3 3h6v18H3zm15.625 10h3.75L20.5 16zm3.75-2h-3.75L20.5 8z"/><path fill="none" d="M0 0h24v24H0z"/></svg>
@@ -0,0 +1 @@
1
+ export { default } from "./Elevator";