@map-colonies/react-components 3.11.0 → 3.12.0
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 +26 -0
- package/dist/cesium-map/layers/imagery.layer.js +9 -5
- package/dist/cesium-map/layers-manager.d.ts +10 -2
- package/dist/cesium-map/layers-manager.js +19 -1
- package/dist/cesium-map/map-legend/MapLegend.css +135 -0
- package/dist/cesium-map/map-legend/MapLegend.d.ts +15 -0
- package/dist/cesium-map/map-legend/MapLegend.js +57 -0
- package/dist/cesium-map/map-legend/MapLegendList.d.ts +13 -0
- package/dist/cesium-map/map-legend/MapLegendList.js +43 -0
- package/dist/cesium-map/map-legend/MapLegendSidebar.d.ts +16 -0
- package/dist/cesium-map/map-legend/MapLegendSidebar.js +20 -0
- package/dist/cesium-map/map-legend/MapLegendToggle.d.ts +7 -0
- package/dist/cesium-map/map-legend/MapLegendToggle.js +20 -0
- package/dist/cesium-map/map-legend/index.d.ts +3 -0
- package/dist/cesium-map/map-legend/index.js +14 -0
- package/dist/cesium-map/map.css +6 -1
- package/dist/cesium-map/map.d.ts +16 -1
- package/dist/cesium-map/map.js +51 -21
- package/dist/cesium-map/settings/settings.css +5 -2
- package/package.json +3 -3
- package/src/lib/cesium-map/layers/imagery.layer.tsx +12 -7
- package/src/lib/cesium-map/layers-manager.ts +35 -2
- package/src/lib/cesium-map/map-legend/MapLegend.css +135 -0
- package/src/lib/cesium-map/map-legend/MapLegend.tsx +91 -0
- package/src/lib/cesium-map/map-legend/MapLegendList.tsx +46 -0
- package/src/lib/cesium-map/map-legend/MapLegendSidebar.tsx +55 -0
- package/src/lib/cesium-map/map-legend/MapLegendToggle.tsx +31 -0
- package/src/lib/cesium-map/map-legend/index.tsx +3 -0
- package/src/lib/cesium-map/map-legend/legends-sidebar.stories.tsx +201 -0
- package/src/lib/cesium-map/map.css +6 -1
- package/src/lib/cesium-map/map.tsx +86 -20
- package/src/lib/cesium-map/settings/settings.css +5 -2
package/dist/cesium-map/map.js
CHANGED
|
@@ -50,6 +50,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
50
50
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
51
51
|
exports.CesiumMap = exports.useCesiumMap = exports.CesiumViewer = void 0;
|
|
52
52
|
var react_1 = __importStar(require("react"));
|
|
53
|
+
var react_dom_1 = require("react-dom");
|
|
53
54
|
var resium_1 = require("resium");
|
|
54
55
|
var cesium_1 = require("cesium");
|
|
55
56
|
var lodash_1 = require("lodash");
|
|
@@ -59,6 +60,7 @@ var projections_1 = require("../utils/projections");
|
|
|
59
60
|
var coordinates_tracker_tool_1 = require("./tools/coordinates-tracker.tool");
|
|
60
61
|
var scale_tracker_tool_1 = require("./tools/scale-tracker.tool");
|
|
61
62
|
var settings_1 = require("./settings/settings");
|
|
63
|
+
var map_legend_1 = require("./map-legend");
|
|
62
64
|
var layers_manager_1 = __importDefault(require("./layers-manager"));
|
|
63
65
|
var map_types_1 = require("./map.types");
|
|
64
66
|
require("./map.css");
|
|
@@ -85,18 +87,20 @@ var useCesiumMap = function () {
|
|
|
85
87
|
};
|
|
86
88
|
exports.useCesiumMap = useCesiumMap;
|
|
87
89
|
var CesiumMap = function (props) {
|
|
88
|
-
var _a, _b, _c, _d, _e, _f, _g, _h;
|
|
90
|
+
var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l, _m, _o;
|
|
89
91
|
var ref = react_1.useRef(null);
|
|
90
|
-
var
|
|
91
|
-
var
|
|
92
|
-
var
|
|
93
|
-
var
|
|
94
|
-
var
|
|
95
|
-
var
|
|
96
|
-
var
|
|
97
|
-
var
|
|
98
|
-
var
|
|
99
|
-
var
|
|
92
|
+
var _p = react_1.useState(), mapViewRef = _p[0], setMapViewRef = _p[1];
|
|
93
|
+
var _q = react_1.useState(), projection = _q[0], setProjection = _q[1];
|
|
94
|
+
var _r = react_1.useState(), showMousePosition = _r[0], setShowMousePosition = _r[1];
|
|
95
|
+
var _s = react_1.useState(), showScale = _s[0], setShowScale = _s[1];
|
|
96
|
+
var _t = react_1.useState(), locale = _t[0], setLocale = _t[1];
|
|
97
|
+
var _u = react_1.useState(), cameraState = _u[0], setCameraState = _u[1];
|
|
98
|
+
var _v = react_1.useState(), sceneModes = _v[0], setSceneModes = _v[1];
|
|
99
|
+
var _w = react_1.useState([]), legendsList = _w[0], setLegendsList = _w[1];
|
|
100
|
+
var _x = react_1.useState(), baseMaps = _x[0], setBaseMaps = _x[1];
|
|
101
|
+
var _y = react_1.useState(false), showImageryMenu = _y[0], setShowImageryMenu = _y[1];
|
|
102
|
+
var _z = react_1.useState(undefined), imageryMenuPosition = _z[0], setImageryMenuPosition = _z[1];
|
|
103
|
+
var _0 = react_1.useState(false), isLegendsSidebarOpen = _0[0], setIsLegendsSidebarOpen = _0[1];
|
|
100
104
|
var viewerProps = __assign({ fullscreenButton: true, timeline: false, animation: false, baseLayerPicker: false, geocoder: false, navigationHelpButton: false, homeButton: false, sceneModePicker: false }, props);
|
|
101
105
|
var getImageryMenuStyle = function (x, y, menuWidth, menuHeight, menuDynamicHeightIncrement) {
|
|
102
106
|
var container = mapViewRef.container;
|
|
@@ -114,7 +118,6 @@ var CesiumMap = function (props) {
|
|
|
114
118
|
var _a;
|
|
115
119
|
if (ref.current) {
|
|
116
120
|
var viewer = ref.current.cesiumElement;
|
|
117
|
-
viewer.layersManager = new layers_manager_1.default(viewer);
|
|
118
121
|
if (props.imageryContextMenu) {
|
|
119
122
|
viewer.screenSpaceEventHandler.setInputAction(function (evt) {
|
|
120
123
|
// console.log('RIGHT click', evt.position);
|
|
@@ -126,6 +129,15 @@ var CesiumMap = function (props) {
|
|
|
126
129
|
}
|
|
127
130
|
setMapViewRef((_a = ref.current) === null || _a === void 0 ? void 0 : _a.cesiumElement);
|
|
128
131
|
}, [ref, props.imageryContextMenu]);
|
|
132
|
+
react_1.useEffect(function () {
|
|
133
|
+
var _a;
|
|
134
|
+
if (mapViewRef) {
|
|
135
|
+
mapViewRef.layersManager = new layers_manager_1.default(mapViewRef, (_a = props.legends) === null || _a === void 0 ? void 0 : _a.mapLegendsExtractor, function () {
|
|
136
|
+
var _a;
|
|
137
|
+
setLegendsList((_a = mapViewRef === null || mapViewRef === void 0 ? void 0 : mapViewRef.layersManager) === null || _a === void 0 ? void 0 : _a.legendsList);
|
|
138
|
+
});
|
|
139
|
+
}
|
|
140
|
+
}, [mapViewRef]);
|
|
129
141
|
react_1.useEffect(function () {
|
|
130
142
|
var _a;
|
|
131
143
|
setSceneModes((_a = props.sceneModes) !== null && _a !== void 0 ? _a : [
|
|
@@ -243,25 +255,43 @@ var CesiumMap = function (props) {
|
|
|
243
255
|
});
|
|
244
256
|
}
|
|
245
257
|
}, [props.zoom, props.center, mapViewRef]);
|
|
246
|
-
|
|
258
|
+
var bindCustomToolsToViewer = react_1.useCallback(function () {
|
|
259
|
+
return (mapViewRef &&
|
|
260
|
+
react_dom_1.createPortal(react_1.default.createElement(react_1.default.Fragment, null,
|
|
261
|
+
react_1.default.createElement(box_1.Box, { className: "sideToolsContainer" },
|
|
262
|
+
react_1.default.createElement(settings_1.CesiumSettings, { sceneModes: sceneModes, baseMaps: baseMaps, locale: locale }),
|
|
263
|
+
react_1.default.createElement(map_legend_1.MapLegendToggle, { onClick: function () { return setIsLegendsSidebarOpen(!isLegendsSidebarOpen); } })),
|
|
264
|
+
react_1.default.createElement(box_1.Box, { className: "toolsContainer" },
|
|
265
|
+
showMousePosition === true ? (react_1.default.createElement(coordinates_tracker_tool_1.CoordinatesTrackerTool, { projection: projection })) : (react_1.default.createElement(react_1.default.Fragment, null)),
|
|
266
|
+
showScale === true ? react_1.default.createElement(scale_tracker_tool_1.ScaleTrackerTool, { locale: locale }) : react_1.default.createElement(react_1.default.Fragment, null))), document.querySelector('.cesium-viewer')));
|
|
267
|
+
}, [
|
|
268
|
+
baseMaps,
|
|
269
|
+
locale,
|
|
270
|
+
mapViewRef,
|
|
271
|
+
projection,
|
|
272
|
+
sceneModes,
|
|
273
|
+
showMousePosition,
|
|
274
|
+
showScale,
|
|
275
|
+
isLegendsSidebarOpen,
|
|
276
|
+
]);
|
|
277
|
+
return (react_1.default.createElement(resium_1.Viewer, __assign({ className: "viewer", full: true, ref: ref }, viewerProps),
|
|
247
278
|
react_1.default.createElement(MapViewProvider, { value: mapViewRef },
|
|
279
|
+
react_1.default.createElement(map_legend_1.MapLegendSidebar, { title: (_a = props.legends) === null || _a === void 0 ? void 0 : _a.title, isOpen: isLegendsSidebarOpen, toggleSidebar: function () {
|
|
280
|
+
return setIsLegendsSidebarOpen(!isLegendsSidebarOpen);
|
|
281
|
+
}, noLegendsText: (_b = props.legends) === null || _b === void 0 ? void 0 : _b.emptyText, legends: (_d = (_c = props.legends) === null || _c === void 0 ? void 0 : _c.legendsList) !== null && _d !== void 0 ? _d : legendsList, actionsTexts: (_e = props.legends) === null || _e === void 0 ? void 0 : _e.actionsTexts }),
|
|
248
282
|
props.children,
|
|
249
|
-
|
|
250
|
-
react_1.default.createElement(settings_1.CesiumSettings, { sceneModes: sceneModes, baseMaps: baseMaps, locale: locale })),
|
|
251
|
-
react_1.default.createElement(box_1.Box, { className: "toolsContainer" },
|
|
252
|
-
showMousePosition === true ? (react_1.default.createElement(coordinates_tracker_tool_1.CoordinatesTrackerTool, { projection: projection })) : (react_1.default.createElement(react_1.default.Fragment, null)),
|
|
253
|
-
showScale === true ? react_1.default.createElement(scale_tracker_tool_1.ScaleTrackerTool, { locale: locale }) : react_1.default.createElement(react_1.default.Fragment, null)),
|
|
283
|
+
bindCustomToolsToViewer(),
|
|
254
284
|
props.imageryContextMenu &&
|
|
255
285
|
showImageryMenu &&
|
|
256
286
|
imageryMenuPosition &&
|
|
257
287
|
react_1.default.cloneElement(props.imageryContextMenu, {
|
|
258
|
-
data: (
|
|
288
|
+
data: (_f = mapViewRef === null || mapViewRef === void 0 ? void 0 : mapViewRef.layersManager) === null || _f === void 0 ? void 0 : _f.findLayerByPOI(imageryMenuPosition.x, imageryMenuPosition.y),
|
|
259
289
|
position: {
|
|
260
290
|
x: imageryMenuPosition.x,
|
|
261
291
|
y: imageryMenuPosition.y,
|
|
262
292
|
},
|
|
263
|
-
style: getImageryMenuStyle(imageryMenuPosition.x, imageryMenuPosition.y, (
|
|
264
|
-
size: (
|
|
293
|
+
style: getImageryMenuStyle(imageryMenuPosition.x, imageryMenuPosition.y, (_h = (_g = props.imageryContextMenuSize) === null || _g === void 0 ? void 0 : _g.width) !== null && _h !== void 0 ? _h : DEFAULT_WIDTH, (_k = (_j = props.imageryContextMenuSize) === null || _j === void 0 ? void 0 : _j.height) !== null && _k !== void 0 ? _k : DEFAULT_HEIGHT, (_m = (_l = props.imageryContextMenuSize) === null || _l === void 0 ? void 0 : _l.dynamicHeightIncrement) !== null && _m !== void 0 ? _m : DEFAULT_DYNAMIC_HEIGHT_INCREMENT),
|
|
294
|
+
size: (_o = props.imageryContextMenuSize) !== null && _o !== void 0 ? _o : {
|
|
265
295
|
height: DEFAULT_HEIGHT,
|
|
266
296
|
width: DEFAULT_WIDTH,
|
|
267
297
|
},
|
|
@@ -3,7 +3,8 @@
|
|
|
3
3
|
gap: 8px;
|
|
4
4
|
}
|
|
5
5
|
|
|
6
|
-
.settingsIconContainer
|
|
6
|
+
.settingsIconContainer,
|
|
7
|
+
.mapLegendToggleContainer {
|
|
7
8
|
fill: #fff;
|
|
8
9
|
width: 40px;
|
|
9
10
|
height: 40px;
|
|
@@ -12,7 +13,8 @@
|
|
|
12
13
|
border-radius: 4px;
|
|
13
14
|
}
|
|
14
15
|
|
|
15
|
-
.settingsIconContainer:hover
|
|
16
|
+
.settingsIconContainer:hover,
|
|
17
|
+
.mapLegendToggleContainer:hover {
|
|
16
18
|
background: #48b;
|
|
17
19
|
border-color: #aef;
|
|
18
20
|
}
|
|
@@ -35,6 +37,7 @@
|
|
|
35
37
|
.settingsDialogPortal .mdc-dialog__container {
|
|
36
38
|
align-items: unset;
|
|
37
39
|
}
|
|
40
|
+
|
|
38
41
|
.settingsDialogPortal .mdc-dialog .mdc-dialog__surface {
|
|
39
42
|
max-height: unset;
|
|
40
43
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@map-colonies/react-components",
|
|
3
|
-
"version": "3.
|
|
3
|
+
"version": "3.12.0",
|
|
4
4
|
"module": "dist/index.js",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"types": "dist/index.d.ts",
|
|
@@ -18,7 +18,7 @@
|
|
|
18
18
|
"dependencies": {
|
|
19
19
|
"@date-io/date-fns": "^1.3.13",
|
|
20
20
|
"@here/quantized-mesh-decoder": "^1.2.8",
|
|
21
|
-
"@map-colonies/react-core": "^3.3.
|
|
21
|
+
"@map-colonies/react-core": "^3.3.3",
|
|
22
22
|
"@material-ui/core": "^4.11.0",
|
|
23
23
|
"@material-ui/icons": "^4.9.1",
|
|
24
24
|
"@material-ui/pickers": "^3.2.10",
|
|
@@ -93,7 +93,7 @@
|
|
|
93
93
|
"jest-enzyme": "^7.1.2",
|
|
94
94
|
"react-test-renderer": "^16.13.1"
|
|
95
95
|
},
|
|
96
|
-
"gitHead": "
|
|
96
|
+
"gitHead": "fc727564af113a9fbfc599f1c7720dce840e8e83",
|
|
97
97
|
"jest": {
|
|
98
98
|
"coverageReporters": [
|
|
99
99
|
"text",
|
|
@@ -18,13 +18,18 @@ export const CesiumImageryLayer: React.FC<RCesiumImageryLayerProps> = (
|
|
|
18
18
|
useLayoutEffect(() => {
|
|
19
19
|
mapViewer.layersManager?.addMetaToLayer(
|
|
20
20
|
meta,
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
21
|
+
/* eslint-disable */
|
|
22
|
+
meta.searchLayerPredicate ??
|
|
23
|
+
((layer: ImageryLayer, idx: number): boolean => {
|
|
24
|
+
if (meta !== undefined) {
|
|
25
|
+
return (
|
|
26
|
+
(layer as any)._imageryProvider._resource._url ===
|
|
27
|
+
meta.options.url
|
|
28
|
+
);
|
|
29
|
+
}
|
|
30
|
+
return false;
|
|
31
|
+
})
|
|
32
|
+
/* eslint-enable */
|
|
28
33
|
);
|
|
29
34
|
}, [meta, mapViewer]);
|
|
30
35
|
|
|
@@ -4,6 +4,7 @@ import {
|
|
|
4
4
|
UrlTemplateImageryProvider,
|
|
5
5
|
WebMapServiceImageryProvider,
|
|
6
6
|
WebMapTileServiceImageryProvider,
|
|
7
|
+
Event,
|
|
7
8
|
} from 'cesium';
|
|
8
9
|
import { get } from 'lodash';
|
|
9
10
|
import { Feature, Point, Polygon } from 'geojson';
|
|
@@ -17,6 +18,7 @@ import {
|
|
|
17
18
|
import { CesiumViewer } from './map';
|
|
18
19
|
import { IBaseMap } from './settings/settings';
|
|
19
20
|
import { pointToGeoJSON } from './tools/geojson/point.geojson';
|
|
21
|
+
import { IMapLegend } from './map-legend';
|
|
20
22
|
|
|
21
23
|
const INC = 1;
|
|
22
24
|
const DEC = -1;
|
|
@@ -48,15 +50,38 @@ export interface IVectorLayer {
|
|
|
48
50
|
url: string;
|
|
49
51
|
}
|
|
50
52
|
|
|
53
|
+
export type LegendExtractor = (
|
|
54
|
+
layers: (any & {
|
|
55
|
+
meta: any;
|
|
56
|
+
})[]
|
|
57
|
+
) => IMapLegend[];
|
|
51
58
|
class LayerManager {
|
|
52
59
|
public mapViewer: CesiumViewer;
|
|
53
60
|
|
|
54
61
|
private readonly layers: ICesiumImageryLayer[];
|
|
55
|
-
|
|
56
|
-
public
|
|
62
|
+
public legendsList: IMapLegend[];
|
|
63
|
+
public layerUpdated: Event;
|
|
64
|
+
private readonly legendsExtractor?: LegendExtractor;
|
|
65
|
+
|
|
66
|
+
public constructor(
|
|
67
|
+
mapViewer: CesiumViewer,
|
|
68
|
+
legendsExtractor?: LegendExtractor,
|
|
69
|
+
onLayersUpdate?: () => void
|
|
70
|
+
) {
|
|
57
71
|
this.mapViewer = mapViewer;
|
|
58
72
|
// eslint-disable-next-line
|
|
59
73
|
this.layers = (this.mapViewer.imageryLayers as any)._layers;
|
|
74
|
+
this.legendsList = [];
|
|
75
|
+
this.legendsExtractor = legendsExtractor;
|
|
76
|
+
this.layerUpdated = new Event();
|
|
77
|
+
if (onLayersUpdate) {
|
|
78
|
+
this.layerUpdated.addEventListener(onLayersUpdate, this);
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
this.mapViewer.imageryLayers.layerRemoved.addEventListener(() => {
|
|
82
|
+
this.setLegends();
|
|
83
|
+
this.layerUpdated.raiseEvent();
|
|
84
|
+
});
|
|
60
85
|
}
|
|
61
86
|
|
|
62
87
|
/* eslint-disable */
|
|
@@ -67,6 +92,8 @@ class LayerManager {
|
|
|
67
92
|
const layer = this.layers.find(layerPredicate);
|
|
68
93
|
if (layer) {
|
|
69
94
|
layer.meta = meta;
|
|
95
|
+
this.setLegends();
|
|
96
|
+
this.layerUpdated.raiseEvent();
|
|
70
97
|
}
|
|
71
98
|
}
|
|
72
99
|
/* eslint-enable */
|
|
@@ -287,6 +314,12 @@ class LayerManager {
|
|
|
287
314
|
});
|
|
288
315
|
}
|
|
289
316
|
|
|
317
|
+
private setLegends(): void {
|
|
318
|
+
if (typeof this.legendsExtractor !== 'undefined') {
|
|
319
|
+
this.legendsList = this.legendsExtractor(this.layers);
|
|
320
|
+
}
|
|
321
|
+
}
|
|
322
|
+
|
|
290
323
|
private getBaseLayersCount(): number {
|
|
291
324
|
const baseLayers = this.layers.filter((layer) => {
|
|
292
325
|
const parentId = get(layer.meta, 'parentBasetMapId') as string;
|
|
@@ -0,0 +1,135 @@
|
|
|
1
|
+
.mapLegendSidebarContainer {
|
|
2
|
+
width: 340px;
|
|
3
|
+
height: 100%;
|
|
4
|
+
position: relative;
|
|
5
|
+
overflow-y: auto;
|
|
6
|
+
overflow-x: hidden;
|
|
7
|
+
font-size: 16px;
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
/* Disable drawer animation */
|
|
11
|
+
|
|
12
|
+
.mapLegendSidebarContainer.mdc-drawer--animate {
|
|
13
|
+
transform: translateX(0) !important;
|
|
14
|
+
transition: none !important;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
.mapLegendSidebarContainer.mdc-drawer--dismissible {
|
|
18
|
+
position: relative;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
div.MuiPaper-root {
|
|
22
|
+
transition: none !important;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
.mapLegendCloseBtn {
|
|
26
|
+
position: absolute;
|
|
27
|
+
top: 0;
|
|
28
|
+
right: 0;
|
|
29
|
+
cursor: pointer;
|
|
30
|
+
width: 50px;
|
|
31
|
+
height: 50px;
|
|
32
|
+
padding: 20px;
|
|
33
|
+
font-size: 24px;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
body[dir='rtl'] .mapLegendCloseBtn {
|
|
37
|
+
right: unset;
|
|
38
|
+
left: 0;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
.mapLegendToggleContainer {
|
|
42
|
+
display: flex;
|
|
43
|
+
align-items: center;
|
|
44
|
+
justify-content: center;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
.mapLegendIcon {
|
|
48
|
+
width: 2.5rem;
|
|
49
|
+
height: 2.5rem;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
.sidebarHeaderContainer {
|
|
53
|
+
text-align: center;
|
|
54
|
+
margin-top: 40px;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
.sidebarTitle {
|
|
58
|
+
font-size: 1.5rem;
|
|
59
|
+
font-weight: bold;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
.mapLegend {
|
|
63
|
+
display: flex;
|
|
64
|
+
flex-direction: column;
|
|
65
|
+
align-items: center;
|
|
66
|
+
justify-content: center;
|
|
67
|
+
gap: 4px;
|
|
68
|
+
padding: 10px 14px;
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
.layerNameContainer {
|
|
72
|
+
width: 90%;
|
|
73
|
+
display: flex;
|
|
74
|
+
justify-content: center;
|
|
75
|
+
align-items: center;
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
.layerName {
|
|
79
|
+
overflow: hidden;
|
|
80
|
+
text-overflow: ellipsis;
|
|
81
|
+
white-space: nowrap;
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
.legendImg {
|
|
85
|
+
width: 90%;
|
|
86
|
+
height: 150px;
|
|
87
|
+
border: 1px solid black;
|
|
88
|
+
border-radius: 5px;
|
|
89
|
+
object-fit: cover;
|
|
90
|
+
cursor: pointer;
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
.legendActionsContainer {
|
|
94
|
+
display: flex;
|
|
95
|
+
flex-direction: row;
|
|
96
|
+
align-self: center;
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
.legendAction:not(:last-child)::after {
|
|
100
|
+
content: '|';
|
|
101
|
+
margin: 0px 4px;
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
.mapLegendsList {
|
|
105
|
+
display: flex;
|
|
106
|
+
flex-direction: column;
|
|
107
|
+
padding: 20px 0px;
|
|
108
|
+
width: 100%;
|
|
109
|
+
height: 90%;
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
.legendAction {
|
|
113
|
+
text-decoration: underline;
|
|
114
|
+
cursor: pointer;
|
|
115
|
+
font-size: 1.1rem;
|
|
116
|
+
transition: all 0.1s ease-in-out;
|
|
117
|
+
width: max-content;
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
.legendAction:hover {
|
|
121
|
+
filter: brightness(1.2);
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
.noLegendsContainer {
|
|
125
|
+
width: 100%;
|
|
126
|
+
height: 100%;
|
|
127
|
+
display: flex;
|
|
128
|
+
flex-direction: column;
|
|
129
|
+
justify-content: center;
|
|
130
|
+
align-items: center;
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
.noLegendsMsg {
|
|
134
|
+
text-align: center;
|
|
135
|
+
}
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
import { Tooltip } from '@map-colonies/react-core';
|
|
2
|
+
import React, { useCallback } from 'react';
|
|
3
|
+
import { Box } from '../../box';
|
|
4
|
+
import './MapLegend.css';
|
|
5
|
+
|
|
6
|
+
export interface IMapLegend {
|
|
7
|
+
layer?: string;
|
|
8
|
+
legendDoc?: string;
|
|
9
|
+
legendImg?: string;
|
|
10
|
+
legend?: Record<string, unknown>[];
|
|
11
|
+
}
|
|
12
|
+
interface MapLegendProps {
|
|
13
|
+
legend: IMapLegend;
|
|
14
|
+
docText?: string;
|
|
15
|
+
imgText?: string;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
export const MapLegend: React.FC<MapLegendProps> = ({
|
|
19
|
+
legend: { legendImg, legendDoc, layer },
|
|
20
|
+
docText,
|
|
21
|
+
imgText,
|
|
22
|
+
}) => {
|
|
23
|
+
const handleLegendImgOpen = useCallback(() => {
|
|
24
|
+
// Open image in a new tab.
|
|
25
|
+
window.open(legendImg, '_blank');
|
|
26
|
+
}, [legendImg]);
|
|
27
|
+
|
|
28
|
+
const handleLegendDocOpen = useCallback(() => {
|
|
29
|
+
// Open doc in a new tab.
|
|
30
|
+
window.open(legendDoc, '_blank');
|
|
31
|
+
}, [legendDoc]);
|
|
32
|
+
|
|
33
|
+
const renderLayerName = useCallback(() => {
|
|
34
|
+
const MAX_LAYER_NAME_LENGTH = 15;
|
|
35
|
+
|
|
36
|
+
const layerNameContainer = (
|
|
37
|
+
<Box className="layerNameContainer">
|
|
38
|
+
<h3
|
|
39
|
+
style={{ maxWidth: `${MAX_LAYER_NAME_LENGTH}ch` }}
|
|
40
|
+
className="layerName"
|
|
41
|
+
>
|
|
42
|
+
{layer}
|
|
43
|
+
</h3>
|
|
44
|
+
</Box>
|
|
45
|
+
);
|
|
46
|
+
|
|
47
|
+
if ((layer ?? '').length > MAX_LAYER_NAME_LENGTH) {
|
|
48
|
+
return <Tooltip content={layer}>{layerNameContainer}</Tooltip>;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
return layerNameContainer;
|
|
52
|
+
}, [layer]);
|
|
53
|
+
|
|
54
|
+
const renderLinks = useCallback(() => {
|
|
55
|
+
return [
|
|
56
|
+
typeof legendImg === 'string' && (
|
|
57
|
+
<a
|
|
58
|
+
className="legendAction"
|
|
59
|
+
href={legendImg}
|
|
60
|
+
target="_blank"
|
|
61
|
+
referrerPolicy="noreferrer"
|
|
62
|
+
>
|
|
63
|
+
{imgText}
|
|
64
|
+
</a>
|
|
65
|
+
),
|
|
66
|
+
typeof legendDoc === 'string' && (
|
|
67
|
+
<a
|
|
68
|
+
className="legendAction"
|
|
69
|
+
href={legendDoc}
|
|
70
|
+
target="_blank"
|
|
71
|
+
referrerPolicy="noreferrer"
|
|
72
|
+
>
|
|
73
|
+
{docText}
|
|
74
|
+
</a>
|
|
75
|
+
),
|
|
76
|
+
];
|
|
77
|
+
}, [legendImg, imgText, legendDoc, docText]);
|
|
78
|
+
|
|
79
|
+
return (
|
|
80
|
+
<Box className="mapLegend">
|
|
81
|
+
{renderLayerName()}
|
|
82
|
+
<img
|
|
83
|
+
alt="Map Legend"
|
|
84
|
+
className="legendImg"
|
|
85
|
+
src={legendImg}
|
|
86
|
+
onClick={handleLegendImgOpen}
|
|
87
|
+
/>
|
|
88
|
+
<Box className="legendActionsContainer">{renderLinks()}</Box>
|
|
89
|
+
</Box>
|
|
90
|
+
);
|
|
91
|
+
};
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
import React, { useCallback } from 'react';
|
|
2
|
+
import { Box } from '../../box';
|
|
3
|
+
import { IMapLegend, MapLegend } from './MapLegend';
|
|
4
|
+
import './MapLegend.css';
|
|
5
|
+
|
|
6
|
+
interface MapLegendListProps {
|
|
7
|
+
legends: IMapLegend[];
|
|
8
|
+
actionsTexts: {
|
|
9
|
+
docText: string;
|
|
10
|
+
imgText: string;
|
|
11
|
+
};
|
|
12
|
+
noLegendsText: string;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
export const MapLegendList: React.FC<MapLegendListProps> = ({
|
|
16
|
+
legends,
|
|
17
|
+
actionsTexts: { docText, imgText },
|
|
18
|
+
noLegendsText,
|
|
19
|
+
}) => {
|
|
20
|
+
const handleNoLegends = useCallback(() => {
|
|
21
|
+
return (
|
|
22
|
+
<Box className="noLegendsContainer">
|
|
23
|
+
<h2 className="noLegendsMsg">{noLegendsText}</h2>
|
|
24
|
+
</Box>
|
|
25
|
+
);
|
|
26
|
+
}, [noLegendsText]);
|
|
27
|
+
|
|
28
|
+
const renderList = useCallback(() => {
|
|
29
|
+
if (!legends.length) {
|
|
30
|
+
return handleNoLegends();
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
return legends.map((legend, i) => {
|
|
34
|
+
return (
|
|
35
|
+
<MapLegend
|
|
36
|
+
key={`${legend.layer as string}_${i}`}
|
|
37
|
+
legend={legend}
|
|
38
|
+
docText={docText}
|
|
39
|
+
imgText={imgText}
|
|
40
|
+
/>
|
|
41
|
+
);
|
|
42
|
+
});
|
|
43
|
+
}, [legends]);
|
|
44
|
+
|
|
45
|
+
return <Box className="mapLegendsList">{renderList()}</Box>;
|
|
46
|
+
};
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
import {
|
|
2
|
+
Icon,
|
|
3
|
+
Drawer,
|
|
4
|
+
DrawerHeader,
|
|
5
|
+
DrawerTitle,
|
|
6
|
+
DrawerContent,
|
|
7
|
+
} from '@map-colonies/react-core';
|
|
8
|
+
import React from 'react';
|
|
9
|
+
import { Box } from '../../box';
|
|
10
|
+
import { IMapLegend } from './MapLegend';
|
|
11
|
+
import './MapLegend.css';
|
|
12
|
+
import { MapLegendList } from './MapLegendList';
|
|
13
|
+
|
|
14
|
+
interface MapLegendSidebarProps {
|
|
15
|
+
isOpen: boolean;
|
|
16
|
+
toggleSidebar: () => void;
|
|
17
|
+
title?: string;
|
|
18
|
+
noLegendsText?: string;
|
|
19
|
+
legends?: IMapLegend[];
|
|
20
|
+
actionsTexts?: { docText: string; imgText: string };
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
export const MapLegendSidebar: React.FC<MapLegendSidebarProps> = ({
|
|
24
|
+
isOpen,
|
|
25
|
+
toggleSidebar,
|
|
26
|
+
title = 'Map Legends',
|
|
27
|
+
noLegendsText = 'No legends to display...',
|
|
28
|
+
legends = [],
|
|
29
|
+
actionsTexts = { docText: 'Docs', imgText: 'View Image' },
|
|
30
|
+
}) => {
|
|
31
|
+
return isOpen ? (
|
|
32
|
+
<Drawer
|
|
33
|
+
className="mapLegendSidebarContainer"
|
|
34
|
+
modal={false}
|
|
35
|
+
dismissible={true}
|
|
36
|
+
open={isOpen}
|
|
37
|
+
>
|
|
38
|
+
<DrawerHeader className="sidebarHeaderContainer">
|
|
39
|
+
<DrawerTitle className="sidebarTitle">{title}</DrawerTitle>
|
|
40
|
+
</DrawerHeader>
|
|
41
|
+
<DrawerContent className="sidebarContent">
|
|
42
|
+
<Icon
|
|
43
|
+
onClick={toggleSidebar}
|
|
44
|
+
className="mapLegendCloseBtn"
|
|
45
|
+
icon={{ icon: 'close', size: 'small' }}
|
|
46
|
+
/>
|
|
47
|
+
<MapLegendList
|
|
48
|
+
noLegendsText={noLegendsText}
|
|
49
|
+
legends={legends}
|
|
50
|
+
actionsTexts={actionsTexts}
|
|
51
|
+
/>
|
|
52
|
+
</DrawerContent>
|
|
53
|
+
</Drawer>
|
|
54
|
+
) : null;
|
|
55
|
+
};
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import { Icon } from '@map-colonies/react-core';
|
|
3
|
+
import { Box } from '../../box';
|
|
4
|
+
import './MapLegend.css';
|
|
5
|
+
|
|
6
|
+
interface MapLegendProps {
|
|
7
|
+
onClick: () => void;
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
const legendIcon = (
|
|
11
|
+
<svg
|
|
12
|
+
xmlns="http://www.w3.org/2000/svg"
|
|
13
|
+
enable-background="new 0 0 24 24"
|
|
14
|
+
height="24"
|
|
15
|
+
width="24"
|
|
16
|
+
viewBox="0 0 612 612"
|
|
17
|
+
>
|
|
18
|
+
<g xmlns="http://www.w3.org/2000/svg">
|
|
19
|
+
<path d="M322.4,173.9l-129,16.2l-4.6,21.4l25.3,4.7c16.5,3.9,19.8,9.9,16.2,26.4l-41.5,195.3c-10.9,50.5,5.9,74.3,45.5,74.3 c30.7,0,66.3-14.2,82.5-33.6l4.9-23.4c-11.3,9.9-27.7,13.9-38.6,13.9c-15.5,0-21.1-10.9-17.1-30L322.4,173.9z" />
|
|
20
|
+
<circle cx="270.1" cy="56.3" r="56.3" />
|
|
21
|
+
</g>
|
|
22
|
+
</svg>
|
|
23
|
+
);
|
|
24
|
+
|
|
25
|
+
export const MapLegendToggle: React.FC<MapLegendProps> = ({ onClick }) => {
|
|
26
|
+
return (
|
|
27
|
+
<Box onClick={onClick} className="mapLegendToggleContainer">
|
|
28
|
+
<Icon icon={legendIcon} className="mapLegendIcon" />
|
|
29
|
+
</Box>
|
|
30
|
+
);
|
|
31
|
+
};
|