@geops/rvf-mobility-web-component 0.1.85 → 0.1.88
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 +39 -0
- package/index.js +137 -117
- package/package.json +2 -2
- package/src/FeaturesInfosListener/FeaturesInfosListener.tsx +21 -11
- package/src/I18n/I18n.tsx +38 -0
- package/src/I18n/index.tsx +1 -0
- package/src/LayoutState/LayoutState.tsx +4 -30
- package/src/MapLayout/MapLayout.tsx +2 -1
- package/src/MobilityMap/MobilityMap.tsx +4 -9
- package/src/MobilityNotifications/MobilityNotifications.tsx +3 -9
- package/src/MobilitySearch/MobilitySearch.tsx +4 -4
- package/src/MobilitySearch/MobilitySearchAttributes.ts +7 -0
- package/src/NotificationDetails/NotificationDetails.tsx +5 -6
- package/src/RouteStop/RouteStop.tsx +1 -1
- package/src/RvfMapLayout/RvfMapLayout.tsx +1 -1
- package/src/RvfSearch/RvfSearch.tsx +3 -1
- package/src/Search/Search.tsx +3 -3
- package/src/Search/SearchBase.tsx +19 -1
- package/src/Search/SearchHeadless.tsx +4 -4
- package/src/SearchTrainsResult/SearchTrainsResult.tsx +2 -2
- package/src/SearchTrainsResults/SearchTrainsResults.tsx +1 -4
- package/src/SingleClickListener/SingleClickListener.tsx +20 -7
- package/src/SituationDetails/SituationDetails.tsx +8 -6
- package/src/StopsSearch/StopsSearch.tsx +6 -1
- package/src/icons/Warning/exported_features_20251029101203.kml +4 -1
- package/src/ui/Input/Input.tsx +16 -9
- package/src/ui/InputSearch/InputSearch.tsx +4 -3
- package/src/utils/hooks/useFit.tsx +4 -16
- package/src/utils/hooks/useI18n.tsx +4 -3
- package/src/utils/hooks/useMocoSituation.tsx +10 -2
- package/src/utils/hooks/useRealtimeTrainsByRouteIdentifier.tsx +5 -4
- package/src/utils/translations.ts +5 -5
- package/src/utils/i18n.ts +0 -7
package/package.json
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
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.88",
|
|
6
6
|
"homepage": "https://rvf-mobility-web-component-geops.vercel.app/",
|
|
7
7
|
"type": "module",
|
|
8
8
|
"main": "index.js",
|
|
@@ -12,7 +12,7 @@
|
|
|
12
12
|
"jspdf": "^3.0.3",
|
|
13
13
|
"lodash.debounce": "^4.0.8",
|
|
14
14
|
"maplibre-gl": "^5.10.0",
|
|
15
|
-
"mobility-toolbox-js": "3.5.1-beta.
|
|
15
|
+
"mobility-toolbox-js": "3.5.1-beta.5",
|
|
16
16
|
"ol": "^10.6.1",
|
|
17
17
|
"preact": "^10.27.2",
|
|
18
18
|
"preact-custom-element": "^4.5.1",
|
|
@@ -12,6 +12,7 @@ function FeaturesInfosListener() {
|
|
|
12
12
|
featuresInfos,
|
|
13
13
|
featuresInfosHovered,
|
|
14
14
|
linesNetworkPlanLayer,
|
|
15
|
+
notificationsLayer,
|
|
15
16
|
realtimeLayer,
|
|
16
17
|
setLinesIds,
|
|
17
18
|
setSelectedFeature,
|
|
@@ -36,6 +37,11 @@ function FeaturesInfosListener() {
|
|
|
36
37
|
return info.layer === realtimeLayer;
|
|
37
38
|
})?.features || [];
|
|
38
39
|
|
|
40
|
+
const [notificationFeature] =
|
|
41
|
+
featuresInfos?.find((info) => {
|
|
42
|
+
return info.layer === notificationsLayer;
|
|
43
|
+
})?.features || [];
|
|
44
|
+
|
|
39
45
|
const [stationFeature] =
|
|
40
46
|
featuresInfos?.find((info) => {
|
|
41
47
|
return info.layer === stationsLayer;
|
|
@@ -60,25 +66,29 @@ function FeaturesInfosListener() {
|
|
|
60
66
|
return info.features;
|
|
61
67
|
}) || [];
|
|
62
68
|
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
);
|
|
69
|
+
const priorityFeature =
|
|
70
|
+
realtimeFeature || notificationFeature || stationFeature;
|
|
71
|
+
|
|
72
|
+
// TODO this if/else must be refactored. We should not have to do setLinesIds here
|
|
73
|
+
if (priorityFeature) {
|
|
74
|
+
setSelectedFeature(priorityFeature);
|
|
75
|
+
setSelectedFeatures([priorityFeature]);
|
|
76
|
+
setLinesIds(null);
|
|
77
|
+
} else if (!features.length) {
|
|
78
|
+
setSelectedFeature(null);
|
|
79
|
+
setSelectedFeatures([]);
|
|
80
|
+
setLinesIds(null);
|
|
70
81
|
} else {
|
|
71
82
|
setSelectedFeatures(features);
|
|
72
|
-
setSelectedFeature(
|
|
73
|
-
|
|
74
|
-
setLinesIds(linesIds?.length ? linesIds : null);
|
|
75
|
-
}
|
|
83
|
+
setSelectedFeature(features[0]);
|
|
84
|
+
setLinesIds(linesIds?.length ? linesIds : null);
|
|
76
85
|
}
|
|
77
86
|
}, [
|
|
78
87
|
baseLayer?.mapLibreMap,
|
|
79
88
|
featuresInfos,
|
|
80
89
|
linesNetworkPlanLayer,
|
|
81
90
|
realtimeLayer,
|
|
91
|
+
notificationsLayer,
|
|
82
92
|
setLinesIds,
|
|
83
93
|
setSelectedFeature,
|
|
84
94
|
setSelectedFeatures,
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import { memo, useMemo } from "preact/compat";
|
|
2
|
+
import rosetta from "rosetta";
|
|
3
|
+
|
|
4
|
+
import { I18nContext } from "../utils/hooks/useI18n";
|
|
5
|
+
import translations from "../utils/translations";
|
|
6
|
+
// import rosetta from 'rosetta/debug';
|
|
7
|
+
|
|
8
|
+
const i18n = rosetta(translations);
|
|
9
|
+
|
|
10
|
+
export const defaultLanguage = "en";
|
|
11
|
+
export const languages = ["de", "en", "fr", "it"];
|
|
12
|
+
|
|
13
|
+
export interface I18nProps {
|
|
14
|
+
children: React.ReactNode;
|
|
15
|
+
lngDict?: Record<string, Record<string, string>>;
|
|
16
|
+
locale?: string;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
function I18n({ children, locale = defaultLanguage }: I18nProps) {
|
|
20
|
+
const i18nWrapper = useMemo(() => {
|
|
21
|
+
i18n.locale(locale);
|
|
22
|
+
|
|
23
|
+
return {
|
|
24
|
+
locale: (lang?: string) => {
|
|
25
|
+
return i18n.locale(lang);
|
|
26
|
+
},
|
|
27
|
+
t: (...args: [key: string, params?: Record<string, unknown>]) => {
|
|
28
|
+
return i18n.t(...args);
|
|
29
|
+
},
|
|
30
|
+
};
|
|
31
|
+
}, [locale]);
|
|
32
|
+
|
|
33
|
+
return (
|
|
34
|
+
<I18nContext.Provider value={i18nWrapper}>{children}</I18nContext.Provider>
|
|
35
|
+
);
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
export default memo(I18n);
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { default } from "./I18n";
|
|
@@ -284,14 +284,7 @@ function LayoutState() {
|
|
|
284
284
|
setLinesIds(null);
|
|
285
285
|
setNotificationId(null);
|
|
286
286
|
}
|
|
287
|
-
}, [
|
|
288
|
-
setFeaturesInfos,
|
|
289
|
-
closeMenus,
|
|
290
|
-
setLinesIds,
|
|
291
|
-
setNotificationId,
|
|
292
|
-
setTrainId,
|
|
293
|
-
stationId,
|
|
294
|
-
]);
|
|
287
|
+
}, [closeMenus, setLinesIds, setNotificationId, setTrainId, stationId]);
|
|
295
288
|
|
|
296
289
|
useEffect(() => {
|
|
297
290
|
if (trainId) {
|
|
@@ -302,14 +295,7 @@ function LayoutState() {
|
|
|
302
295
|
setLinesIds(null);
|
|
303
296
|
setNotificationId(null);
|
|
304
297
|
}
|
|
305
|
-
}, [
|
|
306
|
-
closeMenus,
|
|
307
|
-
setFeaturesInfos,
|
|
308
|
-
setLinesIds,
|
|
309
|
-
setNotificationId,
|
|
310
|
-
setStationId,
|
|
311
|
-
trainId,
|
|
312
|
-
]);
|
|
298
|
+
}, [closeMenus, setLinesIds, setNotificationId, setStationId, trainId]);
|
|
313
299
|
|
|
314
300
|
useEffect(() => {
|
|
315
301
|
if (notificationId) {
|
|
@@ -320,15 +306,7 @@ function LayoutState() {
|
|
|
320
306
|
setLinesIds(null);
|
|
321
307
|
setTrainId(null);
|
|
322
308
|
}
|
|
323
|
-
}, [
|
|
324
|
-
closeMenus,
|
|
325
|
-
notificationId,
|
|
326
|
-
setFeaturesInfos,
|
|
327
|
-
setLinesIds,
|
|
328
|
-
setNotificationId,
|
|
329
|
-
setStationId,
|
|
330
|
-
setTrainId,
|
|
331
|
-
]);
|
|
309
|
+
}, [closeMenus, notificationId, setLinesIds, setStationId, setTrainId]);
|
|
332
310
|
|
|
333
311
|
useEffect(() => {
|
|
334
312
|
if (linesIds?.length) {
|
|
@@ -340,12 +318,8 @@ function LayoutState() {
|
|
|
340
318
|
setTrainId(null);
|
|
341
319
|
}
|
|
342
320
|
}, [
|
|
343
|
-
linesIds?.length,
|
|
344
|
-
notificationId,
|
|
345
|
-
setFeaturesInfos,
|
|
346
321
|
closeMenus,
|
|
347
|
-
|
|
348
|
-
setLinesIds,
|
|
322
|
+
linesIds?.length,
|
|
349
323
|
setNotificationId,
|
|
350
324
|
setStationId,
|
|
351
325
|
setTrainId,
|
|
@@ -96,9 +96,9 @@ function MapLayout({
|
|
|
96
96
|
: "",
|
|
97
97
|
)}
|
|
98
98
|
>
|
|
99
|
+
{hasLayerTree && <LayerTreeButton />}
|
|
99
100
|
{hasPrint && <ExportMenuButton />}
|
|
100
101
|
{hasShare && <ShareMenuButton />}
|
|
101
|
-
{hasLayerTree && <LayerTreeButton />}
|
|
102
102
|
{hasSearch && <SearchButton />}
|
|
103
103
|
</div>
|
|
104
104
|
|
|
@@ -110,6 +110,7 @@ function MapLayout({
|
|
|
110
110
|
)}
|
|
111
111
|
>
|
|
112
112
|
<Search
|
|
113
|
+
autofocus
|
|
113
114
|
className={
|
|
114
115
|
"@container @sm/main:gap-4 @sm/main:rounded-l-none @sm/main:rounded-r-2xl"
|
|
115
116
|
}
|
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
import { memo } from "preact/compat";
|
|
2
|
-
import {
|
|
2
|
+
import { useMemo, useRef, useState } from "preact/hooks";
|
|
3
3
|
|
|
4
4
|
import BaseLayer from "../BaseLayer";
|
|
5
5
|
import FeaturesInfosListener from "../FeaturesInfosListener";
|
|
6
|
+
import I18n from "../I18n";
|
|
6
7
|
import LayoutState from "../LayoutState";
|
|
7
8
|
import LinesNetworkPlanLayer from "../LinesNetworkPlanLayer";
|
|
8
9
|
import LinesNetworkPlanLayerHighlight from "../LinesNetworkPlanLayerHighlight";
|
|
@@ -14,10 +15,8 @@ import Permalink from "../Permalink";
|
|
|
14
15
|
import RealtimeLayer from "../RealtimeLayer";
|
|
15
16
|
import SingleClickListener from "../SingleClickListener";
|
|
16
17
|
import StationsLayer from "../StationsLayer";
|
|
17
|
-
import { I18nContext } from "../utils/hooks/useI18n";
|
|
18
18
|
import useInitialLayersVisiblity from "../utils/hooks/useInitialLayersVisiblity";
|
|
19
19
|
import { MapContext } from "../utils/hooks/useMapContext";
|
|
20
|
-
import i18n from "../utils/i18n";
|
|
21
20
|
import WindowMessageListener from "../WindowMessageListener";
|
|
22
21
|
|
|
23
22
|
import MobilityMapAttributes from "./MobilityMapAttributes";
|
|
@@ -234,12 +233,8 @@ function MobilityMap(props: MobilityMapProps) {
|
|
|
234
233
|
trainId,
|
|
235
234
|
]);
|
|
236
235
|
|
|
237
|
-
useEffect(() => {
|
|
238
|
-
i18n.locale(lang);
|
|
239
|
-
}, [lang]);
|
|
240
|
-
|
|
241
236
|
return (
|
|
242
|
-
<
|
|
237
|
+
<I18n locale={lang}>
|
|
243
238
|
{/* There is a bug in tailwindcss@4 , variables are not imported in the shadow dom
|
|
244
239
|
see https://github.com/tailwindlabs/tailwindcss/issues/15005*/}
|
|
245
240
|
<style>
|
|
@@ -319,7 +314,7 @@ function MobilityMap(props: MobilityMapProps) {
|
|
|
319
314
|
<MapLayout />
|
|
320
315
|
</div>
|
|
321
316
|
</MapContext.Provider>
|
|
322
|
-
</
|
|
317
|
+
</I18n>
|
|
323
318
|
);
|
|
324
319
|
}
|
|
325
320
|
|
|
@@ -2,9 +2,8 @@ import { MocoAPI } from "mobility-toolbox-js/ol";
|
|
|
2
2
|
import { SeverityEnumeration } from "mobility-toolbox-js/types";
|
|
3
3
|
import { memo, useEffect, useState } from "preact/compat";
|
|
4
4
|
|
|
5
|
+
import I18n from "../I18n";
|
|
5
6
|
import SituationDetails from "../SituationDetails";
|
|
6
|
-
import { I18nContext } from "../utils/hooks/useI18n";
|
|
7
|
-
import i18n from "../utils/i18n";
|
|
8
7
|
|
|
9
8
|
import MobilityNotificationsAttributes from "./MobilityNotificationsAttributes";
|
|
10
9
|
|
|
@@ -64,12 +63,8 @@ function MobilityNotifications({
|
|
|
64
63
|
});
|
|
65
64
|
}, [apikey, notificationurl, notificationtenant]);
|
|
66
65
|
|
|
67
|
-
useEffect(() => {
|
|
68
|
-
i18n.locale(lang);
|
|
69
|
-
}, [lang]);
|
|
70
|
-
|
|
71
66
|
return (
|
|
72
|
-
<
|
|
67
|
+
<I18n locale={lang}>
|
|
73
68
|
<style>{tailwind}</style>
|
|
74
69
|
<div className="flex w-full flex-col gap-6">
|
|
75
70
|
{situations
|
|
@@ -106,7 +101,6 @@ function MobilityNotifications({
|
|
|
106
101
|
}
|
|
107
102
|
})
|
|
108
103
|
.map((situation) => {
|
|
109
|
-
console.log("Rendering situation", situation.id, situation.title);
|
|
110
104
|
return (
|
|
111
105
|
<SituationDetails
|
|
112
106
|
canToggle={true}
|
|
@@ -121,7 +115,7 @@ function MobilityNotifications({
|
|
|
121
115
|
);
|
|
122
116
|
})}
|
|
123
117
|
</div>
|
|
124
|
-
</
|
|
118
|
+
</I18n>
|
|
125
119
|
);
|
|
126
120
|
}
|
|
127
121
|
|
|
@@ -1,8 +1,7 @@
|
|
|
1
1
|
import { memo } from "preact/compat";
|
|
2
2
|
|
|
3
|
+
import I18n from "../I18n";
|
|
3
4
|
import StopsSearch from "../StopsSearch/StopsSearch";
|
|
4
|
-
import { I18nContext } from "../utils/hooks/useI18n";
|
|
5
|
-
import i18n from "../utils/i18n";
|
|
6
5
|
|
|
7
6
|
import MobilitySearchAttributes from "./MobilitySearchAttributes";
|
|
8
7
|
|
|
@@ -14,11 +13,12 @@ import type { StopsSearchProps } from "../StopsSearch/StopsSearch";
|
|
|
14
13
|
export type MobilitySearchProps = StopsSearchProps;
|
|
15
14
|
|
|
16
15
|
function MobilitySearch(props: MobilitySearchProps) {
|
|
16
|
+
const { lang } = props;
|
|
17
17
|
return (
|
|
18
|
-
<
|
|
18
|
+
<I18n lang={lang}>
|
|
19
19
|
<style>{tailwind}</style>
|
|
20
20
|
<StopsSearch {...props} />
|
|
21
|
-
</
|
|
21
|
+
</I18n>
|
|
22
22
|
);
|
|
23
23
|
}
|
|
24
24
|
|
|
@@ -9,6 +9,7 @@ export type MobilitySearchAttributeName =
|
|
|
9
9
|
| "countrycode"
|
|
10
10
|
| "event"
|
|
11
11
|
| "field"
|
|
12
|
+
| "lang"
|
|
12
13
|
| "limit"
|
|
13
14
|
| "mots"
|
|
14
15
|
| "onselect"
|
|
@@ -44,6 +45,12 @@ const attrs: MobilitySearchAttributes = {
|
|
|
44
45
|
description: `Which field to look up, default all of them, Possible values:id, name, coords. See the ${geopsStopsApiLink} documentation.`,
|
|
45
46
|
public: true,
|
|
46
47
|
},
|
|
48
|
+
lang: {
|
|
49
|
+
defaultValue: "en",
|
|
50
|
+
description:
|
|
51
|
+
"The language to use for the search input texts. Supported languages are : de, en, fr, it.",
|
|
52
|
+
public: true,
|
|
53
|
+
},
|
|
47
54
|
limit: {
|
|
48
55
|
defaultValue: "5",
|
|
49
56
|
description: `The number of suggestions to show. See the ${geopsStopsApiLink} documentation.`,
|
|
@@ -145,14 +145,12 @@ function NotificationDetails({
|
|
|
145
145
|
return (
|
|
146
146
|
<div className={"text-base"} key={id}>
|
|
147
147
|
<div className="text-xs uppercase">{reasonsToDisplay}</div>
|
|
148
|
-
<h3 className="space-x-2 text-lg font-bold text-balance">
|
|
149
|
-
<span
|
|
150
|
-
className={"line-height-[1.3] inline-block align-middle"}
|
|
151
|
-
>
|
|
148
|
+
<h3 className="space-x-2 text-lg leading-[1.3] font-bold text-balance">
|
|
149
|
+
<span className={"inline-block align-middle"}>
|
|
152
150
|
<Warning />
|
|
153
151
|
</span>
|
|
154
152
|
<span
|
|
155
|
-
className={"*:inline"}
|
|
153
|
+
className={"align-middle *:inline"}
|
|
156
154
|
dangerouslySetInnerHTML={{
|
|
157
155
|
__html: textualContent?.summary,
|
|
158
156
|
}}
|
|
@@ -252,8 +250,9 @@ function NotificationDetails({
|
|
|
252
250
|
{pubLines?.map((name) => {
|
|
253
251
|
return (
|
|
254
252
|
<div
|
|
253
|
+
// px-4px and py-2px are important for bermekt
|
|
255
254
|
className={
|
|
256
|
-
"rounded-md bg-black px-
|
|
255
|
+
"rounded-md bg-black px-[4px] py-[2px] font-bold text-white"
|
|
257
256
|
}
|
|
258
257
|
key={name}
|
|
259
258
|
>
|
|
@@ -92,7 +92,7 @@ function RouteStop({
|
|
|
92
92
|
<RouteStopTime className="ml-4 flex w-10 shrink-0 flex-col justify-center text-xs" />
|
|
93
93
|
<RouteStopDelay className="flex w-8 shrink-0 flex-col justify-center text-[0.6rem]" />
|
|
94
94
|
<RouteStopProgress className="relative flex w-8 shrink-0 items-center" />
|
|
95
|
-
<RouteStopStation className="flex grow
|
|
95
|
+
<RouteStopStation className="flex grow items-center justify-center pr-2 text-sm font-medium" />
|
|
96
96
|
</>
|
|
97
97
|
)}
|
|
98
98
|
</button>
|
|
@@ -139,9 +139,9 @@ function RvfMapLayout({
|
|
|
139
139
|
: "",
|
|
140
140
|
)}
|
|
141
141
|
>
|
|
142
|
+
{hasLayerTree && <LayerTreeButton />}
|
|
142
143
|
{hasPrint && <ExportMenuButton />}
|
|
143
144
|
{hasShare && <ShareMenuButton />}
|
|
144
|
-
{hasLayerTree && <LayerTreeButton />}
|
|
145
145
|
{hasSearch && <SearchButton />}
|
|
146
146
|
</div>
|
|
147
147
|
</div>
|
package/src/Search/Search.tsx
CHANGED
|
@@ -23,11 +23,11 @@ function Search({
|
|
|
23
23
|
return (
|
|
24
24
|
<SearchHeadless
|
|
25
25
|
childrenContainerClassName={twMerge(
|
|
26
|
-
"max-h-[
|
|
26
|
+
"max-h-[400px] rounded-b-2xl bg-white shadow overflow-hidden",
|
|
27
27
|
childrenContainerClassName,
|
|
28
28
|
)}
|
|
29
29
|
className={twMerge(
|
|
30
|
-
"border-grey @container m-0 h-[48px]
|
|
30
|
+
"border-grey @container m-0 h-[48px] rounded-2xl border text-base",
|
|
31
31
|
className,
|
|
32
32
|
)}
|
|
33
33
|
inputContainerClassName={twMerge("border-none", inputContainerClassName)}
|
|
@@ -36,7 +36,7 @@ function Search({
|
|
|
36
36
|
resultClassName,
|
|
37
37
|
)}
|
|
38
38
|
resultsContainerClassName={twMerge(
|
|
39
|
-
"min-h-[
|
|
39
|
+
"min-h-[50px] max-h-[200px] border border-t-0",
|
|
40
40
|
resultsContainerClassName,
|
|
41
41
|
)}
|
|
42
42
|
withResultsClassName={twMerge(
|
|
@@ -1,5 +1,11 @@
|
|
|
1
1
|
import { memo } from "preact/compat";
|
|
2
|
-
import {
|
|
2
|
+
import {
|
|
3
|
+
useCallback,
|
|
4
|
+
useEffect,
|
|
5
|
+
useMemo,
|
|
6
|
+
useRef,
|
|
7
|
+
useState,
|
|
8
|
+
} from "preact/hooks";
|
|
3
9
|
|
|
4
10
|
import InputSearch from "../ui/InputSearch";
|
|
5
11
|
import useI18n from "../utils/hooks/useI18n";
|
|
@@ -12,6 +18,7 @@ import type { InputProps } from "../ui/Input/Input";
|
|
|
12
18
|
import type { InputSearchProps } from "../ui/InputSearch/InputSearch";
|
|
13
19
|
|
|
14
20
|
export type SearchBaseProps = {
|
|
21
|
+
autofocus?: boolean;
|
|
15
22
|
cancelButtonProps?: IconButtonProps;
|
|
16
23
|
childrenContainerClassName?: string;
|
|
17
24
|
inputProps?: InputProps;
|
|
@@ -48,6 +55,7 @@ export const SearchContext = createContext<null | SearchContextType>({
|
|
|
48
55
|
});
|
|
49
56
|
|
|
50
57
|
function SearchBase({
|
|
58
|
+
autofocus,
|
|
51
59
|
cancelButtonProps,
|
|
52
60
|
children,
|
|
53
61
|
childrenContainerClassName,
|
|
@@ -66,6 +74,15 @@ function SearchBase({
|
|
|
66
74
|
const [open, setOpen] = useState(false);
|
|
67
75
|
const resultsBySearchRef = useRef<Record<string, unknown[]>>({});
|
|
68
76
|
|
|
77
|
+
// Autofocus the input
|
|
78
|
+
const inputRef = useRef<HTMLInputElement>(null);
|
|
79
|
+
|
|
80
|
+
useEffect(() => {
|
|
81
|
+
if (autofocus && inputRef.current) {
|
|
82
|
+
inputRef.current.focus();
|
|
83
|
+
}
|
|
84
|
+
}, [autofocus]);
|
|
85
|
+
|
|
69
86
|
const setResults = useCallback(
|
|
70
87
|
(id: string, results: unknown[]) => {
|
|
71
88
|
resultsBySearchRef.current[id] = results;
|
|
@@ -94,6 +111,7 @@ function SearchBase({
|
|
|
94
111
|
const inputPropss: InputProps = useMemo(() => {
|
|
95
112
|
return {
|
|
96
113
|
placeholder: t("search_placeholder"),
|
|
114
|
+
ref: inputRef,
|
|
97
115
|
...(inputProps || {}),
|
|
98
116
|
onChange: (evt: TargetedInputEvent<HTMLInputElement>) => {
|
|
99
117
|
setQuery((evt.target as HTMLInputElement).value);
|
|
@@ -41,7 +41,7 @@ function SearchHeadless({ ...props }: SearchProps) {
|
|
|
41
41
|
(stop: StopsFeature) => {
|
|
42
42
|
setStationId(stop.properties.uid);
|
|
43
43
|
// It means that the station wil have informations
|
|
44
|
-
fit.current(stop, !!tenant);
|
|
44
|
+
fit.current?.(stop, !!tenant);
|
|
45
45
|
},
|
|
46
46
|
[fit, setStationId, tenant],
|
|
47
47
|
);
|
|
@@ -49,15 +49,15 @@ function SearchHeadless({ ...props }: SearchProps) {
|
|
|
49
49
|
const onSelectLine = useCallback(
|
|
50
50
|
(line: LnpLineInfo) => {
|
|
51
51
|
setLinesIds([line.external_id]);
|
|
52
|
-
fit.current(line, true);
|
|
52
|
+
fit.current?.(line, true);
|
|
53
53
|
},
|
|
54
54
|
[fit, setLinesIds],
|
|
55
55
|
);
|
|
56
56
|
|
|
57
57
|
const onSelectTrain = useCallback(
|
|
58
58
|
(match: RealtimeRouteIdentifierMatch) => {
|
|
59
|
-
setTrainId(match.
|
|
60
|
-
fit.current(match, true);
|
|
59
|
+
setTrainId(match.train_id);
|
|
60
|
+
fit.current?.(match, true);
|
|
61
61
|
},
|
|
62
62
|
[fit, setTrainId],
|
|
63
63
|
);
|
|
@@ -33,8 +33,8 @@ function SearchTrainsResult({
|
|
|
33
33
|
onSelectItem?.(item, evt);
|
|
34
34
|
}}
|
|
35
35
|
>
|
|
36
|
-
<RouteIcon line={item.
|
|
37
|
-
<div className="grow">{`${item.
|
|
36
|
+
<RouteIcon line={item.line}></RouteIcon>
|
|
37
|
+
<div className="grow">{`${item.destination} (${item.route_identifier})`}</div>
|
|
38
38
|
</button>
|
|
39
39
|
);
|
|
40
40
|
}
|
|
@@ -76,10 +76,7 @@ function SearchTrainsResults({
|
|
|
76
76
|
>
|
|
77
77
|
{results.map((item: RealtimeRouteIdentifierMatch) => {
|
|
78
78
|
return (
|
|
79
|
-
<SearchResult
|
|
80
|
-
className={resultClassName}
|
|
81
|
-
key={item.trains[0].train_id}
|
|
82
|
-
>
|
|
79
|
+
<SearchResult className={resultClassName} key={item.train_id}>
|
|
83
80
|
{toChildArray(children).map(
|
|
84
81
|
(
|
|
85
82
|
child: ReactElement<
|
|
@@ -22,6 +22,7 @@ function SingleClickListener({
|
|
|
22
22
|
}: SingleClickListenerProps) {
|
|
23
23
|
const {
|
|
24
24
|
map,
|
|
25
|
+
mots,
|
|
25
26
|
queryablelayers,
|
|
26
27
|
setFeaturesInfos,
|
|
27
28
|
setFeaturesInfosHovered,
|
|
@@ -54,10 +55,14 @@ function SingleClickListener({
|
|
|
54
55
|
const stationsFeatures = featuresInfoStations?.features || [];
|
|
55
56
|
|
|
56
57
|
const [stationFeature] = stationsFeatures.filter((feat) => {
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
58
|
+
let found = false;
|
|
59
|
+
if (mots?.split(",")?.length > 0) {
|
|
60
|
+
found = !!mots.split(",").find((mot) => {
|
|
61
|
+
const hasMot = feat.get(mot.trim().toLowerCase());
|
|
62
|
+
if (hasMot === 1) {
|
|
63
|
+
return true;
|
|
64
|
+
}
|
|
65
|
+
});
|
|
61
66
|
}
|
|
62
67
|
|
|
63
68
|
// We move the external_id to uid to be consistent across all stations (lnp and others)
|
|
@@ -65,8 +70,16 @@ function SingleClickListener({
|
|
|
65
70
|
feat.set("uid", feat.get("external_id"));
|
|
66
71
|
}
|
|
67
72
|
|
|
68
|
-
//
|
|
69
|
-
|
|
73
|
+
// Travic stations have a tralis_network property and mots property
|
|
74
|
+
if (found) {
|
|
75
|
+
return !!feat.get("tralis_network")?.includes(tenant);
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
// LNP stations have network property with CamelCase value like "Trenord"
|
|
79
|
+
if (feat.get("network")) {
|
|
80
|
+
return feat.get("network").toLowerCase().includes(tenant);
|
|
81
|
+
}
|
|
82
|
+
return found;
|
|
70
83
|
});
|
|
71
84
|
|
|
72
85
|
// Replace the features clicked in the stations layer by the filtered one
|
|
@@ -79,7 +92,7 @@ function SingleClickListener({
|
|
|
79
92
|
|
|
80
93
|
return featuresInfos;
|
|
81
94
|
},
|
|
82
|
-
[queryablelayers, stationsLayer, tenant],
|
|
95
|
+
[mots, queryablelayers, stationsLayer, tenant],
|
|
83
96
|
);
|
|
84
97
|
|
|
85
98
|
const onPointerMove = useCallback(
|
|
@@ -156,15 +156,15 @@ function SituationDetails({
|
|
|
156
156
|
</div>
|
|
157
157
|
<h3
|
|
158
158
|
className={twMerge(
|
|
159
|
-
"space-x-2 text-xl font-bold text-balance",
|
|
159
|
+
"space-x-2 text-xl leading-[1.3] font-bold text-balance",
|
|
160
160
|
headerClassName,
|
|
161
161
|
)}
|
|
162
162
|
>
|
|
163
|
-
<span className={"
|
|
163
|
+
<span className={"inline-block align-middle"}>
|
|
164
164
|
<Warning className={twMerge(iconClassName)} />
|
|
165
165
|
</span>
|
|
166
166
|
<span
|
|
167
|
-
className={"*:inline"}
|
|
167
|
+
className={"align-middle *:inline"}
|
|
168
168
|
dangerouslySetInnerHTML={{
|
|
169
169
|
__html: textualContent?.summary,
|
|
170
170
|
}}
|
|
@@ -221,7 +221,7 @@ function SituationDetails({
|
|
|
221
221
|
key={startTime}
|
|
222
222
|
>
|
|
223
223
|
<span>
|
|
224
|
-
{from && to ? `${from}
|
|
224
|
+
{from && to ? `${from} – ${to}` : null}
|
|
225
225
|
{from && !to ? t("from", { from }) : null}
|
|
226
226
|
</span>
|
|
227
227
|
{hasDailyTime && (
|
|
@@ -290,9 +290,10 @@ function SituationDetails({
|
|
|
290
290
|
<div className={"flex flex-wrap gap-1 text-sm"}>
|
|
291
291
|
{pubLines?.map((name) => {
|
|
292
292
|
return (
|
|
293
|
+
// px-[8px] py-[4px] are important for bermekt
|
|
293
294
|
<div
|
|
294
295
|
className={
|
|
295
|
-
"bg-red rounded-md px-
|
|
296
|
+
"bg-red rounded-md px-[8px] py-[4px] font-bold text-white"
|
|
296
297
|
}
|
|
297
298
|
key={name}
|
|
298
299
|
>
|
|
@@ -311,8 +312,9 @@ function SituationDetails({
|
|
|
311
312
|
{stations.map((name) => {
|
|
312
313
|
return (
|
|
313
314
|
<div
|
|
315
|
+
// px-[8px] py-[4px] are important for bermekt
|
|
314
316
|
className={
|
|
315
|
-
"bg-red rounded-md px-
|
|
317
|
+
"bg-red rounded-md px-[8px] py-[4px] font-bold text-white"
|
|
316
318
|
}
|
|
317
319
|
key={name}
|
|
318
320
|
>
|