@geops/rvf-mobility-web-component 0.1.56 → 0.1.58
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 +31 -0
- package/docutils.js +1 -1
- package/global.d.ts +1 -0
- package/iframe.html +15 -0
- package/index.html +2 -1
- package/index.js +278 -330
- package/package.json +16 -17
- package/src/{RvfExportMenu/RvfExportMenu.tsx → ExportMenu/ExportMenu.tsx} +11 -11
- package/src/ExportMenu/index.tsx +1 -0
- package/src/{RvfExportMenuButton/RvfExportMenuButton.tsx → ExportMenuButton/ExportMenuButton.tsx} +5 -5
- package/src/ExportMenuButton/index.tsx +1 -0
- package/src/FeatureDetails/FeatureDetails.tsx +57 -0
- package/src/FeatureDetails/index.ts +1 -0
- package/src/FeaturesInfosListener/FeaturesInfosListener.tsx +73 -0
- package/src/FeaturesInfosListener/index.tsx +1 -0
- package/src/GeolocationButton/GeolocationButton.tsx +0 -1
- package/src/LayerTree/LayerTree.tsx +58 -0
- package/src/LayerTree/TreeItem/TreeItem.tsx +151 -0
- package/src/LayerTree/TreeItem/index.tsx +1 -0
- package/src/LayerTree/index.tsx +1 -0
- package/src/LayerTree/layersTreeContext.ts +4 -0
- package/src/LayerTree/layersTreeReducer.ts +158 -0
- package/src/{RvfLayerTreeButton/RvfLayerTreeButton.tsx → LayerTreeButton/LayerTreeButton.tsx} +5 -5
- package/src/LayerTreeButton/index.tsx +1 -0
- package/src/{RvfTopics/RvfTopics.tsx → LayerTreeMenu/LayerTreeMenu.tsx} +17 -13
- package/src/LayerTreeMenu/index.tsx +1 -0
- package/src/LayoutState/LayoutState.tsx +277 -0
- package/src/LayoutState/index.tsx +1 -0
- package/src/LinesNetworkPlanDetails/LinesNetworkPlanDetails.tsx +292 -0
- package/src/LinesNetworkPlanDetails/index.tsx +1 -0
- package/src/{RvfLineNetworkPlanLayer/RvfLineNetworkPlanLayer.tsx → LinesNetworkPlanLayer/LinesNetworkPlanLayer.tsx} +7 -9
- package/src/LinesNetworkPlanLayer/index.tsx +1 -0
- package/src/MobilityMap/MobilityMap.tsx +274 -60
- package/src/MobilityMap/MobilityMapAttributes.ts +27 -43
- package/src/NotificationDetails/NotificationDetails.tsx +468 -0
- package/src/NotificationDetails/index.ts +1 -0
- package/src/{NotificationLayer/NotificationLayer.tsx → NotificationsLayer/NotificationsLayer.tsx} +9 -4
- package/src/NotificationsLayer/index.tsx +1 -0
- package/src/Overlay/Overlay.tsx +1 -6
- package/src/OverlayContent/OverlayContent.tsx +87 -0
- package/src/OverlayContent/index.ts +1 -0
- package/src/OverlayDetails/OverlayDetails.tsx +47 -0
- package/src/OverlayDetails/index.ts +1 -0
- package/src/OverlayDetailsFooter/OverlayDetailsFooter.tsx +51 -0
- package/src/OverlayDetailsFooter/index.tsx +1 -0
- package/src/OverlayDetailsHeader/OverlayDetailsHeader.tsx +35 -0
- package/src/OverlayDetailsHeader/index.ts +1 -0
- package/src/OverlayFooter/OverlayFooter.tsx +41 -0
- package/src/OverlayFooter/index.tsx +1 -0
- package/src/OverlayHeader/OverlayHeader.tsx +44 -0
- package/src/OverlayHeader/index.tsx +1 -0
- package/src/PermalinkInput/PermalinkInput.tsx +28 -0
- package/src/PermalinkInput/index.tsx +1 -0
- package/src/RouteSchedule/RouteSchedule.tsx +22 -18
- package/src/RvfFeatureDetails/RvfFeatureDetails.tsx +22 -50
- package/src/RvfFeatureDetails/RvfLineNetworkDetails/RvfLineNetworkDetails.tsx +108 -104
- package/src/RvfFeatureDetails/RvfNotificationDetails/RvfNotificationDetails.tsx +189 -154
- package/src/RvfFeatureDetails/RvfSharedMobilityDetail/RvfSharedMobilityDetails.tsx +12 -14
- package/src/RvfFeatureDetailsTitle/RvfFeatureDetailsTitle.tsx +5 -5
- package/src/RvfMainLinkButton/RvfMainLinkButton.tsx +62 -0
- package/src/RvfMainLinkButton/index.tsx +1 -0
- package/src/RvfMobilityMap/RvfMobilityMap.tsx +182 -394
- package/src/RvfOverlayContent/RvfOverlayContent.tsx +2 -2
- package/src/RvfPoisLayer/RvfPoisLayer.tsx +2 -2
- package/src/RvfSearch/RvfSearch.tsx +1 -1
- package/src/RvfSellingPointsLayer/RvfSellingPointsLayer.tsx +2 -2
- package/src/RvfSharedMobilityLayerGroup/RvfSharedMobilityLayerGroup.tsx +23 -23
- package/src/RvfTarifZonenLayer/RvfTarifZonenLayer.tsx +2 -2
- package/src/Search/Search.tsx +11 -2
- package/src/{RvfSearchButton/RvfSearchButton.tsx → SearchButton/SearchButton.tsx} +5 -5
- package/src/SearchButton/index.tsx +1 -0
- package/src/ShadowOverflow/ShadowOverflow.tsx +20 -0
- package/src/ShadowOverflow/index.tsx +1 -0
- package/src/{RvfShare/RvfPermalinkButton/RvfPermalinkButton.tsx → ShareMenu/PermalinkButton/PermalinkButton.tsx} +4 -4
- package/src/ShareMenu/PermalinkButton/index.tsx +1 -0
- package/src/{RvfShare/RvfShare.tsx → ShareMenu/ShareMenu.tsx} +9 -11
- package/src/ShareMenu/index.tsx +1 -0
- package/src/{RvfShareMenuButton/RvfShareMenuButton.tsx → ShareMenuButton/ShareMenuButton.tsx} +6 -6
- package/src/ShareMenuButton/index.tsx +1 -0
- package/src/SingleClickListener/SingleClickListener.tsx +55 -113
- package/src/SingleClickListener/index.tsx +1 -1
- package/src/Station/Station.tsx +10 -3
- package/src/StationsLayer/StationsLayer.tsx +0 -1
- package/src/StopsSearch/StopsSearch.tsx +3 -4
- package/src/StopsSearch/index.tsx +2 -1
- package/src/WindowMessageListener/WindowMessageListener.tsx +7 -1
- package/src/{RvfZoomButtons/RvfZoomButtons.tsx → ZoomButtons/ZoomButtons.tsx} +9 -12
- package/src/ZoomButtons/index.tsx +1 -0
- package/src/icons/Geolocation/airport-14-svgrepo-com.svg +41 -0
- package/src/ui/Button/Button.tsx +9 -2
- package/src/ui/Checkbox/Checkbox.tsx +32 -0
- package/src/ui/Checkbox/index.tsx +1 -0
- package/src/ui/IconButton/IconButton.tsx +24 -4
- package/src/ui/Input/Input.tsx +17 -0
- package/src/ui/Input/index.tsx +1 -0
- package/src/ui/InputCopy/InputCopy.tsx +86 -0
- package/src/ui/InputCopy/index.tsx +1 -0
- package/src/ui/Select/Select.tsx +24 -0
- package/src/ui/Select/index.tsx +1 -0
- package/src/utils/constants.ts +43 -42
- package/src/utils/hooks/useInitialLayersVisiblity.tsx +1 -1
- package/src/utils/hooks/useLayerConfig.tsx +2 -2
- package/src/utils/hooks/useLayersConfig.tsx +3 -3
- package/src/utils/hooks/useMapContext.tsx +67 -8
- package/src/utils/hooks/useRvfContext.tsx +0 -44
- package/src/NotificationLayer/index.tsx +0 -1
- package/src/RvfEmbedNavigation/DragPanWarning.ts +0 -124
- package/src/RvfEmbedNavigation/RvfEmbedNavigation.tsx +0 -51
- package/src/RvfEmbedNavigation/index.js +0 -1
- package/src/RvfExportMenu/index.tsx +0 -1
- package/src/RvfExportMenuButton/index.tsx +0 -1
- package/src/RvfFloatingMenu/RvfFloatingMenu.tsx +0 -44
- package/src/RvfFloatingMenu/index.tsx +0 -1
- package/src/RvfLayerTreeButton/index.tsx +0 -1
- package/src/RvfLineNetworkPlanLayer/index.tsx +0 -1
- package/src/RvfPermalink/RvfPermalink.tsx +0 -18
- package/src/RvfPermalink/index.tsx +0 -1
- package/src/RvfSearchButton/index.tsx +0 -1
- package/src/RvfShare/RvfPermalinkButton/index.tsx +0 -1
- package/src/RvfShare/index.tsx +0 -1
- package/src/RvfShareMenuButton/index.tsx +0 -1
- package/src/RvfTopics/index.tsx +0 -1
- package/src/RvfZoomButtons/index.tsx +0 -1
package/package.json
CHANGED
|
@@ -2,17 +2,17 @@
|
|
|
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.58",
|
|
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
|
"graphql": "^16.10.0",
|
|
11
11
|
"graphql-request": "^7.1.2",
|
|
12
|
-
"jspdf": "^
|
|
12
|
+
"jspdf": "^3.0.3",
|
|
13
13
|
"lodash.debounce": "^4.0.8",
|
|
14
|
-
"maplibre-gl": "^5.
|
|
15
|
-
"mobility-toolbox-js": "3.
|
|
14
|
+
"maplibre-gl": "^5.8.0",
|
|
15
|
+
"mobility-toolbox-js": "3.4.0",
|
|
16
16
|
"ol": "^10.6.1",
|
|
17
17
|
"preact": "^10.27.2",
|
|
18
18
|
"preact-custom-element": "^4.5.1",
|
|
@@ -20,43 +20,42 @@
|
|
|
20
20
|
"react-dom": "npm:@preact/compat@^18.3.1",
|
|
21
21
|
"react-icons": "^5.5.0",
|
|
22
22
|
"react-spatial": "^2.0.0",
|
|
23
|
-
"rosetta": "^1.1.0"
|
|
24
|
-
"showdown": "^2.1.0"
|
|
23
|
+
"rosetta": "^1.1.0"
|
|
25
24
|
},
|
|
26
25
|
"devDependencies": {
|
|
27
|
-
"@commitlint/cli": "^
|
|
28
|
-
"@commitlint/config-conventional": "^
|
|
29
|
-
"@eslint/js": "^9.
|
|
26
|
+
"@commitlint/cli": "^20.1.0",
|
|
27
|
+
"@commitlint/config-conventional": "^20.0.0",
|
|
28
|
+
"@eslint/js": "^9.36.0",
|
|
30
29
|
"@geops/eslint-config-react": "^1.5.0",
|
|
31
30
|
"@tailwindcss/cli": "^4.1.13",
|
|
32
31
|
"@tailwindcss/container-queries": "^0.1.1",
|
|
33
32
|
"@testing-library/preact": "^3.2.4",
|
|
34
33
|
"@types/geojson": "^7946.0.16",
|
|
35
34
|
"@types/jest": "^30.0.0",
|
|
35
|
+
"@types/lodash": "^4.17.20",
|
|
36
36
|
"@types/preact-custom-element": "^4.0.4",
|
|
37
37
|
"concurrently": "^9.2.1",
|
|
38
38
|
"esbuild": "^0.25.10",
|
|
39
39
|
"esbuild-sass-plugin": "^3.3.1",
|
|
40
|
-
"eslint": "^9.
|
|
40
|
+
"eslint": "^9.36.0",
|
|
41
41
|
"eslint-plugin-tailwindcss": "^4.0.0-beta.0",
|
|
42
42
|
"fixpack": "^4.0.0",
|
|
43
43
|
"generact": "^0.4.0",
|
|
44
44
|
"husky": "^9.1.7",
|
|
45
|
-
"jest": "^30.
|
|
45
|
+
"jest": "^30.2.0",
|
|
46
46
|
"jest-canvas-mock": "^2.5.2",
|
|
47
|
-
"jest-environment-jsdom": "^30.
|
|
47
|
+
"jest-environment-jsdom": "^30.2.0",
|
|
48
48
|
"jest-preset-preact": "^4.1.1",
|
|
49
|
-
"lint-staged": "^16.2.0",
|
|
50
49
|
"next": "15.5.2",
|
|
51
|
-
"preact-render-to-string": "^6.6.
|
|
50
|
+
"preact-render-to-string": "^6.6.2",
|
|
52
51
|
"prettier": "^3.6.2",
|
|
53
52
|
"prettier-plugin-tailwindcss": "^0.6.14",
|
|
54
53
|
"standard-version": "^9.5.0",
|
|
55
54
|
"tailwind-merge": "^3.3.1",
|
|
56
55
|
"tailwindcss": "^4.1.13",
|
|
57
|
-
"ts-jest": "^29.4.
|
|
58
|
-
"typescript": "^5.9.
|
|
59
|
-
"typescript-eslint": "^8.
|
|
56
|
+
"ts-jest": "^29.4.4",
|
|
57
|
+
"typescript": "^5.9.3",
|
|
58
|
+
"typescript-eslint": "^8.45.0"
|
|
60
59
|
},
|
|
61
60
|
"scripts": {
|
|
62
61
|
"build": "yarn build:css && yarn build:js && cp index*.js* doc/public/",
|
|
@@ -1,23 +1,23 @@
|
|
|
1
1
|
import { memo, useId, useState } from "preact/compat";
|
|
2
2
|
|
|
3
|
-
import RvfCheckbox from "../RvfCheckbox";
|
|
4
|
-
import RvfSelect from "../RvfSelect";
|
|
5
3
|
import Button from "../ui/Button";
|
|
6
|
-
import
|
|
4
|
+
import Checkbox from "../ui/Checkbox";
|
|
5
|
+
import Select from "../ui/Select";
|
|
6
|
+
import { LAYER_PROP_IS_EXPORTING, MAX_EXTENT } from "../utils/constants";
|
|
7
7
|
import exportPdf from "../utils/exportPdf";
|
|
8
8
|
import getAllLayers from "../utils/getAllLayers";
|
|
9
9
|
import useMapContext from "../utils/hooks/useMapContext";
|
|
10
10
|
|
|
11
|
-
import type {
|
|
11
|
+
import type { HTMLAttributes, PreactDOMAttributes } from "preact";
|
|
12
12
|
|
|
13
|
-
export type
|
|
13
|
+
export type ExportMenuButtonProps = HTMLAttributes<HTMLDivElement> &
|
|
14
14
|
PreactDOMAttributes;
|
|
15
15
|
|
|
16
16
|
const formats = ["A4", "A3", "A1", "A0"];
|
|
17
17
|
|
|
18
18
|
let prevRealtimeLayerVisibility = false;
|
|
19
19
|
|
|
20
|
-
function
|
|
20
|
+
function ExportMenu({ ...props }: ExportMenuButtonProps) {
|
|
21
21
|
const { map, realtimeLayer } = useMapContext();
|
|
22
22
|
const [useMaxExtent, setUseMaxExtent] = useState(false);
|
|
23
23
|
const [format, setFormat] = useState<string>(formats[0]);
|
|
@@ -30,7 +30,7 @@ function RvfExportMenu({ ...props }: RvfExportMenuButtonProps) {
|
|
|
30
30
|
{/* <!-- Content --> */}
|
|
31
31
|
<div className="flex flex-1 flex-col gap-4">
|
|
32
32
|
<div className={"flex flex-wrap items-center gap-2"}>
|
|
33
|
-
<
|
|
33
|
+
<Checkbox
|
|
34
34
|
checked={useMaxExtent}
|
|
35
35
|
id={checkboxId}
|
|
36
36
|
onChange={() => {
|
|
@@ -41,7 +41,7 @@ function RvfExportMenu({ ...props }: RvfExportMenuButtonProps) {
|
|
|
41
41
|
</div>
|
|
42
42
|
<div className={"flex flex-wrap items-center gap-2"}>
|
|
43
43
|
<label htmlFor={selectId}>Format:</label>
|
|
44
|
-
<
|
|
44
|
+
<Select
|
|
45
45
|
className={"w-24"}
|
|
46
46
|
id={selectId}
|
|
47
47
|
onChange={(evt) => {
|
|
@@ -51,7 +51,7 @@ function RvfExportMenu({ ...props }: RvfExportMenuButtonProps) {
|
|
|
51
51
|
{formats.map((formatt) => {
|
|
52
52
|
return <option key={formatt}>{formatt}</option>;
|
|
53
53
|
})}
|
|
54
|
-
</
|
|
54
|
+
</Select>
|
|
55
55
|
</div>
|
|
56
56
|
</div>
|
|
57
57
|
{/* <!-- Footer --> */}
|
|
@@ -66,7 +66,7 @@ function RvfExportMenu({ ...props }: RvfExportMenuButtonProps) {
|
|
|
66
66
|
map,
|
|
67
67
|
{ format },
|
|
68
68
|
{
|
|
69
|
-
maxExtent: useMaxExtent ?
|
|
69
|
+
maxExtent: useMaxExtent ? MAX_EXTENT : undefined,
|
|
70
70
|
|
|
71
71
|
onAfter: (mapp, layers) => {
|
|
72
72
|
if (
|
|
@@ -112,4 +112,4 @@ function RvfExportMenu({ ...props }: RvfExportMenuButtonProps) {
|
|
|
112
112
|
);
|
|
113
113
|
}
|
|
114
114
|
|
|
115
|
-
export default memo(
|
|
115
|
+
export default memo(ExportMenu);
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { default } from "./ExportMenu";
|
package/src/{RvfExportMenuButton/RvfExportMenuButton.tsx → ExportMenuButton/ExportMenuButton.tsx}
RENAMED
|
@@ -3,14 +3,14 @@ import { useCallback } from "preact/hooks";
|
|
|
3
3
|
|
|
4
4
|
import Download from "../icons/Download";
|
|
5
5
|
import IconButton from "../ui/IconButton";
|
|
6
|
-
import
|
|
6
|
+
import useMapContext from "../utils/hooks/useMapContext";
|
|
7
7
|
|
|
8
8
|
import type { IconButtonProps } from "../ui/IconButton/IconButton";
|
|
9
9
|
|
|
10
|
-
export type
|
|
10
|
+
export type ExportMenuButtonProps = IconButtonProps;
|
|
11
11
|
|
|
12
|
-
function
|
|
13
|
-
const { isExportMenuOpen, setIsExportMenuOpen } =
|
|
12
|
+
function ExportMenuButton({ ...props }: ExportMenuButtonProps) {
|
|
13
|
+
const { isExportMenuOpen, setIsExportMenuOpen } = useMapContext();
|
|
14
14
|
|
|
15
15
|
const onClick = useCallback(() => {
|
|
16
16
|
setIsExportMenuOpen(!isExportMenuOpen);
|
|
@@ -23,4 +23,4 @@ function RvfExportMenuButton({ ...props }: RvfExportMenuButtonProps) {
|
|
|
23
23
|
);
|
|
24
24
|
}
|
|
25
25
|
|
|
26
|
-
export default memo(
|
|
26
|
+
export default memo(ExportMenuButton);
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { default } from "./ExportMenuButton";
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
import { memo } from "preact/compat";
|
|
2
|
+
|
|
3
|
+
import LinesNetworkPlanDetails from "../LinesNetworkPlanDetails";
|
|
4
|
+
import NotificationDetails from "../NotificationDetails";
|
|
5
|
+
import RouteSchedule from "../RouteSchedule";
|
|
6
|
+
import Station from "../Station";
|
|
7
|
+
import useMapContext from "../utils/hooks/useMapContext";
|
|
8
|
+
|
|
9
|
+
import type { FeatureInfo } from "mobility-toolbox-js/common/typedefs";
|
|
10
|
+
import type { Feature } from "ol";
|
|
11
|
+
import type BaseLayer from "ol/layer/Base";
|
|
12
|
+
|
|
13
|
+
const contentClassName = ""; //`h-full overflow-x-hidden text-base bg-white`;
|
|
14
|
+
|
|
15
|
+
export interface FeatureDetailsProps {
|
|
16
|
+
feature?: Feature;
|
|
17
|
+
featuresInfo?: FeatureInfo;
|
|
18
|
+
layer?: BaseLayer;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* This component is repsonsible to display the details of a feature passed as prop.
|
|
23
|
+
*/
|
|
24
|
+
function FeatureDetails({ feature, featuresInfo, layer }: FeatureDetailsProps) {
|
|
25
|
+
const {
|
|
26
|
+
linesNetworkPlanLayer,
|
|
27
|
+
notificationsLayer,
|
|
28
|
+
realtimeLayer,
|
|
29
|
+
stationId,
|
|
30
|
+
stationsLayer,
|
|
31
|
+
tenant,
|
|
32
|
+
trainId,
|
|
33
|
+
} = useMapContext();
|
|
34
|
+
|
|
35
|
+
if (!feature) {
|
|
36
|
+
return null;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
return (
|
|
40
|
+
<>
|
|
41
|
+
{feature && layer === realtimeLayer && trainId && <RouteSchedule />}
|
|
42
|
+
{feature && layer === stationsLayer && tenant && stationId && <Station />}
|
|
43
|
+
{feature &&
|
|
44
|
+
layer === linesNetworkPlanLayer &&
|
|
45
|
+
featuresInfo?.features?.length && (
|
|
46
|
+
<LinesNetworkPlanDetails
|
|
47
|
+
className={contentClassName}
|
|
48
|
+
features={featuresInfo?.features || []}
|
|
49
|
+
/>
|
|
50
|
+
)}
|
|
51
|
+
{feature && layer === notificationsLayer && (
|
|
52
|
+
<NotificationDetails className={contentClassName} feature={feature} />
|
|
53
|
+
)}
|
|
54
|
+
</>
|
|
55
|
+
);
|
|
56
|
+
}
|
|
57
|
+
export default memo(FeatureDetails);
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { default } from "../RvfFeatureDetails";
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
import { useEffect } from "preact/hooks";
|
|
2
|
+
|
|
3
|
+
import useMapContext from "../utils/hooks/useMapContext";
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* This component defines the behavior when user select or hover a queryable feature
|
|
7
|
+
*/
|
|
8
|
+
function FeaturesInfosListener() {
|
|
9
|
+
const {
|
|
10
|
+
featuresInfos,
|
|
11
|
+
featuresInfosHovered,
|
|
12
|
+
realtimeLayer,
|
|
13
|
+
setSelectedFeature,
|
|
14
|
+
setSelectedFeatures,
|
|
15
|
+
setStationId,
|
|
16
|
+
setTrainId,
|
|
17
|
+
stationsLayer,
|
|
18
|
+
tenant,
|
|
19
|
+
} = useMapContext();
|
|
20
|
+
|
|
21
|
+
useEffect(() => {
|
|
22
|
+
const [realtimeFeature] =
|
|
23
|
+
featuresInfosHovered?.find((info) => {
|
|
24
|
+
return info.layer === realtimeLayer;
|
|
25
|
+
})?.features || [];
|
|
26
|
+
realtimeLayer?.highlight(realtimeFeature);
|
|
27
|
+
}, [featuresInfosHovered, realtimeLayer]);
|
|
28
|
+
|
|
29
|
+
useEffect(() => {
|
|
30
|
+
const [realtimeFeature] =
|
|
31
|
+
featuresInfos?.find((info) => {
|
|
32
|
+
return info.layer === realtimeLayer;
|
|
33
|
+
})?.features || [];
|
|
34
|
+
|
|
35
|
+
const stationsFeatures =
|
|
36
|
+
featuresInfos?.find((info) => {
|
|
37
|
+
return info.layer === stationsLayer;
|
|
38
|
+
})?.features || [];
|
|
39
|
+
|
|
40
|
+
const [stationFeature] = stationsFeatures.filter((feat) => {
|
|
41
|
+
return feat.get("tralis_network")?.includes(tenant);
|
|
42
|
+
});
|
|
43
|
+
|
|
44
|
+
const features =
|
|
45
|
+
featuresInfos?.flatMap((info) => {
|
|
46
|
+
return info.features;
|
|
47
|
+
}) || [];
|
|
48
|
+
|
|
49
|
+
if (realtimeFeature || stationFeature || !features.length) {
|
|
50
|
+
setSelectedFeature(realtimeFeature || stationFeature || null);
|
|
51
|
+
setSelectedFeatures(
|
|
52
|
+
realtimeFeature || stationFeature
|
|
53
|
+
? [realtimeFeature || stationFeature]
|
|
54
|
+
: [],
|
|
55
|
+
);
|
|
56
|
+
} else {
|
|
57
|
+
setSelectedFeatures(features);
|
|
58
|
+
setSelectedFeature(realtimeFeature || stationFeature || features[0]);
|
|
59
|
+
}
|
|
60
|
+
}, [
|
|
61
|
+
featuresInfos,
|
|
62
|
+
realtimeLayer,
|
|
63
|
+
setSelectedFeature,
|
|
64
|
+
setSelectedFeatures,
|
|
65
|
+
setStationId,
|
|
66
|
+
setTrainId,
|
|
67
|
+
stationsLayer,
|
|
68
|
+
tenant,
|
|
69
|
+
]);
|
|
70
|
+
|
|
71
|
+
return null;
|
|
72
|
+
}
|
|
73
|
+
export default FeaturesInfosListener;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { default } from "./FeaturesInfosListener";
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
import { memo } from "preact/compat";
|
|
2
|
+
import { useEffect, useReducer } from "preact/hooks";
|
|
3
|
+
import { twMerge } from "tailwind-merge";
|
|
4
|
+
|
|
5
|
+
import {
|
|
6
|
+
LayersTreeContext,
|
|
7
|
+
LayersTreeDispatchContext,
|
|
8
|
+
} from "./layersTreeContext";
|
|
9
|
+
import layersTreeReducer from "./layersTreeReducer";
|
|
10
|
+
import TreeItem from "./TreeItem";
|
|
11
|
+
|
|
12
|
+
import type { HTMLAttributes, PreactDOMAttributes } from "preact";
|
|
13
|
+
|
|
14
|
+
import type { TreeItemProps } from "./TreeItem";
|
|
15
|
+
|
|
16
|
+
export type LayerTreeProps = {
|
|
17
|
+
className?: string;
|
|
18
|
+
layers: TreeItemProps[];
|
|
19
|
+
treeItemProps?: Partial<TreeItemProps>;
|
|
20
|
+
} & HTMLAttributes<HTMLDivElement> &
|
|
21
|
+
PreactDOMAttributes;
|
|
22
|
+
|
|
23
|
+
function LayerTree({
|
|
24
|
+
className,
|
|
25
|
+
layers,
|
|
26
|
+
treeItemProps,
|
|
27
|
+
...props
|
|
28
|
+
}: LayerTreeProps) {
|
|
29
|
+
const [tree, dispatch] = useReducer(layersTreeReducer, layers);
|
|
30
|
+
|
|
31
|
+
useEffect(() => {
|
|
32
|
+
dispatch({ payload: layers, type: "INIT" });
|
|
33
|
+
}, [layers]);
|
|
34
|
+
|
|
35
|
+
return (
|
|
36
|
+
<LayersTreeContext.Provider value={tree}>
|
|
37
|
+
<LayersTreeDispatchContext.Provider value={dispatch}>
|
|
38
|
+
<div
|
|
39
|
+
className={twMerge("relative flex flex-col", className)}
|
|
40
|
+
{...props}
|
|
41
|
+
>
|
|
42
|
+
{layers.map((item) => {
|
|
43
|
+
return (
|
|
44
|
+
<TreeItem
|
|
45
|
+
{...(treeItemProps || {})}
|
|
46
|
+
className={twMerge("w-full", treeItemProps?.className)}
|
|
47
|
+
key={item.id}
|
|
48
|
+
{...item}
|
|
49
|
+
/>
|
|
50
|
+
);
|
|
51
|
+
})}
|
|
52
|
+
</div>
|
|
53
|
+
</LayersTreeDispatchContext.Provider>
|
|
54
|
+
</LayersTreeContext.Provider>
|
|
55
|
+
);
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
export default memo(LayerTree);
|
|
@@ -0,0 +1,151 @@
|
|
|
1
|
+
import { useContext, useEffect, useId, useState } from "preact/compat";
|
|
2
|
+
import { twMerge } from "tailwind-merge";
|
|
3
|
+
|
|
4
|
+
import ArrowDown from "../../icons/ArrowDown";
|
|
5
|
+
import ArrowUp from "../../icons/ArrowUp";
|
|
6
|
+
import minusGrey from "../../icons/Minus/minus.svg";
|
|
7
|
+
import Checkbox from "../../ui/Checkbox";
|
|
8
|
+
// import RvfRadioButton from "../../RvfRadioButton";
|
|
9
|
+
import { LayersTreeDispatchContext } from "../layersTreeContext";
|
|
10
|
+
|
|
11
|
+
import type BaseLayer from "ol/layer/Base";
|
|
12
|
+
import type { HTMLAttributes, PreactDOMAttributes } from "preact";
|
|
13
|
+
import type { SVGProps } from "preact/compat";
|
|
14
|
+
|
|
15
|
+
export enum SelectionType {
|
|
16
|
+
CHECKBOX = "checkbox",
|
|
17
|
+
RADIO = "radio",
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
export type TreeItemProps = {
|
|
21
|
+
childContainerClassName?: string;
|
|
22
|
+
childItems: TreeItemProps[];
|
|
23
|
+
className?: string;
|
|
24
|
+
Icon?: (props: SVGProps<SVGSVGElement>) => preact.JSX.Element;
|
|
25
|
+
id: string;
|
|
26
|
+
isCollapsedOnControlClick?: boolean;
|
|
27
|
+
isControlChecked?: boolean;
|
|
28
|
+
layer?: BaseLayer;
|
|
29
|
+
onIconClick?: () => void;
|
|
30
|
+
selectionType: SelectionType;
|
|
31
|
+
title: string;
|
|
32
|
+
} & HTMLAttributes<HTMLButtonElement> &
|
|
33
|
+
PreactDOMAttributes;
|
|
34
|
+
|
|
35
|
+
function TreeItem({
|
|
36
|
+
childContainerClassName,
|
|
37
|
+
childItems,
|
|
38
|
+
className,
|
|
39
|
+
isCollapsedOnControlClick,
|
|
40
|
+
isControlChecked,
|
|
41
|
+
layer,
|
|
42
|
+
selectionType,
|
|
43
|
+
title,
|
|
44
|
+
}: TreeItemProps) {
|
|
45
|
+
const [isContainerVisible, setIsContainerVisible] = useState(true);
|
|
46
|
+
const dispatch = useContext(LayersTreeDispatchContext);
|
|
47
|
+
const inputId = useId();
|
|
48
|
+
const buttonId = useId();
|
|
49
|
+
|
|
50
|
+
useEffect(() => {
|
|
51
|
+
if (isCollapsedOnControlClick) {
|
|
52
|
+
setIsContainerVisible(isControlChecked);
|
|
53
|
+
}
|
|
54
|
+
}, [isControlChecked, isCollapsedOnControlClick, layer]);
|
|
55
|
+
|
|
56
|
+
const handleItemClick = () => {
|
|
57
|
+
setIsContainerVisible(!isContainerVisible);
|
|
58
|
+
|
|
59
|
+
if (isCollapsedOnControlClick && !isContainerVisible) {
|
|
60
|
+
dispatch({
|
|
61
|
+
payload: {
|
|
62
|
+
...this.props,
|
|
63
|
+
isControlChecked: true,
|
|
64
|
+
},
|
|
65
|
+
type: "SELECT_ITEM",
|
|
66
|
+
});
|
|
67
|
+
}
|
|
68
|
+
};
|
|
69
|
+
|
|
70
|
+
const handleSelectionChange = (event) => {
|
|
71
|
+
dispatch({
|
|
72
|
+
payload: {
|
|
73
|
+
...this.props,
|
|
74
|
+
isControlChecked: event.target.checked,
|
|
75
|
+
},
|
|
76
|
+
type: "SELECT_ITEM",
|
|
77
|
+
});
|
|
78
|
+
};
|
|
79
|
+
|
|
80
|
+
const renderedLayers = childItems
|
|
81
|
+
.filter(({ title: titlee }) => {
|
|
82
|
+
return !!titlee;
|
|
83
|
+
})
|
|
84
|
+
.map((item, idx) => {
|
|
85
|
+
return <TreeItem key={idx} {...item} />;
|
|
86
|
+
});
|
|
87
|
+
|
|
88
|
+
if (!title) {
|
|
89
|
+
return null;
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
const isMiddleState = () => {
|
|
93
|
+
if (childItems.length > 0) {
|
|
94
|
+
const checkedItems = childItems.filter((item) => {
|
|
95
|
+
return item.isControlChecked;
|
|
96
|
+
});
|
|
97
|
+
if (checkedItems.length === childItems.length) {
|
|
98
|
+
return false;
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
return childItems.some((item) => {
|
|
102
|
+
return item.isControlChecked;
|
|
103
|
+
});
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
return false;
|
|
107
|
+
};
|
|
108
|
+
|
|
109
|
+
return (
|
|
110
|
+
<>
|
|
111
|
+
<div className={twMerge("flex items-center gap-2 py-2", className)}>
|
|
112
|
+
{selectionType === SelectionType.RADIO ? null : (
|
|
113
|
+
// <RvfRadioButton
|
|
114
|
+
// checked={isControlChecked}
|
|
115
|
+
// id={inputId}
|
|
116
|
+
// onChange={handleSelectionChange}
|
|
117
|
+
// />
|
|
118
|
+
<Checkbox
|
|
119
|
+
checked={isControlChecked}
|
|
120
|
+
checkedIconUrl={isMiddleState() ? minusGrey : null}
|
|
121
|
+
className={isMiddleState() ? "bg-[length:18px]" : ""}
|
|
122
|
+
id={inputId}
|
|
123
|
+
onChange={handleSelectionChange}
|
|
124
|
+
/>
|
|
125
|
+
)}
|
|
126
|
+
<label
|
|
127
|
+
className={`flex-1 cursor-pointer`}
|
|
128
|
+
htmlFor={renderedLayers.length > 0 ? buttonId : inputId}
|
|
129
|
+
>
|
|
130
|
+
{title}
|
|
131
|
+
</label>
|
|
132
|
+
{renderedLayers.length > 0 && (
|
|
133
|
+
<button
|
|
134
|
+
className={`flex cursor-pointer items-center gap-2`}
|
|
135
|
+
id={buttonId}
|
|
136
|
+
onClick={handleItemClick}
|
|
137
|
+
>
|
|
138
|
+
{isContainerVisible ? <ArrowUp /> : <ArrowDown />}
|
|
139
|
+
</button>
|
|
140
|
+
)}
|
|
141
|
+
</div>
|
|
142
|
+
{isContainerVisible && renderedLayers.length > 0 && (
|
|
143
|
+
<div className={twMerge("pl-6", childContainerClassName)}>
|
|
144
|
+
{renderedLayers}
|
|
145
|
+
</div>
|
|
146
|
+
)}
|
|
147
|
+
</>
|
|
148
|
+
);
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
export default TreeItem;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { default, SelectionType, TreeItemProps } from "./TreeItem";
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { default } from "./LayerTree";
|