@geops/rvf-mobility-web-component 0.1.13 → 0.1.14
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 +30 -0
- package/index.js +217 -112
- package/package.json +5 -3
- package/src/RvfButton/RvfButton.tsx +5 -2
- package/src/RvfCheckbox/RvfCheckbox.tsx +10 -4
- package/src/RvfExportMenu/RvfExportMenu.tsx +59 -77
- package/src/RvfFloatingMenu/RvfFloatingMenu.tsx +1 -1
- package/src/RvfIconButton/RvfIconButton.tsx +4 -1
- package/src/RvfInputCopy/RvfInputCopy.tsx +57 -0
- package/src/RvfInputCopy/index.tsx +1 -0
- package/src/RvfLayerTree/TreeItem/TreeItem.tsx +20 -0
- package/src/RvfLayerTreeButton/RvfLayerTreeButton.tsx +27 -0
- package/src/RvfLayerTreeButton/index.tsx +1 -0
- package/src/RvfLineNetworkPlanLayer/RvfLineNetworkPlanLayer.tsx +2 -5
- package/src/RvfMobilityMap/RvfMobilityMap.tsx +176 -18
- package/src/RvfOverlayHeader/RvfOverlayHeader.tsx +40 -0
- package/src/RvfOverlayHeader/index.tsx +1 -0
- package/src/RvfPermalink/RvfPermalink.tsx +18 -0
- package/src/RvfPermalink/index.tsx +1 -0
- package/src/RvfSelect/RvfSelect.tsx +2 -2
- package/src/RvfSellingPointsLayer/RvfSellingPointsLayer.tsx +16 -1
- package/src/RvfShare/RvfPermalinkButton/RvfPermalinkButton.tsx +61 -0
- package/src/RvfShare/RvfPermalinkButton/index.tsx +1 -0
- package/src/RvfShare/RvfShare.tsx +40 -0
- package/src/RvfShare/index.tsx +1 -0
- package/src/RvfShareMenuButton/RvfShareMenuButton.tsx +27 -0
- package/src/RvfShareMenuButton/index.tsx +1 -0
- package/src/RvfSharedMobilityLayerGroup/RvfSharedMobilityLayerGroup.tsx +64 -15
- package/src/RvfTarifZonenLayer/RvfTarifZonenLayer.tsx +2 -5
- package/src/icons/Automat/Automat.tsx +8 -0
- package/src/icons/Automat/index.tsx +1 -0
- package/src/icons/Automat/rvf_automat.svg +15 -0
- package/src/icons/Bike/Bike.tsx +8 -0
- package/src/icons/Bike/index.tsx +1 -0
- package/src/icons/Bike/rvf_shared_bike.svg +15 -0
- package/src/icons/Car/Car.tsx +8 -0
- package/src/icons/Car/index.tsx +1 -0
- package/src/icons/Car/rvf_shared_car.svg +16 -0
- package/src/icons/CargoBike/CargoBike.tsx +8 -0
- package/src/icons/CargoBike/index.tsx +1 -0
- package/src/icons/CargoBike/rvf_shared_cargo_bike.svg +16 -0
- package/src/icons/Copy/Copy.tsx +25 -0
- package/src/icons/Copy/index.tsx +1 -0
- package/src/icons/Doc/Doc.tsx +19 -0
- package/src/icons/Doc/doc.svg +7 -0
- package/src/icons/Doc/index.tsx +1 -0
- package/src/icons/Ebike/Ebike.tsx +8 -0
- package/src/icons/Ebike/index.tsx +1 -0
- package/src/icons/Ebike/rvf_shared_e-bike.svg +15 -0
- package/src/icons/Email/Email.tsx +19 -0
- package/src/icons/Email/email.svg +7 -0
- package/src/icons/Email/index.tsx +1 -0
- package/src/icons/FilePdf/FilePdf.tsx +19 -0
- package/src/icons/FilePdf/file-pdf.svg +7 -0
- package/src/icons/FilePdf/index.tsx +1 -0
- package/src/icons/Image/Image.tsx +24 -0
- package/src/icons/Image/index.tsx +1 -0
- package/src/icons/InPerson/InPerson.tsx +8 -0
- package/src/icons/InPerson/index.tsx +1 -0
- package/src/icons/InPerson/rvf_persoenlich.svg +17 -0
- package/src/icons/Minus/minus-grey.svg +7 -0
- package/src/icons/Ride/Ride.tsx +8 -0
- package/src/icons/Ride/index.tsx +1 -0
- package/src/icons/Ride/rvf_shared_ride.svg +15 -0
- package/src/icons/Scooter/Scooter.tsx +8 -0
- package/src/icons/Scooter/index.tsx +1 -0
- package/src/icons/Scooter/rvf_shared_scooter.svg +15 -0
- package/src/icons/Share/Share.tsx +24 -0
- package/src/icons/Share/index.tsx +1 -0
- package/src/icons/Stack/Stack.tsx +24 -0
- package/src/icons/Stack/index.tsx +1 -0
- package/src/icons/Video/Video.tsx +8 -0
- package/src/icons/Video/index.tsx +1 -0
- package/src/icons/Video/rvf_video.svg +19 -0
- package/src/icons/rvf_shared_ride_2.svg +12 -0
- package/src/utils/createSharedMobilityLayer.ts +20 -20
- package/src/utils/hooks/useRvfContext.tsx +13 -1
- package/tailwind.config.mjs +3 -0
- package/src/icons/Bicycle/verkehrstraeger-rad-2px-white.svg +0 -19
- package/src/icons/Car/verkehrstraeger-auto-2px-white.svg +0 -14
- package/src/icons/CargoBicycle/verkehrstraeger-lastenrad-2px-white.svg +0 -27
- package/src/icons/Scooter/scooter.svg +0 -10
|
@@ -33,12 +33,16 @@ import RvfExportMenu from "../RvfExportMenu";
|
|
|
33
33
|
import RvfExportMenuButton from "../RvfExportMenuButton";
|
|
34
34
|
import RvfFeatureDetails from "../RvfFeatureDetails";
|
|
35
35
|
import RvfFloatingMenu from "../RvfFloatingMenu";
|
|
36
|
+
import RvfLayerTreeButton from "../RvfLayerTreeButton";
|
|
36
37
|
// Notificationurl example: https://mobility-web-component-tmp.vercel.app/geops-mobility?notificationurl=https%3A%2F%2Fmoco.geops.io%2Fapi%2Fv1%2Fexport%2Fnotification%2F%3Fsso_config%3Dsob&geolocation=false&realtime=false&search=false¬ificationat=2024-01-25T22%3A59%3A00Z
|
|
37
38
|
import RvfLineNetworkPlanLayer from "../RvfLineNetworkPlanLayer";
|
|
38
39
|
import Modal from "../RvfModal";
|
|
40
|
+
import RvfOverlayHeader from "../RvfOverlayHeader";
|
|
39
41
|
import RvfPoisLayer from "../RvfPoisLayer";
|
|
40
42
|
import RvfSellingPointsLayer from "../RvfSellingPointsLayer";
|
|
43
|
+
import RvfShare from "../RvfShare";
|
|
41
44
|
import RvfSharedMobilityLayerGroup from "../RvfSharedMobilityLayerGroup";
|
|
45
|
+
import RvfShareMenuButton from "../RvfShareMenuButton";
|
|
42
46
|
import RvfTarifZonenLayer from "../RvfTarifZonenLayer";
|
|
43
47
|
import Topics from "../RvfTopics";
|
|
44
48
|
import RvfZoomButtons from "../RvfZoomButtons";
|
|
@@ -59,7 +63,7 @@ import MobilityEvent from "../utils/MobilityEvent";
|
|
|
59
63
|
// @ts-expect-error bad type definition
|
|
60
64
|
import style from "./index.css";
|
|
61
65
|
|
|
62
|
-
export type RvfMobilityMapProps = {} & MobilityMapProps;
|
|
66
|
+
export type RvfMobilityMapProps = { toolbar: string } & MobilityMapProps;
|
|
63
67
|
|
|
64
68
|
const bbox = RVF_EXTENT_3857.join(",");
|
|
65
69
|
|
|
@@ -98,6 +102,7 @@ function RvfMobilityMap({
|
|
|
98
102
|
search = "false",
|
|
99
103
|
stopsurl = "https://api.geops.io/stops/v1/",
|
|
100
104
|
tenant = null,
|
|
105
|
+
toolbar = "true",
|
|
101
106
|
zoom = null,
|
|
102
107
|
}: RvfMobilityMapProps) {
|
|
103
108
|
const eventNodeRef = useRef<HTMLDivElement>();
|
|
@@ -112,6 +117,7 @@ function RvfMobilityMap({
|
|
|
112
117
|
const [stationId, setStationId] = useState<RealtimeStationId>();
|
|
113
118
|
const [trainId, setTrainId] = useState<RealtimeTrainId>();
|
|
114
119
|
const [isExportMenuOpen, setIsExportMenuOpen] = useState<boolean>(false);
|
|
120
|
+
const [isShareMenuOpen, setIsShareMenuOpen] = useState<boolean>(false);
|
|
115
121
|
const [selectedFeature, setSelectedFeature] = useState<Feature>();
|
|
116
122
|
const [selectedFeatures, setSelectedFeatures] = useState<Feature[]>();
|
|
117
123
|
const [isLayerTreeOpen, setIsLayerTreeOpen] = useState<boolean>(false);
|
|
@@ -123,6 +129,7 @@ function RvfMobilityMap({
|
|
|
123
129
|
useState<MaplibreStyleLayer>();
|
|
124
130
|
const [sharedMobilityLayerGroup, setSharedMobilityLayerGroup] =
|
|
125
131
|
useState<Group>();
|
|
132
|
+
const [hasToolbar] = useState<boolean>(toolbar === "true");
|
|
126
133
|
|
|
127
134
|
// TODO: this should be removed. The parent application should be responsible to do this
|
|
128
135
|
// or we should find something that fit more usecases
|
|
@@ -223,6 +230,7 @@ function RvfMobilityMap({
|
|
|
223
230
|
realtimeurl,
|
|
224
231
|
search,
|
|
225
232
|
tenant,
|
|
233
|
+
toolbar,
|
|
226
234
|
zoom,
|
|
227
235
|
}),
|
|
228
236
|
);
|
|
@@ -230,6 +238,7 @@ function RvfMobilityMap({
|
|
|
230
238
|
baselayer,
|
|
231
239
|
center,
|
|
232
240
|
geolocation,
|
|
241
|
+
toolbar,
|
|
233
242
|
mapsurl,
|
|
234
243
|
maxzoom,
|
|
235
244
|
minzoom,
|
|
@@ -251,6 +260,7 @@ function RvfMobilityMap({
|
|
|
251
260
|
return {
|
|
252
261
|
isExportMenuOpen,
|
|
253
262
|
isLayerTreeOpen,
|
|
263
|
+
isShareMenuOpen,
|
|
254
264
|
lineNetworkPlanLayer,
|
|
255
265
|
poisLayer,
|
|
256
266
|
selectedFeature,
|
|
@@ -258,6 +268,7 @@ function RvfMobilityMap({
|
|
|
258
268
|
sellingPointsLayer,
|
|
259
269
|
setIsExportMenuOpen,
|
|
260
270
|
setIsLayerTreeOpen,
|
|
271
|
+
setIsShareMenuOpen,
|
|
261
272
|
setLineNetworkPlanLayer,
|
|
262
273
|
setPoisLayer,
|
|
263
274
|
setSelectedFeature,
|
|
@@ -271,6 +282,7 @@ function RvfMobilityMap({
|
|
|
271
282
|
}, [
|
|
272
283
|
isExportMenuOpen,
|
|
273
284
|
isLayerTreeOpen,
|
|
285
|
+
isShareMenuOpen,
|
|
274
286
|
lineNetworkPlanLayer,
|
|
275
287
|
poisLayer,
|
|
276
288
|
selectedFeature,
|
|
@@ -302,6 +314,76 @@ function RvfMobilityMap({
|
|
|
302
314
|
};
|
|
303
315
|
}, []);
|
|
304
316
|
|
|
317
|
+
const stationsLayerProps = useMemo(() => {
|
|
318
|
+
return {
|
|
319
|
+
layersFilter: ({ metadata }) => {
|
|
320
|
+
return metadata?.["rvf.filter"] === "netzplan_stations";
|
|
321
|
+
},
|
|
322
|
+
};
|
|
323
|
+
}, []);
|
|
324
|
+
|
|
325
|
+
useEffect(() => {
|
|
326
|
+
if (hasToolbar && isLayerTreeOpen) {
|
|
327
|
+
setIsExportMenuOpen(false);
|
|
328
|
+
setIsLayerTreeOpen(isLayerTreeOpen);
|
|
329
|
+
setSelectedFeature(null);
|
|
330
|
+
setTrainId(null);
|
|
331
|
+
setStationId(null);
|
|
332
|
+
setIsShareMenuOpen(false);
|
|
333
|
+
}
|
|
334
|
+
}, [isLayerTreeOpen, hasToolbar]);
|
|
335
|
+
|
|
336
|
+
useEffect(() => {
|
|
337
|
+
if (hasToolbar && isExportMenuOpen) {
|
|
338
|
+
setIsLayerTreeOpen(false);
|
|
339
|
+
setIsExportMenuOpen(isExportMenuOpen);
|
|
340
|
+
setSelectedFeature(null);
|
|
341
|
+
setTrainId(null);
|
|
342
|
+
setIsShareMenuOpen(false);
|
|
343
|
+
setStationId(null);
|
|
344
|
+
}
|
|
345
|
+
}, [isExportMenuOpen, hasToolbar]);
|
|
346
|
+
|
|
347
|
+
useEffect(() => {
|
|
348
|
+
if (hasToolbar && selectedFeature) {
|
|
349
|
+
setIsLayerTreeOpen(false);
|
|
350
|
+
setIsExportMenuOpen(false);
|
|
351
|
+
setTrainId(null);
|
|
352
|
+
setIsShareMenuOpen(false);
|
|
353
|
+
setStationId(null);
|
|
354
|
+
}
|
|
355
|
+
}, [selectedFeature, hasToolbar]);
|
|
356
|
+
|
|
357
|
+
useEffect(() => {
|
|
358
|
+
if (hasToolbar && stationId) {
|
|
359
|
+
setIsLayerTreeOpen(false);
|
|
360
|
+
setIsExportMenuOpen(false);
|
|
361
|
+
setTrainId(null);
|
|
362
|
+
setIsShareMenuOpen(false);
|
|
363
|
+
setSelectedFeature(null);
|
|
364
|
+
}
|
|
365
|
+
}, [stationId, hasToolbar]);
|
|
366
|
+
|
|
367
|
+
useEffect(() => {
|
|
368
|
+
if (hasToolbar && trainId) {
|
|
369
|
+
setIsLayerTreeOpen(false);
|
|
370
|
+
setIsExportMenuOpen(false);
|
|
371
|
+
setStationId(null);
|
|
372
|
+
setIsShareMenuOpen(false);
|
|
373
|
+
setSelectedFeature(null);
|
|
374
|
+
}
|
|
375
|
+
}, [trainId, hasToolbar]);
|
|
376
|
+
|
|
377
|
+
useEffect(() => {
|
|
378
|
+
if (hasToolbar && isShareMenuOpen) {
|
|
379
|
+
setIsLayerTreeOpen(false);
|
|
380
|
+
setIsExportMenuOpen(false);
|
|
381
|
+
setStationId(null);
|
|
382
|
+
setTrainId(null);
|
|
383
|
+
setSelectedFeature(null);
|
|
384
|
+
}
|
|
385
|
+
}, [isShareMenuOpen, hasToolbar]);
|
|
386
|
+
|
|
305
387
|
return (
|
|
306
388
|
<I18nContext.Provider value={i18n}>
|
|
307
389
|
<style>{tailwind}</style>
|
|
@@ -319,11 +401,21 @@ function RvfMobilityMap({
|
|
|
319
401
|
<SingleClickListener />
|
|
320
402
|
|
|
321
403
|
{realtime === "true" && <RealtimeLayer title="Echtzeit" />}
|
|
322
|
-
{
|
|
404
|
+
{
|
|
405
|
+
<StationsLayer
|
|
406
|
+
minZoom={10}
|
|
407
|
+
title="Haltestellen"
|
|
408
|
+
{...stationsLayerProps}
|
|
409
|
+
/>
|
|
410
|
+
}
|
|
323
411
|
{notification === "true" && <NotificationLayer />}
|
|
324
|
-
<RvfSellingPointsLayer title="Verkaufsstellen" />
|
|
325
|
-
<RvfLineNetworkPlanLayer
|
|
326
|
-
|
|
412
|
+
<RvfSellingPointsLayer isQueryable title="Verkaufsstellen" />
|
|
413
|
+
<RvfLineNetworkPlanLayer
|
|
414
|
+
isQueryable
|
|
415
|
+
minZoom={10}
|
|
416
|
+
title="Liniennetz"
|
|
417
|
+
/>
|
|
418
|
+
<RvfTarifZonenLayer isQueryable title="Tarifzonen" />
|
|
327
419
|
<RvfPoisLayer title="POIs" />
|
|
328
420
|
<RvfSharedMobilityLayerGroup title="Shared Mobility" />
|
|
329
421
|
|
|
@@ -337,7 +429,7 @@ function RvfMobilityMap({
|
|
|
337
429
|
|
|
338
430
|
<div className="absolute right-2 top-2 z-10 flex flex-col gap-2">
|
|
339
431
|
{geolocation === "true" && <GeolocationButton />}
|
|
340
|
-
<RvfExportMenuButton />
|
|
432
|
+
{!hasToolbar && <RvfExportMenuButton />}
|
|
341
433
|
</div>
|
|
342
434
|
|
|
343
435
|
{search === "true" && (
|
|
@@ -350,31 +442,97 @@ function RvfMobilityMap({
|
|
|
350
442
|
<RvfZoomButtons />
|
|
351
443
|
</div>
|
|
352
444
|
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
445
|
+
{!hasToolbar && (
|
|
446
|
+
<RvfFloatingMenu
|
|
447
|
+
isOpen={isLayerTreeOpen}
|
|
448
|
+
onClick={onLayerTreeMenuClick}
|
|
449
|
+
title="Kartendaten"
|
|
450
|
+
>
|
|
451
|
+
<Topics className={"w-full px-2"} />
|
|
452
|
+
</RvfFloatingMenu>
|
|
453
|
+
)}
|
|
360
454
|
</Map>
|
|
361
|
-
|
|
362
455
|
<Overlay
|
|
363
456
|
className={"z-50"}
|
|
364
457
|
ScrollableHandlerProps={scrollableHandlerProps}
|
|
365
458
|
>
|
|
366
459
|
{realtime === "true" && trainId && (
|
|
367
|
-
|
|
460
|
+
<>
|
|
461
|
+
<RvfOverlayHeader
|
|
462
|
+
onClose={() => {
|
|
463
|
+
setTrainId(null);
|
|
464
|
+
}}
|
|
465
|
+
title="Train"
|
|
466
|
+
></RvfOverlayHeader>
|
|
467
|
+
<RouteSchedule className="relative overflow-y-auto overflow-x-hidden" />
|
|
468
|
+
</>
|
|
368
469
|
)}
|
|
369
470
|
{tenant && stationId && (
|
|
370
|
-
|
|
471
|
+
<>
|
|
472
|
+
<RvfOverlayHeader
|
|
473
|
+
onClose={() => {
|
|
474
|
+
setStationId(null);
|
|
475
|
+
}}
|
|
476
|
+
title="Station"
|
|
477
|
+
></RvfOverlayHeader>
|
|
478
|
+
<Station className="relative flex flex-col overflow-y-auto overflow-x-hidden p-2" />
|
|
479
|
+
</>
|
|
371
480
|
)}
|
|
372
481
|
{selectedFeature && (
|
|
373
|
-
|
|
482
|
+
<>
|
|
483
|
+
<RvfOverlayHeader
|
|
484
|
+
onClose={() => {
|
|
485
|
+
setSelectedFeature(null);
|
|
486
|
+
}}
|
|
487
|
+
title="Informations"
|
|
488
|
+
></RvfOverlayHeader>
|
|
489
|
+
<RvfFeatureDetails className="relative flex flex-col gap-2 overflow-y-auto overflow-x-hidden p-2" />
|
|
490
|
+
</>
|
|
491
|
+
)}
|
|
492
|
+
{hasToolbar && isExportMenuOpen && (
|
|
493
|
+
<>
|
|
494
|
+
<RvfOverlayHeader
|
|
495
|
+
onClose={() => {
|
|
496
|
+
setIsExportMenuOpen(false);
|
|
497
|
+
}}
|
|
498
|
+
title="Export"
|
|
499
|
+
></RvfOverlayHeader>
|
|
500
|
+
<RvfExportMenu className="relative flex flex-col gap-2 overflow-y-auto overflow-x-hidden p-2" />
|
|
501
|
+
</>
|
|
502
|
+
)}
|
|
503
|
+
{hasToolbar && isLayerTreeOpen && (
|
|
504
|
+
<>
|
|
505
|
+
<RvfOverlayHeader
|
|
506
|
+
onClose={() => {
|
|
507
|
+
setIsLayerTreeOpen(false);
|
|
508
|
+
}}
|
|
509
|
+
title="Layers"
|
|
510
|
+
></RvfOverlayHeader>
|
|
511
|
+
<Topics className="relative flex h-full flex-col overflow-y-auto overflow-x-hidden p-2" />
|
|
512
|
+
</>
|
|
513
|
+
)}
|
|
514
|
+
{hasToolbar && isShareMenuOpen && (
|
|
515
|
+
<>
|
|
516
|
+
<RvfOverlayHeader
|
|
517
|
+
onClose={() => {
|
|
518
|
+
setIsLayerTreeOpen(false);
|
|
519
|
+
}}
|
|
520
|
+
title="Share"
|
|
521
|
+
></RvfOverlayHeader>
|
|
522
|
+
<RvfShare className="relative flex h-full flex-col overflow-y-auto overflow-x-hidden p-2" />
|
|
523
|
+
</>
|
|
374
524
|
)}
|
|
375
525
|
</Overlay>
|
|
376
526
|
|
|
377
|
-
{
|
|
527
|
+
{hasToolbar && (
|
|
528
|
+
<div className={"overflow-x-hidden border-r"}>
|
|
529
|
+
<RvfLayerTreeButton className={"border-none"} />
|
|
530
|
+
<RvfExportMenuButton className={"border-none"} />
|
|
531
|
+
<RvfShareMenuButton className={"border-none"} />
|
|
532
|
+
</div>
|
|
533
|
+
)}
|
|
534
|
+
|
|
535
|
+
{!hasToolbar && isExportMenuOpen && (
|
|
378
536
|
<Modal onClose={onExportMenuClose}>
|
|
379
537
|
<RvfExportMenu className="relative flex h-full flex-col overflow-y-auto overflow-x-hidden" />
|
|
380
538
|
</Modal>
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import type { JSX, PreactDOMAttributes } from "preact";
|
|
2
|
+
|
|
3
|
+
import { memo } from "preact/compat";
|
|
4
|
+
import { twMerge } from "tailwind-merge";
|
|
5
|
+
|
|
6
|
+
import Cancel from "../icons/Cancel";
|
|
7
|
+
import RvfIconButton from "../RvfIconButton";
|
|
8
|
+
|
|
9
|
+
export type RvfOverlayHeaderProps = {
|
|
10
|
+
onClose?: () => void;
|
|
11
|
+
title?: string;
|
|
12
|
+
} & JSX.HTMLAttributes<HTMLDivElement> &
|
|
13
|
+
PreactDOMAttributes;
|
|
14
|
+
|
|
15
|
+
function RvfOverlayHeader({
|
|
16
|
+
children,
|
|
17
|
+
className,
|
|
18
|
+
onClose,
|
|
19
|
+
title,
|
|
20
|
+
...props
|
|
21
|
+
}: RvfOverlayHeaderProps) {
|
|
22
|
+
return (
|
|
23
|
+
<div
|
|
24
|
+
className={twMerge(
|
|
25
|
+
"flex flex-row items-center justify-between gap-2 p-2 border-b" +
|
|
26
|
+
(className || ""),
|
|
27
|
+
)}
|
|
28
|
+
{...props}
|
|
29
|
+
>
|
|
30
|
+
{children || <span className={"font-bold"}>{title}</span>}
|
|
31
|
+
{onClose && (
|
|
32
|
+
<RvfIconButton className={"!size-[32px] border-none"} onClick={onClose}>
|
|
33
|
+
<Cancel />
|
|
34
|
+
</RvfIconButton>
|
|
35
|
+
)}
|
|
36
|
+
</div>
|
|
37
|
+
);
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
export default memo(RvfOverlayHeader);
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { default } from "./RvfOverlayHeader";
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import type { JSX, PreactDOMAttributes } from "preact";
|
|
2
|
+
|
|
3
|
+
import RvfInputCopy from "../RvfInputCopy";
|
|
4
|
+
|
|
5
|
+
function RvfPermalink({
|
|
6
|
+
...props
|
|
7
|
+
}: JSX.HTMLAttributes<HTMLDivElement> & PreactDOMAttributes) {
|
|
8
|
+
return (
|
|
9
|
+
<div {...props}>
|
|
10
|
+
<RvfInputCopy value={window?.location.href} />
|
|
11
|
+
<p className="py-2">
|
|
12
|
+
Sie können auch den Link aus der Adresszeile des Browsers kopieren.
|
|
13
|
+
</p>
|
|
14
|
+
</div>
|
|
15
|
+
);
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
export default RvfPermalink;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { default } from "./RvfPermalink";
|
|
@@ -9,12 +9,12 @@ function RvfSelect({ children, className, onChange }: RvfSelectProps) {
|
|
|
9
9
|
return (
|
|
10
10
|
<div className="relative flex items-center text-grey">
|
|
11
11
|
<select
|
|
12
|
-
className={`
|
|
12
|
+
className={`h-[32px] cursor-pointer appearance-none rounded-[12px] border border-current bg-white px-[13px] text-[16px] leading-[22px] disabled:cursor-default @sm/main:h-[36px] @md/main:h-[40px] ${className}`}
|
|
13
13
|
onChange={onChange}
|
|
14
14
|
>
|
|
15
15
|
{children}
|
|
16
16
|
</select>
|
|
17
|
-
<ArrowDown className="pointer-events-none absolute right-
|
|
17
|
+
<ArrowDown className="pointer-events-none absolute right-[8px]" />
|
|
18
18
|
</div>
|
|
19
19
|
);
|
|
20
20
|
}
|
|
@@ -3,10 +3,14 @@ import { MaplibreStyleLayerOptions } from "mobility-toolbox-js/ol/layers/Maplibr
|
|
|
3
3
|
import { memo } from "preact/compat";
|
|
4
4
|
import { useEffect, useMemo } from "preact/hooks";
|
|
5
5
|
|
|
6
|
+
import Automat from "../icons/Automat";
|
|
7
|
+
import InPerson from "../icons/InPerson";
|
|
8
|
+
import Video from "../icons/Video";
|
|
6
9
|
import useMapContext from "../utils/hooks/useMapContext";
|
|
7
10
|
import useRvfContext from "../utils/hooks/useRvfContext";
|
|
8
11
|
|
|
9
12
|
function RvfSellingPointsLayer(props: MaplibreStyleLayerOptions) {
|
|
13
|
+
const { title } = props;
|
|
10
14
|
const { baseLayer, map } = useMapContext();
|
|
11
15
|
const { setSellingPointsLayer } = useRvfContext();
|
|
12
16
|
|
|
@@ -21,8 +25,19 @@ function RvfSellingPointsLayer(props: MaplibreStyleLayerOptions) {
|
|
|
21
25
|
},
|
|
22
26
|
maplibreLayer: baseLayer,
|
|
23
27
|
...(props || {}),
|
|
28
|
+
|
|
29
|
+
title: (
|
|
30
|
+
<div className="flex items-center justify-between gap-2">
|
|
31
|
+
{title}
|
|
32
|
+
<span className="flex items-center">
|
|
33
|
+
<Automat />
|
|
34
|
+
<InPerson />
|
|
35
|
+
<Video />
|
|
36
|
+
</span>
|
|
37
|
+
</div>
|
|
38
|
+
),
|
|
24
39
|
});
|
|
25
|
-
}, [baseLayer, props]);
|
|
40
|
+
}, [baseLayer, props, title]);
|
|
26
41
|
|
|
27
42
|
useEffect(() => {
|
|
28
43
|
setSellingPointsLayer(layer);
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
import type { JSX, PreactDOMAttributes } from "preact";
|
|
2
|
+
|
|
3
|
+
import { useState } from "preact/hooks";
|
|
4
|
+
|
|
5
|
+
import Cancel from "../../icons/Cancel";
|
|
6
|
+
import Share from "../../icons/Share";
|
|
7
|
+
import RvfIconButton from "../../RvfIconButton";
|
|
8
|
+
import RvfInputCopy from "../../RvfInputCopy";
|
|
9
|
+
|
|
10
|
+
function RvfPermalinkButton({
|
|
11
|
+
className,
|
|
12
|
+
...props
|
|
13
|
+
}: JSX.ButtonHTMLAttributes<HTMLButtonElement> & PreactDOMAttributes) {
|
|
14
|
+
const [showTooltip, setShowTooltip] = useState(null);
|
|
15
|
+
const [positionTooltip, setPositionTooltip] = useState<DOMRect>();
|
|
16
|
+
|
|
17
|
+
const handleIconClick = (event) => {
|
|
18
|
+
setPositionTooltip(event.currentTarget.getBoundingClientRect());
|
|
19
|
+
setShowTooltip(!showTooltip);
|
|
20
|
+
};
|
|
21
|
+
|
|
22
|
+
return (
|
|
23
|
+
<>
|
|
24
|
+
<RvfIconButton
|
|
25
|
+
className={"border-none " + className}
|
|
26
|
+
onClick={handleIconClick}
|
|
27
|
+
selected={showTooltip}
|
|
28
|
+
{...props}
|
|
29
|
+
>
|
|
30
|
+
<Share />
|
|
31
|
+
</RvfIconButton>
|
|
32
|
+
|
|
33
|
+
{showTooltip && positionTooltip && (
|
|
34
|
+
<div
|
|
35
|
+
className="fixed rounded-lg border border-grey bg-white p-2 shadow-lg"
|
|
36
|
+
style={{
|
|
37
|
+
left: positionTooltip.left + 40,
|
|
38
|
+
top: positionTooltip.top - 10,
|
|
39
|
+
}}
|
|
40
|
+
>
|
|
41
|
+
<div className="pr-4">
|
|
42
|
+
<RvfIconButton
|
|
43
|
+
className="absolute right-[-5px] top-[-5px] border-none bg-transparent p-0"
|
|
44
|
+
onClick={handleIconClick}
|
|
45
|
+
>
|
|
46
|
+
<Cancel height="18px" width="18px" />
|
|
47
|
+
</RvfIconButton>
|
|
48
|
+
<RvfInputCopy />
|
|
49
|
+
<p className="py-2">
|
|
50
|
+
Sie können auch den Link aus der Adresszeile des Browsers
|
|
51
|
+
kopieren.
|
|
52
|
+
</p>
|
|
53
|
+
</div>
|
|
54
|
+
<div className="absolute -left-1.5 top-4 size-2.5 rotate-45 border border-r-0 border-t-0 border-grey bg-white"></div>
|
|
55
|
+
</div>
|
|
56
|
+
)}
|
|
57
|
+
</>
|
|
58
|
+
);
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
export default RvfPermalinkButton;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { default } from "./RvfPermalinkButton";
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import type { JSX, PreactDOMAttributes } from "preact";
|
|
2
|
+
|
|
3
|
+
import CanvasSaveButton from "react-spatial/components/CanvasSaveButton";
|
|
4
|
+
import { twMerge } from "tailwind-merge";
|
|
5
|
+
|
|
6
|
+
import Email from "../icons/Email";
|
|
7
|
+
import Image from "../icons/Image";
|
|
8
|
+
import RvfButton from "../RvfButton";
|
|
9
|
+
import RvfPermalink from "../RvfPermalink";
|
|
10
|
+
import useMapContext from "../utils/hooks/useMapContext";
|
|
11
|
+
|
|
12
|
+
function RvfShare({
|
|
13
|
+
className,
|
|
14
|
+
...props
|
|
15
|
+
}: JSX.HTMLAttributes<HTMLDivElement> & PreactDOMAttributes) {
|
|
16
|
+
const { map } = useMapContext();
|
|
17
|
+
|
|
18
|
+
return (
|
|
19
|
+
<div className={twMerge("flex flex-col gap-2 " + className)} {...props}>
|
|
20
|
+
<a
|
|
21
|
+
className="flex items-center gap-2"
|
|
22
|
+
href={`mailto:?subject=Karte&body=${window?.location.href}`}
|
|
23
|
+
>
|
|
24
|
+
<RvfButton>
|
|
25
|
+
<Email />
|
|
26
|
+
<span>Email senden</span>
|
|
27
|
+
</RvfButton>
|
|
28
|
+
</a>
|
|
29
|
+
<CanvasSaveButton map={map}>
|
|
30
|
+
<RvfButton className={"w-fit"}>
|
|
31
|
+
<Image />
|
|
32
|
+
<span>Bild speichern</span>
|
|
33
|
+
</RvfButton>
|
|
34
|
+
</CanvasSaveButton>
|
|
35
|
+
<RvfPermalink />
|
|
36
|
+
</div>
|
|
37
|
+
);
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
export default RvfShare;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { default } from "./RvfShare";
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import type { JSX, PreactDOMAttributes } from "preact";
|
|
2
|
+
|
|
3
|
+
import { memo } from "preact/compat";
|
|
4
|
+
import { useCallback } from "preact/hooks";
|
|
5
|
+
|
|
6
|
+
import Share from "../icons/Share";
|
|
7
|
+
import RvfIconButton from "../RvfIconButton";
|
|
8
|
+
import useRvfContext from "../utils/hooks/useRvfContext";
|
|
9
|
+
|
|
10
|
+
export type RvfShareMenuButtonProps = JSX.HTMLAttributes<HTMLButtonElement> &
|
|
11
|
+
PreactDOMAttributes;
|
|
12
|
+
|
|
13
|
+
function RvfShareMenuButton({ ...props }: RvfShareMenuButtonProps) {
|
|
14
|
+
const { isShareMenuOpen, setIsShareMenuOpen } = useRvfContext();
|
|
15
|
+
|
|
16
|
+
const onClick = useCallback(() => {
|
|
17
|
+
setIsShareMenuOpen(!isShareMenuOpen);
|
|
18
|
+
}, [isShareMenuOpen, setIsShareMenuOpen]);
|
|
19
|
+
|
|
20
|
+
return (
|
|
21
|
+
<RvfIconButton {...props} onClick={onClick} selected={isShareMenuOpen}>
|
|
22
|
+
<Share />
|
|
23
|
+
</RvfIconButton>
|
|
24
|
+
);
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
export default memo(RvfShareMenuButton);
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { default } from "./RvfShareMenuButton";
|