@geops/rvf-mobility-web-component 0.1.61 → 0.1.63
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 +83 -0
- package/README.md +1 -0
- package/docutils.js +103 -4
- package/fonts/source-sans-3/source-sans-3-v15-latin-500.ttf +0 -0
- package/fonts/source-sans-3/source-sans-3-v15-latin-500.woff2 +0 -0
- package/fonts/source-sans-3/source-sans-3-v15-latin-600.ttf +0 -0
- package/fonts/source-sans-3/source-sans-3-v15-latin-600.woff2 +0 -0
- package/fonts/source-sans-3/source-sans-3-v15-latin-700.ttf +0 -0
- package/fonts/source-sans-3/source-sans-3-v15-latin-700.woff2 +0 -0
- package/fonts/source-sans-3/source-sans-3-v15-latin-regular.ttf +0 -0
- package/fonts/source-sans-3/source-sans-3-v15-latin-regular.woff2 +0 -0
- package/index.html +27 -97
- package/index.js +235 -224
- package/notifications.html +144 -0
- package/package.json +2 -2
- package/search.html +24 -65
- package/src/FeatureDetails/FeatureDetails.tsx +20 -6
- package/src/FeaturesInfosListener/FeaturesInfosListener.tsx +2 -0
- package/src/LayerTree/TreeItem/TreeItem.tsx +2 -2
- package/src/LayerTreeMenu/LayerTreeMenu.tsx +19 -3
- package/src/LayoutState/LayoutState.tsx +17 -0
- package/src/LinesNetworkPlanDetails/LinesNetworkPlanDetails.tsx +34 -21
- package/src/LinesNetworkPlanLayer/LinesNetworkPlanLayer.tsx +3 -6
- package/src/LinesNetworkPlanLayerHighlight/LinesNetworkPlanLayerHighlight.tsx +88 -0
- package/src/LinesNetworkPlanLayerHighlight/index.tsx +1 -0
- package/src/MapDispatchEvents/MapDispatchEvents.tsx +6 -4
- package/src/MapLayout/MapLayout.tsx +2 -2
- package/src/MapsetLayer/MapsetLayer.tsx +116 -0
- package/src/MapsetLayer/index.tsx +1 -0
- package/src/MobilityMap/MobilityMap.tsx +27 -5
- package/src/MobilityMap/MobilityMapAttributes.test.ts +38 -0
- package/src/MobilityMap/MobilityMapAttributes.ts +99 -22
- package/src/MobilityMap/MobilityMapEvents.ts +53 -0
- package/src/MobilityNotifications/MobilityNotifications.tsx +93 -0
- package/src/MobilityNotifications/MobilityNotificationsAttributes.test.ts +21 -0
- package/src/MobilityNotifications/MobilityNotificationsAttributes.ts +46 -0
- package/src/MobilityNotifications/index.ts +2 -0
- package/src/MobilitySearch/MobilitySearchEvents.ts +21 -0
- package/src/NotificationDetails/NotificationDetails.tsx +74 -251
- package/src/OverlayContent/OverlayContent.tsx +1 -1
- package/src/OverlayDetails/OverlayDetails.tsx +4 -2
- package/src/OverlayFooter/OverlayFooter.tsx +3 -2
- package/src/RealtimeLayer/RealtimeLayer.tsx +36 -7
- package/src/RouteScheduleFooter/RouteScheduleFooter.tsx +1 -2
- package/src/RvfFeatureDetails/RvfFeatureDetails.tsx +2 -2
- package/src/RvfFeatureDetails/RvfLineNetworkDetails/RvfLineNetworkDetails.tsx +2 -0
- package/src/RvfFeatureDetails/RvfNotificationDetails/RvfNotificationDetails.tsx +12 -453
- package/src/RvfFeatureDetails/RvfSellingPointDetails/RvfSellingPointDetails.tsx +20 -17
- package/src/RvfFeatureDetails/RvfSharedMobilityDetail/RvfSharedMobilityDetails.tsx +93 -36
- package/src/RvfLink/RvfLink.tsx +5 -2
- package/src/RvfMobilityMap/RvfMobilityMap.tsx +27 -9
- package/src/RvfSelectedFeatureHighlightLayer/RvfSelectedFeatureHighlightLayer.tsx +9 -5
- package/src/RvfSingleClickListener/RvfSingleClickListener.tsx +3 -1
- package/src/SituationDetails/SituationDetails.tsx +324 -0
- package/src/SituationDetails/index.ts +1 -0
- package/src/index.tsx +16 -0
- package/src/indexDoc.ts +25 -3
- package/src/ui/Checkbox/Checkbox.tsx +2 -2
- package/src/ui/Link/Link.tsx +49 -0
- package/src/ui/Link/index.tsx +1 -0
- package/src/ui/Select/Select.tsx +2 -2
- package/src/utils/constants.ts +37 -0
- package/src/utils/exportPdf.ts +3 -1
- package/src/utils/highlightLinesNetworkPlan.ts +25 -0
- package/src/utils/hooks/useI18n.tsx +6 -4
- package/src/utils/hooks/useMapContext.tsx +9 -0
- package/src/utils/sharingGraphqlUtils.ts +1 -1
- package/src/utils/translations.ts +12 -1
- package/tailwind.config.mjs +3 -1
- package/src/ShareMenu/PermalinkButton/PermalinkButton.tsx +0 -62
- package/src/ShareMenu/PermalinkButton/index.tsx +0 -1
- package/src/icons/Geolocation/airport-14-svgrepo-com.svg +0 -41
|
@@ -0,0 +1,324 @@
|
|
|
1
|
+
import {
|
|
2
|
+
type AffectedTimeIntervalType,
|
|
3
|
+
type PublicationType,
|
|
4
|
+
type SituationType,
|
|
5
|
+
type TextualContentType,
|
|
6
|
+
} from "mobility-toolbox-js/types";
|
|
7
|
+
import { memo } from "preact/compat";
|
|
8
|
+
import { useState } from "preact/hooks";
|
|
9
|
+
import { twMerge } from "tailwind-merge";
|
|
10
|
+
|
|
11
|
+
import ArrowDown from "../icons/ArrowDown";
|
|
12
|
+
import ArrowUp from "../icons/ArrowUp";
|
|
13
|
+
import Warning from "../icons/Warning";
|
|
14
|
+
import Link from "../ui/Link";
|
|
15
|
+
import useI18n from "../utils/hooks/useI18n";
|
|
16
|
+
|
|
17
|
+
import type { MultilingualTextualContentType } from "mobility-toolbox-js/types";
|
|
18
|
+
|
|
19
|
+
const toShortDate = (
|
|
20
|
+
date: Date,
|
|
21
|
+
showTime?: boolean,
|
|
22
|
+
showYear?: boolean,
|
|
23
|
+
useShortMonth = true,
|
|
24
|
+
) => {
|
|
25
|
+
const time = date.toLocaleTimeString(["de"], {
|
|
26
|
+
hour: "2-digit",
|
|
27
|
+
minute: "2-digit",
|
|
28
|
+
});
|
|
29
|
+
const dateString = date.toLocaleDateString(["de"], {
|
|
30
|
+
day: "2-digit",
|
|
31
|
+
month: useShortMonth ? "short" : "long",
|
|
32
|
+
// weekday: "short",
|
|
33
|
+
year: showYear ? "numeric" : undefined,
|
|
34
|
+
});
|
|
35
|
+
|
|
36
|
+
return `${dateString}${showTime && !!time ? ` ${time}` : ""}`.replace(
|
|
37
|
+
",",
|
|
38
|
+
"",
|
|
39
|
+
);
|
|
40
|
+
};
|
|
41
|
+
|
|
42
|
+
function SituationDetails({
|
|
43
|
+
canToggle = false,
|
|
44
|
+
headerClassName,
|
|
45
|
+
iconClassName,
|
|
46
|
+
publicationClassName,
|
|
47
|
+
reasonClassName,
|
|
48
|
+
situation,
|
|
49
|
+
timeIntervalClassName,
|
|
50
|
+
useShortMonth = true,
|
|
51
|
+
}: {
|
|
52
|
+
canToggle?: boolean;
|
|
53
|
+
headerClassName?: string;
|
|
54
|
+
iconClassName?: string;
|
|
55
|
+
publicationClassName?: string;
|
|
56
|
+
reasonClassName?: string;
|
|
57
|
+
situation?: Partial<SituationType>;
|
|
58
|
+
timeIntervalClassName?: string;
|
|
59
|
+
useShortMonth?: boolean;
|
|
60
|
+
}) {
|
|
61
|
+
const { locale, t } = useI18n();
|
|
62
|
+
const [isOpen, setIsOpen] = useState(!canToggle);
|
|
63
|
+
|
|
64
|
+
if (!situation) {
|
|
65
|
+
return null;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
// moco export v2
|
|
69
|
+
let textualContentMultilingual: Partial<MultilingualTextualContentType> = {};
|
|
70
|
+
let textualContent: Partial<TextualContentType> = {};
|
|
71
|
+
let timeIntervalsToDisplay = [];
|
|
72
|
+
let publicationsToDisplay: PublicationType[] = [];
|
|
73
|
+
let reasonsToDisplay: string[] = [];
|
|
74
|
+
|
|
75
|
+
const publicationsArr: PublicationType[] = situation?.publications || [];
|
|
76
|
+
|
|
77
|
+
// Find the current publication(s) at the current date
|
|
78
|
+
publicationsToDisplay =
|
|
79
|
+
publicationsArr?.filter(({ publicationWindows }) => {
|
|
80
|
+
return publicationWindows.find(({ endTime, startTime }) => {
|
|
81
|
+
const now = new Date();
|
|
82
|
+
const startT = new Date(startTime);
|
|
83
|
+
const endT = new Date(endTime);
|
|
84
|
+
return startT <= now && now <= endT;
|
|
85
|
+
});
|
|
86
|
+
}) || [];
|
|
87
|
+
|
|
88
|
+
// Display the current and next affected time intervals not the one in the past
|
|
89
|
+
timeIntervalsToDisplay =
|
|
90
|
+
(situation?.affectedTimeIntervals || []).filter(
|
|
91
|
+
({ endTime, startTime }) => {
|
|
92
|
+
const now = new Date();
|
|
93
|
+
const startT = new Date(startTime);
|
|
94
|
+
const endT = new Date(endTime);
|
|
95
|
+
return (startT <= now && now <= endT) || now < startT;
|
|
96
|
+
},
|
|
97
|
+
) || [];
|
|
98
|
+
|
|
99
|
+
// Display the reasons
|
|
100
|
+
reasonsToDisplay = (situation?.reasons || []).map(({ name }) => {
|
|
101
|
+
return name;
|
|
102
|
+
});
|
|
103
|
+
|
|
104
|
+
return (
|
|
105
|
+
<>
|
|
106
|
+
{publicationsToDisplay?.map(
|
|
107
|
+
({
|
|
108
|
+
id,
|
|
109
|
+
publicationLines,
|
|
110
|
+
publicationStops,
|
|
111
|
+
textualContentLarge,
|
|
112
|
+
textualContentMedium,
|
|
113
|
+
textualContentSmall,
|
|
114
|
+
}) => {
|
|
115
|
+
// Get the textual content in German
|
|
116
|
+
textualContentMultilingual =
|
|
117
|
+
textualContentLarge || textualContentMedium || textualContentSmall;
|
|
118
|
+
|
|
119
|
+
textualContent = textualContentMultilingual?.[locale()];
|
|
120
|
+
|
|
121
|
+
// Fallback to default language if there is not title in the current language
|
|
122
|
+
if (!textualContent?.summary) {
|
|
123
|
+
textualContent = textualContentMultilingual?.de;
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
const pubLines =
|
|
127
|
+
publicationLines?.flatMap((publication) => {
|
|
128
|
+
return (
|
|
129
|
+
publication.lines?.map(({ name }) => {
|
|
130
|
+
return name;
|
|
131
|
+
}) || []
|
|
132
|
+
);
|
|
133
|
+
}) || [];
|
|
134
|
+
|
|
135
|
+
const stations = publicationStops.map((publication) => {
|
|
136
|
+
return publication?.name || "";
|
|
137
|
+
});
|
|
138
|
+
|
|
139
|
+
return (
|
|
140
|
+
<div
|
|
141
|
+
className={twMerge("text-base", publicationClassName)}
|
|
142
|
+
key={id}
|
|
143
|
+
>
|
|
144
|
+
<div className={twMerge("text-xs uppercase", reasonClassName)}>
|
|
145
|
+
{reasonsToDisplay}
|
|
146
|
+
</div>
|
|
147
|
+
<h3
|
|
148
|
+
className={twMerge(
|
|
149
|
+
"space-x-2 text-xl font-bold text-balance",
|
|
150
|
+
headerClassName,
|
|
151
|
+
)}
|
|
152
|
+
>
|
|
153
|
+
<span className={"line-height-[1.3] inline-block align-middle"}>
|
|
154
|
+
<Warning className={twMerge(iconClassName)} />
|
|
155
|
+
</span>
|
|
156
|
+
<span
|
|
157
|
+
className={"*:inline"}
|
|
158
|
+
dangerouslySetInnerHTML={{
|
|
159
|
+
__html: textualContent?.summary,
|
|
160
|
+
}}
|
|
161
|
+
></span>
|
|
162
|
+
</h3>
|
|
163
|
+
<hr className="my-1" />
|
|
164
|
+
|
|
165
|
+
{timeIntervalsToDisplay?.map(
|
|
166
|
+
(
|
|
167
|
+
{
|
|
168
|
+
dailyEndTime,
|
|
169
|
+
dailyStartTime,
|
|
170
|
+
endTime,
|
|
171
|
+
startTime,
|
|
172
|
+
}: AffectedTimeIntervalType,
|
|
173
|
+
index,
|
|
174
|
+
) => {
|
|
175
|
+
const hasDailyTime = dailyEndTime && dailyStartTime;
|
|
176
|
+
// const isStartCurrentYear =
|
|
177
|
+
// new Date().getFullYear() ===
|
|
178
|
+
// new Date(startTime).getFullYear();
|
|
179
|
+
// const isEndCurrentYear =
|
|
180
|
+
// new Date().getFullYear() ===
|
|
181
|
+
// new Date(endTime).getFullYear();
|
|
182
|
+
const isEndInfinite = endTime.includes("2500");
|
|
183
|
+
|
|
184
|
+
const from = toShortDate(
|
|
185
|
+
new Date(startTime),
|
|
186
|
+
false,
|
|
187
|
+
true,
|
|
188
|
+
useShortMonth,
|
|
189
|
+
);
|
|
190
|
+
const to = !isEndInfinite
|
|
191
|
+
? toShortDate(new Date(endTime), false, true, useShortMonth)
|
|
192
|
+
: undefined;
|
|
193
|
+
|
|
194
|
+
return (
|
|
195
|
+
<button
|
|
196
|
+
className={twMerge(
|
|
197
|
+
"flex w-full items-center",
|
|
198
|
+
canToggle && "cursor-pointer",
|
|
199
|
+
)}
|
|
200
|
+
key={startTime}
|
|
201
|
+
onClick={() => {
|
|
202
|
+
setIsOpen(!isOpen);
|
|
203
|
+
}}
|
|
204
|
+
tabIndex={canToggle ? 0 : -1}
|
|
205
|
+
>
|
|
206
|
+
<div
|
|
207
|
+
className={twMerge(
|
|
208
|
+
"flex-1 text-left text-sm font-bold text-balance",
|
|
209
|
+
timeIntervalClassName,
|
|
210
|
+
)}
|
|
211
|
+
key={startTime}
|
|
212
|
+
>
|
|
213
|
+
<span>
|
|
214
|
+
{from && to ? `${from} - ${to}` : null}
|
|
215
|
+
{from && !to ? t("from", { from }) : null}
|
|
216
|
+
</span>
|
|
217
|
+
{hasDailyTime && (
|
|
218
|
+
<span>{` (${t("daily_from_to", {
|
|
219
|
+
from: dailyStartTime,
|
|
220
|
+
to: dailyEndTime,
|
|
221
|
+
})})`}</span>
|
|
222
|
+
)}
|
|
223
|
+
</div>
|
|
224
|
+
{canToggle && index == 0 && (
|
|
225
|
+
<>
|
|
226
|
+
{isOpen ? (
|
|
227
|
+
<ArrowUp className="text-red" />
|
|
228
|
+
) : (
|
|
229
|
+
<ArrowDown className="text-red" />
|
|
230
|
+
)}
|
|
231
|
+
</>
|
|
232
|
+
)}
|
|
233
|
+
</button>
|
|
234
|
+
);
|
|
235
|
+
},
|
|
236
|
+
)}
|
|
237
|
+
<div className={isOpen ? "my-4 flex flex-col gap-4" : "hidden"}>
|
|
238
|
+
<div
|
|
239
|
+
dangerouslySetInnerHTML={{
|
|
240
|
+
__html:
|
|
241
|
+
textualContent?.description || t("no_details_available"),
|
|
242
|
+
}}
|
|
243
|
+
/>
|
|
244
|
+
{!!textualContentMultilingual?.images?.length && (
|
|
245
|
+
<div className="flex flex-wrap gap-2">
|
|
246
|
+
{textualContentMultilingual.images.map(
|
|
247
|
+
({ image: { absoluteUrl, label } }) => {
|
|
248
|
+
return (
|
|
249
|
+
<img
|
|
250
|
+
alt={label}
|
|
251
|
+
key={absoluteUrl + label}
|
|
252
|
+
src={absoluteUrl}
|
|
253
|
+
title={label}
|
|
254
|
+
/>
|
|
255
|
+
);
|
|
256
|
+
},
|
|
257
|
+
)}
|
|
258
|
+
</div>
|
|
259
|
+
)}
|
|
260
|
+
{!!textualContentMultilingual?.infoLinks?.length && (
|
|
261
|
+
<div>
|
|
262
|
+
{textualContentMultilingual.infoLinks.map(
|
|
263
|
+
({ label, uri }) => {
|
|
264
|
+
const title = label[locale()] || label.de || uri;
|
|
265
|
+
return (
|
|
266
|
+
<Link href={uri} key={uri} title={title}>
|
|
267
|
+
{title}
|
|
268
|
+
</Link>
|
|
269
|
+
);
|
|
270
|
+
},
|
|
271
|
+
)}
|
|
272
|
+
</div>
|
|
273
|
+
)}
|
|
274
|
+
|
|
275
|
+
{!!pubLines?.length && (
|
|
276
|
+
<div>
|
|
277
|
+
<div className={"font-bold"}>{t("affected_lines")}:</div>
|
|
278
|
+
<div className={"flex flex-wrap gap-1 text-sm"}>
|
|
279
|
+
{pubLines?.map((name) => {
|
|
280
|
+
return (
|
|
281
|
+
<div
|
|
282
|
+
className={
|
|
283
|
+
"bg-red rounded-md px-2 py-1 font-bold text-white"
|
|
284
|
+
}
|
|
285
|
+
key={name}
|
|
286
|
+
>
|
|
287
|
+
{name}
|
|
288
|
+
</div>
|
|
289
|
+
);
|
|
290
|
+
})}
|
|
291
|
+
</div>
|
|
292
|
+
</div>
|
|
293
|
+
)}
|
|
294
|
+
|
|
295
|
+
{!!stations?.length && (
|
|
296
|
+
<div>
|
|
297
|
+
<div className={"font-bold"}>{t("affected_stops")}:</div>
|
|
298
|
+
<div className={"flex flex-wrap gap-1 text-sm"}>
|
|
299
|
+
{stations.map((name) => {
|
|
300
|
+
return (
|
|
301
|
+
<div
|
|
302
|
+
className={
|
|
303
|
+
"bg-red rounded-md px-2 py-1 font-bold text-white"
|
|
304
|
+
}
|
|
305
|
+
key={name}
|
|
306
|
+
>
|
|
307
|
+
{name}
|
|
308
|
+
</div>
|
|
309
|
+
);
|
|
310
|
+
})}
|
|
311
|
+
</div>
|
|
312
|
+
</div>
|
|
313
|
+
)}
|
|
314
|
+
</div>
|
|
315
|
+
{canToggle && <hr className="my-1" />}
|
|
316
|
+
</div>
|
|
317
|
+
);
|
|
318
|
+
},
|
|
319
|
+
)}
|
|
320
|
+
</>
|
|
321
|
+
);
|
|
322
|
+
}
|
|
323
|
+
|
|
324
|
+
export default memo(SituationDetails);
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { default } from "./SituationDetails";
|
package/src/index.tsx
CHANGED
|
@@ -2,9 +2,13 @@ import register from "preact-custom-element";
|
|
|
2
2
|
|
|
3
3
|
import MobilityMap from "./MobilityMap";
|
|
4
4
|
import MobilityMapAttributes from "./MobilityMap/MobilityMapAttributes";
|
|
5
|
+
import MobilityNotifications, {
|
|
6
|
+
MobilityNotificationsAttributes,
|
|
7
|
+
} from "./MobilityNotifications";
|
|
5
8
|
import MobilitySearch, { MobilitySearchAttributes } from "./MobilitySearch";
|
|
6
9
|
|
|
7
10
|
import type { MobilityMapAttributeName } from "./MobilityMap/MobilityMapAttributes";
|
|
11
|
+
import type { MobilityNotificationsAttributeName } from "./MobilityNotifications/MobilityNotificationsAttributes";
|
|
8
12
|
import type { MobilitySearchAttributeName } from "./MobilitySearch/MobilitySearchAttributes";
|
|
9
13
|
|
|
10
14
|
register(
|
|
@@ -26,3 +30,15 @@ register(
|
|
|
26
30
|
shadow: true,
|
|
27
31
|
},
|
|
28
32
|
);
|
|
33
|
+
|
|
34
|
+
register(
|
|
35
|
+
MobilityNotifications,
|
|
36
|
+
"geops-mobility-notifications",
|
|
37
|
+
Object.keys(
|
|
38
|
+
MobilityNotificationsAttributes,
|
|
39
|
+
) as MobilityNotificationsAttributeName[],
|
|
40
|
+
{
|
|
41
|
+
mode: "open",
|
|
42
|
+
shadow: true,
|
|
43
|
+
},
|
|
44
|
+
);
|
package/src/indexDoc.ts
CHANGED
|
@@ -1,13 +1,35 @@
|
|
|
1
1
|
// This file contains what we need to build the documentation in the index.html file
|
|
2
2
|
|
|
3
3
|
import MobilityMapAttributes from "./MobilityMap/MobilityMapAttributes";
|
|
4
|
+
import MobilityMapEvents from "./MobilityMap/MobilityMapEvents";
|
|
5
|
+
import MobilityNotificationsAttributes from "./MobilityNotifications/MobilityNotificationsAttributes";
|
|
4
6
|
import MobilitySearchAttributes from "./MobilitySearch/MobilitySearchAttributes";
|
|
7
|
+
import MobilitySearchEvents from "./MobilitySearch/MobilitySearchEvents";
|
|
8
|
+
|
|
9
|
+
declare global {
|
|
10
|
+
interface Window {
|
|
11
|
+
MobilityMapAttributes?: typeof MobilityMapAttributes;
|
|
12
|
+
MobilityMapEvents?: typeof MobilityMapEvents;
|
|
13
|
+
MobilityNotificationsAttributes?: typeof MobilityNotificationsAttributes;
|
|
14
|
+
MobilitySearchAttributes?: typeof MobilitySearchAttributes;
|
|
15
|
+
MobilitySearchEvents?: typeof MobilitySearchEvents;
|
|
16
|
+
}
|
|
17
|
+
}
|
|
5
18
|
|
|
6
19
|
if (typeof window !== "undefined") {
|
|
7
|
-
// @ts-expect-error it's wanted
|
|
8
20
|
window.MobilityMapAttributes = MobilityMapAttributes;
|
|
9
|
-
|
|
21
|
+
window.MobilityMapEvents = MobilityMapEvents;
|
|
22
|
+
|
|
10
23
|
window.MobilitySearchAttributes = MobilitySearchAttributes;
|
|
24
|
+
window.MobilitySearchEvents = MobilitySearchEvents;
|
|
25
|
+
|
|
26
|
+
window.MobilityNotificationsAttributes = MobilityNotificationsAttributes;
|
|
11
27
|
}
|
|
12
28
|
|
|
13
|
-
export default {
|
|
29
|
+
export default {
|
|
30
|
+
MobilityMapAttributes,
|
|
31
|
+
MobilityMapEvents,
|
|
32
|
+
MobilityNotificationsAttributes,
|
|
33
|
+
MobilitySearchAttributes,
|
|
34
|
+
MobilitySearchEvents,
|
|
35
|
+
};
|
|
@@ -5,12 +5,12 @@ import ok from "../../icons/Ok/ok-grey.svg";
|
|
|
5
5
|
|
|
6
6
|
import type { InputHTMLAttributes } from "preact";
|
|
7
7
|
|
|
8
|
-
export type
|
|
8
|
+
export type CheckboxProps = {
|
|
9
9
|
checkedIconUrl?: string;
|
|
10
10
|
className?: string;
|
|
11
11
|
} & InputHTMLAttributes;
|
|
12
12
|
|
|
13
|
-
function Checkbox({ className, ...props }:
|
|
13
|
+
function Checkbox({ className, ...props }: CheckboxProps) {
|
|
14
14
|
const checkedIconUrl = props.checkedIconUrl || ok;
|
|
15
15
|
return (
|
|
16
16
|
<input
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
import { memo, useMemo } from "preact/compat";
|
|
2
|
+
import { twMerge } from "tailwind-merge";
|
|
3
|
+
|
|
4
|
+
import ArrowUpRight from "../../icons/ArrowUpRight";
|
|
5
|
+
|
|
6
|
+
import type { AnchorHTMLAttributes, PreactDOMAttributes } from "preact";
|
|
7
|
+
|
|
8
|
+
export type LinkProps = {
|
|
9
|
+
className?: string;
|
|
10
|
+
theme?: "primary" | "secondary";
|
|
11
|
+
} & AnchorHTMLAttributes<HTMLAnchorElement> &
|
|
12
|
+
PreactDOMAttributes;
|
|
13
|
+
|
|
14
|
+
const baseClasses =
|
|
15
|
+
"my-1 flex items-center leading-[1.1] underline text-[14px] @sm/main:text-[16px] @md/main:text-[18px] items-center justify-left font-semibold";
|
|
16
|
+
|
|
17
|
+
export const themes = {
|
|
18
|
+
primary: {
|
|
19
|
+
classes:
|
|
20
|
+
"bg-red text-white disabled:bg-lightgrey hover:bg-darkred hover:border-darkred active:bg-lightred active:border-lightred ",
|
|
21
|
+
selectedClasses: "bg-darkred border-darkred",
|
|
22
|
+
},
|
|
23
|
+
secondary: {
|
|
24
|
+
classes: "bg-white text-black hover:text-red active:text-lightred",
|
|
25
|
+
selectedClasses: "text-red",
|
|
26
|
+
},
|
|
27
|
+
};
|
|
28
|
+
|
|
29
|
+
function Link({
|
|
30
|
+
children,
|
|
31
|
+
className,
|
|
32
|
+
theme = "secondary",
|
|
33
|
+
...props
|
|
34
|
+
}: LinkProps) {
|
|
35
|
+
const classes = useMemo(() => {
|
|
36
|
+
return twMerge(
|
|
37
|
+
`${baseClasses} ${themes[theme].classes} ${className || ""}`,
|
|
38
|
+
);
|
|
39
|
+
}, [className, theme]);
|
|
40
|
+
|
|
41
|
+
return (
|
|
42
|
+
<a className={classes} rel="noreferrer" target="_blank" {...props}>
|
|
43
|
+
{children}
|
|
44
|
+
<ArrowUpRight />
|
|
45
|
+
</a>
|
|
46
|
+
);
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
export default memo(Link);
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { default } from "../../RvfLink";
|
package/src/ui/Select/Select.tsx
CHANGED
|
@@ -2,12 +2,12 @@ import ArrowDown from "../../icons/ArrowDown";
|
|
|
2
2
|
|
|
3
3
|
import type { HTMLAttributes, PreactDOMAttributes } from "preact";
|
|
4
4
|
|
|
5
|
-
export type
|
|
5
|
+
export type SelectProps = {
|
|
6
6
|
className?: string;
|
|
7
7
|
} & HTMLAttributes<HTMLSelectElement> &
|
|
8
8
|
PreactDOMAttributes;
|
|
9
9
|
|
|
10
|
-
function Select({ children, className, onChange }:
|
|
10
|
+
function Select({ children, className, onChange }: SelectProps) {
|
|
11
11
|
return (
|
|
12
12
|
<div className="text-grey relative flex items-center">
|
|
13
13
|
<select
|
package/src/utils/constants.ts
CHANGED
|
@@ -15,6 +15,7 @@ export const LAYER_NAME_REALTIME = "echtzeit";
|
|
|
15
15
|
export const LAYER_NAME_STATIONS = "haltestellen";
|
|
16
16
|
export const LAYER_NAME_NOTIFICATIONS = "meldungen";
|
|
17
17
|
export const LAYER_NAME_LINESNETWORKPLAN = "liniennetz";
|
|
18
|
+
export const LAYER_NAME_MAPSET = "mapset";
|
|
18
19
|
|
|
19
20
|
export const RVF_EXTENT_4326 = [7.5, 47.7, 8.45, 48.4];
|
|
20
21
|
|
|
@@ -42,6 +43,7 @@ export const LAYERS_NAMES = {
|
|
|
42
43
|
carOthers: "autoandere",
|
|
43
44
|
eroller: "e-roller",
|
|
44
45
|
linesnetworkplan: LAYER_NAME_LINESNETWORKPLAN,
|
|
46
|
+
mapset: LAYER_NAME_MAPSET,
|
|
45
47
|
mitfahrpunkte: "mitfahrpunkte",
|
|
46
48
|
notifications: LAYER_NAME_NOTIFICATIONS,
|
|
47
49
|
pois: "pois",
|
|
@@ -57,6 +59,7 @@ export const DEFAULT_VISIBLE_LAYERS = Object.values(LAYERS_NAMES);
|
|
|
57
59
|
export const DEFAULT_QUERYABLE_LAYERS = Object.values(LAYERS_NAMES).filter(
|
|
58
60
|
(name) => {
|
|
59
61
|
return ![
|
|
62
|
+
LAYERS_NAMES.mapset,
|
|
60
63
|
LAYERS_NAMES.mitfahrpunkte,
|
|
61
64
|
LAYERS_NAMES.pois,
|
|
62
65
|
LAYERS_NAMES.stations,
|
|
@@ -65,6 +68,19 @@ export const DEFAULT_QUERYABLE_LAYERS = Object.values(LAYERS_NAMES).filter(
|
|
|
65
68
|
},
|
|
66
69
|
);
|
|
67
70
|
|
|
71
|
+
// Order of the first level
|
|
72
|
+
export const LAYER_TREE_ORDER = [
|
|
73
|
+
LAYER_NAME_NOTIFICATIONS,
|
|
74
|
+
LAYER_NAME_REALTIME,
|
|
75
|
+
LAYER_NAME_STATIONS,
|
|
76
|
+
LAYER_NAME_LINESNETWORKPLAN,
|
|
77
|
+
LAYERS_NAMES.tarifzonen,
|
|
78
|
+
LAYERS_NAMES.verkaufsstellen,
|
|
79
|
+
LAYERS_NAMES.pois,
|
|
80
|
+
LAYERS_NAMES.sharedMobility,
|
|
81
|
+
LAYER_NAME_MAPSET,
|
|
82
|
+
];
|
|
83
|
+
|
|
68
84
|
export const LAYERS_WITH_LINK = Object.values(LAYERS_NAMES).filter((name) => {
|
|
69
85
|
return (
|
|
70
86
|
name !== LAYERS_NAMES.tarifzonen &&
|
|
@@ -195,3 +211,24 @@ export const LINE_COLOR_BY_NAME = {
|
|
|
195
211
|
"4": "#EA5297",
|
|
196
212
|
"5": "#008BC5",
|
|
197
213
|
};
|
|
214
|
+
|
|
215
|
+
export const EXPORT_PREFIX = "rvf";
|
|
216
|
+
|
|
217
|
+
// Lines network plans
|
|
218
|
+
|
|
219
|
+
// The property used as identifier for a line, this id is used as key in the geops.lnp.lines metadata of the network plan source.
|
|
220
|
+
// See https://maps.test.geops.io/data/network_plans_trenord.json style for an example.
|
|
221
|
+
export const LNP_LINE_ID_PROP = "original_line_id";
|
|
222
|
+
|
|
223
|
+
// LNP data source id in the style
|
|
224
|
+
export const LNP_SOURCE_ID = "network_plans";
|
|
225
|
+
|
|
226
|
+
// Metadata key in the lnp data source
|
|
227
|
+
export const LNP_MD_LINES = "geops.lnp.lines";
|
|
228
|
+
export const LNP_MD_STOPS = "geops.lnp.stops";
|
|
229
|
+
|
|
230
|
+
// LNP style metadata filter to use to show/hide highlight
|
|
231
|
+
export const LNP_GEOPS_FILTER_HIGHLIGHT = "highlightnetzplan";
|
|
232
|
+
|
|
233
|
+
// LNP style layer id where the dynamic filtering will apply
|
|
234
|
+
export const LNP_LAYER_ID_HIGHLIGHT = "netzplan_highlight_trip";
|
package/src/utils/exportPdf.ts
CHANGED
|
@@ -4,6 +4,8 @@ import { Map } from "ol";
|
|
|
4
4
|
import { getBottomRight, getTopLeft } from "ol/extent";
|
|
5
5
|
import View from "ol/View";
|
|
6
6
|
|
|
7
|
+
import { EXPORT_PREFIX } from "./constants";
|
|
8
|
+
|
|
7
9
|
import type { jsPDFOptions } from "jspdf";
|
|
8
10
|
import type { Coordinate } from "ol/coordinate";
|
|
9
11
|
import type { Extent } from "ol/extent";
|
|
@@ -654,7 +656,7 @@ async function exportPdf(
|
|
|
654
656
|
doc.addImage(canvas, "JPEG", 0, 0, sizePt[0], sizePt[1]);
|
|
655
657
|
|
|
656
658
|
// Download the pdf
|
|
657
|
-
doc.save(
|
|
659
|
+
doc.save(`${EXPORT_PREFIX}-${new Date().toISOString().slice(0, 10)}.pdf`);
|
|
658
660
|
} catch (error) {
|
|
659
661
|
console.error(error);
|
|
660
662
|
return false;
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import { LNP_LAYER_ID_HIGHLIGHT, LNP_LINE_ID_PROP } from "./constants";
|
|
2
|
+
|
|
3
|
+
import type { MaplibreLayer } from "mobility-toolbox-js/ol";
|
|
4
|
+
|
|
5
|
+
function highlightLinesNetworkPlan(
|
|
6
|
+
ids: number[] | string[] = [0],
|
|
7
|
+
baseLayer: MaplibreLayer,
|
|
8
|
+
) {
|
|
9
|
+
try {
|
|
10
|
+
const styleLayer = baseLayer?.mapLibreMap?.getLayer(LNP_LAYER_ID_HIGHLIGHT);
|
|
11
|
+
if (styleLayer) {
|
|
12
|
+
baseLayer?.mapLibreMap?.setFilter(styleLayer.id, [
|
|
13
|
+
"match",
|
|
14
|
+
["get", LNP_LINE_ID_PROP],
|
|
15
|
+
ids,
|
|
16
|
+
true,
|
|
17
|
+
false,
|
|
18
|
+
]);
|
|
19
|
+
}
|
|
20
|
+
} catch (e) {
|
|
21
|
+
// eslint-disable-next-line no-console
|
|
22
|
+
console.error("Error setting filter for highlight layer", e);
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
export default highlightLinesNetworkPlan;
|
|
@@ -1,12 +1,14 @@
|
|
|
1
1
|
import { createContext } from "preact";
|
|
2
2
|
import { useContext } from "preact/hooks";
|
|
3
3
|
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
}
|
|
4
|
+
import type { Rosetta } from "rosetta";
|
|
5
|
+
|
|
6
|
+
import type { Translations } from "../translations";
|
|
7
|
+
|
|
8
|
+
export type I18NContextType = Rosetta<Translations>;
|
|
7
9
|
|
|
8
10
|
export const I18nContext = createContext({
|
|
9
|
-
t: (id, templateValues) => {
|
|
11
|
+
t: (id: string, templateValues?: Record<string, string>) => {
|
|
10
12
|
return `${id} ${JSON.stringify(templateValues)}`;
|
|
11
13
|
},
|
|
12
14
|
} as I18NContextType);
|
|
@@ -5,6 +5,7 @@ import { useContext } from "preact/hooks";
|
|
|
5
5
|
import type {
|
|
6
6
|
MaplibreLayer,
|
|
7
7
|
MaplibreStyleLayer,
|
|
8
|
+
MapsetLayer,
|
|
8
9
|
MocoLayer,
|
|
9
10
|
RealtimeLayer,
|
|
10
11
|
} from "mobility-toolbox-js/ol";
|
|
@@ -28,6 +29,7 @@ export type MapContextType = {
|
|
|
28
29
|
hasGeolocation: boolean;
|
|
29
30
|
hasLayerTree: boolean;
|
|
30
31
|
hasLnp: boolean;
|
|
32
|
+
hasMapset: boolean;
|
|
31
33
|
hasNotification: boolean;
|
|
32
34
|
hasPermalink: boolean;
|
|
33
35
|
hasPrint: boolean;
|
|
@@ -44,8 +46,10 @@ export type MapContextType = {
|
|
|
44
46
|
isSearchOpen: boolean;
|
|
45
47
|
isShareMenuOpen: boolean;
|
|
46
48
|
isTracking: boolean;
|
|
49
|
+
linesIds: string[];
|
|
47
50
|
linesNetworkPlanLayer: MaplibreStyleLayer;
|
|
48
51
|
map: Map;
|
|
52
|
+
mapsetLayer?: MapsetLayer;
|
|
49
53
|
notificationsLayer?: MocoLayer;
|
|
50
54
|
permalinkUrlSearchParams: URLSearchParams;
|
|
51
55
|
previewNotifications?: SituationType[];
|
|
@@ -61,6 +65,7 @@ export type MapContextType = {
|
|
|
61
65
|
setHasGeolocation: (hasGeolocation: boolean) => void;
|
|
62
66
|
setHasLayerTree: (hasLayerTree: boolean) => void;
|
|
63
67
|
setHasLnp: (hasLnp: boolean) => void;
|
|
68
|
+
setHasMapset: (hasMapset: boolean) => void;
|
|
64
69
|
setHasNotification: (hasNotification: boolean) => void;
|
|
65
70
|
setHasPermalink: (hasPermalink: boolean) => void;
|
|
66
71
|
setHasPrint: (hasPrint: boolean) => void;
|
|
@@ -77,8 +82,10 @@ export type MapContextType = {
|
|
|
77
82
|
setIsSearchOpen: (isSearchOpen: boolean) => void;
|
|
78
83
|
setIsShareMenuOpen: (isShareMenuOpen: boolean) => void;
|
|
79
84
|
setIsTracking: (isTracking: boolean) => void;
|
|
85
|
+
setLinesIds: (linesIds: string[]) => void;
|
|
80
86
|
setLinesNetworkPlanLayer: (layer?: MaplibreStyleLayer) => void;
|
|
81
87
|
setMap: (map?: Map) => void;
|
|
88
|
+
setMapsetLayer: (mapsetLayer?: MapsetLayer) => void;
|
|
82
89
|
setNotificationsLayer: (notificationsLayer?: MocoLayer) => void;
|
|
83
90
|
setPermalinkUrlSearchParams: (
|
|
84
91
|
setPermalinkUrlSearchParams: URLSearchParams,
|
|
@@ -104,6 +111,7 @@ export const MapContext = createContext<MapContextType>({
|
|
|
104
111
|
hasGeolocation: false,
|
|
105
112
|
hasLayerTree: false,
|
|
106
113
|
hasLnp: false,
|
|
114
|
+
hasMapset: false,
|
|
107
115
|
hasNotification: false,
|
|
108
116
|
hasPermalink: false,
|
|
109
117
|
hasPrint: false,
|
|
@@ -126,6 +134,7 @@ export const MapContext = createContext<MapContextType>({
|
|
|
126
134
|
setIsTracking: (isTracking: boolean) => {},
|
|
127
135
|
setLinesNetworkPlanLayer: (linesNetworkPlanLayer: MaplibreStyleLayer) => {},
|
|
128
136
|
setMap: (map?: Map) => {},
|
|
137
|
+
setMapsetLayer: (mapsetLayer?: MapsetLayer) => {},
|
|
129
138
|
setNotificationsLayer: (notificationsLayer?: MocoLayer) => {},
|
|
130
139
|
setPermalinkUrlSearchParams: (
|
|
131
140
|
permalinkUrlSearchParams: URLSearchParams,
|
|
@@ -5,7 +5,7 @@ import { RVF_EXTENT_4326 } from "./constants";
|
|
|
5
5
|
import type { FeatureCollection, Point } from "geojson";
|
|
6
6
|
import type { Extent } from "ol/extent";
|
|
7
7
|
|
|
8
|
-
const GQL_URL = "https://api.mobidata-bw.de/sharing/graphql";
|
|
8
|
+
export const GQL_URL = "https://api.mobidata-bw.de/sharing/graphql";
|
|
9
9
|
|
|
10
10
|
export interface SharingStation {
|
|
11
11
|
id: string;
|