@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
|
@@ -0,0 +1,165 @@
|
|
|
1
|
+
import { Feature, getUid } from "ol";
|
|
2
|
+
import { GeoJSON } from "ol/format";
|
|
3
|
+
import { Point } from "ol/geom";
|
|
4
|
+
import VectorLayer, { Options } from "ol/layer/Vector";
|
|
5
|
+
import { bbox as bboxStrategy } from "ol/loadingstrategy.js";
|
|
6
|
+
import { Cluster, Vector } from "ol/source";
|
|
7
|
+
import { Circle, Fill, Icon, Stroke, Style, Text } from "ol/style";
|
|
8
|
+
|
|
9
|
+
// @ts-expect-error - load svg as data url
|
|
10
|
+
import bicycle from "../icons/Bicycle/verkehrstraeger-rad-2px-white.svg";
|
|
11
|
+
// @ts-expect-error - load svg as data url
|
|
12
|
+
import car from "../icons/Car/verkehrstraeger-auto-2px-white.svg";
|
|
13
|
+
// @ts-expect-error - load svg as data url
|
|
14
|
+
import cargoBicycle from "../icons/CargoBicycle/verkehrstraeger-lastenrad-2px-white.svg";
|
|
15
|
+
// @ts-expect-error - load svg as data url
|
|
16
|
+
import scooter from "../icons/Scooter/scooter.svg";
|
|
17
|
+
|
|
18
|
+
const iconByFormFactor = {
|
|
19
|
+
bicycle,
|
|
20
|
+
car,
|
|
21
|
+
cargo_bicycle: cargoBicycle,
|
|
22
|
+
scooter,
|
|
23
|
+
};
|
|
24
|
+
|
|
25
|
+
function createSharedMobilityLayer(
|
|
26
|
+
name: string,
|
|
27
|
+
color: string,
|
|
28
|
+
layerOptions: Options = {
|
|
29
|
+
minZoom: 17.99,
|
|
30
|
+
},
|
|
31
|
+
): VectorLayer<Vector<Feature<Point>>> {
|
|
32
|
+
const source = new Vector({
|
|
33
|
+
format: new GeoJSON(),
|
|
34
|
+
loader: function (extent, resolution, projection, success, failure) {
|
|
35
|
+
const url =
|
|
36
|
+
"https://api.mobidata-bw.de/geoserver/MobiData-BW/" +
|
|
37
|
+
name +
|
|
38
|
+
"/ows" +
|
|
39
|
+
"?service=WFS&" +
|
|
40
|
+
"version=1.1.0&request=GetFeature&typename=" +
|
|
41
|
+
"MobiData-BW:" +
|
|
42
|
+
name +
|
|
43
|
+
"&" +
|
|
44
|
+
"outputFormat=application/json&srsname=EPSG:3857&" +
|
|
45
|
+
"bbox=" +
|
|
46
|
+
extent.join(",") +
|
|
47
|
+
",EPSG:3857";
|
|
48
|
+
const xhr = new XMLHttpRequest();
|
|
49
|
+
xhr.open("GET", url);
|
|
50
|
+
const onError = function () {
|
|
51
|
+
source.removeLoadedExtent(extent);
|
|
52
|
+
failure();
|
|
53
|
+
};
|
|
54
|
+
xhr.onerror = onError;
|
|
55
|
+
xhr.onload = function () {
|
|
56
|
+
if (xhr.status == 200) {
|
|
57
|
+
const features = source.getFormat().readFeatures(xhr.responseText);
|
|
58
|
+
if (features) {
|
|
59
|
+
features.forEach((feature) => {
|
|
60
|
+
// The WFS returns everytime a different id for the same vehicle, so we set the vehicle_id as id.
|
|
61
|
+
// and remove the previous feature with the same id.
|
|
62
|
+
const vehicleId = feature.get("vehicle_id");
|
|
63
|
+
if (vehicleId) {
|
|
64
|
+
feature.setId(vehicleId);
|
|
65
|
+
const featuresToRemove = source.getFeatures().filter((feat) => {
|
|
66
|
+
return feat.getId() === feature.getId();
|
|
67
|
+
});
|
|
68
|
+
source.removeFeatures(featuresToRemove);
|
|
69
|
+
}
|
|
70
|
+
});
|
|
71
|
+
}
|
|
72
|
+
source.addFeatures(features);
|
|
73
|
+
success(features);
|
|
74
|
+
} else {
|
|
75
|
+
onError();
|
|
76
|
+
}
|
|
77
|
+
};
|
|
78
|
+
xhr.send();
|
|
79
|
+
},
|
|
80
|
+
strategy: bboxStrategy,
|
|
81
|
+
});
|
|
82
|
+
|
|
83
|
+
const style = new Style({
|
|
84
|
+
image: new Circle({
|
|
85
|
+
declutterMode: "declutter",
|
|
86
|
+
displacement: [-12, 10],
|
|
87
|
+
fill: new Fill({
|
|
88
|
+
color: "white",
|
|
89
|
+
}),
|
|
90
|
+
radius: 10,
|
|
91
|
+
stroke: new Stroke({
|
|
92
|
+
color: color,
|
|
93
|
+
width: 2,
|
|
94
|
+
}),
|
|
95
|
+
}),
|
|
96
|
+
text: new Text({
|
|
97
|
+
declutterMode: "declutter",
|
|
98
|
+
fill: new Fill({
|
|
99
|
+
color: color,
|
|
100
|
+
}),
|
|
101
|
+
font: "bold 12px inherit",
|
|
102
|
+
offsetX: -12,
|
|
103
|
+
offsetY: -10,
|
|
104
|
+
}),
|
|
105
|
+
});
|
|
106
|
+
|
|
107
|
+
const iconStyleByFormFactor = {};
|
|
108
|
+
Object.entries(iconByFormFactor).forEach(([key, value]) => {
|
|
109
|
+
iconStyleByFormFactor[key] = new Style({
|
|
110
|
+
image: new Icon({
|
|
111
|
+
src: value || iconByFormFactor["scooter"],
|
|
112
|
+
width: 17,
|
|
113
|
+
}),
|
|
114
|
+
});
|
|
115
|
+
});
|
|
116
|
+
|
|
117
|
+
const backgroundStyle = new Style({
|
|
118
|
+
image: new Circle({
|
|
119
|
+
declutterMode: "declutter",
|
|
120
|
+
displacement: [0, 0],
|
|
121
|
+
fill: new Fill({
|
|
122
|
+
color: "#00973c",
|
|
123
|
+
}),
|
|
124
|
+
radius: 12,
|
|
125
|
+
}),
|
|
126
|
+
stroke: new Stroke({
|
|
127
|
+
color: "white",
|
|
128
|
+
width: 2,
|
|
129
|
+
}),
|
|
130
|
+
});
|
|
131
|
+
|
|
132
|
+
const clusterSource = new Cluster({
|
|
133
|
+
distance: 40,
|
|
134
|
+
source: source,
|
|
135
|
+
});
|
|
136
|
+
|
|
137
|
+
const styleFunction = (feature) => {
|
|
138
|
+
const clone = style.clone();
|
|
139
|
+
|
|
140
|
+
clone.getText().setText(feature.get("features").length);
|
|
141
|
+
clone.setZIndex(parseInt(getUid(feature), 10));
|
|
142
|
+
|
|
143
|
+
const formFactor =
|
|
144
|
+
feature.get("features")?.[0].get?.("form_factor") || "scooter";
|
|
145
|
+
|
|
146
|
+
return [clone, backgroundStyle, iconStyleByFormFactor[formFactor]];
|
|
147
|
+
};
|
|
148
|
+
|
|
149
|
+
const layer = new VectorLayer({
|
|
150
|
+
// @ts-expect-error - custom properties
|
|
151
|
+
isQueryable: true,
|
|
152
|
+
name,
|
|
153
|
+
source: clusterSource,
|
|
154
|
+
style: styleFunction,
|
|
155
|
+
...layerOptions,
|
|
156
|
+
});
|
|
157
|
+
|
|
158
|
+
clusterSource.on("addfeature", function (event) {
|
|
159
|
+
event.feature?.setStyle(styleFunction);
|
|
160
|
+
});
|
|
161
|
+
|
|
162
|
+
return layer;
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
export default createSharedMobilityLayer;
|
package/src/utils/exportPdf.ts
CHANGED
|
@@ -26,7 +26,7 @@ let multilineCopyright = false;
|
|
|
26
26
|
const copyrightY = 0;
|
|
27
27
|
|
|
28
28
|
// Ensure the font size fita with the image width.
|
|
29
|
-
const decreaseFontSize = (destContext, maxWidth, copyright, scale) => {
|
|
29
|
+
const decreaseFontSize = (destContext, maxWidth, copyright, scale = 1) => {
|
|
30
30
|
const minFontSize = 8;
|
|
31
31
|
let sizeMatch;
|
|
32
32
|
let fontSize;
|
|
@@ -158,7 +158,6 @@ const drawText = (
|
|
|
158
158
|
textBaseline?: CanvasTextBaseline;
|
|
159
159
|
},
|
|
160
160
|
) => {
|
|
161
|
-
const scale = 1;
|
|
162
161
|
const canvas = document.createElement("canvas");
|
|
163
162
|
canvas.width = width;
|
|
164
163
|
canvas.height = height;
|
|
@@ -174,7 +173,7 @@ const drawText = (
|
|
|
174
173
|
|
|
175
174
|
ctx.fillStyle = fillStyle;
|
|
176
175
|
ctx.font = font;
|
|
177
|
-
ctx.font = decreaseFontSize(ctx, canvas.width, text
|
|
176
|
+
ctx.font = decreaseFontSize(ctx, canvas.width, text);
|
|
178
177
|
ctx.textAlign = textAlign;
|
|
179
178
|
ctx.textBaseline = textBaseline;
|
|
180
179
|
|
|
@@ -462,15 +461,6 @@ const createMapToExport = async (
|
|
|
462
461
|
const mapSize = size || map.getSize();
|
|
463
462
|
const extentToFit = extent;
|
|
464
463
|
|
|
465
|
-
// We apply preserveDrawingBuffer=true to be able to export the canvas.
|
|
466
|
-
// layers.forEach((layer) => {
|
|
467
|
-
// // @ts-expect-error - mapLibreMap is private
|
|
468
|
-
// if (layer?.mapLibreMap as MaplibreGlMap) {
|
|
469
|
-
// const opts = layer.get("mapLibreOptions");
|
|
470
|
-
// opts.preserveDrawingBuffer = true;
|
|
471
|
-
// }
|
|
472
|
-
// });
|
|
473
|
-
|
|
474
464
|
// We create a temporary map.
|
|
475
465
|
const div = document.createElement("div");
|
|
476
466
|
div.style.width = `${mapSize[0]}px`;
|
|
@@ -490,6 +480,7 @@ const createMapToExport = async (
|
|
|
490
480
|
projection: map.getView().getProjection(),
|
|
491
481
|
zoom: map.getView().getZoom(),
|
|
492
482
|
...viewOptions,
|
|
483
|
+
// extent: extentToFit,
|
|
493
484
|
}),
|
|
494
485
|
...mapOptions,
|
|
495
486
|
});
|
|
@@ -514,15 +505,6 @@ const createMapToExport = async (
|
|
|
514
505
|
});
|
|
515
506
|
});
|
|
516
507
|
|
|
517
|
-
// We apply preserveDrawingBuffer=true to be able to export the canvas.
|
|
518
|
-
// layers.forEach((layer) => {
|
|
519
|
-
// // @ts-expect-error - mapLibreMap is private
|
|
520
|
-
// if (layer?.mapLibreMap as MaplibreGlMap) {
|
|
521
|
-
// const opts = layer.get("mapLibreOptions");
|
|
522
|
-
// opts.preserveDrawingBuffer = false;
|
|
523
|
-
// }
|
|
524
|
-
// });
|
|
525
|
-
|
|
526
508
|
return await promise;
|
|
527
509
|
};
|
|
528
510
|
|
|
@@ -563,7 +545,10 @@ async function exportPdf(
|
|
|
563
545
|
usePlaceholder = true,
|
|
564
546
|
} = mapExportOptions;
|
|
565
547
|
const format = formatOptions?.format || "A4";
|
|
566
|
-
const
|
|
548
|
+
const sizePt = sizesByFormat72Dpi[format as string] || (format as number[]);
|
|
549
|
+
const size = sizePt.map((n) => {
|
|
550
|
+
return (n * 96) / 72;
|
|
551
|
+
});
|
|
567
552
|
const extent = useMaxExtent
|
|
568
553
|
? RVF_EXTENT_3857
|
|
569
554
|
: map.getView().calculateExtent();
|
|
@@ -640,26 +625,21 @@ async function exportPdf(
|
|
|
640
625
|
if (useCopyright) {
|
|
641
626
|
drawCopyright(copyrightText, canvas, {
|
|
642
627
|
...(mapExportOptions?.copyrightOptions || {}),
|
|
643
|
-
|
|
644
|
-
scale: pixelRatio,
|
|
628
|
+
font: (pixelRatio * 12).toString() + "px Arial",
|
|
645
629
|
});
|
|
646
630
|
}
|
|
647
631
|
|
|
648
632
|
// Transform to PDF
|
|
649
633
|
if (/^A[0-9]{1,2}/.test(format as string) || format?.length === 2) {
|
|
650
634
|
try {
|
|
651
|
-
const size300Dpi = size.map((n) => {
|
|
652
|
-
return (n * 300) / 72;
|
|
653
|
-
});
|
|
654
635
|
const doc = new jsPDF({
|
|
655
636
|
orientation: "landscape",
|
|
656
637
|
unit: "pt",
|
|
657
638
|
...formatOptions,
|
|
658
|
-
format: size300Dpi,
|
|
659
639
|
});
|
|
660
640
|
|
|
661
641
|
// Add canvas to PDF
|
|
662
|
-
doc.addImage(canvas, "JPEG", 0, 0,
|
|
642
|
+
doc.addImage(canvas, "JPEG", 0, 0, sizePt[0], sizePt[1]);
|
|
663
643
|
|
|
664
644
|
// Download the pdf
|
|
665
645
|
doc.save(`rvf-${new Date().toISOString().slice(0, 10)}.pdf`);
|
package/tailwind.config.mjs
CHANGED
|
@@ -39,6 +39,14 @@ export default {
|
|
|
39
39
|
ultradarkgray: "#2D2D2C", // another grey with no name
|
|
40
40
|
ultralightgrey: "#F5F5F5", // ultralight grey
|
|
41
41
|
},
|
|
42
|
+
height: {
|
|
43
|
+
innerControl: "12px",
|
|
44
|
+
inputControl: "20px",
|
|
45
|
+
},
|
|
46
|
+
margin: {
|
|
47
|
+
"4px": "4px",
|
|
48
|
+
"7px": "7px",
|
|
49
|
+
},
|
|
42
50
|
maxHeight: {
|
|
43
51
|
button: "40px",
|
|
44
52
|
},
|
|
@@ -48,6 +56,17 @@ export default {
|
|
|
48
56
|
padding: {
|
|
49
57
|
1.75: "7px",
|
|
50
58
|
},
|
|
59
|
+
screens: {
|
|
60
|
+
large: "993px",
|
|
61
|
+
medium: "577px",
|
|
62
|
+
},
|
|
63
|
+
width: {
|
|
64
|
+
check: "6px",
|
|
65
|
+
innerControl: "12px",
|
|
66
|
+
inputControl: "20px",
|
|
67
|
+
largeMenu: "350px",
|
|
68
|
+
mediumMenu: "300px",
|
|
69
|
+
},
|
|
51
70
|
},
|
|
52
71
|
fontFamily: {
|
|
53
72
|
sans: [
|
|
@@ -66,43 +85,5 @@ export default {
|
|
|
66
85
|
sm: getClamp(13, 15), // small
|
|
67
86
|
xl: getClamp(24, 31), // h3
|
|
68
87
|
},
|
|
69
|
-
extend: {
|
|
70
|
-
colors: {
|
|
71
|
-
darkgray: "#464646", // dark grey
|
|
72
|
-
grey: "#7C7C7C", // grey
|
|
73
|
-
lightgrey: "#D0D0D0", // light grey
|
|
74
|
-
red: "#E3000B", // rvf rot
|
|
75
|
-
ultradarkgray: "#2D2D2C", // another grey with no name
|
|
76
|
-
ultralightgrey: "#F5F5F5", // ultralight grey
|
|
77
|
-
},
|
|
78
|
-
width: {
|
|
79
|
-
'largeMenu': '350px',
|
|
80
|
-
'mediumMenu': '300px',
|
|
81
|
-
'inputControl': '20px',
|
|
82
|
-
'check': '6px',
|
|
83
|
-
'innerControl': '12px',
|
|
84
|
-
},
|
|
85
|
-
height: {
|
|
86
|
-
'inputControl': '20px',
|
|
87
|
-
'innerControl': '12px',
|
|
88
|
-
},
|
|
89
|
-
maxWidth: {
|
|
90
|
-
'button': '40px'
|
|
91
|
-
},
|
|
92
|
-
maxHeight: {
|
|
93
|
-
'button': '40px'
|
|
94
|
-
},
|
|
95
|
-
padding: {
|
|
96
|
-
'1.75': '7px'
|
|
97
|
-
},
|
|
98
|
-
margin: {
|
|
99
|
-
'4px': '4px',
|
|
100
|
-
'7px': '7px'
|
|
101
|
-
},
|
|
102
|
-
screens: {
|
|
103
|
-
'medium': '577px',
|
|
104
|
-
'large': '993px'
|
|
105
|
-
}
|
|
106
|
-
}
|
|
107
88
|
},
|
|
108
89
|
};
|
|
@@ -1,145 +0,0 @@
|
|
|
1
|
-
import type { JSX, PreactDOMAttributes } from "preact";
|
|
2
|
-
|
|
3
|
-
import BaseLayer from "ol/layer/Base";
|
|
4
|
-
import { SVGProps, useContext, useEffect, useState } from "preact/compat";
|
|
5
|
-
|
|
6
|
-
import { LayersTreeDispatchContext } from "../layersTreeContext";
|
|
7
|
-
import TreeItemContainer from "../TreeItemContainer";
|
|
8
|
-
|
|
9
|
-
export enum SelectionType {
|
|
10
|
-
CHECKBOX = "checkbox",
|
|
11
|
-
RADIO = "radio",
|
|
12
|
-
}
|
|
13
|
-
|
|
14
|
-
export type TreeItemProps = {
|
|
15
|
-
childItems: TreeItemProps[];
|
|
16
|
-
Icon?: (props: SVGProps<SVGSVGElement>) => preact.JSX.Element;
|
|
17
|
-
id: string;
|
|
18
|
-
isCollapsedOnControlClick?: boolean;
|
|
19
|
-
isControlChecked?: boolean;
|
|
20
|
-
layer?: BaseLayer;
|
|
21
|
-
onIconClick?: () => void;
|
|
22
|
-
selectionType: SelectionType;
|
|
23
|
-
title: string;
|
|
24
|
-
} & JSX.HTMLAttributes<HTMLButtonElement> &
|
|
25
|
-
PreactDOMAttributes;
|
|
26
|
-
|
|
27
|
-
function TreeItem({
|
|
28
|
-
childItems,
|
|
29
|
-
Icon,
|
|
30
|
-
isCollapsedOnControlClick,
|
|
31
|
-
isControlChecked,
|
|
32
|
-
layer,
|
|
33
|
-
onIconClick,
|
|
34
|
-
selectionType,
|
|
35
|
-
title,
|
|
36
|
-
}: TreeItemProps) {
|
|
37
|
-
const [isContainerVisible, setIsContainerVisible] = useState(true);
|
|
38
|
-
const dispatch = useContext(LayersTreeDispatchContext);
|
|
39
|
-
|
|
40
|
-
useEffect(() => {
|
|
41
|
-
if (isCollapsedOnControlClick) {
|
|
42
|
-
setIsContainerVisible(isControlChecked);
|
|
43
|
-
}
|
|
44
|
-
}, [isControlChecked, isCollapsedOnControlClick]);
|
|
45
|
-
|
|
46
|
-
const handleItemClick = () => {
|
|
47
|
-
setIsContainerVisible(!isContainerVisible);
|
|
48
|
-
|
|
49
|
-
if (isCollapsedOnControlClick && !isContainerVisible) {
|
|
50
|
-
if (selectionType === SelectionType.RADIO) {
|
|
51
|
-
dispatch({
|
|
52
|
-
payload: {
|
|
53
|
-
...this["props"],
|
|
54
|
-
isControlChecked: true,
|
|
55
|
-
},
|
|
56
|
-
type: "SELECT_RADIO_ITEM",
|
|
57
|
-
});
|
|
58
|
-
}
|
|
59
|
-
}
|
|
60
|
-
};
|
|
61
|
-
|
|
62
|
-
const handleSelectionChange = (event) => {
|
|
63
|
-
console.log("TREE LAYER", layer);
|
|
64
|
-
|
|
65
|
-
if (selectionType === SelectionType.RADIO) {
|
|
66
|
-
dispatch({
|
|
67
|
-
payload: {
|
|
68
|
-
...this["props"],
|
|
69
|
-
isControlChecked: event.target.checked,
|
|
70
|
-
},
|
|
71
|
-
type: "SELECT_RADIO_ITEM",
|
|
72
|
-
});
|
|
73
|
-
} else {
|
|
74
|
-
dispatch({
|
|
75
|
-
payload: {
|
|
76
|
-
...this["props"],
|
|
77
|
-
isControlChecked: event.target.checked,
|
|
78
|
-
},
|
|
79
|
-
type: "SELECT_ITEM",
|
|
80
|
-
});
|
|
81
|
-
}
|
|
82
|
-
};
|
|
83
|
-
|
|
84
|
-
const arrowShowedClass = childItems.length > 0 ? "block" : "hidden";
|
|
85
|
-
const changeArrowClass = isContainerVisible
|
|
86
|
-
? "border-l border-t"
|
|
87
|
-
: "border-b border-r";
|
|
88
|
-
const radioButtonClass =
|
|
89
|
-
"appearance-none w-inputControl h-inputControl border-2 border-grey rounded-full mr-2 peer";
|
|
90
|
-
const checkboxClass =
|
|
91
|
-
"appearance-none w-inputControl h-inputControl border-2 border-grey rounded mr-2 peer";
|
|
92
|
-
|
|
93
|
-
const innerCircle = (
|
|
94
|
-
<span className="pointer-events-none absolute ml-4px h-innerControl w-innerControl rounded-full peer-checked:bg-red" />
|
|
95
|
-
);
|
|
96
|
-
const checkSign = (
|
|
97
|
-
<span className="pointer-events-none absolute mb-px ml-7px hidden h-innerControl w-check rotate-45 border-b-2 border-r-2 border-grey peer-checked:block" />
|
|
98
|
-
);
|
|
99
|
-
|
|
100
|
-
const renderedLayers = childItems.map((item, idx) => {
|
|
101
|
-
return <TreeItem key={idx} {...item} />;
|
|
102
|
-
});
|
|
103
|
-
|
|
104
|
-
return (
|
|
105
|
-
<div className="flex flex-col pl-6">
|
|
106
|
-
<div className="flex h-8 cursor-pointer items-center">
|
|
107
|
-
<div className="relative flex items-center">
|
|
108
|
-
<input
|
|
109
|
-
checked={isControlChecked}
|
|
110
|
-
className={
|
|
111
|
-
selectionType === SelectionType.RADIO
|
|
112
|
-
? radioButtonClass
|
|
113
|
-
: checkboxClass
|
|
114
|
-
}
|
|
115
|
-
onChange={handleSelectionChange}
|
|
116
|
-
type={selectionType}
|
|
117
|
-
/>
|
|
118
|
-
{selectionType === SelectionType.RADIO ? innerCircle : checkSign}
|
|
119
|
-
</div>
|
|
120
|
-
<div
|
|
121
|
-
onClick={handleItemClick}
|
|
122
|
-
onKeyDown={handleItemClick}
|
|
123
|
-
role="button"
|
|
124
|
-
tabIndex={0}
|
|
125
|
-
>
|
|
126
|
-
<div className="flex items-center">
|
|
127
|
-
{title}
|
|
128
|
-
<div
|
|
129
|
-
className={`mb-0.5 ml-2 size-1.5 rotate-45 border-grey ${arrowShowedClass} ${changeArrowClass}`}
|
|
130
|
-
></div>
|
|
131
|
-
</div>
|
|
132
|
-
</div>
|
|
133
|
-
<span>{Icon ? <Icon onClick={onIconClick} /> : null}</span>
|
|
134
|
-
</div>
|
|
135
|
-
<TreeItemContainer
|
|
136
|
-
className={isContainerVisible ? "block" : "hidden"}
|
|
137
|
-
selectionType={childItems[0]?.selectionType}
|
|
138
|
-
>
|
|
139
|
-
{renderedLayers}
|
|
140
|
-
</TreeItemContainer>
|
|
141
|
-
</div>
|
|
142
|
-
);
|
|
143
|
-
}
|
|
144
|
-
|
|
145
|
-
export default TreeItem;
|
|
@@ -1,16 +0,0 @@
|
|
|
1
|
-
import { type JSX, type PreactDOMAttributes } from "preact";
|
|
2
|
-
|
|
3
|
-
import { SelectionType } from "../TreeItem/TreeItem";
|
|
4
|
-
|
|
5
|
-
export type TreeItemContainerProps = {
|
|
6
|
-
selectionType: SelectionType;
|
|
7
|
-
} & JSX.HTMLAttributes<HTMLButtonElement> &
|
|
8
|
-
PreactDOMAttributes;
|
|
9
|
-
|
|
10
|
-
function TreeItemContainer({ children, className }: TreeItemContainerProps) {
|
|
11
|
-
const classes = `flex flex-col ${className || ""}`;
|
|
12
|
-
|
|
13
|
-
return <div className={classes}>{children}</div>;
|
|
14
|
-
}
|
|
15
|
-
|
|
16
|
-
export default TreeItemContainer;
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export { default } from "./TreeItemContainer";
|
package/src/LayerTree/index.tsx
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export { default } from "./LayerTree";
|
|
@@ -1,143 +0,0 @@
|
|
|
1
|
-
import { Map } from "ol";
|
|
2
|
-
import { Group } from "ol/layer";
|
|
3
|
-
|
|
4
|
-
import LayerTree from "../LayerTree";
|
|
5
|
-
import { SelectionType } from "../LayerTree/TreeItem/TreeItem";
|
|
6
|
-
|
|
7
|
-
function TopicMenu({ map }: { map: Map }) {
|
|
8
|
-
const layers = map
|
|
9
|
-
.getLayers()
|
|
10
|
-
.getArray()
|
|
11
|
-
.filter((layer) => {
|
|
12
|
-
return !layer.get("isNotInLayerTree");
|
|
13
|
-
})
|
|
14
|
-
.map((layer) => {
|
|
15
|
-
return {
|
|
16
|
-
childItems:
|
|
17
|
-
(layer as Group)
|
|
18
|
-
?.getLayers?.()
|
|
19
|
-
.getArray()
|
|
20
|
-
.map((subLayer) => {
|
|
21
|
-
return {
|
|
22
|
-
childItems: [],
|
|
23
|
-
id: (Math.random() + 1).toString(36).substring(7),
|
|
24
|
-
isControlChecked: subLayer.getVisible(),
|
|
25
|
-
layer: subLayer,
|
|
26
|
-
selectionType: SelectionType.CHECKBOX,
|
|
27
|
-
title: subLayer.get("title"),
|
|
28
|
-
};
|
|
29
|
-
}) || [],
|
|
30
|
-
id: (Math.random() + 1).toString(36).substring(7),
|
|
31
|
-
isControlChecked: layer.getVisible(),
|
|
32
|
-
layer,
|
|
33
|
-
selectionType: SelectionType.CHECKBOX,
|
|
34
|
-
title: layer.get("title"),
|
|
35
|
-
};
|
|
36
|
-
});
|
|
37
|
-
|
|
38
|
-
// const readyLayers = [
|
|
39
|
-
// {
|
|
40
|
-
// childItems: layers.map((layer) => {
|
|
41
|
-
// return {
|
|
42
|
-
// ...layer,
|
|
43
|
-
// id: (Math.random() + 1).toString(36).substring(7),
|
|
44
|
-
// };
|
|
45
|
-
// }),
|
|
46
|
-
// id: (Math.random() + 1).toString(36).substring(7),
|
|
47
|
-
// isCollapsedOnControlClick: true,
|
|
48
|
-
// isControlChecked: true,
|
|
49
|
-
// selectionType: SelectionType.RADIO,
|
|
50
|
-
// title: "Topic 1",
|
|
51
|
-
// },
|
|
52
|
-
// {
|
|
53
|
-
// childItems: layers.map((layer) => {
|
|
54
|
-
// return {
|
|
55
|
-
// ...layer,
|
|
56
|
-
// id: (Math.random() + 1).toString(36).substring(7),
|
|
57
|
-
// };
|
|
58
|
-
// }),
|
|
59
|
-
// id: (Math.random() + 1).toString(36).substring(7),
|
|
60
|
-
// isCollapsedOnControlClick: true,
|
|
61
|
-
// selectionType: SelectionType.RADIO,
|
|
62
|
-
// title: "Topic 2",
|
|
63
|
-
// },
|
|
64
|
-
// {
|
|
65
|
-
// childItems: layers.map((layer) => {
|
|
66
|
-
// return {
|
|
67
|
-
// ...layer,
|
|
68
|
-
// id: (Math.random() + 1).toString(36).substring(7),
|
|
69
|
-
// isControlChecked: false,
|
|
70
|
-
// selectionType: SelectionType.RADIO,
|
|
71
|
-
// };
|
|
72
|
-
// }),
|
|
73
|
-
// id: (Math.random() + 1).toString(36).substring(7),
|
|
74
|
-
// isCollapsedOnControlClick: true,
|
|
75
|
-
// selectionType: SelectionType.RADIO,
|
|
76
|
-
// title: "Topic 3",
|
|
77
|
-
// },
|
|
78
|
-
// ];
|
|
79
|
-
|
|
80
|
-
// readyLayers[0].childItems[0].childItems = layers.map((layer, idx) => {
|
|
81
|
-
// return {
|
|
82
|
-
// ...layer,
|
|
83
|
-
// id: (Math.random() + 1).toString(36).substring(7),
|
|
84
|
-
// isControlChecked: idx === 0,
|
|
85
|
-
// selectionType: SelectionType.RADIO,
|
|
86
|
-
// };
|
|
87
|
-
// });
|
|
88
|
-
|
|
89
|
-
// readyLayers[1].childItems[0].childItems = layers.map((layer, idx) => {
|
|
90
|
-
// return {
|
|
91
|
-
// ...layer,
|
|
92
|
-
// id: (Math.random() + 1).toString(36).substring(7),
|
|
93
|
-
// isControlChecked: idx === 0,
|
|
94
|
-
// selectionType: SelectionType.RADIO,
|
|
95
|
-
// };
|
|
96
|
-
// });
|
|
97
|
-
|
|
98
|
-
// readyLayers[0].childItems[1].childItems = layers.map((layer) => {
|
|
99
|
-
// return {
|
|
100
|
-
// ...layer,
|
|
101
|
-
// id: (Math.random() + 1).toString(36).substring(7),
|
|
102
|
-
// selectionType: SelectionType.CHECKBOX,
|
|
103
|
-
// };
|
|
104
|
-
// });
|
|
105
|
-
|
|
106
|
-
// readyLayers[0].childItems[1].childItems[0].childItems = layers.map(
|
|
107
|
-
// (layer) => {
|
|
108
|
-
// return {
|
|
109
|
-
// ...layer,
|
|
110
|
-
// id: (Math.random() + 1).toString(36).substring(7),
|
|
111
|
-
// selectionType: SelectionType.CHECKBOX,
|
|
112
|
-
// };
|
|
113
|
-
// },
|
|
114
|
-
// );
|
|
115
|
-
|
|
116
|
-
// readyLayers[2].childItems[0].childItems = layers.map((layer) => {
|
|
117
|
-
// return {
|
|
118
|
-
// ...layer,
|
|
119
|
-
// id: (Math.random() + 1).toString(36).substring(7),
|
|
120
|
-
// isControlChecked: false,
|
|
121
|
-
// selectionType: SelectionType.RADIO,
|
|
122
|
-
// };
|
|
123
|
-
// });
|
|
124
|
-
|
|
125
|
-
// readyLayers[2].childItems[1].childItems = layers.map((layer) => {
|
|
126
|
-
// return {
|
|
127
|
-
// ...layer,
|
|
128
|
-
// id: (Math.random() + 1).toString(36).substring(7),
|
|
129
|
-
// isControlChecked: false,
|
|
130
|
-
// selectionType: SelectionType.RADIO,
|
|
131
|
-
// };
|
|
132
|
-
// });
|
|
133
|
-
|
|
134
|
-
// console.log("READY LAYERS", readyLayers);
|
|
135
|
-
|
|
136
|
-
return (
|
|
137
|
-
<div className="max-h-80 overflow-scroll bg-white medium:w-largeMenu large:w-mediumMenu">
|
|
138
|
-
<LayerTree layers={layers} />
|
|
139
|
-
</div>
|
|
140
|
-
);
|
|
141
|
-
}
|
|
142
|
-
|
|
143
|
-
export default TopicMenu;
|
package/src/TopicMenu/index.tsx
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export { default } from "./TopicMenu";
|
|
File without changes
|
|
File without changes
|