@geops/rvf-mobility-web-component 0.1.11 → 0.1.12
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 +19 -0
- package/docutils.js +9 -9
- package/index.js +222 -169
- package/package.json +2 -2
- package/scripts/build.mjs +3 -2
- package/scripts/dev.mjs +1 -0
- package/src/BaseLayer/BaseLayer.tsx +20 -12
- package/src/FloatingMenu/FloatingMenu.tsx +42 -0
- package/src/FloatingMenu/index.tsx +1 -0
- package/src/RealtimeLayer/RealtimeLayer.tsx +1 -2
- package/src/RvfCheckbox/RvfCheckbox.tsx +24 -0
- package/src/RvfCheckbox/index.tsx +1 -0
- package/src/RvfExportMenu/RvfExportMenu.tsx +14 -6
- package/src/RvfFloatingMenu/RvfFloatingMenu.tsx +44 -0
- package/src/RvfFloatingMenu/index.tsx +1 -0
- package/src/{LayerTree/LayerTree.tsx → RvfLayerTree/RvfLayerTree.tsx} +10 -13
- package/src/RvfLayerTree/TreeItem/TreeItem.tsx +120 -0
- package/src/RvfLayerTree/index.tsx +1 -0
- package/src/{LayerTree → RvfLayerTree}/layersTreeReducer.ts +0 -4
- package/src/RvfLineNetworkPlanLayer/RvfLineNetworkPlanLayer.tsx +42 -0
- package/src/RvfLineNetworkPlanLayer/index.tsx +1 -0
- package/src/RvfMobilityMap/RvfMobilityMap.tsx +28 -23
- package/src/RvfRadioButton/RvfRadioButton.tsx +16 -0
- package/src/RvfRadioButton/index.tsx +1 -0
- package/src/RvfSelect/RvfSelect.tsx +22 -0
- package/src/RvfSelect/index.tsx +1 -0
- package/src/RvfSellingPointsLayer/RvfSellingPointsLayer.tsx +41 -0
- package/src/RvfSellingPointsLayer/index.tsx +1 -0
- package/src/RvfSharedMobilityLayerGroup/RvfSharedMobilityLayerGroup.tsx +17 -5
- package/src/RvfSingleClickListener/RvfSingleClickListener.tsx +10 -1
- package/src/RvfTarifZonenLayer/RvfTarifZonenLayer.tsx +41 -0
- package/src/RvfTarifZonenLayer/index.tsx +1 -0
- package/src/RvfTopics/RvfTopics.tsx +47 -0
- package/src/RvfTopics/index.tsx +1 -0
- package/src/icons/ArrowDown/ArrowDown.tsx +22 -0
- package/src/icons/ArrowDown/down-open.svg +7 -0
- package/src/icons/ArrowDown/index.tsx +1 -0
- package/src/icons/ArrowUp/ArrowUp.tsx +22 -0
- package/src/icons/ArrowUp/index.tsx +1 -0
- package/src/icons/ArrowUp/up-open.svg +7 -0
- package/src/icons/Bicycle/verkehrstraeger-rad-2px-white.svg +19 -0
- package/src/icons/Car/verkehrstraeger-auto-2px-white.svg +14 -0
- package/src/icons/CargoBicycle/verkehrstraeger-lastenrad-2px-white.svg +27 -0
- package/src/icons/DownOpen/DownOpen.tsx +24 -0
- package/src/icons/DownOpen/down-open.svg +7 -0
- package/src/icons/DownOpen/index.tsx +1 -0
- package/src/icons/Ok/ok-grey.svg +7 -0
- package/src/icons/Ok/ok.svg +4 -0
- package/src/icons/Scooter/scooter.svg +10 -0
- package/src/utils/createMobiDataBwWfsLayer.ts +3 -3
- package/src/utils/createSharedMobilityLayer.ts +165 -0
- package/src/utils/exportPdf.ts +9 -29
- package/tailwind.config.mjs +19 -38
- package/src/LayerTree/TreeItem/TreeItem.tsx +0 -145
- package/src/LayerTree/TreeItemContainer/TreeItemContainer.tsx +0 -16
- package/src/LayerTree/TreeItemContainer/index.tsx +0 -1
- package/src/LayerTree/index.tsx +0 -1
- package/src/TopicMenu/TopicMenu.tsx +0 -143
- package/src/TopicMenu/index.tsx +0 -1
- /package/src/{LayerTree → RvfLayerTree}/TreeItem/index.tsx +0 -0
- /package/src/{LayerTree → RvfLayerTree}/layersTreeContext.ts +0 -0
package/package.json
CHANGED
|
@@ -2,14 +2,14 @@
|
|
|
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.12",
|
|
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
|
"jspdf": "^2.5.2",
|
|
11
11
|
"maplibre-gl": "^4.7.1",
|
|
12
|
-
"mobility-toolbox-js": "3.1.0
|
|
12
|
+
"mobility-toolbox-js": "3.1.0",
|
|
13
13
|
"ol": "^10.3.1",
|
|
14
14
|
"preact": "^10.25.1",
|
|
15
15
|
"preact-custom-element": "^4.3.0",
|
package/scripts/build.mjs
CHANGED
|
@@ -3,13 +3,14 @@ import * as esbuild from "esbuild";
|
|
|
3
3
|
import { sassPlugin } from "esbuild-sass-plugin";
|
|
4
4
|
|
|
5
5
|
await esbuild.build({
|
|
6
|
-
entryPoints: ["./src/index.js"],
|
|
7
6
|
bundle: true,
|
|
8
|
-
|
|
7
|
+
entryPoints: ["./src/index.js"],
|
|
9
8
|
external: ["mapbox-gl"],
|
|
10
9
|
loader: {
|
|
11
10
|
".png": "dataurl",
|
|
11
|
+
".svg": "dataurl",
|
|
12
12
|
},
|
|
13
|
+
minify: true,
|
|
13
14
|
outfile: "index.js",
|
|
14
15
|
plugins: [sassPlugin({ type: "css-text" })],
|
|
15
16
|
sourcemap: false,
|
package/scripts/dev.mjs
CHANGED
|
@@ -1,34 +1,42 @@
|
|
|
1
1
|
import { MaplibreLayer } from "mobility-toolbox-js/ol";
|
|
2
2
|
import { MaplibreLayerOptions } from "mobility-toolbox-js/ol/layers/MaplibreLayer";
|
|
3
|
-
import { Layer } from "ol/layer";
|
|
4
3
|
import { memo } from "preact/compat";
|
|
5
|
-
import { useEffect } from "preact/hooks";
|
|
4
|
+
import { useEffect, useMemo } from "preact/hooks";
|
|
6
5
|
|
|
7
6
|
import useMapContext from "../utils/hooks/useMapContext";
|
|
8
7
|
|
|
9
|
-
|
|
8
|
+
export type BaseLayerProps = MaplibreLayerOptions;
|
|
9
|
+
|
|
10
|
+
function BaseLayer(props: BaseLayerProps) {
|
|
10
11
|
const { apikey, baselayer, map, mapsurl, setBaseLayer } = useMapContext();
|
|
11
|
-
|
|
12
|
-
|
|
12
|
+
|
|
13
|
+
const layer = useMemo(() => {
|
|
14
|
+
if (!baselayer || !apikey) {
|
|
13
15
|
return;
|
|
14
16
|
}
|
|
15
|
-
|
|
17
|
+
return new MaplibreLayer({
|
|
16
18
|
apiKey: apikey,
|
|
17
19
|
style: baselayer,
|
|
18
20
|
url: mapsurl,
|
|
21
|
+
zIndex: 0,
|
|
19
22
|
...(props || {}),
|
|
20
23
|
});
|
|
21
|
-
|
|
24
|
+
}, [baselayer, apikey, props, mapsurl]);
|
|
22
25
|
|
|
23
|
-
|
|
24
|
-
baseLayer.setZIndex(0);
|
|
25
|
-
map.addLayer(baseLayer);
|
|
26
|
+
useEffect(() => {
|
|
26
27
|
setBaseLayer(layer);
|
|
28
|
+
}, [layer, setBaseLayer]);
|
|
29
|
+
|
|
30
|
+
useEffect(() => {
|
|
31
|
+
if (!map || !layer) {
|
|
32
|
+
return;
|
|
33
|
+
}
|
|
34
|
+
map.addLayer(layer);
|
|
27
35
|
|
|
28
36
|
return () => {
|
|
29
|
-
map
|
|
37
|
+
map.removeLayer(layer);
|
|
30
38
|
};
|
|
31
|
-
}, [map,
|
|
39
|
+
}, [map, layer]);
|
|
32
40
|
|
|
33
41
|
return null;
|
|
34
42
|
}
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import { useState } from "preact/hooks";
|
|
2
|
+
|
|
3
|
+
import ArrowDown from "../icons/ArrowDown";
|
|
4
|
+
import ArrowUp from "../icons/ArrowUp";
|
|
5
|
+
import Topics from "../RvfTopics";
|
|
6
|
+
import useMapContext from "../utils/hooks/useMapContext";
|
|
7
|
+
|
|
8
|
+
function FloatingMenu() {
|
|
9
|
+
const { map } = useMapContext();
|
|
10
|
+
const [isCollapsed, setIsCollapsed] = useState(true);
|
|
11
|
+
|
|
12
|
+
if (!map) {
|
|
13
|
+
return null;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
return (
|
|
17
|
+
<div className="pointer-events-none absolute bottom-8 left-2 top-2 z-10 flex flex-col overflow-hidden rounded-lg">
|
|
18
|
+
<div
|
|
19
|
+
className="pointer-events-auto max-h-full rounded-lg border bg-white shadow-lg medium:w-largeMenu large:w-mediumMenu"
|
|
20
|
+
|
|
21
|
+
// className="flex-column absolute left-2 top-2 z-10 max-h-80 rounded-lg bg-white px-2 py-1.5 shadow-lg medium:w-largeMenu large:w-mediumMenu"
|
|
22
|
+
>
|
|
23
|
+
<button
|
|
24
|
+
className="flex w-full items-center justify-between px-2 py-1.5 "
|
|
25
|
+
onClick={() => {
|
|
26
|
+
return setIsCollapsed(!isCollapsed);
|
|
27
|
+
}}
|
|
28
|
+
>
|
|
29
|
+
<span>Kartendaten</span>
|
|
30
|
+
{isCollapsed ? <ArrowDown /> : <ArrowUp />}
|
|
31
|
+
</button>
|
|
32
|
+
{!isCollapsed && (
|
|
33
|
+
<div className="flex h-[calc(100%-39px)] w-full flex-1 overflow-y-auto">
|
|
34
|
+
<Topics />
|
|
35
|
+
</div>
|
|
36
|
+
)}
|
|
37
|
+
</div>
|
|
38
|
+
</div>
|
|
39
|
+
);
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
export default FloatingMenu;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { default } from "./FloatingMenu";
|
|
@@ -50,6 +50,7 @@ function RealtimeLayer(props: RealtimeLayerProps) {
|
|
|
50
50
|
: undefined,
|
|
51
51
|
tenant,
|
|
52
52
|
url: realtimeurl,
|
|
53
|
+
zIndex: 1,
|
|
53
54
|
...props,
|
|
54
55
|
styleOptions: {
|
|
55
56
|
getDelayColor: getDelayColorForVehicle,
|
|
@@ -61,8 +62,6 @@ function RealtimeLayer(props: RealtimeLayerProps) {
|
|
|
61
62
|
},
|
|
62
63
|
});
|
|
63
64
|
|
|
64
|
-
layer.setZIndex(1);
|
|
65
|
-
|
|
66
65
|
return layer;
|
|
67
66
|
}, [apikey, mots, realtimeurl, tenant, props]);
|
|
68
67
|
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import type { JSX } from "preact";
|
|
2
|
+
|
|
3
|
+
import { memo } from "preact/compat";
|
|
4
|
+
|
|
5
|
+
// @ts-expect-error - required for htm to resolve the JSX pragma
|
|
6
|
+
import ok from "../icons/Ok/ok-grey.svg";
|
|
7
|
+
|
|
8
|
+
export type RvfCheckboxProps = {} & JSX.InputHTMLAttributes<HTMLInputElement>;
|
|
9
|
+
|
|
10
|
+
function RvfCheckbox({ className, ...props }: RvfCheckboxProps) {
|
|
11
|
+
return (
|
|
12
|
+
<input
|
|
13
|
+
className={`box-border size-[20px] cursor-pointer appearance-none rounded border-2 border-grey bg-white bg-contain bg-center text-grey disabled:cursor-default disabled:border-lightgrey ${className}`}
|
|
14
|
+
style={{
|
|
15
|
+
backgroundImage:
|
|
16
|
+
props.checked && !props.disabled ? `url('` + ok + `')` : "",
|
|
17
|
+
}}
|
|
18
|
+
{...props}
|
|
19
|
+
type="checkbox"
|
|
20
|
+
/>
|
|
21
|
+
);
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
export default memo(RvfCheckbox);
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { default } from "./RvfCheckbox";
|
|
@@ -4,7 +4,9 @@ import { memo, useId, useState } from "preact/compat";
|
|
|
4
4
|
|
|
5
5
|
import Cancel from "../icons/Cancel";
|
|
6
6
|
import RvfButton from "../RvfButton";
|
|
7
|
+
import RvfCheckbox from "../RvfCheckbox";
|
|
7
8
|
import RvfIconButton from "../RvfIconButton";
|
|
9
|
+
import RvfSelect from "../RvfSelect";
|
|
8
10
|
import exportPdf from "../utils/exportPdf";
|
|
9
11
|
import useMapContext from "../utils/hooks/useMapContext";
|
|
10
12
|
import useRvfContext from "../utils/hooks/useRvfContext";
|
|
@@ -39,31 +41,37 @@ function RvfExportMenu({ ...props }: RvfExportMenuButtonProps) {
|
|
|
39
41
|
</div>
|
|
40
42
|
{/* <!-- Content --> */}
|
|
41
43
|
<div className="flex flex-1 flex-col gap-2">
|
|
42
|
-
<div className={"flex gap-2"}>
|
|
43
|
-
<input
|
|
44
|
+
<div className={"flex items-center gap-2"}>
|
|
45
|
+
{/* <input
|
|
44
46
|
checked={useMaxExtent}
|
|
45
47
|
id={checkboxId}
|
|
46
48
|
onChange={() => {
|
|
47
49
|
setUseMaxExtent(!useMaxExtent);
|
|
48
50
|
}}
|
|
49
51
|
type="checkbox"
|
|
52
|
+
/> */}
|
|
53
|
+
<RvfCheckbox
|
|
54
|
+
checked={useMaxExtent}
|
|
55
|
+
id={checkboxId}
|
|
56
|
+
onChange={() => {
|
|
57
|
+
return setUseMaxExtent(!useMaxExtent);
|
|
58
|
+
}}
|
|
50
59
|
/>
|
|
51
60
|
<label htmlFor={checkboxId}>Ganze Region exportieren</label>
|
|
52
61
|
</div>
|
|
53
|
-
<div className={"flex gap-2"}>
|
|
62
|
+
<div className={"flex items-center gap-2"}>
|
|
54
63
|
<label htmlFor={selectId}>Format:</label>
|
|
55
|
-
<
|
|
64
|
+
<RvfSelect
|
|
56
65
|
className={"w-24"}
|
|
57
66
|
id={selectId}
|
|
58
67
|
onChange={(evt) => {
|
|
59
68
|
setFormat((evt.target as HTMLSelectElement).value);
|
|
60
69
|
}}
|
|
61
|
-
value={format}
|
|
62
70
|
>
|
|
63
71
|
{formats.map((format) => {
|
|
64
72
|
return <option key={format}>{format}</option>;
|
|
65
73
|
})}
|
|
66
|
-
</
|
|
74
|
+
</RvfSelect>
|
|
67
75
|
</div>
|
|
68
76
|
</div>
|
|
69
77
|
{/* <!-- Footer --> */}
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import type { JSX, PreactDOMAttributes } from "preact";
|
|
2
|
+
|
|
3
|
+
import ArrowDown from "../icons/ArrowDown";
|
|
4
|
+
import ArrowUp from "../icons/ArrowUp";
|
|
5
|
+
import useMapContext from "../utils/hooks/useMapContext";
|
|
6
|
+
|
|
7
|
+
export type RvfFloatingMenuProps = {
|
|
8
|
+
isOpen: boolean;
|
|
9
|
+
onClick: () => void;
|
|
10
|
+
} & JSX.HTMLAttributes<HTMLDivElement> &
|
|
11
|
+
PreactDOMAttributes;
|
|
12
|
+
|
|
13
|
+
function RvfFloatingMenu({
|
|
14
|
+
children,
|
|
15
|
+
isOpen,
|
|
16
|
+
onClick,
|
|
17
|
+
title,
|
|
18
|
+
}: RvfFloatingMenuProps) {
|
|
19
|
+
const { map } = useMapContext();
|
|
20
|
+
|
|
21
|
+
if (!map) {
|
|
22
|
+
return null;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
return (
|
|
26
|
+
<div className="pointer-events-none absolute bottom-8 left-2 top-2 z-10 flex flex-col overflow-hidden rounded-lg">
|
|
27
|
+
<div className="pointer-events-auto max-h-full rounded-lg border bg-white shadow-lg medium:w-largeMenu large:w-mediumMenu">
|
|
28
|
+
<button
|
|
29
|
+
className="flex w-full items-center justify-between px-2 py-1.5 "
|
|
30
|
+
onClick={onClick}
|
|
31
|
+
>
|
|
32
|
+
{title} {isOpen ? <ArrowDown /> : <ArrowUp />}
|
|
33
|
+
</button>
|
|
34
|
+
{!isOpen && (
|
|
35
|
+
<div className="flex h-[calc(100%-39px)] w-full flex-1 overflow-y-auto">
|
|
36
|
+
{children}
|
|
37
|
+
</div>
|
|
38
|
+
)}
|
|
39
|
+
</div>
|
|
40
|
+
</div>
|
|
41
|
+
);
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
export default RvfFloatingMenu;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { default } from "./RvfFloatingMenu";
|
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
import { JSX, PreactDOMAttributes } from "preact";
|
|
2
|
+
import { memo } from "preact/compat";
|
|
1
3
|
import { useEffect, useReducer } from "preact/hooks";
|
|
2
4
|
|
|
3
5
|
import {
|
|
@@ -6,24 +8,23 @@ import {
|
|
|
6
8
|
} from "./layersTreeContext";
|
|
7
9
|
import layersTreeReducer from "./layersTreeReducer";
|
|
8
10
|
import TreeItem, { TreeItemProps } from "./TreeItem/TreeItem";
|
|
9
|
-
import TreeItemContainer from "./TreeItemContainer";
|
|
10
11
|
|
|
11
|
-
export
|
|
12
|
+
export type RvfLayerTreeProps = {
|
|
12
13
|
layers: TreeItemProps[];
|
|
13
|
-
}
|
|
14
|
+
} & JSX.HTMLAttributes<HTMLDivElement> &
|
|
15
|
+
PreactDOMAttributes;
|
|
14
16
|
|
|
15
|
-
function
|
|
17
|
+
function RvfLayerTree({ layers, ...props }: RvfLayerTreeProps) {
|
|
16
18
|
const [tree, dispatch] = useReducer(layersTreeReducer, layers);
|
|
17
19
|
|
|
18
20
|
useEffect(() => {
|
|
19
21
|
dispatch({ payload: layers, type: "INIT" });
|
|
20
|
-
console.log("INIT", layers);
|
|
21
22
|
}, [layers]);
|
|
22
23
|
|
|
23
24
|
const renderedLayers = layers.map((item, idx) => {
|
|
24
25
|
return (
|
|
25
|
-
<div className="
|
|
26
|
-
<TreeItem {...item} />
|
|
26
|
+
<div className="w-full" key={idx}>
|
|
27
|
+
<TreeItem className="w-full" {...item} />
|
|
27
28
|
</div>
|
|
28
29
|
);
|
|
29
30
|
});
|
|
@@ -31,14 +32,10 @@ function LayerTree({ layers }: LayerTreeProps) {
|
|
|
31
32
|
return (
|
|
32
33
|
<LayersTreeContext.Provider value={tree}>
|
|
33
34
|
<LayersTreeDispatchContext.Provider value={dispatch}>
|
|
34
|
-
<div
|
|
35
|
-
<TreeItemContainer selectionType={tree[0]?.selectionType}>
|
|
36
|
-
{renderedLayers}
|
|
37
|
-
</TreeItemContainer>
|
|
38
|
-
</div>
|
|
35
|
+
<div {...props}>{renderedLayers}</div>
|
|
39
36
|
</LayersTreeDispatchContext.Provider>
|
|
40
37
|
</LayersTreeContext.Provider>
|
|
41
38
|
);
|
|
42
39
|
}
|
|
43
40
|
|
|
44
|
-
export default
|
|
41
|
+
export default memo(RvfLayerTree);
|
|
@@ -0,0 +1,120 @@
|
|
|
1
|
+
import type { JSX, PreactDOMAttributes } from "preact";
|
|
2
|
+
|
|
3
|
+
import BaseLayer from "ol/layer/Base";
|
|
4
|
+
import {
|
|
5
|
+
SVGProps,
|
|
6
|
+
useContext,
|
|
7
|
+
useEffect,
|
|
8
|
+
useId,
|
|
9
|
+
useState,
|
|
10
|
+
} from "preact/compat";
|
|
11
|
+
|
|
12
|
+
import ArrowDown from "../../icons/ArrowDown";
|
|
13
|
+
import ArrowUp from "../../icons/ArrowUp";
|
|
14
|
+
import RvfCheckbox from "../../RvfCheckbox";
|
|
15
|
+
import RvfRadioButton from "../../RvfRadioButton";
|
|
16
|
+
import { LayersTreeDispatchContext } from "../layersTreeContext";
|
|
17
|
+
|
|
18
|
+
export enum SelectionType {
|
|
19
|
+
CHECKBOX = "checkbox",
|
|
20
|
+
RADIO = "radio",
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
export type TreeItemProps = {
|
|
24
|
+
childItems: TreeItemProps[];
|
|
25
|
+
Icon?: (props: SVGProps<SVGSVGElement>) => preact.JSX.Element;
|
|
26
|
+
id: string;
|
|
27
|
+
isCollapsedOnControlClick?: boolean;
|
|
28
|
+
isControlChecked?: boolean;
|
|
29
|
+
layer?: BaseLayer;
|
|
30
|
+
onIconClick?: () => void;
|
|
31
|
+
selectionType: SelectionType;
|
|
32
|
+
title: string;
|
|
33
|
+
} & JSX.HTMLAttributes<HTMLButtonElement> &
|
|
34
|
+
PreactDOMAttributes;
|
|
35
|
+
|
|
36
|
+
function TreeItem({
|
|
37
|
+
childItems,
|
|
38
|
+
isCollapsedOnControlClick,
|
|
39
|
+
isControlChecked,
|
|
40
|
+
layer,
|
|
41
|
+
selectionType,
|
|
42
|
+
title,
|
|
43
|
+
}: TreeItemProps) {
|
|
44
|
+
const [isContainerVisible, setIsContainerVisible] = useState(true);
|
|
45
|
+
const dispatch = useContext(LayersTreeDispatchContext);
|
|
46
|
+
const inputId = useId();
|
|
47
|
+
const buttonId = useId();
|
|
48
|
+
|
|
49
|
+
useEffect(() => {
|
|
50
|
+
if (isCollapsedOnControlClick) {
|
|
51
|
+
setIsContainerVisible(isControlChecked);
|
|
52
|
+
}
|
|
53
|
+
}, [isControlChecked, isCollapsedOnControlClick, layer]);
|
|
54
|
+
|
|
55
|
+
const handleItemClick = () => {
|
|
56
|
+
setIsContainerVisible(!isContainerVisible);
|
|
57
|
+
|
|
58
|
+
if (isCollapsedOnControlClick && !isContainerVisible) {
|
|
59
|
+
dispatch({
|
|
60
|
+
payload: {
|
|
61
|
+
...this["props"],
|
|
62
|
+
isControlChecked: true,
|
|
63
|
+
},
|
|
64
|
+
type: "SELECT_ITEM",
|
|
65
|
+
});
|
|
66
|
+
}
|
|
67
|
+
};
|
|
68
|
+
|
|
69
|
+
const handleSelectionChange = (event) => {
|
|
70
|
+
dispatch({
|
|
71
|
+
payload: {
|
|
72
|
+
...this["props"],
|
|
73
|
+
isControlChecked: event.target.checked,
|
|
74
|
+
},
|
|
75
|
+
type: "SELECT_ITEM",
|
|
76
|
+
});
|
|
77
|
+
};
|
|
78
|
+
|
|
79
|
+
const renderedLayers = childItems.map((item, idx) => {
|
|
80
|
+
return <TreeItem key={idx} {...item} />;
|
|
81
|
+
});
|
|
82
|
+
|
|
83
|
+
return (
|
|
84
|
+
<div>
|
|
85
|
+
<div className="flex items-center gap-2 border-b py-2">
|
|
86
|
+
{selectionType === SelectionType.RADIO ? (
|
|
87
|
+
<RvfRadioButton
|
|
88
|
+
checked={isControlChecked}
|
|
89
|
+
id={inputId}
|
|
90
|
+
onChange={handleSelectionChange}
|
|
91
|
+
/>
|
|
92
|
+
) : (
|
|
93
|
+
<RvfCheckbox
|
|
94
|
+
checked={isControlChecked}
|
|
95
|
+
id={inputId}
|
|
96
|
+
onChange={handleSelectionChange}
|
|
97
|
+
/>
|
|
98
|
+
)}
|
|
99
|
+
<label
|
|
100
|
+
className={`cursor-pointer`}
|
|
101
|
+
htmlFor={childItems.length > 0 ? buttonId : inputId}
|
|
102
|
+
>
|
|
103
|
+
{title}
|
|
104
|
+
</label>
|
|
105
|
+
{childItems.length > 0 && (
|
|
106
|
+
<button
|
|
107
|
+
className={`flex cursor-pointer items-center gap-2 font-normal`}
|
|
108
|
+
id={buttonId}
|
|
109
|
+
onClick={handleItemClick}
|
|
110
|
+
>
|
|
111
|
+
{isContainerVisible ? <ArrowUp /> : <ArrowDown />}
|
|
112
|
+
</button>
|
|
113
|
+
)}
|
|
114
|
+
</div>
|
|
115
|
+
{isContainerVisible && <div className="pl-6">{renderedLayers}</div>}
|
|
116
|
+
</div>
|
|
117
|
+
);
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
export default TreeItem;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { default } from "./RvfLayerTree";
|
|
@@ -10,8 +10,6 @@ const initTree = (tree) => {
|
|
|
10
10
|
initializedTree.childItems = tree;
|
|
11
11
|
mapNode(initializedTree.childItems, initializedTree);
|
|
12
12
|
|
|
13
|
-
console.log("initializedTree", initializedTree);
|
|
14
|
-
|
|
15
13
|
return initializedTree;
|
|
16
14
|
};
|
|
17
15
|
|
|
@@ -146,8 +144,6 @@ function layersTreeReducer(state = ROOT, action) {
|
|
|
146
144
|
return initTree(action.payload);
|
|
147
145
|
case "SELECT_ITEM":
|
|
148
146
|
return setNewControlCheckedStatus(state, action.payload);
|
|
149
|
-
case "SELECT_RADIO_ITEM":
|
|
150
|
-
return setNewControlCheckedStatus(state, action.payload);
|
|
151
147
|
default:
|
|
152
148
|
return state;
|
|
153
149
|
}
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import { MaplibreStyleLayer } from "mobility-toolbox-js/ol";
|
|
2
|
+
import { MaplibreStyleLayerOptions } from "mobility-toolbox-js/ol/layers/MaplibreStyleLayer";
|
|
3
|
+
import { memo } from "preact/compat";
|
|
4
|
+
import { useEffect, useMemo } from "preact/hooks";
|
|
5
|
+
|
|
6
|
+
import useMapContext from "../utils/hooks/useMapContext";
|
|
7
|
+
|
|
8
|
+
function RvfLineNetworkPlanLayer(props: MaplibreStyleLayerOptions) {
|
|
9
|
+
const { baseLayer, map } = useMapContext();
|
|
10
|
+
|
|
11
|
+
const layer = useMemo(() => {
|
|
12
|
+
if (!baseLayer) {
|
|
13
|
+
return null;
|
|
14
|
+
}
|
|
15
|
+
return new MaplibreStyleLayer({
|
|
16
|
+
layersFilter: ({ metadata, source }) => {
|
|
17
|
+
return (
|
|
18
|
+
metadata?.["rvf.filter"] === "netowrk_plans" ||
|
|
19
|
+
source === "network_plans"
|
|
20
|
+
);
|
|
21
|
+
},
|
|
22
|
+
maplibreLayer: baseLayer,
|
|
23
|
+
minZoom: 10,
|
|
24
|
+
...(props || {}),
|
|
25
|
+
});
|
|
26
|
+
}, [baseLayer, props]);
|
|
27
|
+
|
|
28
|
+
useEffect(() => {
|
|
29
|
+
if (!map || !layer) {
|
|
30
|
+
return;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
map.addLayer(layer);
|
|
34
|
+
return () => {
|
|
35
|
+
map.removeLayer(layer);
|
|
36
|
+
};
|
|
37
|
+
}, [map, layer]);
|
|
38
|
+
|
|
39
|
+
return null; // <RegisterForSelectFeaturesOnClick />;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
export default memo(RvfLineNetworkPlanLayer);
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { default } from "./RvfLineNetworkPlanLayer";
|
|
@@ -16,8 +16,6 @@ import { useEffect, useMemo, useRef, useState } from "preact/hooks";
|
|
|
16
16
|
import BaseLayer from "../BaseLayer";
|
|
17
17
|
import Copyright from "../Copyright";
|
|
18
18
|
import GeolocationButton from "../GeolocationButton";
|
|
19
|
-
import Cancel from "../icons/Cancel";
|
|
20
|
-
import Menu from "../icons/Menu";
|
|
21
19
|
import Map from "../Map";
|
|
22
20
|
import { MobilityMapProps } from "../MobilityMap/MobilityMap";
|
|
23
21
|
import NotificationLayer from "../NotificationLayer";
|
|
@@ -27,11 +25,15 @@ import RouteSchedule from "../RouteSchedule";
|
|
|
27
25
|
import RvfExportMenu from "../RvfExportMenu";
|
|
28
26
|
import RvfExportMenuButton from "../RvfExportMenuButton";
|
|
29
27
|
import RvfFeatureDetails from "../RvfFeatureDetails";
|
|
28
|
+
import RvfFloatingMenu from "../RvfFloatingMenu";
|
|
30
29
|
// 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
|
|
31
|
-
import
|
|
30
|
+
import RvfLineNetworkPlanLayer from "../RvfLineNetworkPlanLayer";
|
|
32
31
|
import Modal from "../RvfModal";
|
|
33
32
|
import RvfPoisLayer from "../RvfPoisLayer";
|
|
33
|
+
import RvfSellingPointsLayer from "../RvfSellingPointsLayer";
|
|
34
34
|
import RvfSharedMobilityLayerGroup from "../RvfSharedMobilityLayerGroup";
|
|
35
|
+
import RvfTarifZonenLayer from "../RvfTarifZonenLayer";
|
|
36
|
+
import Topics from "../RvfTopics";
|
|
35
37
|
import RvfZoomButtons from "../RvfZoomButtons";
|
|
36
38
|
import ScaleLine from "../ScaleLine";
|
|
37
39
|
import Search from "../Search";
|
|
@@ -40,7 +42,6 @@ import Station from "../Station";
|
|
|
40
42
|
import StationsLayer from "../StationsLayer";
|
|
41
43
|
// @ts-expect-error bad type definition
|
|
42
44
|
import tailwind from "../style.css";
|
|
43
|
-
import TopicMenu from "../TopicMenu";
|
|
44
45
|
import { RVF_EXTENT_3857 } from "../utils/constants";
|
|
45
46
|
import { I18nContext } from "../utils/hooks/useI18n";
|
|
46
47
|
import { MapContext } from "../utils/hooks/useMapContext";
|
|
@@ -57,6 +58,7 @@ const bbox = RVF_EXTENT_3857.join(",");
|
|
|
57
58
|
|
|
58
59
|
const baseLayerProps = {
|
|
59
60
|
mapLibreOptions: {
|
|
61
|
+
maxCanvasSize: [20000, 20000], // remove 4096 limitations
|
|
60
62
|
preserveDrawingBuffer: true,
|
|
61
63
|
},
|
|
62
64
|
};
|
|
@@ -98,7 +100,7 @@ function RvfMobilityMap({
|
|
|
98
100
|
const [isExportMenuOpen, setIsExportMenuOpen] = useState<boolean>(false);
|
|
99
101
|
const [selectedFeature, setSelectedFeature] = useState<Feature>();
|
|
100
102
|
const [selectedFeatures, setSelectedFeatures] = useState<Feature[]>();
|
|
101
|
-
const [isLayerTreeOpen, setIsLayerTreeOpen] = useState(false);
|
|
103
|
+
const [isLayerTreeOpen, setIsLayerTreeOpen] = useState<boolean>(false);
|
|
102
104
|
|
|
103
105
|
// TODO: this should be removed. The parent application should be responsible to do this
|
|
104
106
|
// or we should find something that fit more usecases
|
|
@@ -226,13 +228,15 @@ function RvfMobilityMap({
|
|
|
226
228
|
const rvfContextValue = useMemo(() => {
|
|
227
229
|
return {
|
|
228
230
|
isExportMenuOpen,
|
|
231
|
+
isLayerTreeOpen,
|
|
229
232
|
selectedFeature,
|
|
230
233
|
selectedFeatures,
|
|
231
234
|
setIsExportMenuOpen,
|
|
235
|
+
setIsLayerTreeOpen,
|
|
232
236
|
setSelectedFeature,
|
|
233
237
|
setSelectedFeatures,
|
|
234
238
|
};
|
|
235
|
-
}, [isExportMenuOpen, selectedFeature, selectedFeatures]);
|
|
239
|
+
}, [isExportMenuOpen, selectedFeature, selectedFeatures, isLayerTreeOpen]);
|
|
236
240
|
|
|
237
241
|
return (
|
|
238
242
|
<I18nContext.Provider value={i18n}>
|
|
@@ -243,47 +247,48 @@ function RvfMobilityMap({
|
|
|
243
247
|
<div
|
|
244
248
|
className="relative size-full border font-sans @container/main"
|
|
245
249
|
ref={eventNodeRef}
|
|
250
|
+
style={{ fontSize: 16 }}
|
|
246
251
|
>
|
|
247
252
|
<div className="relative flex size-full flex-col @lg/main:flex-row-reverse">
|
|
248
253
|
<Map className="relative flex-1 overflow-visible ">
|
|
254
|
+
<RvfFloatingMenu
|
|
255
|
+
isOpen={isLayerTreeOpen}
|
|
256
|
+
onClick={() => {
|
|
257
|
+
setIsLayerTreeOpen(!isLayerTreeOpen);
|
|
258
|
+
}}
|
|
259
|
+
title="Kartendaten"
|
|
260
|
+
>
|
|
261
|
+
<Topics className={"w-full px-2"} />
|
|
262
|
+
</RvfFloatingMenu>
|
|
249
263
|
<BaseLayer {...baseLayerProps} isNotInLayerTree />
|
|
250
264
|
<SingleClickListener />
|
|
251
265
|
|
|
252
|
-
{realtime === "true" && <RealtimeLayer title="
|
|
266
|
+
{realtime === "true" && <RealtimeLayer title="Echtzeit" />}
|
|
253
267
|
{tenant && <StationsLayer />}
|
|
254
268
|
{notification === "true" && <NotificationLayer />}
|
|
255
|
-
|
|
256
|
-
<
|
|
257
|
-
<RvfTarifZonenLayer />
|
|
269
|
+
<RvfSellingPointsLayer title="Verkaufsstellen" />
|
|
270
|
+
<RvfLineNetworkPlanLayer title="Liniennetz" />
|
|
271
|
+
<RvfTarifZonenLayer title="Tarifzonen" />
|
|
258
272
|
<RvfPoisLayer title="POIs" />
|
|
259
|
-
|
|
260
273
|
<RvfSharedMobilityLayerGroup title="Shared Mobility" />
|
|
261
274
|
|
|
262
|
-
<div className="absolute left-2 top-2 z-10">
|
|
263
|
-
<RvfIconButton
|
|
264
|
-
onClick={() => {
|
|
265
|
-
return setIsLayerTreeOpen(!isLayerTreeOpen);
|
|
266
|
-
}}
|
|
267
|
-
selected={isLayerTreeOpen}
|
|
268
|
-
>
|
|
269
|
-
{isLayerTreeOpen ? <Cancel /> : <Menu />}
|
|
270
|
-
</RvfIconButton>
|
|
271
|
-
{isLayerTreeOpen && <TopicMenu map={map} />}
|
|
272
|
-
</div>
|
|
273
275
|
<div className="absolute inset-x-2 bottom-2 z-10 flex items-end justify-between gap-2 text-[10px]">
|
|
274
276
|
<ScaleLine className="bg-slate-50/70" />
|
|
275
277
|
<Copyright className="bg-slate-50/70" />
|
|
276
278
|
</div>
|
|
279
|
+
|
|
277
280
|
<div className="absolute right-2 top-2 z-10 flex flex-col gap-2">
|
|
278
281
|
{geolocation === "true" && <GeolocationButton />}
|
|
282
|
+
<RvfExportMenuButton />
|
|
279
283
|
</div>
|
|
284
|
+
|
|
280
285
|
{search === "true" && (
|
|
281
286
|
<div className="absolute left-2 right-12 top-2 z-10 flex max-h-[90%] min-w-64 max-w-96 flex-col">
|
|
282
287
|
<Search />
|
|
283
288
|
</div>
|
|
284
289
|
)}
|
|
290
|
+
|
|
285
291
|
<div className="absolute bottom-10 right-2 z-10 flex flex-col justify-between gap-2">
|
|
286
|
-
<RvfExportMenuButton />
|
|
287
292
|
<RvfZoomButtons />
|
|
288
293
|
</div>
|
|
289
294
|
</Map>
|