@geops/rvf-mobility-web-component 0.1.10 → 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.
Files changed (98) hide show
  1. package/CHANGELOG.md +52 -0
  2. package/docutils.js +198 -0
  3. package/index.html +48 -217
  4. package/index.js +680 -87
  5. package/input.css +11 -1
  6. package/jest-setup.js +3 -2
  7. package/package.json +4 -3
  8. package/scripts/build.mjs +3 -2
  9. package/scripts/dev.mjs +2 -1
  10. package/search.html +38 -69
  11. package/src/BaseLayer/BaseLayer.tsx +20 -12
  12. package/src/FloatingMenu/FloatingMenu.tsx +42 -0
  13. package/src/FloatingMenu/index.tsx +1 -0
  14. package/src/GeolocationButton/GeolocationButton.tsx +6 -5
  15. package/src/Map/Map.tsx +1 -0
  16. package/src/MobilityMap/MobilityMap.tsx +10 -9
  17. package/src/MobilityMap/index.css +0 -13
  18. package/src/RealtimeLayer/RealtimeLayer.tsx +2 -3
  19. package/src/RvfButton/RvfButton.tsx +28 -21
  20. package/src/RvfCheckbox/RvfCheckbox.tsx +24 -0
  21. package/src/RvfCheckbox/index.tsx +1 -0
  22. package/src/RvfExportMenu/RvfExportMenu.tsx +103 -0
  23. package/src/RvfExportMenu/index.tsx +1 -0
  24. package/src/RvfExportMenuButton/RvfExportMenuButton.tsx +27 -0
  25. package/src/RvfExportMenuButton/index.tsx +1 -0
  26. package/src/RvfFeatureDetails/RvfFeatureDetails.tsx +29 -0
  27. package/src/RvfFeatureDetails/index.tsx +1 -0
  28. package/src/RvfFloatingMenu/RvfFloatingMenu.tsx +44 -0
  29. package/src/RvfFloatingMenu/index.tsx +1 -0
  30. package/src/RvfIconButton/RvfIconButton.tsx +35 -0
  31. package/src/RvfIconButton/index.tsx +1 -0
  32. package/src/RvfLayerTree/RvfLayerTree.tsx +41 -0
  33. package/src/RvfLayerTree/TreeItem/TreeItem.tsx +120 -0
  34. package/src/RvfLayerTree/TreeItem/index.tsx +1 -0
  35. package/src/RvfLayerTree/index.tsx +1 -0
  36. package/src/RvfLayerTree/layersTreeContext.ts +4 -0
  37. package/src/RvfLayerTree/layersTreeReducer.ts +152 -0
  38. package/src/RvfLineNetworkPlanLayer/RvfLineNetworkPlanLayer.tsx +42 -0
  39. package/src/RvfLineNetworkPlanLayer/index.tsx +1 -0
  40. package/src/RvfMobilityMap/RvfMobilityMap.tsx +122 -83
  41. package/src/RvfMobilityMap/index.css +0 -13
  42. package/src/RvfModal/RvfModal.tsx +52 -0
  43. package/src/RvfModal/index.tsx +1 -0
  44. package/src/RvfPoisLayer/RvfPoisLayer.tsx +39 -0
  45. package/src/RvfPoisLayer/index.tsx +1 -0
  46. package/src/RvfRadioButton/RvfRadioButton.tsx +16 -0
  47. package/src/RvfRadioButton/index.tsx +1 -0
  48. package/src/RvfSelect/RvfSelect.tsx +22 -0
  49. package/src/RvfSelect/index.tsx +1 -0
  50. package/src/RvfSellingPointsLayer/RvfSellingPointsLayer.tsx +41 -0
  51. package/src/RvfSellingPointsLayer/index.tsx +1 -0
  52. package/src/RvfSharedMobilityLayerGroup/RvfSharedMobilityLayerGroup.tsx +100 -0
  53. package/src/RvfSharedMobilityLayerGroup/index.tsx +1 -0
  54. package/src/RvfSingleClickListener/RvfSingleClickListener.tsx +146 -0
  55. package/src/RvfSingleClickListener/index.tsx +1 -0
  56. package/src/RvfTarifZonenLayer/RvfTarifZonenLayer.tsx +41 -0
  57. package/src/RvfTarifZonenLayer/index.tsx +1 -0
  58. package/src/RvfTopics/RvfTopics.tsx +47 -0
  59. package/src/RvfTopics/index.tsx +1 -0
  60. package/src/RvfZoomButtons/RvfZoomButtons.tsx +36 -29
  61. package/src/Search/Search.tsx +11 -9
  62. package/src/SingleClickListener/index.tsx +1 -1
  63. package/src/StationsLayer/StationsLayer.tsx +0 -1
  64. package/src/StopsSearch/StopsSearch.tsx +38 -6
  65. package/src/icons/ArrowDown/ArrowDown.tsx +22 -0
  66. package/src/icons/ArrowDown/down-open.svg +7 -0
  67. package/src/icons/ArrowDown/index.tsx +1 -0
  68. package/src/icons/ArrowUp/ArrowUp.tsx +22 -0
  69. package/src/icons/ArrowUp/index.tsx +1 -0
  70. package/src/icons/ArrowUp/up-open.svg +7 -0
  71. package/src/icons/Bicycle/verkehrstraeger-rad-2px-white.svg +19 -0
  72. package/src/icons/Cancel/Cancel.tsx +21 -0
  73. package/src/icons/Cancel/cancel.svg +7 -0
  74. package/src/icons/Cancel/index.tsx +1 -0
  75. package/src/icons/Car/verkehrstraeger-auto-2px-white.svg +14 -0
  76. package/src/icons/CargoBicycle/verkehrstraeger-lastenrad-2px-white.svg +27 -0
  77. package/src/icons/DownOpen/DownOpen.tsx +24 -0
  78. package/src/icons/DownOpen/down-open.svg +7 -0
  79. package/src/icons/DownOpen/index.tsx +1 -0
  80. package/src/icons/Download/Download.tsx +20 -0
  81. package/src/icons/Download/download.svg +15 -0
  82. package/src/icons/Download/index.tsx +1 -0
  83. package/src/icons/Elevator/Elevator.tsx +1 -1
  84. package/src/icons/Menu/Menu.tsx +32 -0
  85. package/src/icons/Menu/index.tsx +1 -0
  86. package/src/icons/Menu/menu.svg +9 -0
  87. package/src/icons/Ok/ok-grey.svg +7 -0
  88. package/src/icons/Ok/ok.svg +4 -0
  89. package/src/icons/Scooter/scooter.svg +10 -0
  90. package/src/utils/constants.ts +9 -0
  91. package/src/utils/createMobiDataBwWfsLayer.ts +120 -0
  92. package/src/utils/createSharedMobilityLayer.ts +165 -0
  93. package/src/utils/exportPdf.ts +657 -0
  94. package/src/utils/hooks/useRvfContext.tsx +37 -0
  95. package/src/utils/hooks/useUpdatePermalink.tsx +2 -9
  96. package/tailwind.config.mjs +41 -19
  97. package/src/RvfSharedMobilityLayer/RvfSharedMobilityLayer.tsx +0 -147
  98. package/src/RvfSharedMobilityLayer/index.tsx +0 -1
@@ -0,0 +1,103 @@
1
+ import type { JSX, PreactDOMAttributes } from "preact";
2
+
3
+ import { memo, useId, useState } from "preact/compat";
4
+
5
+ import Cancel from "../icons/Cancel";
6
+ import RvfButton from "../RvfButton";
7
+ import RvfCheckbox from "../RvfCheckbox";
8
+ import RvfIconButton from "../RvfIconButton";
9
+ import RvfSelect from "../RvfSelect";
10
+ import exportPdf from "../utils/exportPdf";
11
+ import useMapContext from "../utils/hooks/useMapContext";
12
+ import useRvfContext from "../utils/hooks/useRvfContext";
13
+
14
+ export type RvfExportMenuButtonProps = JSX.HTMLAttributes<HTMLDivElement> &
15
+ PreactDOMAttributes;
16
+
17
+ const formats = ["A4", "A1", "A3", "A0"];
18
+
19
+ function RvfExportMenu({ ...props }: RvfExportMenuButtonProps) {
20
+ const { setIsExportMenuOpen } = useRvfContext();
21
+ const { map } = useMapContext();
22
+ const [useMaxExtent, setUseMaxExtent] = useState(false);
23
+ const [format, setFormat] = useState<string>(formats[0]);
24
+ const checkboxId = useId();
25
+ const selectId = useId();
26
+ const [isExporting, setIsExporting] = useState(false);
27
+ const [isExportingError, setIsExportingError] = useState(false);
28
+ return (
29
+ <div {...props}>
30
+ <div className={"flex h-full flex-col gap-2 p-2"}>
31
+ {/* <!-- Header --> */}
32
+ <div className={"flex flex-row items-center justify-between gap-2"}>
33
+ <h1>Export </h1>
34
+ <RvfIconButton
35
+ onClick={() => {
36
+ setIsExportMenuOpen(false);
37
+ }}
38
+ >
39
+ <Cancel />
40
+ </RvfIconButton>
41
+ </div>
42
+ {/* <!-- Content --> */}
43
+ <div className="flex flex-1 flex-col gap-2">
44
+ <div className={"flex items-center gap-2"}>
45
+ {/* <input
46
+ checked={useMaxExtent}
47
+ id={checkboxId}
48
+ onChange={() => {
49
+ setUseMaxExtent(!useMaxExtent);
50
+ }}
51
+ type="checkbox"
52
+ /> */}
53
+ <RvfCheckbox
54
+ checked={useMaxExtent}
55
+ id={checkboxId}
56
+ onChange={() => {
57
+ return setUseMaxExtent(!useMaxExtent);
58
+ }}
59
+ />
60
+ <label htmlFor={checkboxId}>Ganze Region exportieren</label>
61
+ </div>
62
+ <div className={"flex items-center gap-2"}>
63
+ <label htmlFor={selectId}>Format:</label>
64
+ <RvfSelect
65
+ className={"w-24"}
66
+ id={selectId}
67
+ onChange={(evt) => {
68
+ setFormat((evt.target as HTMLSelectElement).value);
69
+ }}
70
+ >
71
+ {formats.map((format) => {
72
+ return <option key={format}>{format}</option>;
73
+ })}
74
+ </RvfSelect>
75
+ </div>
76
+ </div>
77
+ {/* <!-- Footer --> */}
78
+ <div>
79
+ <RvfButton
80
+ disabled={isExporting}
81
+ onClick={async () => {
82
+ setIsExportingError(false);
83
+ setIsExporting(true);
84
+ const result = await exportPdf(map, { format }, { useMaxExtent });
85
+ setTimeout(() => {
86
+ setIsExporting(false);
87
+ setIsExportingError(!result);
88
+ }, 1000);
89
+ }}
90
+ >
91
+ {isExporting
92
+ ? "Exporting..."
93
+ : isExportingError
94
+ ? "Error"
95
+ : "Download"}
96
+ </RvfButton>
97
+ </div>
98
+ </div>
99
+ </div>
100
+ );
101
+ }
102
+
103
+ export default memo(RvfExportMenu);
@@ -0,0 +1 @@
1
+ export { default } from "./RvfExportMenu";
@@ -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 Download from "../icons/Download";
7
+ import RvfIconButton from "../RvfIconButton";
8
+ import useRvfContext from "../utils/hooks/useRvfContext";
9
+
10
+ export type RvfExportMenuButtonProps = JSX.HTMLAttributes<HTMLButtonElement> &
11
+ PreactDOMAttributes;
12
+
13
+ function RvfExportMenuButton({ ...props }: RvfExportMenuButtonProps) {
14
+ const { isExportMenuOpen, setIsExportMenuOpen } = useRvfContext();
15
+
16
+ const onClick = useCallback(() => {
17
+ setIsExportMenuOpen(!isExportMenuOpen);
18
+ }, [isExportMenuOpen, setIsExportMenuOpen]);
19
+
20
+ return (
21
+ <RvfIconButton {...props} onClick={onClick} selected={isExportMenuOpen}>
22
+ <Download />
23
+ </RvfIconButton>
24
+ );
25
+ }
26
+
27
+ export default memo(RvfExportMenuButton);
@@ -0,0 +1 @@
1
+ export { default } from "./RvfExportMenuButton";
@@ -0,0 +1,29 @@
1
+ import type { JSX, PreactDOMAttributes } from "preact";
2
+
3
+ import { memo } from "preact/compat";
4
+
5
+ import useRvfContext from "../utils/hooks/useRvfContext";
6
+
7
+ export type RvfFeatureDetailsProps = JSX.HTMLAttributes<HTMLDivElement> &
8
+ PreactDOMAttributes;
9
+
10
+ function RvfFeatureDetails(props: RvfFeatureDetailsProps) {
11
+ const { selectedFeature } = useRvfContext();
12
+
13
+ return (
14
+ <div {...props}>
15
+ {Object.entries(selectedFeature.getProperties()).map(([key, value]) => {
16
+ return (
17
+ <div className="flex gap-2" key={key}>
18
+ <span>
19
+ <b>{key}:</b>
20
+ </span>
21
+ <div>{value}</div>
22
+ </div>
23
+ );
24
+ })}
25
+ </div>
26
+ );
27
+ }
28
+
29
+ export default memo(RvfFeatureDetails);
@@ -0,0 +1 @@
1
+ export { default } from "./RvfFeatureDetails";
@@ -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";
@@ -0,0 +1,35 @@
1
+ import type { JSX, PreactDOMAttributes } from "preact";
2
+
3
+ import { memo, SVGProps, useMemo } from "preact/compat";
4
+
5
+ import { RvfButtonProps, themes } from "../RvfButton/RvfButton";
6
+
7
+ export type RvfIconButtonProps = {
8
+ Icon?: (props: SVGProps<SVGSVGElement>) => preact.JSX.Element;
9
+ } & JSX.ButtonHTMLAttributes<HTMLButtonElement> &
10
+ PreactDOMAttributes &
11
+ RvfButtonProps;
12
+
13
+ const baseClasses =
14
+ "flex h-8 w-8 md:h-9 md:w-9 lg:w-10 lg:h-10 p-1.75 max-w-button max-h-button items-center justify-center rounded-full border";
15
+
16
+ function RvfIconButton({
17
+ children,
18
+ className,
19
+ Icon,
20
+ selected = false,
21
+ theme = "secondary",
22
+ ...props
23
+ }: RvfIconButtonProps) {
24
+ const classes = useMemo(() => {
25
+ return `${baseClasses} ${themes[theme].classes} ${selected ? themes[theme].selectedClasses : ""} ${className || ""}`;
26
+ }, [className, selected, theme]);
27
+
28
+ return (
29
+ <button className={classes} {...props}>
30
+ {children || <Icon height={"100%"} width={"100%"} />}
31
+ </button>
32
+ );
33
+ }
34
+
35
+ export default memo(RvfIconButton);
@@ -0,0 +1 @@
1
+ export { default } from "./RvfIconButton";
@@ -0,0 +1,41 @@
1
+ import { JSX, PreactDOMAttributes } from "preact";
2
+ import { memo } from "preact/compat";
3
+ import { useEffect, useReducer } from "preact/hooks";
4
+
5
+ import {
6
+ LayersTreeContext,
7
+ LayersTreeDispatchContext,
8
+ } from "./layersTreeContext";
9
+ import layersTreeReducer from "./layersTreeReducer";
10
+ import TreeItem, { TreeItemProps } from "./TreeItem/TreeItem";
11
+
12
+ export type RvfLayerTreeProps = {
13
+ layers: TreeItemProps[];
14
+ } & JSX.HTMLAttributes<HTMLDivElement> &
15
+ PreactDOMAttributes;
16
+
17
+ function RvfLayerTree({ layers, ...props }: RvfLayerTreeProps) {
18
+ const [tree, dispatch] = useReducer(layersTreeReducer, layers);
19
+
20
+ useEffect(() => {
21
+ dispatch({ payload: layers, type: "INIT" });
22
+ }, [layers]);
23
+
24
+ const renderedLayers = layers.map((item, idx) => {
25
+ return (
26
+ <div className="w-full" key={idx}>
27
+ <TreeItem className="w-full" {...item} />
28
+ </div>
29
+ );
30
+ });
31
+
32
+ return (
33
+ <LayersTreeContext.Provider value={tree}>
34
+ <LayersTreeDispatchContext.Provider value={dispatch}>
35
+ <div {...props}>{renderedLayers}</div>
36
+ </LayersTreeDispatchContext.Provider>
37
+ </LayersTreeContext.Provider>
38
+ );
39
+ }
40
+
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 "./TreeItem";
@@ -0,0 +1 @@
1
+ export { default } from "./RvfLayerTree";
@@ -0,0 +1,4 @@
1
+ import { createContext } from "preact";
2
+
3
+ export const LayersTreeContext = createContext(null);
4
+ export const LayersTreeDispatchContext = createContext(null);
@@ -0,0 +1,152 @@
1
+ import { SelectionType } from "./TreeItem/TreeItem";
2
+
3
+ const ROOT = {
4
+ childItems: [],
5
+ parent: null,
6
+ };
7
+
8
+ const initTree = (tree) => {
9
+ const initializedTree = { ...ROOT };
10
+ initializedTree.childItems = tree;
11
+ mapNode(initializedTree.childItems, initializedTree);
12
+
13
+ return initializedTree;
14
+ };
15
+
16
+ const mapNode = (childItems, parent) => {
17
+ for (const child of childItems) {
18
+ child.parent = parent;
19
+
20
+ if (child.childItems.length) {
21
+ mapNode(child.childItems, child);
22
+ }
23
+ }
24
+ };
25
+
26
+ const findNodeInTree = (node, nodeToFind) => {
27
+ if (node.id === nodeToFind.id) {
28
+ return node;
29
+ }
30
+
31
+ if (node.childItems.length) {
32
+ for (const child of node.childItems) {
33
+ const res = findNodeInTree(child, nodeToFind);
34
+ if (res) {
35
+ return res;
36
+ }
37
+ }
38
+ }
39
+ };
40
+
41
+ const setNewControlCheckedStatus = (tree, newItem) => {
42
+ const currentItem = findNodeInTree(tree, newItem);
43
+
44
+ if (currentItem) {
45
+ updateCheckedControlStatus(currentItem, newItem, false);
46
+ }
47
+
48
+ return { ...tree };
49
+ };
50
+
51
+ const updateCheckedControlStatus = (currentItem, newItem, isParentUpdate) => {
52
+ if (newItem.isControlChecked) {
53
+ if (newItem.selectionType === SelectionType.CHECKBOX) {
54
+ currentItem.isControlChecked = newItem.isControlChecked;
55
+ currentItem.layer.setVisible(currentItem.isControlChecked);
56
+ } else {
57
+ for (const child of currentItem.parent.childItems) {
58
+ child.isControlChecked = child.id === currentItem.id;
59
+
60
+ if (!child.isControlChecked) {
61
+ updateRadioChildNodes(child);
62
+ }
63
+ }
64
+ }
65
+ } else {
66
+ if (newItem.selectionType === SelectionType.CHECKBOX) {
67
+ currentItem.isControlChecked = newItem.isControlChecked;
68
+ currentItem.layer.setVisible(currentItem.isControlChecked);
69
+ }
70
+ }
71
+
72
+ // check all children
73
+ if (currentItem.childItems.length && !isParentUpdate) {
74
+ updateChildNodes(currentItem);
75
+ }
76
+
77
+ // check all parents
78
+ updateParentNodes(currentItem);
79
+ };
80
+
81
+ const updateChildNodes = (node) => {
82
+ if (node.childItems[0].selectionType === SelectionType.RADIO) {
83
+ updateRadioChildNodes(node);
84
+ } else {
85
+ updateCheckboxChildNodes(node);
86
+ }
87
+ };
88
+
89
+ const updateRadioChildNodes = (parent) => {
90
+ if (parent.isControlChecked) {
91
+ for (let i = 0; i < parent.childItems.length; i++) {
92
+ parent.childItems[i].isControlChecked = i === 0;
93
+ }
94
+ } else {
95
+ for (const child of parent.childItems) {
96
+ child.isControlChecked = false;
97
+ }
98
+ }
99
+
100
+ if (parent.childItems.length) {
101
+ if (parent.childItems[0].childItems.length) {
102
+ updateChildNodes(parent.childItems[0]);
103
+ }
104
+ }
105
+ };
106
+
107
+ const updateCheckboxChildNodes = (parent) => {
108
+ for (const child of parent.childItems) {
109
+ child.isControlChecked = parent.isControlChecked;
110
+
111
+ if (child.childItems.length) {
112
+ updateChildNodes(child);
113
+ }
114
+ }
115
+ };
116
+
117
+ const updateParentNodes = (node) => {
118
+ if (node.parent) {
119
+ if (node.parent.selectionType === SelectionType.CHECKBOX) {
120
+ const newItem = {
121
+ ...node.parent,
122
+ isControlChecked: node.parent.childItems.some((child) => {
123
+ return child.isControlChecked;
124
+ }),
125
+ };
126
+ updateCheckedControlStatus(node.parent, newItem, true);
127
+ } else {
128
+ if (node?.parent?.parent) {
129
+ const newItem = {
130
+ ...node.parent,
131
+ isControlChecked: node.parent.childItems.some((child) => {
132
+ return child.isControlChecked;
133
+ }),
134
+ };
135
+ updateCheckedControlStatus(node.parent, newItem, true);
136
+ }
137
+ }
138
+ }
139
+ };
140
+
141
+ function layersTreeReducer(state = ROOT, action) {
142
+ switch (action.type) {
143
+ case "INIT":
144
+ return initTree(action.payload);
145
+ case "SELECT_ITEM":
146
+ return setNewControlCheckedStatus(state, action.payload);
147
+ default:
148
+ return state;
149
+ }
150
+ }
151
+
152
+ export default layersTreeReducer;
@@ -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";