@geops/rvf-mobility-web-component 0.1.56 → 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.
- package/CHANGELOG.md +23 -0
- package/docutils.js +1 -1
- package/global.d.ts +1 -0
- package/iframe.html +15 -0
- package/index.html +2 -1
- package/index.js +278 -330
- package/package.json +16 -17
- package/src/{RvfExportMenu/RvfExportMenu.tsx → ExportMenu/ExportMenu.tsx} +11 -11
- package/src/ExportMenu/index.tsx +1 -0
- package/src/{RvfExportMenuButton/RvfExportMenuButton.tsx → ExportMenuButton/ExportMenuButton.tsx} +5 -5
- package/src/ExportMenuButton/index.tsx +1 -0
- package/src/FeatureDetails/FeatureDetails.tsx +57 -0
- package/src/FeatureDetails/index.ts +1 -0
- package/src/FeaturesInfosListener/FeaturesInfosListener.tsx +73 -0
- package/src/FeaturesInfosListener/index.tsx +1 -0
- package/src/GeolocationButton/GeolocationButton.tsx +0 -1
- package/src/LayerTree/LayerTree.tsx +58 -0
- package/src/LayerTree/TreeItem/TreeItem.tsx +151 -0
- package/src/LayerTree/TreeItem/index.tsx +1 -0
- package/src/LayerTree/index.tsx +1 -0
- package/src/LayerTree/layersTreeContext.ts +4 -0
- package/src/LayerTree/layersTreeReducer.ts +158 -0
- package/src/{RvfLayerTreeButton/RvfLayerTreeButton.tsx → LayerTreeButton/LayerTreeButton.tsx} +5 -5
- package/src/LayerTreeButton/index.tsx +1 -0
- package/src/{RvfTopics/RvfTopics.tsx → LayerTreeMenu/LayerTreeMenu.tsx} +17 -13
- package/src/LayerTreeMenu/index.tsx +1 -0
- package/src/LayoutState/LayoutState.tsx +277 -0
- package/src/LayoutState/index.tsx +1 -0
- package/src/LinesNetworkPlanDetails/LinesNetworkPlanDetails.tsx +292 -0
- package/src/LinesNetworkPlanDetails/index.tsx +1 -0
- package/src/{RvfLineNetworkPlanLayer/RvfLineNetworkPlanLayer.tsx → LinesNetworkPlanLayer/LinesNetworkPlanLayer.tsx} +7 -9
- package/src/LinesNetworkPlanLayer/index.tsx +1 -0
- package/src/MobilityMap/MobilityMap.tsx +274 -60
- package/src/MobilityMap/MobilityMapAttributes.ts +27 -43
- package/src/NotificationDetails/NotificationDetails.tsx +468 -0
- package/src/NotificationDetails/index.ts +1 -0
- package/src/{NotificationLayer/NotificationLayer.tsx → NotificationsLayer/NotificationsLayer.tsx} +9 -4
- package/src/NotificationsLayer/index.tsx +1 -0
- package/src/Overlay/Overlay.tsx +1 -6
- package/src/OverlayContent/OverlayContent.tsx +87 -0
- package/src/OverlayContent/index.ts +1 -0
- package/src/OverlayDetails/OverlayDetails.tsx +47 -0
- package/src/OverlayDetails/index.ts +1 -0
- package/src/OverlayDetailsFooter/OverlayDetailsFooter.tsx +51 -0
- package/src/OverlayDetailsFooter/index.tsx +1 -0
- package/src/OverlayDetailsHeader/OverlayDetailsHeader.tsx +35 -0
- package/src/OverlayDetailsHeader/index.ts +1 -0
- package/src/OverlayFooter/OverlayFooter.tsx +41 -0
- package/src/OverlayFooter/index.tsx +1 -0
- package/src/OverlayHeader/OverlayHeader.tsx +44 -0
- package/src/OverlayHeader/index.tsx +1 -0
- package/src/PermalinkInput/PermalinkInput.tsx +28 -0
- package/src/PermalinkInput/index.tsx +1 -0
- package/src/RouteSchedule/RouteSchedule.tsx +22 -18
- package/src/RvfFeatureDetails/RvfFeatureDetails.tsx +22 -50
- package/src/RvfFeatureDetails/RvfLineNetworkDetails/RvfLineNetworkDetails.tsx +108 -104
- package/src/RvfFeatureDetails/RvfNotificationDetails/RvfNotificationDetails.tsx +189 -154
- package/src/RvfFeatureDetails/RvfSharedMobilityDetail/RvfSharedMobilityDetails.tsx +12 -14
- package/src/RvfFeatureDetailsTitle/RvfFeatureDetailsTitle.tsx +5 -5
- package/src/RvfMainLinkButton/RvfMainLinkButton.tsx +65 -0
- package/src/RvfMainLinkButton/index.tsx +1 -0
- package/src/RvfMobilityMap/RvfMobilityMap.tsx +182 -394
- package/src/RvfOverlayContent/RvfOverlayContent.tsx +2 -2
- package/src/RvfPoisLayer/RvfPoisLayer.tsx +2 -2
- package/src/RvfSearch/RvfSearch.tsx +1 -1
- package/src/RvfSellingPointsLayer/RvfSellingPointsLayer.tsx +2 -2
- package/src/RvfSharedMobilityLayerGroup/RvfSharedMobilityLayerGroup.tsx +23 -23
- package/src/RvfTarifZonenLayer/RvfTarifZonenLayer.tsx +2 -2
- package/src/Search/Search.tsx +11 -2
- package/src/{RvfSearchButton/RvfSearchButton.tsx → SearchButton/SearchButton.tsx} +5 -5
- package/src/SearchButton/index.tsx +1 -0
- package/src/ShadowOverflow/ShadowOverflow.tsx +20 -0
- package/src/ShadowOverflow/index.tsx +1 -0
- package/src/{RvfShare/RvfPermalinkButton/RvfPermalinkButton.tsx → ShareMenu/PermalinkButton/PermalinkButton.tsx} +4 -4
- package/src/ShareMenu/PermalinkButton/index.tsx +1 -0
- package/src/{RvfShare/RvfShare.tsx → ShareMenu/ShareMenu.tsx} +9 -11
- package/src/ShareMenu/index.tsx +1 -0
- package/src/{RvfShareMenuButton/RvfShareMenuButton.tsx → ShareMenuButton/ShareMenuButton.tsx} +6 -6
- package/src/ShareMenuButton/index.tsx +1 -0
- package/src/SingleClickListener/SingleClickListener.tsx +55 -113
- package/src/SingleClickListener/index.tsx +1 -1
- package/src/Station/Station.tsx +10 -3
- package/src/StationsLayer/StationsLayer.tsx +0 -1
- package/src/StopsSearch/StopsSearch.tsx +3 -4
- package/src/StopsSearch/index.tsx +2 -1
- package/src/WindowMessageListener/WindowMessageListener.tsx +7 -1
- package/src/{RvfZoomButtons/RvfZoomButtons.tsx → ZoomButtons/ZoomButtons.tsx} +9 -12
- package/src/ZoomButtons/index.tsx +1 -0
- package/src/icons/Geolocation/airport-14-svgrepo-com.svg +41 -0
- package/src/ui/Button/Button.tsx +9 -2
- package/src/ui/Checkbox/Checkbox.tsx +32 -0
- package/src/ui/Checkbox/index.tsx +1 -0
- package/src/ui/IconButton/IconButton.tsx +24 -4
- package/src/ui/Input/Input.tsx +17 -0
- package/src/ui/Input/index.tsx +1 -0
- package/src/ui/InputCopy/InputCopy.tsx +86 -0
- package/src/ui/InputCopy/index.tsx +1 -0
- package/src/ui/Select/Select.tsx +24 -0
- package/src/ui/Select/index.tsx +1 -0
- package/src/utils/constants.ts +43 -42
- package/src/utils/hooks/useInitialLayersVisiblity.tsx +1 -1
- package/src/utils/hooks/useLayerConfig.tsx +2 -2
- package/src/utils/hooks/useLayersConfig.tsx +3 -3
- package/src/utils/hooks/useMapContext.tsx +67 -8
- package/src/utils/hooks/useRvfContext.tsx +0 -44
- package/src/NotificationLayer/index.tsx +0 -1
- package/src/RvfEmbedNavigation/DragPanWarning.ts +0 -124
- package/src/RvfEmbedNavigation/RvfEmbedNavigation.tsx +0 -51
- package/src/RvfEmbedNavigation/index.js +0 -1
- package/src/RvfExportMenu/index.tsx +0 -1
- package/src/RvfExportMenuButton/index.tsx +0 -1
- package/src/RvfFloatingMenu/RvfFloatingMenu.tsx +0 -44
- package/src/RvfFloatingMenu/index.tsx +0 -1
- package/src/RvfLayerTreeButton/index.tsx +0 -1
- package/src/RvfLineNetworkPlanLayer/index.tsx +0 -1
- package/src/RvfPermalink/RvfPermalink.tsx +0 -18
- package/src/RvfPermalink/index.tsx +0 -1
- package/src/RvfSearchButton/index.tsx +0 -1
- package/src/RvfShare/RvfPermalinkButton/index.tsx +0 -1
- package/src/RvfShare/index.tsx +0 -1
- package/src/RvfShareMenuButton/index.tsx +0 -1
- package/src/RvfTopics/index.tsx +0 -1
- 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";
|
package/src/{NotificationLayer/NotificationLayer.tsx → NotificationsLayer/NotificationsLayer.tsx}
RENAMED
|
@@ -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 {
|
|
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
|
|
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:
|
|
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(
|
|
72
|
+
export default memo(NotificationsLayer);
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { default } from "./NotificationsLayer";
|
package/src/Overlay/Overlay.tsx
CHANGED
|
@@ -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";
|