@geops/rvf-mobility-web-component 0.1.59 → 0.1.61
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 +46 -0
- package/README.md +16 -123
- package/docutils.js +1 -1
- package/index.html +4 -0
- package/index.js +154 -151
- package/package.json +1 -1
- package/src/ExportMenu/ExportMenu.tsx +7 -5
- package/src/ExportMenuButton/ExportMenuButton.tsx +8 -1
- package/src/GeolocationButton/GeolocationButton.tsx +9 -3
- package/src/LayerTree/TreeItem/TreeItem.tsx +3 -1
- package/src/LayerTreeButton/LayerTreeButton.tsx +10 -4
- package/src/LayerTreeMenu/LayerTreeMenu.tsx +15 -5
- package/src/LinesNetworkPlanLayer/LinesNetworkPlanLayer.tsx +5 -2
- package/src/MapDispatchEvents/MapDispatchEvents.tsx +7 -0
- package/src/MapLayout/MapLayout.tsx +176 -0
- package/src/MapLayout/index.tsx +1 -0
- package/src/MobilityMap/MobilityMap.tsx +25 -139
- package/src/MobilityMap/MobilityMapAttributes.ts +15 -3
- package/src/NotificationDetails/NotificationDetails.tsx +23 -8
- package/src/OverlayContent/OverlayContent.tsx +5 -3
- package/src/OverlayDetails/OverlayDetails.tsx +29 -11
- package/src/OverlayDetailsHeader/OverlayDetailsHeader.tsx +11 -14
- package/src/Permalink/Permalink.tsx +5 -5
- package/src/PermalinkInput/PermalinkInput.tsx +6 -4
- package/src/RealtimeLayer/RealtimeLayer.tsx +10 -9
- package/src/RouteScheduleFooter/RouteScheduleFooter.tsx +1 -1
- package/src/RvfFeatureDetails/RvfNotificationDetails/RvfNotificationDetails.tsx +23 -8
- package/src/RvfMobilityMap/RvfMobilityMap.tsx +44 -41
- package/src/RvfSellingPointsLayer/RvfSellingPointsLayer.tsx +6 -6
- package/src/RvfSharedMobilityLayerGroup/RvfSharedMobilityLayerGroup.tsx +76 -58
- package/src/Search/Search.tsx +1 -1
- package/src/SearchButton/SearchButton.tsx +10 -4
- package/src/ShareMenu/ShareMenu.tsx +4 -2
- package/src/ShareMenuButton/ShareMenuButton.tsx +10 -3
- package/src/StationsLayer/StationsLayer.tsx +4 -1
- package/src/ZoomButtons/ZoomButtons.tsx +12 -2
- package/src/index.tsx +13 -5
- package/src/utils/constants.ts +20 -40
- package/src/utils/getPermalinkParameters.ts +12 -4
- package/src/utils/hooks/useInitialPermalink.tsx +83 -0
- package/src/utils/hooks/useLayerConfig.tsx +1 -3
- package/src/utils/hooks/useLayersConfig.tsx +2 -2
- package/src/utils/i18n.ts +0 -10
- package/src/utils/translations.ts +165 -0
- package/src/RvfFeatureDetailsFooter/RvfFeatureDetailsFooter.tsx +0 -43
- package/src/RvfFeatureDetailsFooter/index.tsx +0 -1
- package/src/RvfFeatureDetailsTitle/RvfFeatureDetailsTitle.tsx +0 -81
- package/src/RvfFeatureDetailsTitle/index.tsx +0 -1
- package/src/RvfLayerTree/RvfLayerTree.tsx +0 -40
- package/src/RvfLayerTree/TreeItem/TreeItem.tsx +0 -145
- package/src/RvfLayerTree/TreeItem/index.tsx +0 -1
- package/src/RvfLayerTree/index.tsx +0 -1
- package/src/RvfLayerTree/layersTreeContext.ts +0 -4
- package/src/RvfLayerTree/layersTreeReducer.ts +0 -158
- package/src/RvfOverlayContent/RvfOverlayContent.tsx +0 -128
- package/src/RvfOverlayContent/index.ts +0 -0
- package/src/utils/hooks/useUpdatePermalink.tsx +0 -76
|
@@ -361,6 +361,7 @@ function NotificationDetails({
|
|
|
361
361
|
<Warning />
|
|
362
362
|
</span>
|
|
363
363
|
<span
|
|
364
|
+
className={"*:inline"}
|
|
364
365
|
dangerouslySetInnerHTML={{
|
|
365
366
|
__html: textualContent?.summary,
|
|
366
367
|
}}
|
|
@@ -390,12 +391,26 @@ function NotificationDetails({
|
|
|
390
391
|
key={startTime}
|
|
391
392
|
>
|
|
392
393
|
<span>
|
|
393
|
-
{
|
|
394
|
-
|
|
395
|
-
|
|
394
|
+
{t("from_to", {
|
|
395
|
+
from: toShortDate(
|
|
396
|
+
new Date(startTime),
|
|
397
|
+
!hasDailyTime,
|
|
398
|
+
!isStartCurrentYear,
|
|
399
|
+
),
|
|
400
|
+
to: !isEndInfinite
|
|
401
|
+
? toShortDate(
|
|
402
|
+
new Date(endTime),
|
|
403
|
+
!hasDailyTime,
|
|
404
|
+
!isEndCurrentYear,
|
|
405
|
+
)
|
|
406
|
+
: undefined,
|
|
407
|
+
})}
|
|
396
408
|
</span>
|
|
397
409
|
{hasDailyTime && (
|
|
398
|
-
<span>{` (
|
|
410
|
+
<span>{` (${t("daily_from_to", {
|
|
411
|
+
from: dailyStartTime,
|
|
412
|
+
to: dailyEndTime,
|
|
413
|
+
})})`}</span>
|
|
399
414
|
)}
|
|
400
415
|
</div>
|
|
401
416
|
);
|
|
@@ -405,13 +420,13 @@ function NotificationDetails({
|
|
|
405
420
|
className="mt-4"
|
|
406
421
|
dangerouslySetInnerHTML={{
|
|
407
422
|
__html:
|
|
408
|
-
textualContent?.description || "
|
|
423
|
+
textualContent?.description || t("no_details_available"),
|
|
409
424
|
}}
|
|
410
425
|
/>
|
|
411
426
|
{!!pubLines?.length && (
|
|
412
427
|
<div>
|
|
413
428
|
<br />
|
|
414
|
-
<div className={"font-bold"}>
|
|
429
|
+
<div className={"font-bold"}>{t("affected_lines")}:</div>
|
|
415
430
|
<div className={"flex flex-wrap gap-1 text-sm"}>
|
|
416
431
|
{pubLines?.map((name) => {
|
|
417
432
|
return (
|
|
@@ -430,7 +445,7 @@ function NotificationDetails({
|
|
|
430
445
|
)}
|
|
431
446
|
<div>
|
|
432
447
|
<br />
|
|
433
|
-
<div className={"font-bold"}>
|
|
448
|
+
<div className={"font-bold"}>{t("affected_stops")}:</div>
|
|
434
449
|
<div className={"flex flex-wrap gap-1 text-sm"}>
|
|
435
450
|
{stations?.length ? (
|
|
436
451
|
stations.map((name) => {
|
|
@@ -451,7 +466,7 @@ function NotificationDetails({
|
|
|
451
466
|
"bg-red rounded-md px-2 py-1 font-bold text-white"
|
|
452
467
|
}
|
|
453
468
|
>
|
|
454
|
-
|
|
469
|
+
{t("all_line_stops")}
|
|
455
470
|
</div>
|
|
456
471
|
)}
|
|
457
472
|
</div>
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { memo } from "preact/compat";
|
|
2
|
-
import { useMemo, useRef, useState } from "preact/hooks";
|
|
2
|
+
import { useEffect, useMemo, useRef, useState } from "preact/hooks";
|
|
3
3
|
import { twMerge } from "tailwind-merge";
|
|
4
4
|
|
|
5
5
|
import BaseLayer from "../BaseLayer";
|
|
@@ -31,13 +31,13 @@ import SearchButton from "../SearchButton";
|
|
|
31
31
|
import ShareMenuButton from "../ShareMenuButton";
|
|
32
32
|
import SingleClickListener from "../SingleClickListener";
|
|
33
33
|
import StationsLayer from "../StationsLayer";
|
|
34
|
-
import { LAYERS_TITLES } from "../utils/constants";
|
|
35
34
|
import fullTrajectoryStyle from "../utils/fullTrajectoryStyle";
|
|
36
35
|
import getBgColor from "../utils/getBgColor";
|
|
37
36
|
import { getRadius } from "../utils/getRadius";
|
|
38
37
|
import getTextColor from "../utils/getTextColor";
|
|
39
38
|
import { I18nContext } from "../utils/hooks/useI18n";
|
|
40
39
|
import useInitialLayersVisiblity from "../utils/hooks/useInitialLayersVisiblity";
|
|
40
|
+
import useInitialPermalink from "../utils/hooks/useInitialPermalink";
|
|
41
41
|
import { MapContext } from "../utils/hooks/useMapContext";
|
|
42
42
|
import { RvfContext } from "../utils/hooks/useRvfContext";
|
|
43
43
|
import i18n from "../utils/i18n";
|
|
@@ -199,7 +199,7 @@ function RvfMobilityMap(props: RvfMobilityMapProps) {
|
|
|
199
199
|
const [previewNotifications, setPreviewNotifications] =
|
|
200
200
|
useState<SituationType[]>();
|
|
201
201
|
|
|
202
|
-
const { layers, mainlink } = props;
|
|
202
|
+
const { lang, layers, mainlink } = props;
|
|
203
203
|
|
|
204
204
|
// Apply initial visibility of layers
|
|
205
205
|
useInitialLayersVisiblity(map, layers);
|
|
@@ -371,6 +371,10 @@ function RvfMobilityMap(props: RvfMobilityMapProps) {
|
|
|
371
371
|
};
|
|
372
372
|
}, []);
|
|
373
373
|
|
|
374
|
+
useEffect(() => {
|
|
375
|
+
i18n.locale(lang);
|
|
376
|
+
}, [lang]);
|
|
377
|
+
|
|
374
378
|
return (
|
|
375
379
|
<I18nContext.Provider value={i18n}>
|
|
376
380
|
{/* There is a bug in tailwindcss@4 , variables are not imported in the shadow dom
|
|
@@ -445,35 +449,15 @@ function RvfMobilityMap(props: RvfMobilityMapProps) {
|
|
|
445
449
|
<Map className="relative flex-1 overflow-visible">
|
|
446
450
|
<BaseLayer {...baseLayerProps} />
|
|
447
451
|
{isEmbed && <EmbedNavigation />}
|
|
448
|
-
{hasNotification &&
|
|
449
|
-
<NotificationsLayer
|
|
450
|
-
isQueryable={true}
|
|
451
|
-
title={LAYERS_TITLES.notifications}
|
|
452
|
-
/>
|
|
453
|
-
)}
|
|
452
|
+
{hasNotification && <NotificationsLayer />}
|
|
454
453
|
<RvfSelectedFeatureHighlightLayer />
|
|
455
|
-
{hasRealtime &&
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
<
|
|
462
|
-
minZoom={10}
|
|
463
|
-
{...stationsLayerProps}
|
|
464
|
-
title={LAYERS_TITLES.stations}
|
|
465
|
-
/>
|
|
466
|
-
<RvfTarifZonenLayer title={LAYERS_TITLES.tarifzonen} />
|
|
467
|
-
<RvfSellingPointsLayer title={LAYERS_TITLES.verkaufsstellen} />
|
|
468
|
-
{hasLnp && (
|
|
469
|
-
<LinesNetworkPlanLayer
|
|
470
|
-
title={LAYERS_TITLES.linesnetworkplan}
|
|
471
|
-
/>
|
|
472
|
-
)}
|
|
473
|
-
<RvfPoisLayer title={LAYERS_TITLES.pois} />
|
|
474
|
-
<RvfSharedMobilityLayerGroup
|
|
475
|
-
title={LAYERS_TITLES.sharedMobility}
|
|
476
|
-
/>
|
|
454
|
+
{hasRealtime && <RealtimeLayer {...realtimeLayerProps} />}
|
|
455
|
+
<StationsLayer minZoom={10} {...stationsLayerProps} />
|
|
456
|
+
<RvfTarifZonenLayer />
|
|
457
|
+
<RvfSellingPointsLayer />
|
|
458
|
+
{hasLnp && <LinesNetworkPlanLayer />}
|
|
459
|
+
<RvfPoisLayer />
|
|
460
|
+
<RvfSharedMobilityLayerGroup />
|
|
477
461
|
{mainlink && (
|
|
478
462
|
<RvfMainLinkButton className="absolute inset-x-2 bottom-8 z-10" />
|
|
479
463
|
)}
|
|
@@ -493,6 +477,17 @@ function RvfMobilityMap(props: RvfMobilityMapProps) {
|
|
|
493
477
|
<div className="absolute right-2 bottom-10 z-10 flex flex-col justify-between gap-2">
|
|
494
478
|
<ZoomButtons />
|
|
495
479
|
</div>
|
|
480
|
+
|
|
481
|
+
{!hasToolbar && hasSearch && (
|
|
482
|
+
<div
|
|
483
|
+
className={twMerge(
|
|
484
|
+
"absolute top-2 right-2 left-2 z-10 max-w-96",
|
|
485
|
+
isOverlayOpen && "@lg:left-68",
|
|
486
|
+
)}
|
|
487
|
+
>
|
|
488
|
+
<Search />
|
|
489
|
+
</div>
|
|
490
|
+
)}
|
|
496
491
|
</Map>
|
|
497
492
|
|
|
498
493
|
<div className="pointer-events-none absolute top-2 bottom-2 left-2 z-10 flex flex-col gap-2 *:pointer-events-auto">
|
|
@@ -506,7 +501,7 @@ function RvfMobilityMap(props: RvfMobilityMapProps) {
|
|
|
506
501
|
{hasSearch && (
|
|
507
502
|
<div
|
|
508
503
|
className={twMerge(
|
|
509
|
-
"absolute top-12 left-0
|
|
504
|
+
"absolute top-12 left-0 w-0 p-0 opacity-0 transition-all @sm:top-0 @sm:left-[calc(100%-43px)] @md:left-[calc(100%-47px)]",
|
|
510
505
|
isSearchOpen ? "w-64 opacity-100" : "",
|
|
511
506
|
)}
|
|
512
507
|
>
|
|
@@ -525,7 +520,7 @@ function RvfMobilityMap(props: RvfMobilityMapProps) {
|
|
|
525
520
|
|
|
526
521
|
<div
|
|
527
522
|
className={twMerge(
|
|
528
|
-
"border-grey relative flex gap-[1px] overflow-hidden rounded-2xl border",
|
|
523
|
+
"border-grey relative flex gap-[1px] overflow-hidden rounded-2xl border @sm/main:h-[44px] @md/main:h-[48px]",
|
|
529
524
|
"*:size-[38px] *:rounded-none *:border-none *:@sm/main:size-[42px] *:@md/main:!size-[46px]",
|
|
530
525
|
"*:first:!rounded-l-2xl",
|
|
531
526
|
"*:last:!rounded-r-2xl",
|
|
@@ -534,18 +529,19 @@ function RvfMobilityMap(props: RvfMobilityMapProps) {
|
|
|
534
529
|
: "",
|
|
535
530
|
)}
|
|
536
531
|
>
|
|
537
|
-
{hasPrint && <ExportMenuButton
|
|
538
|
-
{hasShare && <ShareMenuButton
|
|
539
|
-
{hasLayerTree && <LayerTreeButton
|
|
540
|
-
{hasSearch && <SearchButton
|
|
532
|
+
{hasPrint && <ExportMenuButton />}
|
|
533
|
+
{hasShare && <ShareMenuButton />}
|
|
534
|
+
{hasLayerTree && <LayerTreeButton />}
|
|
535
|
+
{hasSearch && <SearchButton />}
|
|
541
536
|
</div>
|
|
542
537
|
</div>
|
|
543
538
|
)}
|
|
544
539
|
|
|
540
|
+
{/* Desktop (>= lg) */}
|
|
545
541
|
<div
|
|
546
542
|
className={twMerge(
|
|
547
543
|
"flex w-0 flex-1 flex-col overflow-hidden rounded-2xl",
|
|
548
|
-
isOverlayOpen ? "@lg:min-w-
|
|
544
|
+
isOverlayOpen ? "@lg:min-w-64" : "p-0",
|
|
549
545
|
)}
|
|
550
546
|
style={{ containerType: "normal" }}
|
|
551
547
|
>
|
|
@@ -594,14 +590,21 @@ function RvfMobilityMap(props: RvfMobilityMapProps) {
|
|
|
594
590
|
}
|
|
595
591
|
|
|
596
592
|
// We creates a wrapper to inject the default props values from MobilityMapAttributes.
|
|
597
|
-
const defaultProps = {};
|
|
598
|
-
|
|
593
|
+
const defaultProps: Partial<MobilityMapProps> = {};
|
|
599
594
|
Object.entries(MobilityMapAttributes).forEach(([key]) => {
|
|
600
595
|
defaultProps[key] = MobilityMapAttributes[key].defaultValue || null;
|
|
601
596
|
});
|
|
602
597
|
|
|
603
598
|
function MobilityMapWithDefaultProps(props: MobilityMapProps) {
|
|
604
|
-
|
|
599
|
+
// Apply initial value from permalink, only x,y,z
|
|
600
|
+
const { permalinktemplate } = props;
|
|
601
|
+
const { permalinktemplate: defaultPermalinkTemplate } = defaultProps;
|
|
602
|
+
const propsFromPermalink = useInitialPermalink(
|
|
603
|
+
permalinktemplate || defaultPermalinkTemplate,
|
|
604
|
+
);
|
|
605
|
+
return (
|
|
606
|
+
<RvfMobilityMap {...defaultProps} {...props} {...propsFromPermalink} />
|
|
607
|
+
);
|
|
605
608
|
}
|
|
606
609
|
|
|
607
610
|
export default memo(MobilityMapWithDefaultProps);
|
|
@@ -6,32 +6,31 @@ import Automat from "../icons/Automat";
|
|
|
6
6
|
import InPerson from "../icons/InPerson";
|
|
7
7
|
import Video from "../icons/Video";
|
|
8
8
|
import { LAYERS_NAMES } from "../utils/constants";
|
|
9
|
+
import useLayerConfig from "../utils/hooks/useLayerConfig";
|
|
9
10
|
import useMapContext from "../utils/hooks/useMapContext";
|
|
10
11
|
import useRvfContext from "../utils/hooks/useRvfContext";
|
|
11
12
|
|
|
12
13
|
import type { MaplibreStyleLayerOptions } from "mobility-toolbox-js/ol/layers/MaplibreStyleLayer";
|
|
13
14
|
|
|
14
15
|
function RvfSellingPointsLayer(props: MaplibreStyleLayerOptions) {
|
|
15
|
-
const { title } = props;
|
|
16
16
|
const { baseLayer, map } = useMapContext();
|
|
17
17
|
const { setSellingPointsLayer } = useRvfContext();
|
|
18
|
+
const layerConfig = useLayerConfig(LAYERS_NAMES.verkaufsstellen);
|
|
18
19
|
|
|
19
20
|
const layer = useMemo(() => {
|
|
20
21
|
if (!baseLayer) {
|
|
21
22
|
return null;
|
|
22
23
|
}
|
|
23
24
|
return new MaplibreStyleLayer({
|
|
24
|
-
isQueryable: true,
|
|
25
25
|
layersFilter: ({ metadata }) => {
|
|
26
26
|
return metadata?.["general.filter"] === "selling_points";
|
|
27
27
|
},
|
|
28
28
|
maplibreLayer: baseLayer,
|
|
29
29
|
name: LAYERS_NAMES.verkaufsstellen,
|
|
30
30
|
...(props || {}),
|
|
31
|
-
|
|
32
|
-
title: (
|
|
31
|
+
layerTreeTitle: (
|
|
33
32
|
<div className="flex items-center justify-between gap-2">
|
|
34
|
-
{title}
|
|
33
|
+
{layerConfig?.title}
|
|
35
34
|
<span className="flex items-center">
|
|
36
35
|
<Automat />
|
|
37
36
|
<InPerson />
|
|
@@ -39,8 +38,9 @@ function RvfSellingPointsLayer(props: MaplibreStyleLayerOptions) {
|
|
|
39
38
|
</span>
|
|
40
39
|
</div>
|
|
41
40
|
),
|
|
41
|
+
title: layerConfig?.title,
|
|
42
42
|
});
|
|
43
|
-
}, [baseLayer, props,
|
|
43
|
+
}, [baseLayer, props, layerConfig]);
|
|
44
44
|
|
|
45
45
|
useEffect(() => {
|
|
46
46
|
setSellingPointsLayer(layer);
|
|
@@ -26,7 +26,7 @@ import {
|
|
|
26
26
|
WFS_FREE_FLOAT_TYPE,
|
|
27
27
|
WFS_STATIONS_TYPE,
|
|
28
28
|
} from "../utils/constants";
|
|
29
|
-
import
|
|
29
|
+
import useI18n from "../utils/hooks/useI18n";
|
|
30
30
|
import useMapContext from "../utils/hooks/useMapContext";
|
|
31
31
|
import useRvfContext from "../utils/hooks/useRvfContext";
|
|
32
32
|
import { fetchSharingWFS } from "../utils/sharingWFSUtils";
|
|
@@ -65,7 +65,7 @@ const fetchStations = async (
|
|
|
65
65
|
function RvfSharedMobilityLayerGroup(props: GroupOptions) {
|
|
66
66
|
const { baseLayer, map } = useMapContext();
|
|
67
67
|
const { setSharedMobilityLayerGroup } = useRvfContext();
|
|
68
|
-
const
|
|
68
|
+
const { t } = useI18n();
|
|
69
69
|
|
|
70
70
|
// Layers
|
|
71
71
|
// const [bikeFreloLayer, setBikeFreloLayer] = useState<MaplibreStyleLayer>();
|
|
@@ -234,33 +234,36 @@ function RvfSharedMobilityLayerGroup(props: GroupOptions) {
|
|
|
234
234
|
}
|
|
235
235
|
|
|
236
236
|
const bikeFrelo = new MaplibreStyleLayer({
|
|
237
|
+
layerTreeTitle: (title) => {
|
|
238
|
+
return (
|
|
239
|
+
<div className="flex items-center justify-between gap-2">
|
|
240
|
+
{t(title)}
|
|
241
|
+
<Bicycle />
|
|
242
|
+
</div>
|
|
243
|
+
);
|
|
244
|
+
},
|
|
237
245
|
maplibreLayer: baseLayer,
|
|
238
246
|
name: LAYERS_NAMES.bikeFrelo,
|
|
239
247
|
styleLayersFilter: ({ metadata }) => {
|
|
240
248
|
return metadata?.["rvf.filter"] === "bike.frelo";
|
|
241
249
|
},
|
|
242
|
-
|
|
243
|
-
title: (
|
|
244
|
-
<div className="flex items-center justify-between gap-2">
|
|
245
|
-
{"Frelo"}
|
|
246
|
-
<Bicycle />
|
|
247
|
-
</div>
|
|
248
|
-
),
|
|
249
250
|
});
|
|
250
251
|
// setBikeFreloLayer(bikeFrelo);
|
|
251
252
|
|
|
252
253
|
const bikeOthers = new MaplibreStyleLayer({
|
|
254
|
+
layerTreeTitle: (title) => {
|
|
255
|
+
return (
|
|
256
|
+
<div className="flex items-center justify-between gap-2">
|
|
257
|
+
{t(title)}
|
|
258
|
+
<Bicycle />
|
|
259
|
+
</div>
|
|
260
|
+
);
|
|
261
|
+
},
|
|
253
262
|
maplibreLayer: baseLayer,
|
|
254
263
|
name: LAYERS_NAMES.bikeOthers,
|
|
255
264
|
styleLayersFilter: ({ metadata }) => {
|
|
256
265
|
return metadata?.["rvf.filter"] === "bike.other";
|
|
257
266
|
},
|
|
258
|
-
title: (
|
|
259
|
-
<div className="flex items-center justify-between gap-2">
|
|
260
|
-
{layersConfig[LAYERS_NAMES.bikeOthers]?.title}
|
|
261
|
-
<Bicycle />
|
|
262
|
-
</div>
|
|
263
|
-
),
|
|
264
267
|
});
|
|
265
268
|
// setBikeOthersLayer(bikeOthers);
|
|
266
269
|
|
|
@@ -268,14 +271,16 @@ function RvfSharedMobilityLayerGroup(props: GroupOptions) {
|
|
|
268
271
|
layersFilter: ({ metadata }) => {
|
|
269
272
|
return metadata?.["rvf.filter"] === "cargo_bike.frelo";
|
|
270
273
|
},
|
|
274
|
+
layerTreeTitle: (title) => {
|
|
275
|
+
return (
|
|
276
|
+
<div className="flex items-center justify-between gap-2">
|
|
277
|
+
{t(title)}
|
|
278
|
+
<CargoBike />
|
|
279
|
+
</div>
|
|
280
|
+
);
|
|
281
|
+
},
|
|
271
282
|
maplibreLayer: baseLayer,
|
|
272
283
|
name: LAYERS_NAMES.cargobikeFrelo,
|
|
273
|
-
title: (
|
|
274
|
-
<div className="flex items-center justify-between gap-2">
|
|
275
|
-
{layersConfig[LAYERS_NAMES.cargobikeFrelo]?.title}
|
|
276
|
-
<CargoBike />
|
|
277
|
-
</div>
|
|
278
|
-
),
|
|
279
284
|
});
|
|
280
285
|
// setCargobikeFreloLayer(cargobikeFrelo);
|
|
281
286
|
|
|
@@ -283,14 +288,16 @@ function RvfSharedMobilityLayerGroup(props: GroupOptions) {
|
|
|
283
288
|
layersFilter: ({ metadata }) => {
|
|
284
289
|
return metadata?.["rvf.filter"] === "cargo_bike.other";
|
|
285
290
|
},
|
|
291
|
+
layerTreeTitle: (title) => {
|
|
292
|
+
return (
|
|
293
|
+
<div className="flex items-center justify-between gap-2">
|
|
294
|
+
{t(title)}
|
|
295
|
+
<CargoBike />
|
|
296
|
+
</div>
|
|
297
|
+
);
|
|
298
|
+
},
|
|
286
299
|
maplibreLayer: baseLayer,
|
|
287
300
|
name: LAYERS_NAMES.cargobikeOthers,
|
|
288
|
-
title: (
|
|
289
|
-
<div className="flex items-center justify-between gap-2">
|
|
290
|
-
{layersConfig[LAYERS_NAMES.cargobikeOthers]?.title}
|
|
291
|
-
<CargoBike />
|
|
292
|
-
</div>
|
|
293
|
-
),
|
|
294
301
|
});
|
|
295
302
|
// setCargobikeOthersLayer(cargobikeOthers);
|
|
296
303
|
|
|
@@ -298,14 +305,16 @@ function RvfSharedMobilityLayerGroup(props: GroupOptions) {
|
|
|
298
305
|
layersFilter: ({ metadata }) => {
|
|
299
306
|
return metadata?.["rvf.filter"] === "car.grueneflotte";
|
|
300
307
|
},
|
|
308
|
+
layerTreeTitle: (title) => {
|
|
309
|
+
return (
|
|
310
|
+
<div className="flex items-center justify-between gap-2">
|
|
311
|
+
{t(title)}
|
|
312
|
+
<Car />
|
|
313
|
+
</div>
|
|
314
|
+
);
|
|
315
|
+
},
|
|
301
316
|
maplibreLayer: baseLayer,
|
|
302
317
|
name: LAYERS_NAMES.carGrf,
|
|
303
|
-
title: (
|
|
304
|
-
<div className="flex items-center justify-between gap-2">
|
|
305
|
-
{layersConfig[LAYERS_NAMES.carGrf]?.title}
|
|
306
|
-
<Car />
|
|
307
|
-
</div>
|
|
308
|
-
),
|
|
309
318
|
});
|
|
310
319
|
// setCarGrfLayer(carGrf);
|
|
311
320
|
|
|
@@ -313,14 +322,16 @@ function RvfSharedMobilityLayerGroup(props: GroupOptions) {
|
|
|
313
322
|
layersFilter: ({ metadata }) => {
|
|
314
323
|
return metadata?.["rvf.filter"] === "car.naturenergy";
|
|
315
324
|
},
|
|
325
|
+
layerTreeTitle: (title) => {
|
|
326
|
+
return (
|
|
327
|
+
<div className="flex items-center justify-between gap-2">
|
|
328
|
+
{t(title)}
|
|
329
|
+
<Car />
|
|
330
|
+
</div>
|
|
331
|
+
);
|
|
332
|
+
},
|
|
316
333
|
maplibreLayer: baseLayer,
|
|
317
334
|
name: LAYERS_NAMES.carNatur,
|
|
318
|
-
title: (
|
|
319
|
-
<div className="flex items-center justify-between gap-2">
|
|
320
|
-
{layersConfig[LAYERS_NAMES.carNatur]?.title}
|
|
321
|
-
<Car />
|
|
322
|
-
</div>
|
|
323
|
-
),
|
|
324
335
|
});
|
|
325
336
|
// setCarNaturLayer(carNatur);
|
|
326
337
|
|
|
@@ -328,14 +339,16 @@ function RvfSharedMobilityLayerGroup(props: GroupOptions) {
|
|
|
328
339
|
layersFilter: ({ metadata }) => {
|
|
329
340
|
return metadata?.["rvf.filter"] === "car.other";
|
|
330
341
|
},
|
|
342
|
+
layerTreeTitle: (title) => {
|
|
343
|
+
return (
|
|
344
|
+
<div className="flex items-center justify-between gap-2">
|
|
345
|
+
{t(title)}
|
|
346
|
+
<Car />
|
|
347
|
+
</div>
|
|
348
|
+
);
|
|
349
|
+
},
|
|
331
350
|
maplibreLayer: baseLayer,
|
|
332
351
|
name: LAYERS_NAMES.carOthers,
|
|
333
|
-
title: (
|
|
334
|
-
<div className="flex items-center justify-between gap-2">
|
|
335
|
-
{layersConfig[LAYERS_NAMES.carOthers]?.title}
|
|
336
|
-
<Car />
|
|
337
|
-
</div>
|
|
338
|
-
),
|
|
339
352
|
});
|
|
340
353
|
// setCarOthersLayer(carOthers);
|
|
341
354
|
|
|
@@ -343,14 +356,16 @@ function RvfSharedMobilityLayerGroup(props: GroupOptions) {
|
|
|
343
356
|
layersFilter: ({ metadata }) => {
|
|
344
357
|
return metadata?.["rvf.filter"] === "scooter";
|
|
345
358
|
},
|
|
359
|
+
layerTreeTitle: (title) => {
|
|
360
|
+
return (
|
|
361
|
+
<div className="flex items-center justify-between gap-2">
|
|
362
|
+
{t(title)}
|
|
363
|
+
<Scooter />
|
|
364
|
+
</div>
|
|
365
|
+
);
|
|
366
|
+
},
|
|
346
367
|
maplibreLayer: baseLayer,
|
|
347
368
|
name: LAYERS_NAMES.eroller,
|
|
348
|
-
title: (
|
|
349
|
-
<div className="flex items-center justify-between gap-2">
|
|
350
|
-
{layersConfig[LAYERS_NAMES.eroller]?.title}
|
|
351
|
-
<Scooter />
|
|
352
|
-
</div>
|
|
353
|
-
),
|
|
354
369
|
});
|
|
355
370
|
// setScooterLayer(scooter);
|
|
356
371
|
|
|
@@ -358,14 +373,16 @@ function RvfSharedMobilityLayerGroup(props: GroupOptions) {
|
|
|
358
373
|
layersFilter: ({ metadata }) => {
|
|
359
374
|
return metadata?.["rvf.filter"] === "hitchhiking";
|
|
360
375
|
},
|
|
376
|
+
layerTreeTitle: (title) => {
|
|
377
|
+
return (
|
|
378
|
+
<div className="flex items-center justify-between gap-2">
|
|
379
|
+
{t(title)}
|
|
380
|
+
<Ride />
|
|
381
|
+
</div>
|
|
382
|
+
);
|
|
383
|
+
},
|
|
361
384
|
maplibreLayer: baseLayer,
|
|
362
385
|
name: LAYERS_NAMES.mitfahrpunkte,
|
|
363
|
-
title: (
|
|
364
|
-
<div className="flex items-center justify-between gap-2">
|
|
365
|
-
{layersConfig[LAYERS_NAMES.mitfahrpunkte]?.title}
|
|
366
|
-
<Ride />
|
|
367
|
-
</div>
|
|
368
|
-
),
|
|
369
386
|
});
|
|
370
387
|
|
|
371
388
|
return new Group({
|
|
@@ -380,9 +397,10 @@ function RvfSharedMobilityLayerGroup(props: GroupOptions) {
|
|
|
380
397
|
scooter,
|
|
381
398
|
hitchHiking,
|
|
382
399
|
],
|
|
400
|
+
name: LAYERS_NAMES.sharedMobility,
|
|
383
401
|
...props,
|
|
384
402
|
} as GroupOptions);
|
|
385
|
-
}, [baseLayer,
|
|
403
|
+
}, [baseLayer, props, t]);
|
|
386
404
|
|
|
387
405
|
// Reload features every minute
|
|
388
406
|
useEffect(() => {
|
package/src/Search/Search.tsx
CHANGED
|
@@ -7,7 +7,7 @@ import useMapContext from "../utils/hooks/useMapContext";
|
|
|
7
7
|
|
|
8
8
|
import type { StopsSearchProps } from "../StopsSearch/StopsSearch";
|
|
9
9
|
|
|
10
|
-
function Search(props: StopsSearchProps) {
|
|
10
|
+
function Search(props: Partial<StopsSearchProps>) {
|
|
11
11
|
const { apikey, map, stopsurl } = useMapContext();
|
|
12
12
|
|
|
13
13
|
const onSelect = useCallback(
|
|
@@ -3,22 +3,28 @@ import { useCallback } from "preact/hooks";
|
|
|
3
3
|
|
|
4
4
|
import Search from "../icons/Search";
|
|
5
5
|
import IconButton from "../ui/IconButton";
|
|
6
|
+
import useI18n from "../utils/hooks/useI18n";
|
|
6
7
|
import useMapContext from "../utils/hooks/useMapContext";
|
|
7
8
|
|
|
8
|
-
import type {
|
|
9
|
+
import type { IconButtonProps } from "../ui/IconButton/IconButton";
|
|
9
10
|
|
|
10
|
-
export type SearchButtonProps =
|
|
11
|
-
PreactDOMAttributes;
|
|
11
|
+
export type SearchButtonProps = IconButtonProps;
|
|
12
12
|
|
|
13
13
|
function SearchButton({ ...props }: SearchButtonProps) {
|
|
14
14
|
const { isSearchOpen, setIsSearchOpen } = useMapContext();
|
|
15
|
+
const { t } = useI18n();
|
|
15
16
|
|
|
16
17
|
const onClick = useCallback(() => {
|
|
17
18
|
setIsSearchOpen(!isSearchOpen);
|
|
18
19
|
}, [isSearchOpen, setIsSearchOpen]);
|
|
19
20
|
|
|
20
21
|
return (
|
|
21
|
-
<IconButton
|
|
22
|
+
<IconButton
|
|
23
|
+
title={t("search_menu_title")}
|
|
24
|
+
{...props}
|
|
25
|
+
onClick={onClick}
|
|
26
|
+
selected={isSearchOpen}
|
|
27
|
+
>
|
|
22
28
|
<Search />
|
|
23
29
|
</IconButton>
|
|
24
30
|
);
|
|
@@ -5,6 +5,7 @@ import Email from "../icons/Email";
|
|
|
5
5
|
import Image from "../icons/Image";
|
|
6
6
|
import PermalinkInput from "../PermalinkInput";
|
|
7
7
|
import Button from "../ui/Button";
|
|
8
|
+
import useI18n from "../utils/hooks/useI18n";
|
|
8
9
|
import useMapContext from "../utils/hooks/useMapContext";
|
|
9
10
|
|
|
10
11
|
import type { HTMLAttributes, PreactDOMAttributes } from "preact";
|
|
@@ -14,6 +15,7 @@ function ShareMenu({
|
|
|
14
15
|
...props
|
|
15
16
|
}: HTMLAttributes<HTMLDivElement> & PreactDOMAttributes) {
|
|
16
17
|
const { map } = useMapContext();
|
|
18
|
+
const { t } = useI18n();
|
|
17
19
|
|
|
18
20
|
return (
|
|
19
21
|
// eslint-disable-next-line @typescript-eslint/no-base-to-string, @typescript-eslint/restrict-template-expressions
|
|
@@ -23,12 +25,12 @@ function ShareMenu({
|
|
|
23
25
|
href={`mailto:?subject=Karte&body=${window?.location.href}`}
|
|
24
26
|
>
|
|
25
27
|
<Email />
|
|
26
|
-
<span>
|
|
28
|
+
<span>{t("share_email_send")}</span>
|
|
27
29
|
</Button>
|
|
28
30
|
<CanvasSaveButton map={map}>
|
|
29
31
|
<Button className={"w-fit"}>
|
|
30
32
|
<Image />
|
|
31
|
-
<span>
|
|
33
|
+
<span>{t("share_image_save")}</span>
|
|
32
34
|
</Button>
|
|
33
35
|
</CanvasSaveButton>
|
|
34
36
|
<PermalinkInput />
|
|
@@ -3,22 +3,29 @@ import { useCallback } from "preact/hooks";
|
|
|
3
3
|
|
|
4
4
|
import Share from "../icons/Share";
|
|
5
5
|
import IconButton from "../ui/IconButton";
|
|
6
|
+
import useI18n from "../utils/hooks/useI18n";
|
|
6
7
|
import useMapContext from "../utils/hooks/useMapContext";
|
|
7
8
|
|
|
8
9
|
import type { HTMLAttributes, PreactDOMAttributes } from "preact";
|
|
9
10
|
|
|
10
|
-
export type
|
|
11
|
+
export type ShareMenuButtonProps = HTMLAttributes<HTMLButtonElement> &
|
|
11
12
|
PreactDOMAttributes;
|
|
12
13
|
|
|
13
|
-
function ShareMenuButton({ ...props }:
|
|
14
|
+
function ShareMenuButton({ ...props }: ShareMenuButtonProps) {
|
|
14
15
|
const { isShareMenuOpen, setIsShareMenuOpen } = useMapContext();
|
|
16
|
+
const { t } = useI18n();
|
|
15
17
|
|
|
16
18
|
const onClick = useCallback(() => {
|
|
17
19
|
setIsShareMenuOpen(!isShareMenuOpen);
|
|
18
20
|
}, [isShareMenuOpen, setIsShareMenuOpen]);
|
|
19
21
|
|
|
20
22
|
return (
|
|
21
|
-
<IconButton
|
|
23
|
+
<IconButton
|
|
24
|
+
title={t("share_menu_title")}
|
|
25
|
+
{...props}
|
|
26
|
+
onClick={onClick}
|
|
27
|
+
selected={isShareMenuOpen}
|
|
28
|
+
>
|
|
22
29
|
<Share />
|
|
23
30
|
</IconButton>
|
|
24
31
|
);
|
|
@@ -16,7 +16,10 @@ function StationsLayer(props: Partial<MaplibreStyleLayerOptions>) {
|
|
|
16
16
|
}
|
|
17
17
|
return new MaplibreStyleLayer({
|
|
18
18
|
layersFilter: ({ metadata }) => {
|
|
19
|
-
return
|
|
19
|
+
return (
|
|
20
|
+
metadata?.["tralis.variable"] === "station" ||
|
|
21
|
+
metadata?.["general.filter"] === "stations"
|
|
22
|
+
);
|
|
20
23
|
},
|
|
21
24
|
maplibreLayer: baseLayer,
|
|
22
25
|
name: LAYER_NAME_STATIONS,
|