diff --git a/src/plugins/maps_dashboards/common/index.ts b/src/plugins/maps_dashboards/common/index.ts
index 3688f479..e131abe8 100644
--- a/src/plugins/maps_dashboards/common/index.ts
+++ b/src/plugins/maps_dashboards/common/index.ts
@@ -19,9 +19,11 @@ export const APP_PATH = {
CREATE_MAP: '/create-map',
};
-export enum LAYER_TYPE {
- BASE_MAP = 'base map',
- CLUSTER_MAP = 'cluster',
- CHOROPLETH_MAP = 'choropleth',
- WEB_MAP_SERVICE = 'wms',
+export enum DASHBOARDS_MAPS_LAYER_TYPE {
+ OPENSEARCH_MAP = 'OpenSearch Map',
}
+
+export const LAYER_VISIBILITY = {
+ NONE: 'none',
+ VISIBLE: 'visible',
+};
diff --git a/src/plugins/maps_dashboards/public/components/add_layer_panel/add_layer_panel.tsx b/src/plugins/maps_dashboards/public/components/add_layer_panel/add_layer_panel.tsx
index e4a43cf1..45fa843f 100644
--- a/src/plugins/maps_dashboards/public/components/add_layer_panel/add_layer_panel.tsx
+++ b/src/plugins/maps_dashboards/public/components/add_layer_panel/add_layer_panel.tsx
@@ -19,16 +19,24 @@ import {
EuiKeyPadMenuItem,
} from '@elastic/eui';
import './add_layer_panel.scss';
-import { LAYER_TYPE } from '../../../common';
+import { DASHBOARDS_MAPS_LAYER_TYPE } from '../../../common';
interface Props {
setIsLayerConfigVisible: Function;
+ setSelectedLayerConfig: Function;
+ addNewLayerToList: Function;
}
-export const AddLayerPanel = ({ setIsLayerConfigVisible }: Props) => {
+export const AddLayerPanel = ({ setIsLayerConfigVisible, setSelectedLayerConfig }: Props) => {
const [isAddNewLayerModalVisible, setIsAddNewLayerModalVisible] = useState(false);
- const availableLayers = Object.values(LAYER_TYPE).map((layerItem, index) => {
+ function onClickAddNewLayer(layerItem: string) {
+ // Will add new layer logic
+ setIsAddNewLayerModalVisible(false);
+ setIsLayerConfigVisible(true);
+ }
+
+ const availableLayers = Object.values(DASHBOARDS_MAPS_LAYER_TYPE).map((layerItem, index) => {
return (
@@ -37,8 +45,7 @@ export const AddLayerPanel = ({ setIsLayerConfigVisible }: Props) => {
size="xxl"
color="primary"
onClick={() => {
- setIsAddNewLayerModalVisible(false);
- setIsLayerConfigVisible(true);
+ onClickAddNewLayer(layerItem);
}}
/>
@@ -88,7 +95,6 @@ export const AddLayerPanel = ({ setIsLayerConfigVisible }: Props) => {
Add layer
-
{
+ const onZoomChange = (value: number[]) => {
+ setSelectedLayerConfig({ ...selectedLayerConfig, zoomRange: value });
+ };
+
+ const onOpacityChange = (e) => {
+ setSelectedLayerConfig({ ...selectedLayerConfig, opacity: Number(e.target.value) });
+ };
+
+ const onNameChange = (e) => {
+ setSelectedLayerConfig({ ...selectedLayerConfig, name: String(e.target.value) });
+ };
+
+ return (
+
+
+
+
+
+
+
+
+
+
+
+ );
+};
diff --git a/src/plugins/maps_dashboards/public/components/layer_config/index.ts b/src/plugins/maps_dashboards/public/components/layer_config/index.ts
index 9f6f2e88..4d043e76 100644
--- a/src/plugins/maps_dashboards/public/components/layer_config/index.ts
+++ b/src/plugins/maps_dashboards/public/components/layer_config/index.ts
@@ -4,3 +4,4 @@
*/
export { LayerConfigPanel } from './layer_config_panel';
+export { BaseMapLayerConfigPanel } from './base_map_layer_config_panel';
diff --git a/src/plugins/maps_dashboards/public/components/layer_config/layer_config_panel.tsx b/src/plugins/maps_dashboards/public/components/layer_config/layer_config_panel.tsx
index c64bef52..f1802d4b 100644
--- a/src/plugins/maps_dashboards/public/components/layer_config/layer_config_panel.tsx
+++ b/src/plugins/maps_dashboards/public/components/layer_config/layer_config_panel.tsx
@@ -10,38 +10,82 @@ import {
EuiFlyoutBody,
EuiFlyoutFooter,
EuiFlyoutHeader,
- EuiCodeBlock,
EuiTitle,
+ EuiFlexItem,
+ EuiButtonEmpty,
+ EuiFlexGroup,
} from '@elastic/eui';
import { ILayerConfig } from '../../model/ILayerConfig';
+import { BaseMapLayerConfigPanel } from './index';
+import { DASHBOARDS_MAPS_LAYER_TYPE } from '../../../common';
interface Props {
setIsLayerConfigVisible: Function;
selectedLayerConfig: ILayerConfig;
+ setSelectedLayerConfig: Function;
+ addNewLayerFunction: Function;
+ updateLayer: Function;
}
-export const LayerConfigPanel = ({ setIsLayerConfigVisible, selectedLayerConfig }: Props) => {
+export const LayerConfigPanel = ({
+ setIsLayerConfigVisible,
+ selectedLayerConfig,
+ setSelectedLayerConfig,
+ updateLayer,
+}: Props) => {
+ const onClose = () => {
+ setIsLayerConfigVisible(false);
+ setSelectedLayerConfig({});
+ };
+ const onUpdate = () => {
+ updateLayer();
+ selectedLayerConfig.update?.();
+ onClose();
+ };
+
return (
- setIsLayerConfigVisible(false)}>
+
{selectedLayerConfig.name}
-
- {JSON.stringify(selectedLayerConfig)}
-
+
+
+
+ Layer settings
+
+
+
+ {selectedLayerConfig.type === DASHBOARDS_MAPS_LAYER_TYPE.OPENSEARCH_MAP && (
+
+ )}
+
+
- setIsLayerConfigVisible(false)}>Close
+
+
+
+ Discard
+
+
+
+
+ Update
+
+
+
);
diff --git a/src/plugins/maps_dashboards/public/components/layer_control_panel/MaplibreLayersPanelControl.tsx b/src/plugins/maps_dashboards/public/components/layer_control_panel/MaplibreLayersPanelControl.tsx
deleted file mode 100644
index 6e276134..00000000
--- a/src/plugins/maps_dashboards/public/components/layer_control_panel/MaplibreLayersPanelControl.tsx
+++ /dev/null
@@ -1,27 +0,0 @@
-/*
- * Copyright OpenSearch Contributors
- * SPDX-License-Identifier: Apache-2.0
- */
-
-import React from 'react';
-import ReactDOM from 'react-dom';
-import { LayerControlPanel } from './layer_control_panel';
-
-// Render layer panel on top of Maplibre
-class MaplibreLayersPanelControl {
- private _container: HTMLElement | undefined;
-
- onAdd(map: any) {
- this._container = document.createElement('div');
- ReactDOM.render(, this._container);
- return this._container;
- }
-
- onRemove() {
- if (this._container && this._container.parentNode) {
- this._container.parentNode.removeChild(this._container);
- }
- }
-}
-
-export { MaplibreLayersPanelControl };
diff --git a/src/plugins/maps_dashboards/public/components/layer_control_panel/index.ts b/src/plugins/maps_dashboards/public/components/layer_control_panel/index.ts
index 00ed4cfb..30cd7379 100644
--- a/src/plugins/maps_dashboards/public/components/layer_control_panel/index.ts
+++ b/src/plugins/maps_dashboards/public/components/layer_control_panel/index.ts
@@ -4,4 +4,3 @@
*/
export { LayerControlPanel } from './layer_control_panel';
-export { MaplibreLayersPanelControl } from './MaplibreLayersPanelControl';
diff --git a/src/plugins/maps_dashboards/public/components/layer_control_panel/layer_control_panel.tsx b/src/plugins/maps_dashboards/public/components/layer_control_panel/layer_control_panel.tsx
index 27a2fba2..3f1c71d9 100644
--- a/src/plugins/maps_dashboards/public/components/layer_control_panel/layer_control_panel.tsx
+++ b/src/plugins/maps_dashboards/public/components/layer_control_panel/layer_control_panel.tsx
@@ -3,7 +3,7 @@
* SPDX-License-Identifier: Apache-2.0
*/
-import React, { useState } from 'react';
+import React, { useEffect, useState } from 'react';
import {
EuiPanel,
EuiTitle,
@@ -15,40 +15,117 @@ import {
EuiButton,
} from '@elastic/eui';
import { I18nProvider } from '@osd/i18n/react';
+import { Map as Maplibre } from 'maplibre-gl';
import './layer_control_panel.scss';
import { AddLayerPanel } from '../add_layer_panel';
import { LayerConfigPanel } from '../layer_config';
-import { LAYER_TYPE } from '../../../common';
import { ILayerConfig } from '../../model/ILayerConfig';
+import { DASHBOARDS_MAPS_LAYER_TYPE, MAP_VECTOR_TILE_URL, LAYER_VISIBILITY } from '../../../common';
-const LayerControlPanel = () => {
+interface MaplibreRef {
+ current: Maplibre | null;
+}
+
+interface Props {
+ maplibreRef: MaplibreRef;
+}
+
+const LayerControlPanel = ({ maplibreRef }: Props) => {
const [isLayerConfigVisible, setIsLayerConfigVisible] = useState(false);
const [isLayerControlVisible, setIsLayerControlVisible] = useState(true);
+
const [selectedLayerConfig, setSelectedLayerConfig] = useState({
iconType: '',
- label: '',
name: '',
type: '',
id: '',
+ zoomRange: [],
+ opacity: 1,
+ visibility: '',
});
- // TODO: replace it once layers model ready
- const demoLayers: ILayerConfig[] = [
+ const [layers, setLayers] = useState([
{
- label: 'Base Map',
iconType: 'visMapRegion',
id: 'example_id_1',
- type: LAYER_TYPE.BASE_MAP,
+ type: DASHBOARDS_MAPS_LAYER_TYPE.OPENSEARCH_MAP,
name: 'Base Map Layer',
+ zoomRange: [0, 12],
+ opacity: 1,
+ visibility: LAYER_VISIBILITY.VISIBLE,
+ update() {
+ const maplibreInstance = maplibreRef.current;
+ if (maplibreInstance) {
+ const baseMapJson = maplibreInstance.getStyle().layers;
+ if (baseMapJson) {
+ baseMapJson.forEach((mbLayer) => {
+ maplibreInstance.setLayerZoomRange(mbLayer.id, this.zoomRange[0], this.zoomRange[1]);
+ // it will catch error when update opacity in symbol type layer, need figure out later
+ if (mbLayer.type === 'symbol') {
+ return;
+ }
+ maplibreInstance.setPaintProperty(
+ mbLayer.id,
+ `${mbLayer.type}-opacity`,
+ this.opacity
+ );
+ maplibreInstance.setLayoutProperty(mbLayer.id, 'visibility', this.visibility);
+ });
+ } else {
+ maplibreInstance.setStyle(MAP_VECTOR_TILE_URL);
+ }
+ }
+ },
+ hide() {
+ const maplibreInstance = maplibreRef.current;
+ if (maplibreInstance) {
+ const baseMapJson = maplibreInstance.getStyle().layers;
+ if (baseMapJson) {
+ baseMapJson.forEach((mbLayer) => {
+ maplibreInstance.setLayoutProperty(mbLayer.id, 'visibility', this.visibility);
+ });
+ }
+ }
+ },
+ remove() {
+ const maplibreInstance = maplibreRef.current;
+ if (maplibreInstance) {
+ const baseMapJson = maplibreInstance.getStyle().layers;
+ if (baseMapJson) {
+ baseMapJson.forEach((mbLayer) => {
+ maplibreInstance.removeLayer(mbLayer.id);
+ });
+ }
+ }
+ },
},
- {
- label: 'Cluster Layer',
- iconType: 'visMapRegion',
- id: 'example_id_2',
- type: LAYER_TYPE.CLUSTER_MAP,
- name: 'Cluster Layer',
- },
- ];
+ ]);
+
+ useEffect(() => {
+ layers.forEach((layer) => {
+ layer.update?.();
+ });
+ }, [layers]);
+
+ const updateLayers = () => {
+ const layersClone = [...layers];
+ const index = layersClone.findIndex((layer) => layer.id === selectedLayerConfig.id);
+ if (index > -1) {
+ layersClone[index] = {
+ ...layersClone[index],
+ ...selectedLayerConfig,
+ };
+ } else {
+ layersClone.push(selectedLayerConfig);
+ }
+ setLayers(layersClone);
+ };
+
+ const removeLayer = (index: number) => {
+ const layersClone = [...layers];
+ layersClone.splice(index, 1);
+ setLayers(layersClone);
+ };
if (isLayerControlVisible) {
return (
@@ -77,66 +154,87 @@ const LayerControlPanel = () => {
/>
-
- {demoLayers.map((layer) => (
-
-
{
- setSelectedLayerConfig(layer);
- if (selectedLayerConfig.id === layer.id && isLayerConfigVisible === false) {
- setIsLayerConfigVisible((visible) => !visible);
- }
- }}
- >
-
- {
+ const isDisabled =
+ isLayerConfigVisible && selectedLayerConfig && selectedLayerConfig.id === layer.id;
+ return (
+
+ {
+ if (!isLayerConfigVisible) {
+ setSelectedLayerConfig(layer);
}
- />
-
-
-
- window.alert('Hidden button clicked')}
- aria-label="Hide or show layer"
- color="text"
- />
-
-
- window.alert('Delete button clicked')}
- aria-label="Delete layer"
- color="text"
+ }}
+ >
+
+ {
+ if (selectedLayerConfig.id === layer.id && !isLayerConfigVisible) {
+ setIsLayerConfigVisible(true);
+ }
+ }}
/>
+
+
+ {
+ if (layer.visibility === LAYER_VISIBILITY.VISIBLE) {
+ layer.visibility = LAYER_VISIBILITY.NONE;
+ } else {
+ layer.visibility = LAYER_VISIBILITY.VISIBLE;
+ }
+ layer.hide(index);
+ }}
+ aria-label="Hide or show layer"
+ color="text"
+ isDisabled={isDisabled}
+ />
+
+
+ {
+ layer.remove(index);
+ removeLayer(index);
+ }}
+ aria-label="Delete layer"
+ color="text"
+ isDisabled={isDisabled}
+ />
+
+
-
-
-
- ))}
+
+
+ );
+ })}
{isLayerConfigVisible && (
)}
-
+
diff --git a/src/plugins/maps_dashboards/public/components/map_container/map_container.scss b/src/plugins/maps_dashboards/public/components/map_container/map_container.scss
new file mode 100644
index 00000000..e73cad5b
--- /dev/null
+++ b/src/plugins/maps_dashboards/public/components/map_container/map_container.scss
@@ -0,0 +1,24 @@
+/*
+ * Copyright OpenSearch Contributors
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+@import "maplibre-gl/dist/maplibre-gl.css";
+
+/* stylelint-disable no-empty-source */
+.map-container {
+ width: 100%;
+ min-height: calc(100vh - 98px);
+}
+
+.maplibregl-ctrl-top-left {
+ left: $euiSizeS;
+ top: $euiSizeS;
+}
+
+.layerControlPanel-container {
+ z-index: 1;
+ position: absolute;
+ left: $euiSizeS;
+ top: $euiSizeS;
+}
diff --git a/src/plugins/maps_dashboards/public/components/map_container/map_container.tsx b/src/plugins/maps_dashboards/public/components/map_container/map_container.tsx
index 42d7eb36..8d53d3e7 100644
--- a/src/plugins/maps_dashboards/public/components/map_container/map_container.tsx
+++ b/src/plugins/maps_dashboards/public/components/map_container/map_container.tsx
@@ -5,31 +5,36 @@
import React, { useEffect, useRef } from 'react';
import { Map as Maplibre, NavigationControl } from 'maplibre-gl';
-import { MaplibreLayersPanelControl } from '../../components/layer_control_panel';
-import '../../index.scss';
+import { LayerControlPanel } from '../layer_control_panel';
+import './map_container.scss';
import { MAP_VECTOR_TILE_URL, MAP_INITIAL_STATE } from '../../../common/index';
export const MapContainer = () => {
+ const maplibreRef = useRef(null);
const mapContainer = useRef(null);
useEffect(() => {
const mapStyle = MAP_VECTOR_TILE_URL;
-
const initialState = MAP_INITIAL_STATE;
- const map = new Maplibre({
+ const maplibre = new Maplibre({
// @ts-ignore
container: mapContainer.current,
- style: `${mapStyle}`,
center: [initialState.lng, initialState.lat],
zoom: initialState.zoom,
- logoPosition: 'bottom-left',
});
- map.addControl(new MaplibreLayersPanelControl(), 'top-left');
- map.addControl(new NavigationControl({ showCompass: false }), 'top-right');
+ maplibre.setStyle(mapStyle);
+ maplibreRef.current = maplibre;
+ maplibre.addControl(new NavigationControl({ showCompass: false }), 'top-right');
}, []);
- // render the map DOM
- return ;
+ return (
+
+ );
};
diff --git a/src/plugins/maps_dashboards/public/index.scss b/src/plugins/maps_dashboards/public/index.scss
index 4288e690..a850c169 100644
--- a/src/plugins/maps_dashboards/public/index.scss
+++ b/src/plugins/maps_dashboards/public/index.scss
@@ -2,16 +2,3 @@
* Copyright OpenSearch Contributors
* SPDX-License-Identifier: Apache-2.0
*/
-
-@import "maplibre-gl/dist/maplibre-gl.css";
-
-/* stylelint-disable no-empty-source */
-.map-container {
- width: 100%;
- min-height: calc(100vh - 98px);
-}
-
-.maplibregl-ctrl-top-left {
- left: $euiSizeS;
- top: $euiSizeS;
-}
diff --git a/src/plugins/maps_dashboards/public/model/ILayerConfig.ts b/src/plugins/maps_dashboards/public/model/ILayerConfig.ts
index e7a80a4a..f9c54749 100644
--- a/src/plugins/maps_dashboards/public/model/ILayerConfig.ts
+++ b/src/plugins/maps_dashboards/public/model/ILayerConfig.ts
@@ -1,9 +1,17 @@
+/*
+ * Copyright OpenSearch Contributors
+ * SPDX-License-Identifier: Apache-2.0
+ */
-//TODO: this is used for UI component development, remove or update it once layer model ready
export interface ILayerConfig {
name: string;
id: string;
type: string;
- label: string;
iconType: string;
+ zoomRange: number[];
+ opacity: number;
+ visibility: string;
+ update: any;
+ remove: any;
+ hide: any;
}