@geops/rvf-mobility-web-component 0.1.55 → 0.1.57

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 (124) hide show
  1. package/CHANGELOG.md +31 -0
  2. package/docutils.js +21 -6
  3. package/global.d.ts +1 -0
  4. package/iframe.html +15 -0
  5. package/index.html +2 -1
  6. package/index.js +295 -335
  7. package/package.json +16 -17
  8. package/src/{RvfExportMenu/RvfExportMenu.tsx → ExportMenu/ExportMenu.tsx} +12 -12
  9. package/src/ExportMenu/index.tsx +1 -0
  10. package/src/{RvfExportMenuButton/RvfExportMenuButton.tsx → ExportMenuButton/ExportMenuButton.tsx} +5 -5
  11. package/src/ExportMenuButton/index.tsx +1 -0
  12. package/src/FeatureDetails/FeatureDetails.tsx +57 -0
  13. package/src/FeatureDetails/index.ts +1 -0
  14. package/src/FeaturesInfosListener/FeaturesInfosListener.tsx +73 -0
  15. package/src/FeaturesInfosListener/index.tsx +1 -0
  16. package/src/GeolocationButton/GeolocationButton.tsx +0 -1
  17. package/src/LayerTree/LayerTree.tsx +58 -0
  18. package/src/LayerTree/TreeItem/TreeItem.tsx +151 -0
  19. package/src/LayerTree/TreeItem/index.tsx +1 -0
  20. package/src/LayerTree/index.tsx +1 -0
  21. package/src/LayerTree/layersTreeContext.ts +4 -0
  22. package/src/LayerTree/layersTreeReducer.ts +158 -0
  23. package/src/{RvfLayerTreeButton/RvfLayerTreeButton.tsx → LayerTreeButton/LayerTreeButton.tsx} +5 -5
  24. package/src/LayerTreeButton/index.tsx +1 -0
  25. package/src/{RvfTopics/RvfTopics.tsx → LayerTreeMenu/LayerTreeMenu.tsx} +17 -13
  26. package/src/LayerTreeMenu/index.tsx +1 -0
  27. package/src/LayoutState/LayoutState.tsx +277 -0
  28. package/src/LayoutState/index.tsx +1 -0
  29. package/src/LinesNetworkPlanDetails/LinesNetworkPlanDetails.tsx +292 -0
  30. package/src/LinesNetworkPlanDetails/index.tsx +1 -0
  31. package/src/{RvfLineNetworkPlanLayer/RvfLineNetworkPlanLayer.tsx → LinesNetworkPlanLayer/LinesNetworkPlanLayer.tsx} +7 -9
  32. package/src/LinesNetworkPlanLayer/index.tsx +1 -0
  33. package/src/MobilityMap/MobilityMap.tsx +274 -60
  34. package/src/MobilityMap/MobilityMapAttributes.ts +46 -50
  35. package/src/NotificationDetails/NotificationDetails.tsx +468 -0
  36. package/src/NotificationDetails/index.ts +1 -0
  37. package/src/{NotificationLayer/NotificationLayer.tsx → NotificationsLayer/NotificationsLayer.tsx} +9 -4
  38. package/src/NotificationsLayer/index.tsx +1 -0
  39. package/src/Overlay/Overlay.tsx +1 -6
  40. package/src/OverlayContent/OverlayContent.tsx +87 -0
  41. package/src/OverlayContent/index.ts +1 -0
  42. package/src/OverlayDetails/OverlayDetails.tsx +47 -0
  43. package/src/OverlayDetails/index.ts +1 -0
  44. package/src/OverlayDetailsFooter/OverlayDetailsFooter.tsx +51 -0
  45. package/src/OverlayDetailsFooter/index.tsx +1 -0
  46. package/src/OverlayDetailsHeader/OverlayDetailsHeader.tsx +35 -0
  47. package/src/OverlayDetailsHeader/index.ts +1 -0
  48. package/src/OverlayFooter/OverlayFooter.tsx +41 -0
  49. package/src/OverlayFooter/index.tsx +1 -0
  50. package/src/OverlayHeader/OverlayHeader.tsx +44 -0
  51. package/src/OverlayHeader/index.tsx +1 -0
  52. package/src/PermalinkInput/PermalinkInput.tsx +28 -0
  53. package/src/PermalinkInput/index.tsx +1 -0
  54. package/src/RouteSchedule/RouteSchedule.tsx +22 -18
  55. package/src/RvfFeatureDetails/RvfFeatureDetails.tsx +22 -50
  56. package/src/RvfFeatureDetails/RvfLineNetworkDetails/RvfLineNetworkDetails.tsx +108 -104
  57. package/src/RvfFeatureDetails/RvfNotificationDetails/RvfNotificationDetails.tsx +189 -154
  58. package/src/RvfFeatureDetails/RvfSharedMobilityDetail/RvfSharedMobilityDetails.tsx +12 -14
  59. package/src/RvfFeatureDetailsTitle/RvfFeatureDetailsTitle.tsx +5 -5
  60. package/src/RvfMainLinkButton/RvfMainLinkButton.tsx +65 -0
  61. package/src/RvfMainLinkButton/index.tsx +1 -0
  62. package/src/RvfMobilityMap/RvfMobilityMap.tsx +183 -395
  63. package/src/RvfOverlayContent/RvfOverlayContent.tsx +2 -2
  64. package/src/RvfPoisLayer/RvfPoisLayer.tsx +2 -2
  65. package/src/RvfSearch/RvfSearch.tsx +1 -1
  66. package/src/RvfSellingPointsLayer/RvfSellingPointsLayer.tsx +2 -2
  67. package/src/RvfSharedMobilityLayerGroup/RvfSharedMobilityLayerGroup.tsx +23 -23
  68. package/src/RvfTarifZonenLayer/RvfTarifZonenLayer.tsx +2 -2
  69. package/src/Search/Search.tsx +11 -2
  70. package/src/{RvfSearchButton/RvfSearchButton.tsx → SearchButton/SearchButton.tsx} +5 -5
  71. package/src/SearchButton/index.tsx +1 -0
  72. package/src/ShadowOverflow/ShadowOverflow.tsx +20 -0
  73. package/src/ShadowOverflow/index.tsx +1 -0
  74. package/src/{RvfShare/RvfPermalinkButton/RvfPermalinkButton.tsx → ShareMenu/PermalinkButton/PermalinkButton.tsx} +4 -4
  75. package/src/ShareMenu/PermalinkButton/index.tsx +1 -0
  76. package/src/{RvfShare/RvfShare.tsx → ShareMenu/ShareMenu.tsx} +9 -11
  77. package/src/ShareMenu/index.tsx +1 -0
  78. package/src/{RvfShareMenuButton/RvfShareMenuButton.tsx → ShareMenuButton/ShareMenuButton.tsx} +6 -6
  79. package/src/ShareMenuButton/index.tsx +1 -0
  80. package/src/SingleClickListener/SingleClickListener.tsx +55 -113
  81. package/src/SingleClickListener/index.tsx +1 -1
  82. package/src/Station/Station.tsx +10 -3
  83. package/src/StationsLayer/StationsLayer.tsx +0 -1
  84. package/src/StopsSearch/StopsSearch.tsx +3 -4
  85. package/src/StopsSearch/index.tsx +2 -1
  86. package/src/WindowMessageListener/WindowMessageListener.tsx +7 -1
  87. package/src/{RvfZoomButtons/RvfZoomButtons.tsx → ZoomButtons/ZoomButtons.tsx} +9 -12
  88. package/src/ZoomButtons/index.tsx +1 -0
  89. package/src/icons/Geolocation/airport-14-svgrepo-com.svg +41 -0
  90. package/src/ui/Button/Button.tsx +9 -2
  91. package/src/ui/Checkbox/Checkbox.tsx +32 -0
  92. package/src/ui/Checkbox/index.tsx +1 -0
  93. package/src/ui/IconButton/IconButton.tsx +24 -4
  94. package/src/ui/Input/Input.tsx +17 -0
  95. package/src/ui/Input/index.tsx +1 -0
  96. package/src/ui/InputCopy/InputCopy.tsx +86 -0
  97. package/src/ui/InputCopy/index.tsx +1 -0
  98. package/src/ui/Select/Select.tsx +24 -0
  99. package/src/ui/Select/index.tsx +1 -0
  100. package/src/utils/constants.ts +43 -42
  101. package/src/utils/exportPdf.ts +3 -5
  102. package/src/utils/hooks/useInitialLayersVisiblity.tsx +1 -1
  103. package/src/utils/hooks/useLayerConfig.tsx +2 -2
  104. package/src/utils/hooks/useLayersConfig.tsx +3 -3
  105. package/src/utils/hooks/useMapContext.tsx +67 -8
  106. package/src/utils/hooks/useRvfContext.tsx +0 -44
  107. package/src/NotificationLayer/index.tsx +0 -1
  108. package/src/RvfEmbedNavigation/DragPanWarning.ts +0 -124
  109. package/src/RvfEmbedNavigation/RvfEmbedNavigation.tsx +0 -51
  110. package/src/RvfEmbedNavigation/index.js +0 -1
  111. package/src/RvfExportMenu/index.tsx +0 -1
  112. package/src/RvfExportMenuButton/index.tsx +0 -1
  113. package/src/RvfFloatingMenu/RvfFloatingMenu.tsx +0 -44
  114. package/src/RvfFloatingMenu/index.tsx +0 -1
  115. package/src/RvfLayerTreeButton/index.tsx +0 -1
  116. package/src/RvfLineNetworkPlanLayer/index.tsx +0 -1
  117. package/src/RvfPermalink/RvfPermalink.tsx +0 -18
  118. package/src/RvfPermalink/index.tsx +0 -1
  119. package/src/RvfSearchButton/index.tsx +0 -1
  120. package/src/RvfShare/RvfPermalinkButton/index.tsx +0 -1
  121. package/src/RvfShare/index.tsx +0 -1
  122. package/src/RvfShareMenuButton/index.tsx +0 -1
  123. package/src/RvfTopics/index.tsx +0 -1
  124. package/src/RvfZoomButtons/index.tsx +0 -1
@@ -0,0 +1,468 @@
1
+ import {
2
+ type AffectedTimeIntervalType,
3
+ type PublicationType,
4
+ type SituationType,
5
+ type TextualContentType,
6
+ } from "mobility-toolbox-js/types";
7
+ import { useEffect, useState } from "preact/hooks";
8
+ import { twMerge } from "tailwind-merge";
9
+
10
+ import Warning from "../icons/Warning";
11
+ import ShadowOverflow from "../ShadowOverflow";
12
+ import getBgColor from "../utils/getBgColor";
13
+ import useI18n from "../utils/hooks/useI18n";
14
+ import useMapContext from "../utils/hooks/useMapContext";
15
+
16
+ import type { RealtimeLine, RealtimeMot } from "mobility-toolbox-js/types";
17
+ import type { Feature } from "ol";
18
+
19
+ const toShortDate = (date: Date, showTime, showYear?: boolean) => {
20
+ const time = date.toLocaleTimeString(["de"], {
21
+ hour: "2-digit",
22
+ minute: "2-digit",
23
+ });
24
+ const dateString = date.toLocaleDateString(["de"], {
25
+ day: "2-digit",
26
+ month: "short",
27
+ weekday: "short",
28
+ year: showYear ? "numeric" : undefined,
29
+ });
30
+
31
+ return `${dateString}${showTime && showTime !== time ? ` ${time}` : ""}`
32
+ .replace(",", "")
33
+ .replace(/\./, "")
34
+ .replace(/\.$/, "");
35
+ };
36
+
37
+ const getLine = (name: string, lines: NotificationLine[]): NotificationLine => {
38
+ if (lines?.length) {
39
+ const line = lines.find((linee) => {
40
+ return linee.name === name;
41
+ });
42
+ if (line) {
43
+ return line;
44
+ }
45
+ }
46
+ return { mot: "bus", name } as NotificationLine;
47
+ };
48
+
49
+ export type NotificationLine = {
50
+ mot?: RealtimeMot;
51
+ operator_name?: string;
52
+ short_name?: string;
53
+ tags?: string[];
54
+ } & RealtimeLine;
55
+
56
+ function NotificationDetails({
57
+ className,
58
+ feature,
59
+ ...props
60
+ }: {
61
+ className?: string;
62
+ feature: Feature;
63
+ }) {
64
+ const { t } = useI18n();
65
+ const { notificationtenant } = useMapContext();
66
+ const [lines, setLines] = useState<NotificationLine[]>([]);
67
+ const {
68
+ affected_products: affectedProducts,
69
+ affected_time_intervals: timeIntervals,
70
+ consequence_de: consequence,
71
+ description_de: descriptionDe,
72
+ // title,
73
+ disruption_type: disruptionType,
74
+ // disruption_type: disruptionType,
75
+ // duration_text_de: durationText,
76
+ // links,
77
+ // long_description: description,
78
+ reason_de: reason,
79
+ recommendation_de: recommendation,
80
+ situation,
81
+ summary_de: summary,
82
+ } = feature.getProperties();
83
+
84
+ useEffect(() => {
85
+ const abortController = new AbortController();
86
+ if (!notificationtenant) {
87
+ setLines([]);
88
+ }
89
+ fetch(
90
+ `https://tralis-tracker-api.geops.io/api/lines/${notificationtenant}/`,
91
+ )
92
+ .then((res) => {
93
+ return res.json();
94
+ })
95
+ .then((data) => {
96
+ setLines(data);
97
+ })
98
+ .catch((err) => {
99
+ // eslint-disable-next-line no-console
100
+ console.error("Failed to fetch lines", err);
101
+ });
102
+ return () => {
103
+ abortController?.abort();
104
+ };
105
+ }, [notificationtenant]);
106
+
107
+ // "title_de": "",
108
+ // "title_fr": "",
109
+ // "title_it": "",
110
+ // "title_en": "",
111
+ // "summary_de": "",
112
+ // "summary_fr": "",
113
+ // "summary_it": "",
114
+ // "summary_en": "",
115
+ // "reason_de": "Baustelle",
116
+ // "reason_fr": "",
117
+ // "reason_it": "",
118
+ // "reason_en": "",
119
+ // "description_de": "",
120
+ // "description_fr": "",
121
+ // "description_it": "",
122
+ // "description_en": "",
123
+ // "consequence_de": "Umleitung",
124
+ // "consequence_fr": "",
125
+ // "consequence_it": "",
126
+ // "consequence_en": "",
127
+ // "duration_text_de": "",
128
+ // "duration_text_fr": "",
129
+ // "duration_text_it": "",
130
+ // "duration_text_en": "",
131
+ // "recommendation_de": "",
132
+ // "recommendation_fr": "",
133
+ // "recommendation_it": "",
134
+ // "recommendation_en": "",
135
+ // "reasons": [
136
+ // "Ausfall des Aufzuges"
137
+ // ]
138
+
139
+ let end = "",
140
+ start = "";
141
+ let products = [];
142
+ // let externalLinks = [];
143
+
144
+ try {
145
+ const timeInterval = JSON.parse(timeIntervals)?.[0] || {};
146
+ start = timeInterval.start;
147
+ end = timeInterval.end;
148
+ // const dateStart = new Date(timeInterval.start);
149
+ // start =
150
+ // dateStart.toLocaleDateString() + " " + dateStart.toLocaleTimeString();
151
+ // const dateEnd = new Date(timeInterval.end);
152
+ // console.log("dateEnd", dateEnd);
153
+ // end = dateEnd.toLocaleDateString() + " " + dateEnd.toLocaleTimeString();
154
+
155
+ products = JSON.parse(affectedProducts);
156
+ products?.sort((a, b) => {
157
+ return a.name.localeCompare(b.name);
158
+ });
159
+ // externalLinks = JSON.parse(links);
160
+ } catch (e) {
161
+ console.error(e);
162
+ }
163
+
164
+ // moco export v1
165
+ if (disruptionType) {
166
+ return (
167
+ // <div className={"flex gap-2 text-sm"}>
168
+ // <div className="min-w-8 shrink-0 grow-0">
169
+ // <img
170
+ // alt={disruptionType}
171
+ // className={"w-8"}
172
+ // src={icons[disruptionType]}
173
+ // ></img>
174
+ // </div>
175
+ <div className={"flex flex-col gap-2 text-sm"}>
176
+ {/* <div className="text-base font-bold">{title}</div> */}
177
+ {!!products?.length && (
178
+ <div className="flex flex-wrap gap-2">
179
+ {products?.map(({ name }) => {
180
+ const line = getLine(name, lines);
181
+ return (
182
+ <>
183
+ <div
184
+ className={
185
+ "w-fit rounded-md bg-black px-[12px] py-[9px] leading-none font-bold text-white"
186
+ }
187
+ key={name}
188
+ style={{
189
+ backgroundColor: getBgColor(line.mot, line),
190
+ }}
191
+ >
192
+ {name}
193
+ </div>
194
+ </>
195
+ );
196
+ })}
197
+ </div>
198
+ )}
199
+ <div className="text-base font-bold">
200
+ {!!start && !end && `ab${toShortDate(new Date(start), true)}`}
201
+ {!start && !!end && `bis${toShortDate(new Date(end), true)}`}
202
+ {!!start &&
203
+ !!end &&
204
+ `${toShortDate(new Date(start), true)} - ${toShortDate(new Date(end), true)}`}
205
+ </div>
206
+ <div className={"flex flex-col gap-2"}>
207
+ <p className={"text-base"}>{summary}</p>
208
+ <p className={"text-base"}>{descriptionDe}</p>
209
+
210
+ {[
211
+ {
212
+ content: recommendation,
213
+ label: "recommendation",
214
+ },
215
+ {
216
+ content: reason,
217
+ label: "reason",
218
+ },
219
+ {
220
+ content: consequence,
221
+ label: "consequence",
222
+ },
223
+ ].map((item) => {
224
+ if (!item.content) {
225
+ return null;
226
+ }
227
+ return (
228
+ <div key={item.content}>
229
+ {!!item.label && <p className="font-bold">{t(item.label)}:</p>}
230
+ <p>{item.content}</p>
231
+ </div>
232
+ );
233
+ })}
234
+ </div>
235
+ </div>
236
+ // </div>
237
+ // <div className={"flex flex-col gap-4 text-sm"}>
238
+ // <div>
239
+ // <div className="text-base font-bold">{title}</div>
240
+ // <div className="text-xs">
241
+ // {start} - {end}
242
+ // </div>
243
+ // </div>
244
+ // <div
245
+ // className={"flex flex-col gap-2"}
246
+ // dangerouslySetInnerHTML={{
247
+ // __html: converter.makeHtml(description).replace("<hr />", "<br />"),
248
+ // }}
249
+ // ></div>
250
+ // {externalLinks?.map(({ label_de: label, uri }) => {
251
+ // return (
252
+ // <RvfLink href={uri} key={uri}>
253
+ // {label}
254
+ // </RvfLink>
255
+ // );
256
+ // })}
257
+ // {!!products?.length && (
258
+ // <>
259
+ // <div className={"font-bold"}>Betroffene Lines:</div>
260
+ // <div className={"flex flex-wrap gap-1"}>
261
+ // {products?.map(({ name }) => {
262
+ // return (
263
+ // <div
264
+ // className={
265
+ // "rounded-md bg-red px-[12px] py-[9px] font-bold leading-none text-white"
266
+ // }
267
+ // key={name}
268
+ // >
269
+ // {name}
270
+ // </div>
271
+ // );
272
+ // })}
273
+ // </div>
274
+ // </>
275
+ // )}
276
+ // </div>
277
+ );
278
+ }
279
+
280
+ // moco export v2
281
+ let textualContent: Partial<TextualContentType> = {};
282
+ let timeIntervalsToDisplay = [];
283
+ let publicationsToDisplay: PublicationType[] = [];
284
+ let reasonsToDisplay: string[] = [];
285
+
286
+ try {
287
+ const situationParsed: SituationType = JSON.parse(situation) || {};
288
+ const publicationsArr: PublicationType[] =
289
+ situationParsed?.publications || [];
290
+
291
+ // Find the current publication(s) at the current date
292
+ publicationsToDisplay =
293
+ publicationsArr?.filter(({ publicationWindows }) => {
294
+ return publicationWindows.find(({ endTime, startTime }) => {
295
+ const now = new Date();
296
+ const startT = new Date(startTime);
297
+ const endT = new Date(endTime);
298
+ return startT <= now && now <= endT;
299
+ });
300
+ }) || [];
301
+
302
+ // Display the current and next affected time intervals not the one in the past
303
+ timeIntervalsToDisplay =
304
+ (situationParsed?.affectedTimeIntervals || []).filter(
305
+ ({ endTime, startTime }) => {
306
+ const now = new Date();
307
+ const startT = new Date(startTime);
308
+ const endT = new Date(endTime);
309
+ return (startT <= now && now <= endT) || now < startT;
310
+ },
311
+ ) || [];
312
+
313
+ // Display the reasons
314
+ reasonsToDisplay = (situationParsed?.reasons || []).map(({ name }) => {
315
+ return name;
316
+ });
317
+ } catch (e) {
318
+ // eslint-disable-next-line no-console
319
+ console.error("Failed to parse publications", e);
320
+ }
321
+
322
+ return (
323
+ <ShadowOverflow {...props} className={twMerge("px-4 text-base", className)}>
324
+ <div>
325
+ {publicationsToDisplay?.map(
326
+ ({
327
+ id,
328
+ publicationLines,
329
+ publicationStops,
330
+ textualContentLarge,
331
+ textualContentMedium,
332
+ textualContentSmall,
333
+ }) => {
334
+ // Get the textual content in German
335
+ textualContent = (
336
+ textualContentLarge ||
337
+ textualContentMedium ||
338
+ textualContentSmall
339
+ )?.de;
340
+
341
+ const pubLines =
342
+ publicationLines?.flatMap((publication) => {
343
+ return (
344
+ publication.lines?.map(({ name }) => {
345
+ return name;
346
+ }) || []
347
+ );
348
+ }) || [];
349
+
350
+ const stations = publicationStops.map((publication) => {
351
+ return publication?.name || "";
352
+ });
353
+
354
+ return (
355
+ <div className={"text-base"} key={id}>
356
+ <div className="text-xs uppercase">{reasonsToDisplay}</div>
357
+ <h3 className="space-x-2 text-lg font-bold text-balance">
358
+ <span
359
+ className={"line-height-[1.3] inline-block align-middle"}
360
+ >
361
+ <Warning />
362
+ </span>
363
+ <span
364
+ dangerouslySetInnerHTML={{
365
+ __html: textualContent?.summary,
366
+ }}
367
+ ></span>
368
+ </h3>
369
+ <hr className="my-1" />
370
+
371
+ {timeIntervalsToDisplay?.map(
372
+ ({
373
+ dailyEndTime,
374
+ dailyStartTime,
375
+ endTime,
376
+ startTime,
377
+ }: AffectedTimeIntervalType) => {
378
+ const hasDailyTime = dailyEndTime && dailyStartTime;
379
+ const isStartCurrentYear =
380
+ new Date().getFullYear() ===
381
+ new Date(startTime).getFullYear();
382
+ const isEndCurrentYear =
383
+ new Date().getFullYear() ===
384
+ new Date(endTime).getFullYear();
385
+ const isEndInfinite = endTime.includes("2500");
386
+
387
+ return (
388
+ <div
389
+ className="text-sm font-bold text-balance"
390
+ key={startTime}
391
+ >
392
+ <span>
393
+ {`von ${toShortDate(new Date(startTime), !hasDailyTime, !isStartCurrentYear)}`}
394
+ {!isEndInfinite &&
395
+ ` bis ${toShortDate(new Date(endTime), !hasDailyTime, !isEndCurrentYear)}`}
396
+ </span>
397
+ {hasDailyTime && (
398
+ <span>{` (täglich von ${dailyStartTime} bis ${dailyEndTime})`}</span>
399
+ )}
400
+ </div>
401
+ );
402
+ },
403
+ )}
404
+ <div
405
+ className="mt-4"
406
+ dangerouslySetInnerHTML={{
407
+ __html:
408
+ textualContent?.description || "Keine Details verfügbar",
409
+ }}
410
+ />
411
+ {!!pubLines?.length && (
412
+ <div>
413
+ <br />
414
+ <div className={"font-bold"}>Betroffene Lines:</div>
415
+ <div className={"flex flex-wrap gap-1 text-sm"}>
416
+ {pubLines?.map((name) => {
417
+ return (
418
+ <div
419
+ className={
420
+ "rounded-md bg-black px-2 py-1 font-bold text-white"
421
+ }
422
+ key={name}
423
+ >
424
+ {name}
425
+ </div>
426
+ );
427
+ })}
428
+ </div>
429
+ </div>
430
+ )}
431
+ <div>
432
+ <br />
433
+ <div className={"font-bold"}>Betroffene Haltestellen:</div>
434
+ <div className={"flex flex-wrap gap-1 text-sm"}>
435
+ {stations?.length ? (
436
+ stations.map((name) => {
437
+ return (
438
+ <div
439
+ className={
440
+ "rounded-md bg-black px-2 py-1 font-bold text-white"
441
+ }
442
+ key={name}
443
+ >
444
+ {name}
445
+ </div>
446
+ );
447
+ })
448
+ ) : (
449
+ <div
450
+ className={
451
+ "rounded-md bg-black px-2 py-1 font-bold text-white"
452
+ }
453
+ >
454
+ Alle Bahnhöfe auf dieser Strecke
455
+ </div>
456
+ )}
457
+ </div>
458
+ </div>
459
+ </div>
460
+ );
461
+ },
462
+ )}
463
+ </div>
464
+ </ShadowOverflow>
465
+ );
466
+ }
467
+
468
+ export default NotificationDetails;
@@ -0,0 +1 @@
1
+ export { default } from "../RvfFeatureDetails/RvfNotificationDetails";
@@ -2,12 +2,12 @@ import { MocoLayer } from "mobility-toolbox-js/ol";
2
2
  import { memo } from "preact/compat";
3
3
  import { useEffect, useMemo } from "preact/hooks";
4
4
 
5
- import { LAYER_NAME_NOTIFICATION } from "../utils/constants";
5
+ import { LAYER_NAME_NOTIFICATIONS } from "../utils/constants";
6
6
  import useMapContext from "../utils/hooks/useMapContext";
7
7
 
8
8
  import type { MocoLayerOptions } from "mobility-toolbox-js/ol";
9
9
 
10
- function NotificationLayer(props?: Partial<MocoLayerOptions>) {
10
+ function NotificationsLayer(props?: Partial<MocoLayerOptions>) {
11
11
  const {
12
12
  apikey,
13
13
  baseLayer,
@@ -16,6 +16,7 @@ function NotificationLayer(props?: Partial<MocoLayerOptions>) {
16
16
  notificationtenant,
17
17
  notificationurl,
18
18
  previewNotifications,
19
+ setNotificationsLayer,
19
20
  } = useMapContext();
20
21
 
21
22
  const layer = useMemo(() => {
@@ -29,7 +30,7 @@ function NotificationLayer(props?: Partial<MocoLayerOptions>) {
29
30
  },
30
31
  date: notificationat ? new Date(notificationat) : undefined,
31
32
  maplibreLayer: baseLayer,
32
- name: LAYER_NAME_NOTIFICATION,
33
+ name: LAYER_NAME_NOTIFICATIONS,
33
34
  situations: previewNotifications,
34
35
  tenant: notificationtenant,
35
36
  url: notificationurl,
@@ -50,6 +51,10 @@ function NotificationLayer(props?: Partial<MocoLayerOptions>) {
50
51
  props,
51
52
  ]);
52
53
 
54
+ useEffect(() => {
55
+ setNotificationsLayer?.(layer);
56
+ }, [layer, setNotificationsLayer]);
57
+
53
58
  useEffect(() => {
54
59
  if (!map || !layer) {
55
60
  return;
@@ -64,4 +69,4 @@ function NotificationLayer(props?: Partial<MocoLayerOptions>) {
64
69
  return null;
65
70
  }
66
71
 
67
- export default memo(NotificationLayer);
72
+ export default memo(NotificationsLayer);
@@ -0,0 +1 @@
1
+ export { default } from "./NotificationsLayer";
@@ -7,6 +7,7 @@ import type { HTMLAttributes, PreactDOMAttributes } from "preact";
7
7
  import type { ScrollableHandlerProps } from "../ScrollableHandler";
8
8
 
9
9
  export type OverlayProps = {
10
+ className;
10
11
  ScrollableHandlerProps?: ScrollableHandlerProps;
11
12
  } & HTMLAttributes<HTMLDivElement> &
12
13
  PreactDOMAttributes;
@@ -29,14 +30,8 @@ function Overlay({
29
30
  return null;
30
31
  }
31
32
 
32
- // ${
33
- // // children
34
- // // ? "max-h-[70%] min-h-[75px] w-full border-t @lg:w-[350px] @lg:border-t-0 @lg:border-r"
35
- // // : "max-h-0 min-h-0 @lg:w-0"
36
- // }
37
33
  return (
38
34
  <div
39
- // We set text-base so the clamp works on overlay container, not main
40
35
  className={
41
36
  (twMerge(
42
37
  `pointer-events-auto relative z-50 flex flex-col overflow-hidden bg-white transition-[min-height,max-height] @lg:transition-[width]`,
@@ -0,0 +1,87 @@
1
+ import { twMerge } from "tailwind-merge";
2
+
3
+ import ExportMenu from "../ExportMenu";
4
+ import LayerTreeMenu from "../LayerTreeMenu";
5
+ import OverlayDetails from "../OverlayDetails";
6
+ import OverlayHeader from "../OverlayHeader";
7
+ import Search from "../Search";
8
+ import ShareMenu from "../ShareMenu";
9
+ import useMapContext from "../utils/hooks/useMapContext";
10
+
11
+ const contentClassName = `relative h-full overflow-x-hidden overflow-y-auto text-base bg-white`;
12
+
13
+ function OverlayContent({
14
+ hasDetails,
15
+ hasLayerTree,
16
+ hasPrint,
17
+ hasSearch,
18
+ hasShare,
19
+ }: {
20
+ hasDetails: boolean;
21
+ hasLayerTree: boolean;
22
+ hasPrint: boolean;
23
+ hasRealtime: boolean;
24
+ hasSearch: boolean;
25
+ hasShare: boolean;
26
+ }) {
27
+ const {
28
+ isExportMenuOpen,
29
+ isLayerTreeOpen,
30
+ isSearchOpen,
31
+ isShareMenuOpen,
32
+ selectedFeature,
33
+ setIsExportMenuOpen,
34
+ setIsLayerTreeOpen,
35
+ setIsShareMenuOpen,
36
+ } = useMapContext();
37
+
38
+ return (
39
+ <>
40
+ {hasDetails && selectedFeature && <OverlayDetails />}
41
+ {hasPrint && isExportMenuOpen && (
42
+ <>
43
+ <OverlayHeader
44
+ onClose={() => {
45
+ setIsExportMenuOpen(false);
46
+ }}
47
+ title={"Print"}
48
+ ></OverlayHeader>
49
+ <ExportMenu
50
+ className={twMerge(contentClassName, "flex flex-col gap-4 p-4")}
51
+ />
52
+ </>
53
+ )}
54
+ {hasLayerTree && isLayerTreeOpen && (
55
+ <>
56
+ <OverlayHeader
57
+ onClose={() => {
58
+ setIsLayerTreeOpen(false);
59
+ }}
60
+ title={"Layers"}
61
+ ></OverlayHeader>
62
+ <LayerTreeMenu
63
+ className="relative flex h-full flex-col overflow-x-hidden overflow-y-auto p-2 text-base *:not-last:border-b"
64
+ treeItemProps={{ childContainerClassName: "*:not-last:border-b" }}
65
+ />
66
+ </>
67
+ )}
68
+ {hasShare && isShareMenuOpen && (
69
+ <>
70
+ <OverlayHeader
71
+ onClose={() => {
72
+ setIsShareMenuOpen(false);
73
+ }}
74
+ title="Share"
75
+ ></OverlayHeader>
76
+ <ShareMenu className="h-full overflow-x-hidden overflow-y-auto p-4 text-base" />
77
+ </>
78
+ )}
79
+ {hasSearch && isSearchOpen && (
80
+ <>
81
+ <Search className="relative flex h-full flex-col overflow-x-hidden overflow-y-auto p-2 text-base" />
82
+ </>
83
+ )}
84
+ </>
85
+ );
86
+ }
87
+ export default OverlayContent;
@@ -0,0 +1 @@
1
+ export { default } from "./OverlayContent";
@@ -0,0 +1,47 @@
1
+ import { memo } from "preact/compat";
2
+ import { useMemo } from "preact/hooks";
3
+
4
+ import FeatureDetails from "../FeatureDetails";
5
+ import OverlayDetailsFooter from "../OverlayDetailsFooter";
6
+ import OverlayDetailsHeader from "../OverlayDetailsHeader";
7
+ import useMapContext from "../utils/hooks/useMapContext";
8
+
9
+ /**
10
+ * This component is responsible to display the informations about the selectedFeature prop
11
+ * in the Overlay component.
12
+ */
13
+ function OverlayDetails() {
14
+ const { featuresInfos, selectedFeature, setSelectedFeature } =
15
+ useMapContext();
16
+
17
+ const featuresInfo = useMemo(() => {
18
+ return featuresInfos?.find((featureInfo) => {
19
+ return featureInfo?.features.includes(selectedFeature);
20
+ });
21
+ }, [featuresInfos, selectedFeature]);
22
+
23
+ if (!selectedFeature) {
24
+ return null;
25
+ }
26
+ return (
27
+ <>
28
+ <OverlayDetailsHeader
29
+ feature={selectedFeature}
30
+ layer={featuresInfo?.layer}
31
+ onClose={() => {
32
+ setSelectedFeature(null);
33
+ }}
34
+ />
35
+ <FeatureDetails
36
+ feature={selectedFeature}
37
+ featuresInfo={featuresInfo}
38
+ layer={featuresInfo?.layer}
39
+ />
40
+ <OverlayDetailsFooter
41
+ feature={selectedFeature}
42
+ layer={featuresInfo?.layer}
43
+ />
44
+ </>
45
+ );
46
+ }
47
+ export default memo(OverlayDetails);
@@ -0,0 +1 @@
1
+ export { default } from "./OverlayDetails";