@geops/rvf-mobility-web-component 0.1.10 → 0.1.11
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 +33 -0
- package/docutils.js +198 -0
- package/index.html +48 -217
- package/index.js +627 -87
- package/input.css +11 -1
- package/jest-setup.js +3 -2
- package/package.json +4 -3
- package/scripts/dev.mjs +1 -1
- package/search.html +38 -69
- package/src/GeolocationButton/GeolocationButton.tsx +6 -5
- package/src/LayerTree/LayerTree.tsx +44 -0
- package/src/LayerTree/TreeItem/TreeItem.tsx +145 -0
- package/src/LayerTree/TreeItem/index.tsx +1 -0
- package/src/LayerTree/TreeItemContainer/TreeItemContainer.tsx +16 -0
- package/src/LayerTree/TreeItemContainer/index.tsx +1 -0
- package/src/LayerTree/index.tsx +1 -0
- package/src/LayerTree/layersTreeContext.ts +4 -0
- package/src/LayerTree/layersTreeReducer.ts +156 -0
- package/src/Map/Map.tsx +1 -0
- package/src/MobilityMap/MobilityMap.tsx +10 -9
- package/src/MobilityMap/index.css +0 -13
- package/src/RealtimeLayer/RealtimeLayer.tsx +1 -1
- package/src/RvfButton/RvfButton.tsx +28 -21
- package/src/RvfExportMenu/RvfExportMenu.tsx +95 -0
- package/src/RvfExportMenu/index.tsx +1 -0
- package/src/RvfExportMenuButton/RvfExportMenuButton.tsx +27 -0
- package/src/RvfExportMenuButton/index.tsx +1 -0
- package/src/RvfFeatureDetails/RvfFeatureDetails.tsx +29 -0
- package/src/RvfFeatureDetails/index.tsx +1 -0
- package/src/RvfIconButton/RvfIconButton.tsx +35 -0
- package/src/RvfIconButton/index.tsx +1 -0
- package/src/RvfMobilityMap/RvfMobilityMap.tsx +117 -83
- package/src/RvfMobilityMap/index.css +0 -13
- package/src/RvfModal/RvfModal.tsx +52 -0
- package/src/RvfModal/index.tsx +1 -0
- package/src/RvfPoisLayer/RvfPoisLayer.tsx +39 -0
- package/src/RvfPoisLayer/index.tsx +1 -0
- package/src/RvfSharedMobilityLayerGroup/RvfSharedMobilityLayerGroup.tsx +88 -0
- package/src/RvfSharedMobilityLayerGroup/index.tsx +1 -0
- package/src/RvfSingleClickListener/RvfSingleClickListener.tsx +137 -0
- package/src/RvfSingleClickListener/index.tsx +1 -0
- package/src/RvfZoomButtons/RvfZoomButtons.tsx +36 -29
- package/src/Search/Search.tsx +11 -9
- package/src/SingleClickListener/index.tsx +1 -1
- package/src/StationsLayer/StationsLayer.tsx +0 -1
- package/src/StopsSearch/StopsSearch.tsx +38 -6
- package/src/TopicMenu/TopicMenu.tsx +143 -0
- package/src/TopicMenu/index.tsx +1 -0
- package/src/icons/Cancel/Cancel.tsx +21 -0
- package/src/icons/Cancel/cancel.svg +7 -0
- package/src/icons/Cancel/index.tsx +1 -0
- package/src/icons/Download/Download.tsx +20 -0
- package/src/icons/Download/download.svg +15 -0
- package/src/icons/Download/index.tsx +1 -0
- package/src/icons/Elevator/Elevator.tsx +1 -1
- package/src/icons/Menu/Menu.tsx +32 -0
- package/src/icons/Menu/index.tsx +1 -0
- package/src/icons/Menu/menu.svg +9 -0
- package/src/utils/constants.ts +9 -0
- package/src/utils/createMobiDataBwWfsLayer.ts +120 -0
- package/src/utils/exportPdf.ts +677 -0
- package/src/utils/hooks/useRvfContext.tsx +37 -0
- package/src/utils/hooks/useUpdatePermalink.tsx +2 -9
- package/tailwind.config.mjs +41 -0
- package/src/RvfSharedMobilityLayer/RvfSharedMobilityLayer.tsx +0 -147
- package/src/RvfSharedMobilityLayer/index.tsx +0 -1
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
import { JSX, PreactDOMAttributes } from "preact";
|
|
2
|
+
import { memo } from "preact/compat";
|
|
3
|
+
import { useEffect, useState } from "preact/hooks";
|
|
4
|
+
|
|
5
|
+
export type ModalProps = {
|
|
6
|
+
onClose: () => void;
|
|
7
|
+
} & JSX.HTMLAttributes<HTMLDialogElement> &
|
|
8
|
+
PreactDOMAttributes;
|
|
9
|
+
|
|
10
|
+
function RvfModal({ children, onClose, ...props }: ModalProps) {
|
|
11
|
+
const [node, setNode] = useState<HTMLDialogElement>();
|
|
12
|
+
let hasChildren = !!children;
|
|
13
|
+
if (Array.isArray(children)) {
|
|
14
|
+
hasChildren =
|
|
15
|
+
children?.length &&
|
|
16
|
+
(children || []).find((c) => {
|
|
17
|
+
return !!c;
|
|
18
|
+
});
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
useEffect(() => {
|
|
22
|
+
node?.addEventListener("close", onClose);
|
|
23
|
+
return () => {
|
|
24
|
+
node?.removeEventListener("close", onClose);
|
|
25
|
+
};
|
|
26
|
+
}, [node, onClose]);
|
|
27
|
+
|
|
28
|
+
if (!hasChildren) {
|
|
29
|
+
return null;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
return (
|
|
33
|
+
<dialog
|
|
34
|
+
className={
|
|
35
|
+
"absolute inset-0 z-50 flex size-full items-center justify-center bg-transparent"
|
|
36
|
+
}
|
|
37
|
+
ref={(n) => {
|
|
38
|
+
setNode(n);
|
|
39
|
+
n?.focus();
|
|
40
|
+
}}
|
|
41
|
+
{...props}
|
|
42
|
+
>
|
|
43
|
+
<button
|
|
44
|
+
className="absolute inset-0 z-0 size-full bg-black/60"
|
|
45
|
+
onClick={onClose}
|
|
46
|
+
></button>
|
|
47
|
+
<div className="z-10 h-3/4 w-1/2 bg-white">{children}</div>
|
|
48
|
+
</dialog>
|
|
49
|
+
);
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
export default memo(RvfModal);
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { default } from "./RvfModal";
|
|
@@ -0,0 +1,39 @@
|
|
|
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 RvfPoisLayer(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 }) => {
|
|
17
|
+
return metadata?.["mapset.filter"] === "mapset_poi";
|
|
18
|
+
},
|
|
19
|
+
maplibreLayer: baseLayer,
|
|
20
|
+
visible: false,
|
|
21
|
+
...(props || {}),
|
|
22
|
+
});
|
|
23
|
+
}, [baseLayer, props]);
|
|
24
|
+
|
|
25
|
+
useEffect(() => {
|
|
26
|
+
if (!map || !layer) {
|
|
27
|
+
return;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
map.addLayer(layer);
|
|
31
|
+
return () => {
|
|
32
|
+
map.removeLayer(layer);
|
|
33
|
+
};
|
|
34
|
+
}, [map, layer]);
|
|
35
|
+
|
|
36
|
+
return null; // <RegisterForSelectFeaturesOnClick />;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
export default memo(RvfPoisLayer);
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { default } from "./RvfPoisLayer";
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
import type { Options } from "ol/layer/Group";
|
|
2
|
+
|
|
3
|
+
import { Group, Vector } from "ol/layer";
|
|
4
|
+
import { memo } from "preact/compat";
|
|
5
|
+
import { useEffect, useMemo } from "preact/hooks";
|
|
6
|
+
|
|
7
|
+
import createMobiDataBwWfsLayer from "../utils/createMobiDataBwWfsLayer";
|
|
8
|
+
import useMapContext from "../utils/hooks/useMapContext";
|
|
9
|
+
|
|
10
|
+
function RvfSharedMobilityLayerGroup(props: Options & Record<string, unknown>) {
|
|
11
|
+
const { map } = useMapContext();
|
|
12
|
+
|
|
13
|
+
const group = useMemo(() => {
|
|
14
|
+
const sharingStationsBicycle = createMobiDataBwWfsLayer(
|
|
15
|
+
"sharing_stations_bicycle",
|
|
16
|
+
"red",
|
|
17
|
+
);
|
|
18
|
+
sharingStationsBicycle.set("title", "Stations Bicycle");
|
|
19
|
+
|
|
20
|
+
const sharingStationsCar = createMobiDataBwWfsLayer(
|
|
21
|
+
"sharing_stations_car",
|
|
22
|
+
"blue",
|
|
23
|
+
);
|
|
24
|
+
sharingStationsCar.set("title", "Stations Car");
|
|
25
|
+
|
|
26
|
+
const sharingStationsCargoBicycle = createMobiDataBwWfsLayer(
|
|
27
|
+
"sharing_stations_cargo_bicycle",
|
|
28
|
+
"green",
|
|
29
|
+
);
|
|
30
|
+
sharingStationsCargoBicycle.set("title", "Stations Cargo Bicycle");
|
|
31
|
+
|
|
32
|
+
const sharingStationsScooterStanding = createMobiDataBwWfsLayer(
|
|
33
|
+
"sharing_stations_scooters_standing",
|
|
34
|
+
"purple",
|
|
35
|
+
);
|
|
36
|
+
sharingStationsScooterStanding.set("title", "Stations Scooter");
|
|
37
|
+
|
|
38
|
+
const sharingVehicles = createMobiDataBwWfsLayer(
|
|
39
|
+
"sharing_vehicles",
|
|
40
|
+
"orange",
|
|
41
|
+
);
|
|
42
|
+
sharingVehicles.set("title", "Free Floating");
|
|
43
|
+
console.log("ici");
|
|
44
|
+
return new Group({
|
|
45
|
+
layers: [
|
|
46
|
+
sharingStationsBicycle,
|
|
47
|
+
sharingStationsCar,
|
|
48
|
+
sharingStationsCargoBicycle,
|
|
49
|
+
sharingStationsScooterStanding,
|
|
50
|
+
sharingVehicles,
|
|
51
|
+
],
|
|
52
|
+
...props,
|
|
53
|
+
});
|
|
54
|
+
}, [props]);
|
|
55
|
+
|
|
56
|
+
// Reload features every minute
|
|
57
|
+
useEffect(() => {
|
|
58
|
+
const interval = window.setInterval(() => {
|
|
59
|
+
group.getLayers().forEach((layer: Vector) => {
|
|
60
|
+
// @ts-expect-error - private property
|
|
61
|
+
layer.getSource().loadedExtentsRtree_.clear();
|
|
62
|
+
layer.getSource().clear(true);
|
|
63
|
+
layer.getSource().changed();
|
|
64
|
+
});
|
|
65
|
+
}, 60000);
|
|
66
|
+
return () => {
|
|
67
|
+
window.clearInterval(interval);
|
|
68
|
+
};
|
|
69
|
+
}, [group]);
|
|
70
|
+
|
|
71
|
+
useEffect(() => {
|
|
72
|
+
if (!map || !group) {
|
|
73
|
+
return;
|
|
74
|
+
}
|
|
75
|
+
map.on("moveend", () => {
|
|
76
|
+
console.log("ZOOM", map.getView().getZoom());
|
|
77
|
+
});
|
|
78
|
+
|
|
79
|
+
map.addLayer(group);
|
|
80
|
+
|
|
81
|
+
return () => {
|
|
82
|
+
map.removeLayer(group);
|
|
83
|
+
};
|
|
84
|
+
});
|
|
85
|
+
|
|
86
|
+
return null;
|
|
87
|
+
}
|
|
88
|
+
export default memo(RvfSharedMobilityLayerGroup);
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { default } from "./RvfSharedMobilityLayerGroup";
|
|
@@ -0,0 +1,137 @@
|
|
|
1
|
+
import { Feature, MapBrowserEvent } from "ol";
|
|
2
|
+
import { GeoJSON } from "ol/format";
|
|
3
|
+
import { unByKey } from "ol/Observable";
|
|
4
|
+
import { toLonLat } from "ol/proj";
|
|
5
|
+
import { useCallback, useEffect } from "preact/hooks";
|
|
6
|
+
|
|
7
|
+
import useMapContext from "../utils/hooks/useMapContext";
|
|
8
|
+
import useRvfContext from "../utils/hooks/useRvfContext";
|
|
9
|
+
import MobilityEvent from "../utils/MobilityEvent";
|
|
10
|
+
|
|
11
|
+
const geojson = new GeoJSON();
|
|
12
|
+
|
|
13
|
+
function SingleClickListener() {
|
|
14
|
+
const {
|
|
15
|
+
map,
|
|
16
|
+
realtimeLayer,
|
|
17
|
+
setStationId,
|
|
18
|
+
setTrainId,
|
|
19
|
+
stationId,
|
|
20
|
+
stationsLayer,
|
|
21
|
+
tenant,
|
|
22
|
+
trainId,
|
|
23
|
+
} = useMapContext();
|
|
24
|
+
const { setSelectedFeature, setSelectedFeatures } = useRvfContext();
|
|
25
|
+
|
|
26
|
+
const onPointerMove = useCallback(
|
|
27
|
+
async (evt: MapBrowserEvent<PointerEvent>) => {
|
|
28
|
+
const [realtimeFeature] = evt.map.getFeaturesAtPixel(evt.pixel, {
|
|
29
|
+
layerFilter: (l) => {
|
|
30
|
+
return l === realtimeLayer;
|
|
31
|
+
},
|
|
32
|
+
});
|
|
33
|
+
realtimeLayer?.highlight(realtimeFeature as Feature);
|
|
34
|
+
|
|
35
|
+
const stationsFeatures = evt.map.getFeaturesAtPixel(evt.pixel, {
|
|
36
|
+
layerFilter: (l) => {
|
|
37
|
+
return l === stationsLayer;
|
|
38
|
+
},
|
|
39
|
+
});
|
|
40
|
+
|
|
41
|
+
const [stationFeature] = stationsFeatures.filter((feat) => {
|
|
42
|
+
return feat.get("tralis_network")?.includes(tenant);
|
|
43
|
+
});
|
|
44
|
+
|
|
45
|
+
evt.map.getTargetElement().style.cursor =
|
|
46
|
+
realtimeFeature || stationFeature ? "pointer" : "default";
|
|
47
|
+
},
|
|
48
|
+
[realtimeLayer, stationsLayer, tenant],
|
|
49
|
+
);
|
|
50
|
+
|
|
51
|
+
const onSingleClick = useCallback(
|
|
52
|
+
async (evt: MapBrowserEvent<PointerEvent>) => {
|
|
53
|
+
const [realtimeFeature] = evt.map.getFeaturesAtPixel(evt.pixel, {
|
|
54
|
+
layerFilter: (l) => {
|
|
55
|
+
return l === realtimeLayer;
|
|
56
|
+
},
|
|
57
|
+
});
|
|
58
|
+
|
|
59
|
+
const stationsFeatures = evt.map.getFeaturesAtPixel(evt.pixel, {
|
|
60
|
+
layerFilter: (l) => {
|
|
61
|
+
return l === stationsLayer;
|
|
62
|
+
},
|
|
63
|
+
});
|
|
64
|
+
const [stationFeature] = stationsFeatures.filter((feat) => {
|
|
65
|
+
return feat.get("tralis_network")?.includes(tenant);
|
|
66
|
+
});
|
|
67
|
+
|
|
68
|
+
const newStationId = stationFeature?.get("uid");
|
|
69
|
+
|
|
70
|
+
const newTrainId = realtimeFeature?.get("train_id");
|
|
71
|
+
|
|
72
|
+
if (newStationId && stationId !== newStationId) {
|
|
73
|
+
setStationId(newStationId);
|
|
74
|
+
setTrainId(null);
|
|
75
|
+
} else if (newTrainId && newTrainId !== trainId) {
|
|
76
|
+
setTrainId(realtimeFeature.get("train_id"));
|
|
77
|
+
setStationId(null);
|
|
78
|
+
} else {
|
|
79
|
+
setTrainId(null);
|
|
80
|
+
setStationId(null);
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
// Send all the features under the cursor
|
|
84
|
+
const features = evt.map.getFeaturesAtPixel(evt.pixel, {
|
|
85
|
+
layerFilter: (l) => {
|
|
86
|
+
return l.get("isQueryable");
|
|
87
|
+
},
|
|
88
|
+
}) as Feature[];
|
|
89
|
+
evt.map.getTargetElement().dispatchEvent(
|
|
90
|
+
new MobilityEvent("singleclick", {
|
|
91
|
+
...evt,
|
|
92
|
+
features: geojson.writeFeaturesObject(features),
|
|
93
|
+
lonlat: toLonLat(evt.coordinate),
|
|
94
|
+
}),
|
|
95
|
+
);
|
|
96
|
+
// feature.get("form_factor"); //free float
|
|
97
|
+
// feature.get("num_vehicles_available"); // wfs livedata
|
|
98
|
+
// feature.get("provider_name"); // station sharing
|
|
99
|
+
|
|
100
|
+
if (newStationId || newTrainId || !features.length) {
|
|
101
|
+
setSelectedFeature(null);
|
|
102
|
+
setSelectedFeatures([]);
|
|
103
|
+
} else {
|
|
104
|
+
setSelectedFeatures(features);
|
|
105
|
+
setSelectedFeature(features[0]);
|
|
106
|
+
}
|
|
107
|
+
},
|
|
108
|
+
[
|
|
109
|
+
stationId,
|
|
110
|
+
trainId,
|
|
111
|
+
realtimeLayer,
|
|
112
|
+
stationsLayer,
|
|
113
|
+
tenant,
|
|
114
|
+
setStationId,
|
|
115
|
+
setTrainId,
|
|
116
|
+
setSelectedFeature,
|
|
117
|
+
setSelectedFeatures,
|
|
118
|
+
],
|
|
119
|
+
);
|
|
120
|
+
|
|
121
|
+
useEffect(() => {
|
|
122
|
+
const key = map?.on("singleclick", onSingleClick);
|
|
123
|
+
return () => {
|
|
124
|
+
unByKey(key);
|
|
125
|
+
};
|
|
126
|
+
}, [map, onSingleClick]);
|
|
127
|
+
|
|
128
|
+
useEffect(() => {
|
|
129
|
+
const key = map?.on("pointermove", onPointerMove);
|
|
130
|
+
return () => {
|
|
131
|
+
unByKey(key);
|
|
132
|
+
};
|
|
133
|
+
}, [map, onPointerMove]);
|
|
134
|
+
|
|
135
|
+
return null;
|
|
136
|
+
}
|
|
137
|
+
export default SingleClickListener;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { default } from "./RvfSingleClickListener";
|
|
@@ -1,9 +1,10 @@
|
|
|
1
|
+
import { unByKey } from "ol/Observable";
|
|
1
2
|
import { memo } from "preact/compat";
|
|
2
|
-
import { useState } from "preact/hooks";
|
|
3
|
+
import { useCallback, useEffect, useState } from "preact/hooks";
|
|
3
4
|
|
|
4
5
|
import Minus from "../icons/Minus";
|
|
5
6
|
import Plus from "../icons/Plus";
|
|
6
|
-
import
|
|
7
|
+
import RvfIconButton from "../RvfIconButton";
|
|
7
8
|
import useMapContext from "../utils/hooks/useMapContext";
|
|
8
9
|
|
|
9
10
|
function RvfZoomButtons() {
|
|
@@ -11,50 +12,56 @@ function RvfZoomButtons() {
|
|
|
11
12
|
const [isZoomInDisabled, setIsZoomInDisabled] = useState(false);
|
|
12
13
|
const [isZoomOutDisabled, setIsZoomOutDisabled] = useState(false);
|
|
13
14
|
|
|
14
|
-
const handleZoomIn = () => {
|
|
15
|
-
|
|
16
|
-
const zoom = view.getZoom();
|
|
17
|
-
const maxzoom = view.getMaxZoom();
|
|
18
|
-
const minzoom = view.getMinZoom();
|
|
19
|
-
|
|
20
|
-
if (maxzoom && zoom === Number(maxzoom)) {
|
|
21
|
-
setIsZoomInDisabled(true);
|
|
15
|
+
const handleZoomIn = useCallback(() => {
|
|
16
|
+
if (!map?.getView()) {
|
|
22
17
|
return;
|
|
23
18
|
}
|
|
24
|
-
|
|
19
|
+
const view = map.getView();
|
|
20
|
+
const zoom = view.getZoom();
|
|
25
21
|
view.setZoom(zoom + 1);
|
|
22
|
+
}, [map]);
|
|
26
23
|
|
|
27
|
-
|
|
28
|
-
|
|
24
|
+
const handleZoomOut = useCallback(() => {
|
|
25
|
+
if (!map?.getView()) {
|
|
26
|
+
return;
|
|
29
27
|
}
|
|
30
|
-
};
|
|
31
|
-
|
|
32
|
-
const handleZoomOut = () => {
|
|
33
28
|
const view = map.getView();
|
|
34
29
|
const zoom = view.getZoom();
|
|
35
|
-
|
|
36
|
-
|
|
30
|
+
view.setZoom(zoom - 1);
|
|
31
|
+
}, [map]);
|
|
37
32
|
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
33
|
+
useEffect(() => {
|
|
34
|
+
const key = map?.on("moveend", () => {
|
|
35
|
+
const view = map.getView();
|
|
36
|
+
const zoom = view.getZoom();
|
|
37
|
+
const maxzoom = view.getMaxZoom();
|
|
38
|
+
const minzoom = view.getMinZoom();
|
|
42
39
|
|
|
43
|
-
|
|
40
|
+
if (maxzoom && zoom === Number(maxzoom)) {
|
|
41
|
+
setIsZoomInDisabled(true);
|
|
42
|
+
} else {
|
|
43
|
+
setIsZoomInDisabled(false);
|
|
44
|
+
}
|
|
44
45
|
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
46
|
+
if (minzoom && zoom === Number(minzoom)) {
|
|
47
|
+
setIsZoomOutDisabled(true);
|
|
48
|
+
} else {
|
|
49
|
+
setIsZoomOutDisabled(false);
|
|
50
|
+
}
|
|
51
|
+
});
|
|
52
|
+
return () => {
|
|
53
|
+
unByKey(key);
|
|
54
|
+
};
|
|
55
|
+
}, [map]);
|
|
49
56
|
|
|
50
57
|
return (
|
|
51
58
|
<>
|
|
52
|
-
<
|
|
59
|
+
<RvfIconButton
|
|
53
60
|
disabled={isZoomInDisabled}
|
|
54
61
|
Icon={Plus}
|
|
55
62
|
onClick={handleZoomIn}
|
|
56
63
|
/>
|
|
57
|
-
<
|
|
64
|
+
<RvfIconButton
|
|
58
65
|
disabled={isZoomOutDisabled}
|
|
59
66
|
Icon={Minus}
|
|
60
67
|
onClick={handleZoomOut}
|
package/src/Search/Search.tsx
CHANGED
|
@@ -1,3 +1,6 @@
|
|
|
1
|
+
import { memo } from "preact/compat";
|
|
2
|
+
import { useCallback } from "preact/hooks";
|
|
3
|
+
|
|
1
4
|
import StopsSearch from "../StopsSearch";
|
|
2
5
|
import centerOnStation from "../utils/centerOnStation";
|
|
3
6
|
import useMapContext from "../utils/hooks/useMapContext";
|
|
@@ -5,14 +8,13 @@ import useMapContext from "../utils/hooks/useMapContext";
|
|
|
5
8
|
function Search() {
|
|
6
9
|
const { apikey, map, stopsurl } = useMapContext();
|
|
7
10
|
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
}}
|
|
14
|
-
url={stopsurl}
|
|
15
|
-
/>
|
|
11
|
+
const onSelect = useCallback(
|
|
12
|
+
(selected) => {
|
|
13
|
+
return centerOnStation(selected, map);
|
|
14
|
+
},
|
|
15
|
+
[map],
|
|
16
16
|
);
|
|
17
|
+
|
|
18
|
+
return <StopsSearch apikey={apikey} onselect={onSelect} url={stopsurl} />;
|
|
17
19
|
}
|
|
18
|
-
export default Search;
|
|
20
|
+
export default memo(Search);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
export { default } from "
|
|
1
|
+
export { default } from "../RvfSingleClickListener";
|
|
@@ -18,7 +18,7 @@ import tailwind from "../style.css";
|
|
|
18
18
|
import i18n from "../utils/i18n";
|
|
19
19
|
import MobilityEvent from "../utils/MobilityEvent";
|
|
20
20
|
|
|
21
|
-
export type
|
|
21
|
+
export type MobilityStopsSearchProps = {
|
|
22
22
|
apikey: string;
|
|
23
23
|
bbox?: string;
|
|
24
24
|
countrycode?: string;
|
|
@@ -58,13 +58,45 @@ function StopsSearch({
|
|
|
58
58
|
prefagencies,
|
|
59
59
|
reflocation,
|
|
60
60
|
url = "https://api.geops.io/stops/v1/",
|
|
61
|
-
}:
|
|
61
|
+
}: MobilityStopsSearchProps) {
|
|
62
62
|
const { t } = i18n;
|
|
63
63
|
const [query, setQuery] = useState("");
|
|
64
64
|
const [selectedStation, setSelectedStation] = useState<StationFeature>();
|
|
65
65
|
const [results, setResults] = useState<StopsResponse["features"]>(undefined);
|
|
66
66
|
const myRef = useRef<HTMLDivElement>();
|
|
67
67
|
|
|
68
|
+
useEffect(() => {
|
|
69
|
+
myRef.current?.dispatchEvent(
|
|
70
|
+
new MobilityEvent<MobilityStopsSearchProps>("mwc:attribute", {
|
|
71
|
+
apikey,
|
|
72
|
+
bbox,
|
|
73
|
+
countrycode,
|
|
74
|
+
event,
|
|
75
|
+
field,
|
|
76
|
+
limit,
|
|
77
|
+
mots,
|
|
78
|
+
onselect,
|
|
79
|
+
params,
|
|
80
|
+
prefagencies,
|
|
81
|
+
reflocation,
|
|
82
|
+
url,
|
|
83
|
+
}),
|
|
84
|
+
);
|
|
85
|
+
}, [
|
|
86
|
+
apikey,
|
|
87
|
+
bbox,
|
|
88
|
+
countrycode,
|
|
89
|
+
event,
|
|
90
|
+
field,
|
|
91
|
+
limit,
|
|
92
|
+
mots,
|
|
93
|
+
onselect,
|
|
94
|
+
params,
|
|
95
|
+
prefagencies,
|
|
96
|
+
reflocation,
|
|
97
|
+
url,
|
|
98
|
+
]);
|
|
99
|
+
|
|
68
100
|
const api: StopsAPI = useMemo(() => {
|
|
69
101
|
return new StopsAPI({ apiKey: apikey, url: url });
|
|
70
102
|
}, [apikey, url]);
|
|
@@ -78,9 +110,9 @@ function StopsSearch({
|
|
|
78
110
|
bubbles: true,
|
|
79
111
|
},
|
|
80
112
|
);
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
113
|
+
|
|
114
|
+
myRef.current?.dispatchEvent(customEvt);
|
|
115
|
+
|
|
84
116
|
if (onselect && typeof onselect === "function") {
|
|
85
117
|
onselect(station);
|
|
86
118
|
}
|
|
@@ -208,7 +240,7 @@ function StopsSearch({
|
|
|
208
240
|
</div>
|
|
209
241
|
</div>
|
|
210
242
|
|
|
211
|
-
<div className="mt-
|
|
243
|
+
<div className="-mt-4px flex grow overflow-auto rounded-md rounded-t-none bg-white shadow">
|
|
212
244
|
{results && results.length === 0 && (
|
|
213
245
|
<div
|
|
214
246
|
className={
|