@geops/rvf-mobility-web-component 0.1.94 → 0.1.96
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 +20 -0
- package/doc/package.json +1 -1
- package/index.js +207 -207
- package/package.json +21 -21
- package/src/GeolocationButton/GeolocationButton.tsx +1 -2
- package/src/GeolocationButton/index.tsx +1 -1
- package/src/LayoutState/LayoutState.tsx +18 -0
- package/src/MobilityMap/MobilityMap.tsx +6 -1
- package/src/MobilityMap/MobilityMapAttributes.ts +8 -2
- package/src/MobilityNotifications/MobilityNotifications.tsx +19 -1
- package/src/MobilityNotifications/MobilityNotificationsAttributes.ts +8 -1
- package/src/NotificationDetails/NotificationDetails.tsx +16 -6
- package/src/RvfFeatureDetails/RvfNotificationDetails/RvfNotificationDetails.tsx +8 -5
- package/src/RvfMapLayout/RvfMapLayout.tsx +1 -1
- package/src/SituationDetails/SituationDetails.tsx +19 -4
- package/src/StopsSearch/StopsSearch.tsx +0 -1
- package/src/utils/applyInitialLayerVisibility.ts +2 -20
- package/src/utils/hooks/useInitialLayersVisiblity.tsx +23 -1
- package/src/utils/hooks/useMapContext.tsx +4 -0
- package/.yarnrc.yml +0 -1
- package/src/RvfGeolocationButton/GeolocationButton.tsx +0 -98
- package/src/RvfGeolocationButton/index.tsx +0 -1
package/package.json
CHANGED
|
@@ -2,20 +2,20 @@
|
|
|
2
2
|
"name": "@geops/rvf-mobility-web-component",
|
|
3
3
|
"license": "UNLICENSED",
|
|
4
4
|
"description": "Web components for rvf in the domains of mobility and logistics.",
|
|
5
|
-
"version": "0.1.
|
|
5
|
+
"version": "0.1.96",
|
|
6
6
|
"homepage": "https://rvf-mobility-web-component-geops.vercel.app/",
|
|
7
7
|
"type": "module",
|
|
8
8
|
"main": "index.js",
|
|
9
9
|
"dependencies": {
|
|
10
10
|
"graphql": "^16.10.0",
|
|
11
11
|
"graphql-request": "^7.1.2",
|
|
12
|
-
"jspdf": "^3.0.
|
|
12
|
+
"jspdf": "^3.0.4",
|
|
13
13
|
"lodash.debounce": "^4.0.8",
|
|
14
|
-
"maplibre-gl": "
|
|
15
|
-
"mobility-toolbox-js": "3.
|
|
16
|
-
"ol": "^10.
|
|
17
|
-
"preact": "^10.
|
|
18
|
-
"preact-custom-element": "^4.
|
|
14
|
+
"maplibre-gl": "5.12.0",
|
|
15
|
+
"mobility-toolbox-js": "3.6.0",
|
|
16
|
+
"ol": "^10.7.0",
|
|
17
|
+
"preact": "^10.28.0",
|
|
18
|
+
"preact-custom-element": "^4.6.0",
|
|
19
19
|
"react": "npm:@preact/compat@^18.3.1",
|
|
20
20
|
"react-dom": "npm:@preact/compat@^18.3.1",
|
|
21
21
|
"react-icons": "^5.5.0",
|
|
@@ -23,21 +23,21 @@
|
|
|
23
23
|
"rosetta": "^1.1.0"
|
|
24
24
|
},
|
|
25
25
|
"devDependencies": {
|
|
26
|
-
"@commitlint/cli": "^20.
|
|
27
|
-
"@commitlint/config-conventional": "^20.
|
|
28
|
-
"@eslint/js": "^9.
|
|
26
|
+
"@commitlint/cli": "^20.2.0",
|
|
27
|
+
"@commitlint/config-conventional": "^20.2.0",
|
|
28
|
+
"@eslint/js": "^9.39.1",
|
|
29
29
|
"@geops/eslint-config-react": "^1.6.0-beta.1",
|
|
30
|
-
"@tailwindcss/cli": "^4.1.
|
|
30
|
+
"@tailwindcss/cli": "^4.1.17",
|
|
31
31
|
"@tailwindcss/container-queries": "^0.1.1",
|
|
32
32
|
"@testing-library/preact": "^3.2.4",
|
|
33
33
|
"@types/geojson": "^7946.0.16",
|
|
34
34
|
"@types/jest": "^30.0.0",
|
|
35
|
-
"@types/lodash": "^4.17.
|
|
35
|
+
"@types/lodash": "^4.17.21",
|
|
36
36
|
"@types/preact-custom-element": "^4.0.4",
|
|
37
37
|
"concurrently": "^9.2.1",
|
|
38
|
-
"esbuild": "^0.
|
|
38
|
+
"esbuild": "^0.27.1",
|
|
39
39
|
"esbuild-sass-plugin": "^3.3.1",
|
|
40
|
-
"eslint": "^9.
|
|
40
|
+
"eslint": "^9.39.1",
|
|
41
41
|
"eslint-plugin-tailwindcss": "^4.0.0-beta.0",
|
|
42
42
|
"fixpack": "^4.0.0",
|
|
43
43
|
"generact": "^0.4.0",
|
|
@@ -46,16 +46,16 @@
|
|
|
46
46
|
"jest-canvas-mock": "^2.5.2",
|
|
47
47
|
"jest-environment-jsdom": "^30.2.0",
|
|
48
48
|
"jest-preset-preact": "^4.1.1",
|
|
49
|
-
"next": "15.5.
|
|
49
|
+
"next": "15.5.7",
|
|
50
50
|
"preact-render-to-string": "^6.6.3",
|
|
51
|
-
"prettier": "^3.
|
|
52
|
-
"prettier-plugin-tailwindcss": "^0.7.
|
|
51
|
+
"prettier": "^3.7.4",
|
|
52
|
+
"prettier-plugin-tailwindcss": "^0.7.2",
|
|
53
53
|
"standard-version": "^9.5.0",
|
|
54
|
-
"tailwind-merge": "^3.
|
|
55
|
-
"tailwindcss": "^4.1.
|
|
56
|
-
"ts-jest": "^29.4.
|
|
54
|
+
"tailwind-merge": "^3.4.0",
|
|
55
|
+
"tailwindcss": "^4.1.17",
|
|
56
|
+
"ts-jest": "^29.4.6",
|
|
57
57
|
"typescript": "^5.9.3",
|
|
58
|
-
"typescript-eslint": "^8.
|
|
58
|
+
"typescript-eslint": "^8.49.0"
|
|
59
59
|
},
|
|
60
60
|
"scripts": {
|
|
61
61
|
"build": "yarn build:css && yarn build:js && cp index*.js* doc/public/",
|
|
@@ -16,8 +16,7 @@ export type GeolocationButtonProps = IconButtonProps;
|
|
|
16
16
|
const TRACKING_ZOOM = 16;
|
|
17
17
|
|
|
18
18
|
function GeolocationButton({ ...props }: GeolocationButtonProps) {
|
|
19
|
-
const
|
|
20
|
-
const { isTracking, map, setIsTracking } = mapContext;
|
|
19
|
+
const { isTracking, map, setIsTracking } = useMapContext();
|
|
21
20
|
const { t } = useI18n();
|
|
22
21
|
|
|
23
22
|
const geolocation = useMemo(() => {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
export { default } from "
|
|
1
|
+
export { default } from "./GeolocationButton";
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { useCallback, useEffect } from "preact/hooks";
|
|
2
2
|
|
|
3
3
|
import useFit from "../utils/hooks/useFit";
|
|
4
|
+
import useI18n from "../utils/hooks/useI18n";
|
|
4
5
|
import useLnpLineInfo, { useLnpStopInfo } from "../utils/hooks/useLnp";
|
|
5
6
|
import useMapContext from "../utils/hooks/useMapContext";
|
|
6
7
|
import useRealtimeTrainByRouteIdentifier from "../utils/hooks/useRealtimeTrainsByRouteIdentifier";
|
|
@@ -32,6 +33,7 @@ function LayoutState() {
|
|
|
32
33
|
notification,
|
|
33
34
|
notificationid,
|
|
34
35
|
notificationId,
|
|
36
|
+
notificationlang,
|
|
35
37
|
permalink,
|
|
36
38
|
previewNotifications,
|
|
37
39
|
print,
|
|
@@ -60,6 +62,7 @@ function LayoutState() {
|
|
|
60
62
|
setIsShareMenuOpen,
|
|
61
63
|
setLinesIds,
|
|
62
64
|
setNotificationId,
|
|
65
|
+
setNotificationLangFallbacks,
|
|
63
66
|
setStationId,
|
|
64
67
|
setTrainId,
|
|
65
68
|
share,
|
|
@@ -75,6 +78,21 @@ function LayoutState() {
|
|
|
75
78
|
const stopInfo = useLnpStopInfo(stationid);
|
|
76
79
|
const trainInfo = useRealtimeTrainByRouteIdentifier(trainid);
|
|
77
80
|
const fit = useFit();
|
|
81
|
+
const { locale } = useI18n();
|
|
82
|
+
|
|
83
|
+
// Define fallback languages from the notifications details
|
|
84
|
+
useEffect(() => {
|
|
85
|
+
const fallbackLangs =
|
|
86
|
+
notificationlang
|
|
87
|
+
?.split(",")
|
|
88
|
+
.map((lang) => {
|
|
89
|
+
return lang.trim();
|
|
90
|
+
})
|
|
91
|
+
.filter((lang) => {
|
|
92
|
+
return lang !== locale();
|
|
93
|
+
}) || [];
|
|
94
|
+
setNotificationLangFallbacks(fallbackLangs);
|
|
95
|
+
}, [notificationlang, setNotificationLangFallbacks, locale]);
|
|
78
96
|
|
|
79
97
|
useEffect(() => {
|
|
80
98
|
setHasStations(!!tenant);
|
|
@@ -85,6 +85,8 @@ function MobilityMap(props: MobilityMapProps) {
|
|
|
85
85
|
const [trainId, setTrainId] = useState<RealtimeTrainId>();
|
|
86
86
|
const [linesIds, setLinesIds] = useState<string[]>();
|
|
87
87
|
const [notificationId, setNotificationId] = useState<string>();
|
|
88
|
+
const [notificationLangFallbacks, setNotificationLangFallbacks] =
|
|
89
|
+
useState<string[]>();
|
|
88
90
|
|
|
89
91
|
const [featuresInfos, setFeaturesInfos] = useState<
|
|
90
92
|
LayerGetFeatureInfoResponse[]
|
|
@@ -110,7 +112,6 @@ function MobilityMap(props: MobilityMapProps) {
|
|
|
110
112
|
return {
|
|
111
113
|
// MobilityMapProps
|
|
112
114
|
...props,
|
|
113
|
-
// MapContextProps
|
|
114
115
|
baseLayer,
|
|
115
116
|
featuresInfos,
|
|
116
117
|
featuresInfosHovered,
|
|
@@ -140,6 +141,8 @@ function MobilityMap(props: MobilityMapProps) {
|
|
|
140
141
|
map,
|
|
141
142
|
mapsetLayer,
|
|
142
143
|
notificationId,
|
|
144
|
+
// MapContextProps
|
|
145
|
+
notificationLangFallbacks,
|
|
143
146
|
notificationsLayer,
|
|
144
147
|
permalinkUrlSearchParams,
|
|
145
148
|
previewNotifications,
|
|
@@ -175,6 +178,7 @@ function MobilityMap(props: MobilityMapProps) {
|
|
|
175
178
|
setMap,
|
|
176
179
|
setMapsetLayer,
|
|
177
180
|
setNotificationId,
|
|
181
|
+
setNotificationLangFallbacks,
|
|
178
182
|
setNotificationsLayer,
|
|
179
183
|
setPermalinkUrlSearchParams,
|
|
180
184
|
setPreviewNotifications,
|
|
@@ -221,6 +225,7 @@ function MobilityMap(props: MobilityMapProps) {
|
|
|
221
225
|
map,
|
|
222
226
|
mapsetLayer,
|
|
223
227
|
notificationId,
|
|
228
|
+
notificationLangFallbacks,
|
|
224
229
|
notificationsLayer,
|
|
225
230
|
permalinkUrlSearchParams,
|
|
226
231
|
previewNotifications,
|
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
//type: "checkbox" | "date" | "select" | "textfield";
|
|
2
|
-
|
|
3
2
|
import {
|
|
4
3
|
DEFAULT_QUERYABLE_LAYERS,
|
|
5
4
|
LAYERS_NAMES,
|
|
@@ -53,6 +52,7 @@ export type MobilityMapAttributeName =
|
|
|
53
52
|
| "notification"
|
|
54
53
|
| "notificationat"
|
|
55
54
|
| "notificationid"
|
|
55
|
+
| "notificationlang"
|
|
56
56
|
| "notificationtenant"
|
|
57
57
|
| "notificationurl"
|
|
58
58
|
| "permalink"
|
|
@@ -125,7 +125,7 @@ const attrs: MobilityMapAttributes = {
|
|
|
125
125
|
defaultValue: "de",
|
|
126
126
|
description:
|
|
127
127
|
"The language to use for the map. Supported languages are : de, en, fr, it.",
|
|
128
|
-
public:
|
|
128
|
+
public: true,
|
|
129
129
|
},
|
|
130
130
|
layers: {
|
|
131
131
|
defaultValue: null,
|
|
@@ -258,6 +258,12 @@ where:
|
|
|
258
258
|
description: `An id of a notification to show details of.`,
|
|
259
259
|
public: false,
|
|
260
260
|
},
|
|
261
|
+
notificationlang: {
|
|
262
|
+
defaultValue: "en,de,fr",
|
|
263
|
+
description:
|
|
264
|
+
"A comma separated list of languages supported by the notification, if a textual content is not available in the current lang it will try to display the content in one of these languages. Order is important. <br/>Supported languages are: en,de,fr,it",
|
|
265
|
+
public: true,
|
|
266
|
+
},
|
|
261
267
|
notificationtenant: {
|
|
262
268
|
defaultValue: "rvf",
|
|
263
269
|
description: `The ${geopsMocoApiLink} tenant to get the notification from.`,
|
|
@@ -1,9 +1,10 @@
|
|
|
1
1
|
import { MocoAPI } from "mobility-toolbox-js/ol";
|
|
2
2
|
import { SeverityEnumeration } from "mobility-toolbox-js/types";
|
|
3
|
-
import { memo, useEffect, useState } from "preact/compat";
|
|
3
|
+
import { memo, useEffect, useMemo, useState } from "preact/compat";
|
|
4
4
|
|
|
5
5
|
import I18n from "../I18n";
|
|
6
6
|
import SituationDetails from "../SituationDetails";
|
|
7
|
+
import useI18n from "../utils/hooks/useI18n";
|
|
7
8
|
|
|
8
9
|
import MobilityNotificationsAttributes from "./MobilityNotificationsAttributes";
|
|
9
10
|
|
|
@@ -33,10 +34,26 @@ const severityOrder = {
|
|
|
33
34
|
function MobilityNotifications({
|
|
34
35
|
apikey,
|
|
35
36
|
lang,
|
|
37
|
+
notificationlang,
|
|
36
38
|
notificationtenant,
|
|
37
39
|
notificationurl,
|
|
38
40
|
}: MobilityNotificationsProps) {
|
|
39
41
|
const [situations, setSituations] = useState<SituationType[]>([]);
|
|
42
|
+
const { locale } = useI18n();
|
|
43
|
+
|
|
44
|
+
// Define fallback languages from the notificationlang attribute
|
|
45
|
+
const fallbackLangs = useMemo(() => {
|
|
46
|
+
return (
|
|
47
|
+
notificationlang
|
|
48
|
+
?.split(",")
|
|
49
|
+
.map((l) => {
|
|
50
|
+
return l.trim();
|
|
51
|
+
})
|
|
52
|
+
.filter((l) => {
|
|
53
|
+
return l !== locale();
|
|
54
|
+
}) || []
|
|
55
|
+
);
|
|
56
|
+
}, [locale, notificationlang]);
|
|
40
57
|
|
|
41
58
|
useEffect(() => {
|
|
42
59
|
new MocoAPI({
|
|
@@ -104,6 +121,7 @@ function MobilityNotifications({
|
|
|
104
121
|
return (
|
|
105
122
|
<SituationDetails
|
|
106
123
|
canToggle={true}
|
|
124
|
+
fallbackLangs={fallbackLangs}
|
|
107
125
|
headerClassName="text-rvf-h3 font-semibold"
|
|
108
126
|
iconClassName="w-8 h-8 text-red"
|
|
109
127
|
key={situation.id}
|
|
@@ -7,6 +7,7 @@ export type MobilityNotificationsAttributeName =
|
|
|
7
7
|
| "apikey"
|
|
8
8
|
| "lang"
|
|
9
9
|
| "notificationat"
|
|
10
|
+
| "notificationlang"
|
|
10
11
|
| "notificationtenant"
|
|
11
12
|
| "notificationurl";
|
|
12
13
|
|
|
@@ -24,13 +25,19 @@ const attrs: MobilityNotificationsAttributes = {
|
|
|
24
25
|
lang: {
|
|
25
26
|
defaultValue: "de",
|
|
26
27
|
description: "The language to use for the notifications.",
|
|
27
|
-
public:
|
|
28
|
+
public: true,
|
|
28
29
|
},
|
|
29
30
|
notificationat: {
|
|
30
31
|
description:
|
|
31
32
|
"An ISO date string used to display active notification at this date in the notification layer. If not defined the current date will be used.<br/>Ex: 2025-08-01T00:00:00Z .",
|
|
32
33
|
public: false,
|
|
33
34
|
},
|
|
35
|
+
notificationlang: {
|
|
36
|
+
defaultValue: "en,de,fr",
|
|
37
|
+
description:
|
|
38
|
+
"A comma separated list of languages supported by the notification, if a textual content is not available in the current lang it will try to display the content in one of these languages. Order is important. <br/>Supported languages are: en,de,fr,it",
|
|
39
|
+
public: true,
|
|
40
|
+
},
|
|
34
41
|
notificationtenant: {
|
|
35
42
|
defaultValue: "rvf",
|
|
36
43
|
description: `The ${geopsMocoApiLink} tenant to get the notification from.`,
|
|
@@ -52,7 +52,7 @@ function NotificationDetails({
|
|
|
52
52
|
feature: Feature;
|
|
53
53
|
}) {
|
|
54
54
|
const { locale, t } = useI18n();
|
|
55
|
-
const { notificationId } = useMapContext();
|
|
55
|
+
const { notificationId, notificationLangFallbacks } = useMapContext();
|
|
56
56
|
const situationParsed = useMocoSituation(notificationId);
|
|
57
57
|
|
|
58
58
|
// moco export v2
|
|
@@ -116,17 +116,28 @@ function NotificationDetails({
|
|
|
116
116
|
textualContentMedium,
|
|
117
117
|
textualContentSmall,
|
|
118
118
|
}) => {
|
|
119
|
+
let localeToUse = locale();
|
|
120
|
+
|
|
119
121
|
// Get the textual content in German
|
|
120
122
|
textualContentMultilingual =
|
|
121
123
|
textualContentLarge ||
|
|
122
124
|
textualContentMedium ||
|
|
123
125
|
textualContentSmall;
|
|
124
126
|
|
|
125
|
-
textualContent = textualContentMultilingual?.[
|
|
127
|
+
textualContent = textualContentMultilingual?.[localeToUse];
|
|
126
128
|
|
|
127
|
-
// Fallback to default language if there is not title in the current language
|
|
128
129
|
if (!textualContent?.summary) {
|
|
129
|
-
textualContent
|
|
130
|
+
// Try to find textualContent with a title using fallback languages
|
|
131
|
+
// If we do notfind a fallback we stick to the current language.
|
|
132
|
+
for (const fallbackLang of notificationLangFallbacks) {
|
|
133
|
+
const goodTextualContent =
|
|
134
|
+
textualContentMultilingual?.[fallbackLang];
|
|
135
|
+
if (goodTextualContent?.summary) {
|
|
136
|
+
localeToUse = fallbackLang;
|
|
137
|
+
textualContent = goodTextualContent;
|
|
138
|
+
break;
|
|
139
|
+
}
|
|
140
|
+
}
|
|
130
141
|
}
|
|
131
142
|
|
|
132
143
|
let pubLines =
|
|
@@ -236,8 +247,7 @@ function NotificationDetails({
|
|
|
236
247
|
<div>
|
|
237
248
|
{textualContentMultilingual.infoLinks.map(
|
|
238
249
|
({ label, uri }) => {
|
|
239
|
-
const title =
|
|
240
|
-
label?.[locale()] || label?.de || uri || "";
|
|
250
|
+
const title = label?.[localeToUse] || uri || "";
|
|
241
251
|
return (
|
|
242
252
|
<Link href={uri} key={uri} title={title}>
|
|
243
253
|
{title}
|
|
@@ -2,13 +2,13 @@ import { twMerge } from "tailwind-merge";
|
|
|
2
2
|
|
|
3
3
|
import ShadowOverflow from "../../ShadowOverflow";
|
|
4
4
|
import SituationDetails from "../../SituationDetails";
|
|
5
|
+
import useMapContext from "../../utils/hooks/useMapContext";
|
|
5
6
|
import useMocoSituation from "../../utils/hooks/useMocoSituation";
|
|
6
7
|
|
|
7
8
|
import type { Feature } from "ol";
|
|
8
9
|
|
|
9
10
|
function NotificationDetails({
|
|
10
11
|
className,
|
|
11
|
-
feature,
|
|
12
12
|
...props
|
|
13
13
|
}: {
|
|
14
14
|
className?: string;
|
|
@@ -20,12 +20,15 @@ function NotificationDetails({
|
|
|
20
20
|
timeIntervalClassName?: string;
|
|
21
21
|
toggleable?: boolean;
|
|
22
22
|
}) {
|
|
23
|
-
const {
|
|
24
|
-
const situationParsed = useMocoSituation(
|
|
25
|
-
|
|
23
|
+
const { notificationId, notificationLangFallbacks } = useMapContext();
|
|
24
|
+
const situationParsed = useMocoSituation(notificationId);
|
|
25
|
+
|
|
26
26
|
return (
|
|
27
27
|
<ShadowOverflow {...props} className={twMerge("px-4 text-base", className)}>
|
|
28
|
-
<SituationDetails
|
|
28
|
+
<SituationDetails
|
|
29
|
+
fallbackLangs={notificationLangFallbacks}
|
|
30
|
+
situation={situationParsed}
|
|
31
|
+
/>
|
|
29
32
|
</ShadowOverflow>
|
|
30
33
|
);
|
|
31
34
|
}
|
|
@@ -82,7 +82,7 @@ function RvfMapLayout({
|
|
|
82
82
|
<Copyright className="pointer-events-auto bg-slate-50/70" />
|
|
83
83
|
</div>
|
|
84
84
|
<div className="absolute top-2 right-2 z-10 flex">
|
|
85
|
-
{hasGeolocation && <GeolocationButton
|
|
85
|
+
{hasGeolocation && <GeolocationButton />}
|
|
86
86
|
</div>
|
|
87
87
|
<div className="absolute right-2 bottom-10 z-10 flex flex-col justify-between gap-2">
|
|
88
88
|
<ZoomButtons />
|
|
@@ -39,8 +39,11 @@ const toShortDate = (
|
|
|
39
39
|
);
|
|
40
40
|
};
|
|
41
41
|
|
|
42
|
+
const emptyArray: string[] = [];
|
|
43
|
+
|
|
42
44
|
function SituationDetails({
|
|
43
45
|
canToggle = false,
|
|
46
|
+
fallbackLangs = emptyArray,
|
|
44
47
|
headerClassName,
|
|
45
48
|
iconClassName,
|
|
46
49
|
publicationClassName,
|
|
@@ -50,6 +53,7 @@ function SituationDetails({
|
|
|
50
53
|
useShortMonth = true,
|
|
51
54
|
}: {
|
|
52
55
|
canToggle?: boolean;
|
|
56
|
+
fallbackLangs?: string[];
|
|
53
57
|
headerClassName?: string;
|
|
54
58
|
iconClassName?: string;
|
|
55
59
|
publicationClassName?: string;
|
|
@@ -122,15 +126,27 @@ function SituationDetails({
|
|
|
122
126
|
textualContentMedium,
|
|
123
127
|
textualContentSmall,
|
|
124
128
|
}) => {
|
|
129
|
+
let localeToUse = locale();
|
|
130
|
+
|
|
125
131
|
// Get the textual content in German
|
|
126
132
|
textualContentMultilingual =
|
|
127
133
|
textualContentLarge || textualContentMedium || textualContentSmall;
|
|
128
134
|
|
|
129
|
-
textualContent = textualContentMultilingual?.[
|
|
135
|
+
textualContent = textualContentMultilingual?.[localeToUse];
|
|
130
136
|
|
|
131
137
|
// Fallback to default language if there is not title in the current language
|
|
132
138
|
if (!textualContent?.summary) {
|
|
133
|
-
textualContent
|
|
139
|
+
// Try to find textualContent with a title using fallback languages
|
|
140
|
+
// If we do notfind a fallback we stick to the current language.
|
|
141
|
+
for (const fallbackLang of fallbackLangs) {
|
|
142
|
+
const goodTextualContent =
|
|
143
|
+
textualContentMultilingual?.[fallbackLang];
|
|
144
|
+
if (goodTextualContent?.summary) {
|
|
145
|
+
localeToUse = fallbackLang;
|
|
146
|
+
textualContent = goodTextualContent;
|
|
147
|
+
break;
|
|
148
|
+
}
|
|
149
|
+
}
|
|
134
150
|
}
|
|
135
151
|
|
|
136
152
|
const pubLines =
|
|
@@ -273,8 +289,7 @@ function SituationDetails({
|
|
|
273
289
|
<div>
|
|
274
290
|
{textualContentMultilingual.infoLinks.map(
|
|
275
291
|
({ label, uri }) => {
|
|
276
|
-
const title =
|
|
277
|
-
label?.[locale()] || label?.de || uri || "";
|
|
292
|
+
const title = label?.[localeToUse] || uri || "";
|
|
278
293
|
return (
|
|
279
294
|
<Link href={uri} key={uri} title={title}>
|
|
280
295
|
{title}
|
|
@@ -14,28 +14,10 @@ const applyInitialLayerVisibility = (
|
|
|
14
14
|
const names = layersAttrValue?.split(",") || [];
|
|
15
15
|
const name = layer.get("name");
|
|
16
16
|
const shouldBeVisible = names.includes(name);
|
|
17
|
-
|
|
17
|
+
// Visiblity of group are defined by their children, they should not appear in the permalink.
|
|
18
|
+
if (layer.getVisible() !== shouldBeVisible && !(layer as Group).getLayers) {
|
|
18
19
|
layer.setVisible(shouldBeVisible);
|
|
19
|
-
|
|
20
|
-
if ((layer as Group).getLayers) {
|
|
21
|
-
(layer as Group)
|
|
22
|
-
.getLayers()
|
|
23
|
-
.getArray()
|
|
24
|
-
.forEach((subLayer) => {
|
|
25
|
-
subLayer.setVisible(shouldBeVisible);
|
|
26
|
-
// applyInitialLayerVisibility(layersAttrValue, subLayer);
|
|
27
|
-
});
|
|
28
|
-
}
|
|
29
20
|
}
|
|
30
21
|
}
|
|
31
|
-
if ((layer as Group).getLayers) {
|
|
32
|
-
(layer as Group)
|
|
33
|
-
.getLayers()
|
|
34
|
-
.getArray()
|
|
35
|
-
.forEach((subLayer) => {
|
|
36
|
-
// subLayer.setVisible(false);
|
|
37
|
-
applyInitialLayerVisibility(layersAttrValue, subLayer);
|
|
38
|
-
});
|
|
39
|
-
}
|
|
40
22
|
};
|
|
41
23
|
export default applyInitialLayerVisibility;
|
|
@@ -7,6 +7,7 @@ import applyInitialLayerVisibility from "../applyInitialLayerVisibility";
|
|
|
7
7
|
import useInitialPermalink from "./useInitialPermalink";
|
|
8
8
|
|
|
9
9
|
import type { Map } from "ol";
|
|
10
|
+
import type { Group } from "ol/layer";
|
|
10
11
|
const useInitialLayersVisiblity = (
|
|
11
12
|
map: Map,
|
|
12
13
|
layers: string,
|
|
@@ -34,10 +35,31 @@ const useInitialLayersVisiblity = (
|
|
|
34
35
|
isPermalinkAlreadyUsed.current = true;
|
|
35
36
|
}
|
|
36
37
|
|
|
37
|
-
|
|
38
|
+
// We reverse the array to begin by the end of the tree (so the group layers are applied last)
|
|
39
|
+
const layersAsArray = getLayersAsFlatArray(map.getLayers().getArray());
|
|
40
|
+
layersAsArray.forEach((layer) => {
|
|
38
41
|
applyInitialLayerVisibility(layersToUse, layer);
|
|
39
42
|
});
|
|
40
43
|
|
|
44
|
+
// Hide group if there is no children visible
|
|
45
|
+
layersAsArray
|
|
46
|
+
.filter((l) => {
|
|
47
|
+
return (l as Group).getLayers;
|
|
48
|
+
})
|
|
49
|
+
.forEach((layer) => {
|
|
50
|
+
if (
|
|
51
|
+
layer.getVisible() &&
|
|
52
|
+
!(layer as Group)
|
|
53
|
+
.getLayers()
|
|
54
|
+
?.getArray()
|
|
55
|
+
.some((l) => {
|
|
56
|
+
return l.getVisible();
|
|
57
|
+
})
|
|
58
|
+
) {
|
|
59
|
+
layer.setVisible(false);
|
|
60
|
+
}
|
|
61
|
+
});
|
|
62
|
+
|
|
41
63
|
const key = map.getLayers().on("add", (event) => {
|
|
42
64
|
applyInitialLayerVisibility(layersToUse, event.element);
|
|
43
65
|
});
|
|
@@ -51,6 +51,7 @@ export type MapContextType = {
|
|
|
51
51
|
map: Map;
|
|
52
52
|
mapsetLayer?: MapsetLayer;
|
|
53
53
|
notificationId?: string;
|
|
54
|
+
notificationLangFallbacks: string[];
|
|
54
55
|
notificationsLayer?: MocoLayer;
|
|
55
56
|
permalinkUrlSearchParams: URLSearchParams;
|
|
56
57
|
previewNotifications?: SituationType[];
|
|
@@ -88,6 +89,7 @@ export type MapContextType = {
|
|
|
88
89
|
setMap: (map?: Map) => void;
|
|
89
90
|
setMapsetLayer: (mapsetLayer?: MapsetLayer) => void;
|
|
90
91
|
setNotificationId: (notificationId?: string) => void;
|
|
92
|
+
setNotificationLangFallbacks: (langs: string[]) => void;
|
|
91
93
|
setNotificationsLayer: (notificationsLayer?: MocoLayer) => void;
|
|
92
94
|
setPermalinkUrlSearchParams: (
|
|
93
95
|
setPermalinkUrlSearchParams: URLSearchParams,
|
|
@@ -128,6 +130,7 @@ export const MapContext = createContext<MapContextType>({
|
|
|
128
130
|
isSearchOpen: false,
|
|
129
131
|
isShareMenuOpen: false,
|
|
130
132
|
isTracking: false,
|
|
133
|
+
notificationLangFallbacks: [],
|
|
131
134
|
selectedFeatures: [],
|
|
132
135
|
setBaseLayer: (baseLayer?: MaplibreLayer) => {},
|
|
133
136
|
setIsFollowing: (isFollowing: boolean) => {},
|
|
@@ -137,6 +140,7 @@ export const MapContext = createContext<MapContextType>({
|
|
|
137
140
|
setMap: (map?: Map) => {},
|
|
138
141
|
setMapsetLayer: (mapsetLayer?: MapsetLayer) => {},
|
|
139
142
|
setNotificationId: (notificationId?: string) => {},
|
|
143
|
+
setNotificationLangFallbacks: (langs: string[]) => {},
|
|
140
144
|
setNotificationsLayer: (notificationsLayer?: MocoLayer) => {},
|
|
141
145
|
setPermalinkUrlSearchParams: (
|
|
142
146
|
permalinkUrlSearchParams: URLSearchParams,
|
package/.yarnrc.yml
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
nodeLinker: node-modules
|
|
@@ -1,98 +0,0 @@
|
|
|
1
|
-
import { Feature, Geolocation } from "ol";
|
|
2
|
-
import { Point } from "ol/geom";
|
|
3
|
-
import VectorLayer from "ol/layer/Vector";
|
|
4
|
-
import { unByKey } from "ol/Observable";
|
|
5
|
-
import { fromLonLat } from "ol/proj";
|
|
6
|
-
import VectorSource from "ol/source/Vector";
|
|
7
|
-
import { Icon, Style } from "ol/style";
|
|
8
|
-
import { memo } from "preact/compat";
|
|
9
|
-
import { useEffect, useMemo } from "preact/hooks";
|
|
10
|
-
|
|
11
|
-
import GeolocationIcon from "../icons/Geolocation";
|
|
12
|
-
import locationSvg from "../icons/Geolocation/location.svg";
|
|
13
|
-
import IconButton from "../ui/IconButton";
|
|
14
|
-
import useMapContext from "../utils/hooks/useMapContext";
|
|
15
|
-
|
|
16
|
-
import type { JSX, PreactDOMAttributes } from "preact";
|
|
17
|
-
|
|
18
|
-
const point = new Point([0, 0]);
|
|
19
|
-
const feature = new Feature(point);
|
|
20
|
-
const layer = new VectorLayer({
|
|
21
|
-
source: new VectorSource({ features: [feature] }),
|
|
22
|
-
style: new Style({
|
|
23
|
-
image: new Icon({
|
|
24
|
-
anchor: [0.5, 1],
|
|
25
|
-
src: locationSvg,
|
|
26
|
-
}),
|
|
27
|
-
}),
|
|
28
|
-
});
|
|
29
|
-
|
|
30
|
-
export type GeolocationButtonProps = JSX.HTMLAttributes<HTMLButtonElement> &
|
|
31
|
-
PreactDOMAttributes;
|
|
32
|
-
|
|
33
|
-
const TRACKING_ZOOM = 16;
|
|
34
|
-
|
|
35
|
-
function GeolocationButton({ ...props }: GeolocationButtonProps) {
|
|
36
|
-
const mapContext = useMapContext();
|
|
37
|
-
const { isTracking, map, setIsTracking } = mapContext;
|
|
38
|
-
|
|
39
|
-
const geolocation = useMemo(() => {
|
|
40
|
-
return new Geolocation();
|
|
41
|
-
}, []);
|
|
42
|
-
|
|
43
|
-
useEffect(() => {
|
|
44
|
-
let keys = [];
|
|
45
|
-
if (!map || !geolocation) {
|
|
46
|
-
return;
|
|
47
|
-
}
|
|
48
|
-
keys = [
|
|
49
|
-
// First time we zoom and center on the position
|
|
50
|
-
geolocation.once("change:position", (evt) => {
|
|
51
|
-
const position = evt.target.getPosition();
|
|
52
|
-
if (evt.target.getPosition()) {
|
|
53
|
-
const coord = fromLonLat(position, "EPSG:3857");
|
|
54
|
-
map.getView().setZoom(TRACKING_ZOOM);
|
|
55
|
-
map.getView().setCenter(coord);
|
|
56
|
-
point.setCoordinates(coord);
|
|
57
|
-
}
|
|
58
|
-
}),
|
|
59
|
-
// then we only center the map.
|
|
60
|
-
geolocation.on("change:position", (evt) => {
|
|
61
|
-
const position = evt.target.getPosition();
|
|
62
|
-
if (evt.target.getPosition()) {
|
|
63
|
-
const coord = fromLonLat(position, "EPSG:3857");
|
|
64
|
-
map.getView().setCenter(coord);
|
|
65
|
-
point.setCoordinates(coord);
|
|
66
|
-
}
|
|
67
|
-
}),
|
|
68
|
-
];
|
|
69
|
-
|
|
70
|
-
return () => {
|
|
71
|
-
unByKey(keys);
|
|
72
|
-
};
|
|
73
|
-
}, [map, geolocation]);
|
|
74
|
-
|
|
75
|
-
useEffect(() => {
|
|
76
|
-
geolocation.setTracking(isTracking);
|
|
77
|
-
if (isTracking) {
|
|
78
|
-
layer.setMap(map);
|
|
79
|
-
}
|
|
80
|
-
return () => {
|
|
81
|
-
layer.setMap(null);
|
|
82
|
-
};
|
|
83
|
-
}, [map, geolocation, isTracking]);
|
|
84
|
-
|
|
85
|
-
return (
|
|
86
|
-
<IconButton
|
|
87
|
-
onClick={() => {
|
|
88
|
-
setIsTracking(!isTracking);
|
|
89
|
-
}}
|
|
90
|
-
selected={isTracking}
|
|
91
|
-
{...props}
|
|
92
|
-
>
|
|
93
|
-
<GeolocationIcon className={isTracking ? "animate-pulse" : ""} />
|
|
94
|
-
</IconButton>
|
|
95
|
-
);
|
|
96
|
-
}
|
|
97
|
-
|
|
98
|
-
export default memo(GeolocationButton);
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export { default } from "./GeolocationButton";
|