@geops/rvf-mobility-web-component 0.1.60 → 0.1.62

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 (73) hide show
  1. package/CHANGELOG.md +47 -0
  2. package/README.md +1 -0
  3. package/docutils.js +103 -4
  4. package/fonts/source-sans-3/source-sans-3-v15-latin-500.ttf +0 -0
  5. package/fonts/source-sans-3/source-sans-3-v15-latin-500.woff2 +0 -0
  6. package/fonts/source-sans-3/source-sans-3-v15-latin-600.ttf +0 -0
  7. package/fonts/source-sans-3/source-sans-3-v15-latin-600.woff2 +0 -0
  8. package/fonts/source-sans-3/source-sans-3-v15-latin-700.ttf +0 -0
  9. package/fonts/source-sans-3/source-sans-3-v15-latin-700.woff2 +0 -0
  10. package/fonts/source-sans-3/source-sans-3-v15-latin-regular.ttf +0 -0
  11. package/fonts/source-sans-3/source-sans-3-v15-latin-regular.woff2 +0 -0
  12. package/index.html +28 -94
  13. package/index.js +235 -224
  14. package/notifications.html +144 -0
  15. package/package.json +2 -2
  16. package/search.html +24 -65
  17. package/src/FeatureDetails/FeatureDetails.tsx +20 -6
  18. package/src/FeaturesInfosListener/FeaturesInfosListener.tsx +2 -0
  19. package/src/LayerTree/TreeItem/TreeItem.tsx +2 -2
  20. package/src/LayerTreeMenu/LayerTreeMenu.tsx +19 -3
  21. package/src/LayoutState/LayoutState.tsx +17 -0
  22. package/src/LinesNetworkPlanDetails/LinesNetworkPlanDetails.tsx +34 -21
  23. package/src/LinesNetworkPlanLayer/LinesNetworkPlanLayer.tsx +3 -6
  24. package/src/LinesNetworkPlanLayerHighlight/LinesNetworkPlanLayerHighlight.tsx +88 -0
  25. package/src/LinesNetworkPlanLayerHighlight/index.tsx +1 -0
  26. package/src/MapDispatchEvents/MapDispatchEvents.tsx +6 -4
  27. package/src/MapLayout/MapLayout.tsx +2 -2
  28. package/src/MapsetLayer/MapsetLayer.tsx +116 -0
  29. package/src/MapsetLayer/index.tsx +1 -0
  30. package/src/MobilityMap/MobilityMap.tsx +27 -5
  31. package/src/MobilityMap/MobilityMapAttributes.test.ts +38 -0
  32. package/src/MobilityMap/MobilityMapAttributes.ts +99 -22
  33. package/src/MobilityMap/MobilityMapEvents.ts +53 -0
  34. package/src/MobilityNotifications/MobilityNotifications.tsx +93 -0
  35. package/src/MobilityNotifications/MobilityNotificationsAttributes.test.ts +21 -0
  36. package/src/MobilityNotifications/MobilityNotificationsAttributes.ts +46 -0
  37. package/src/MobilityNotifications/index.ts +2 -0
  38. package/src/MobilitySearch/MobilitySearchEvents.ts +21 -0
  39. package/src/NotificationDetails/NotificationDetails.tsx +74 -251
  40. package/src/OverlayContent/OverlayContent.tsx +1 -1
  41. package/src/OverlayDetails/OverlayDetails.tsx +4 -2
  42. package/src/OverlayFooter/OverlayFooter.tsx +3 -2
  43. package/src/RealtimeLayer/RealtimeLayer.tsx +36 -7
  44. package/src/RouteScheduleFooter/RouteScheduleFooter.tsx +1 -2
  45. package/src/RvfFeatureDetails/RvfFeatureDetails.tsx +2 -2
  46. package/src/RvfFeatureDetails/RvfLineNetworkDetails/RvfLineNetworkDetails.tsx +2 -0
  47. package/src/RvfFeatureDetails/RvfNotificationDetails/RvfNotificationDetails.tsx +12 -453
  48. package/src/RvfFeatureDetails/RvfSellingPointDetails/RvfSellingPointDetails.tsx +20 -17
  49. package/src/RvfFeatureDetails/RvfSharedMobilityDetail/RvfSharedMobilityDetails.tsx +93 -36
  50. package/src/RvfLink/RvfLink.tsx +5 -2
  51. package/src/RvfMobilityMap/RvfMobilityMap.tsx +28 -11
  52. package/src/RvfSelectedFeatureHighlightLayer/RvfSelectedFeatureHighlightLayer.tsx +9 -5
  53. package/src/RvfSingleClickListener/RvfSingleClickListener.tsx +3 -1
  54. package/src/SituationDetails/SituationDetails.tsx +324 -0
  55. package/src/SituationDetails/index.ts +1 -0
  56. package/src/index.tsx +16 -0
  57. package/src/indexDoc.ts +25 -3
  58. package/src/ui/Checkbox/Checkbox.tsx +2 -2
  59. package/src/ui/Link/Link.tsx +49 -0
  60. package/src/ui/Link/index.tsx +1 -0
  61. package/src/ui/Select/Select.tsx +2 -2
  62. package/src/utils/constants.ts +37 -0
  63. package/src/utils/exportPdf.ts +3 -1
  64. package/src/utils/highlightLinesNetworkPlan.ts +25 -0
  65. package/src/utils/hooks/useI18n.tsx +6 -4
  66. package/src/utils/hooks/useInitialPermalink.tsx +9 -2
  67. package/src/utils/hooks/useMapContext.tsx +9 -0
  68. package/src/utils/sharingGraphqlUtils.ts +1 -1
  69. package/src/utils/translations.ts +12 -1
  70. package/tailwind.config.mjs +3 -1
  71. package/src/ShareMenu/PermalinkButton/PermalinkButton.tsx +0 -62
  72. package/src/ShareMenu/PermalinkButton/index.tsx +0 -1
  73. 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
- // @ts-expect-error it's wanted
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 { MobilityMapAttributes, MobilitySearchAttributes };
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 RvfCheckboxProps = {
8
+ export type CheckboxProps = {
9
9
  checkedIconUrl?: string;
10
10
  className?: string;
11
11
  } & InputHTMLAttributes;
12
12
 
13
- function Checkbox({ className, ...props }: RvfCheckboxProps) {
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";
@@ -2,12 +2,12 @@ import ArrowDown from "../../icons/ArrowDown";
2
2
 
3
3
  import type { HTMLAttributes, PreactDOMAttributes } from "preact";
4
4
 
5
- export type RvfSelectProps = {
5
+ export type SelectProps = {
6
6
  className?: string;
7
7
  } & HTMLAttributes<HTMLSelectElement> &
8
8
  PreactDOMAttributes;
9
9
 
10
- function Select({ children, className, onChange }: RvfSelectProps) {
10
+ function Select({ children, className, onChange }: SelectProps) {
11
11
  return (
12
12
  <div className="text-grey relative flex items-center">
13
13
  <select
@@ -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";
@@ -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(`rvf-${new Date().toISOString().slice(0, 10)}.pdf`);
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
- export interface I18NContextType {
5
- t: (id: string, templateValues?: Record<string, string>) => string;
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);
@@ -1,4 +1,4 @@
1
- import { useMemo } from "preact/hooks";
1
+ import { useMemo, useRef } from "preact/hooks";
2
2
 
3
3
  import type { MobilityMapProps } from "../../MobilityMap/MobilityMap";
4
4
 
@@ -9,6 +9,8 @@ import type { MobilityMapProps } from "../../MobilityMap/MobilityMap";
9
9
  const useInitialPermalink = (
10
10
  permalinktemplate: string,
11
11
  ): null | Partial<MobilityMapProps> => {
12
+ const prevProps = useRef<Partial<MobilityMapProps>>(null);
13
+
12
14
  const props = useMemo(() => {
13
15
  if (!permalinktemplate) {
14
16
  return null;
@@ -70,7 +72,12 @@ const useInitialPermalink = (
70
72
  return null;
71
73
  }, [permalinktemplate]);
72
74
 
73
- return props;
75
+ // We want to apply the value from the url only once
76
+ if (!prevProps.current && props) {
77
+ prevProps.current = props;
78
+ return props;
79
+ }
80
+ return {};
74
81
  };
75
82
 
76
83
  export default useInitialPermalink;