diff --git a/src/app.js b/src/app.js index d1ea00fd..9414e789 100644 --- a/src/app.js +++ b/src/app.js @@ -6,7 +6,7 @@ import { useLayers } from '@context'; import { Sidebar } from '@components/sidebar'; import { ControlPanel } from '@components/control-panel'; import { ComparePanel } from '@components/compare-panel'; -import { MapLegend } from '@components/legend'; +import { MapLegend, AdditionalMapLegend } from '@components/legend'; import { AlertUser } from '@components/alert-user'; import { Config } from '@components/config'; import { Acknowledgements } from "@components/acknowledgements"; @@ -43,6 +43,7 @@ const Content = () => { { (defaultInstanceName != null) && } + ); diff --git a/src/components/legend/additional-legend.js b/src/components/legend/additional-legend.js new file mode 100644 index 00000000..fce01f40 --- /dev/null +++ b/src/components/legend/additional-legend.js @@ -0,0 +1,106 @@ +import React, { useRef, Fragment } from 'react'; +import { Card, Box, Stack, Avatar, Typography } from '@mui/joy'; +import { useLayers } from '@context'; +import Draggable from "react-draggable"; + +/** + * renders the legend for selected additional layers + * + * @constructor + */ +export const AdditionalMapLegend = () => { + // install the selected observation list from the layer context + const { externalLayers, topMostExtLegendIndex, setTopMostExtLegendIndex } = useLayers(); + + // create a reference to avoid the findDOMNode deprecation issue + const nodeRef = useRef(null); + + /** + * handles a click on the legend to make it have focus + */ + const handleClick = (index) => { + // set the new topmost dialog in the stack + setTopMostExtLegendIndex(index); + }; + + // render the legends + return ( + + { + // loop through the external layers and create checkbox selections grouped by the source + externalLayers + // group by the source name + .filter((val, idx, self) => ( + idx === self.findIndex((t)=> ( t['source'] === val['source']) )) + ) + // output sources + .map((layer) => ( + // display legends for all selected external layers + externalLayers + // group by the source name + .filter(item => item.source === layer.source) + // only render visible layers that have a URL to a legend + .filter(item => (item.state.visible && item.params['legendURL'])) + // run through all the layers in each group + .map((sublayer, itemIndex) => ( + + + handleClick(sublayer['row_num']) } + sx={{ + position: 'absolute', + top: 10 + ( 15 * itemIndex ), + right: 75 + ( 25 * itemIndex ), + transition: 'filter 250ms', + padding: '5px', + border: 1, + borderRadius: 'sm', + filter: 'opacity(0.9)', '&:hover': { filter: 'opacity(1.0)' }, + width: '60px', + zIndex: (sublayer['row_num'] === topMostExtLegendIndex) ? 1000 : 999 }}> + + + + + + + { sublayer.source.split(' ').map(word => word.charAt(0)) } { sublayer.row_num } + + + + + + + + )) + )) + } + + ); +}; diff --git a/src/components/legend/index.js b/src/components/legend/index.js index 74075732..f1883c2a 100644 --- a/src/components/legend/index.js +++ b/src/components/legend/index.js @@ -1 +1,2 @@ -export * from './legend'; \ No newline at end of file +export * from './legend'; +export * from './additional-legend'; \ No newline at end of file diff --git a/src/components/legend/legend.js b/src/components/legend/legend.js index 73b49bef..fd21be21 100644 --- a/src/components/legend/legend.js +++ b/src/components/legend/legend.js @@ -136,6 +136,7 @@ export const MapLegend = () => { '&:hover': { filter: 'opacity(1.0)' }, padding: '10px', zIndex: 999, + border: 1, borderRadius: 'sm', visibility: legendVisibilty, diff --git a/src/components/map/external-layers.js b/src/components/map/external-layers.js index 7efa444c..09326eb3 100644 --- a/src/components/map/external-layers.js +++ b/src/components/map/external-layers.js @@ -16,15 +16,7 @@ export const ExternalLayers = () => { externalLayers } = useLayers(); - //const sst_url = "https://coastwatch.noaa.gov/erddap/wms/noaacwBLENDEDsstDNDaily/request"; - //const sst_layer = "noaacwBLENDEDsstDNDaily:analysed_sst"; - - //const sst_url = "https://coastwatch.noaa.gov/erddap/wms/noaacwecnAVHRRmultisatsstEastCoastMonthly/request"; - //const sst_layer = "noaacwecnAVHRRmultisatsstEastCoastMonthly:sst"; - - //const sst_url = "https://coastwatch.noaa.gov/erddap/wms/noaacwecnAVHRRmultisatsstEastCoast3Day/request"; - //const sst_layer = "noaacwecnAVHRRmultisatsstEastCoast3Day:sst"; - + // if there are external layers to render if (externalLayers != null) { return ( @@ -34,10 +26,10 @@ export const ExternalLayers = () => { .map( (layer) => ( ) ) diff --git a/src/components/trays/additional-layers/externalLayerItems.js b/src/components/trays/additional-layers/externalLayerItems.js index 3732185d..c1497005 100644 --- a/src/components/trays/additional-layers/externalLayerItems.js +++ b/src/components/trays/additional-layers/externalLayerItems.js @@ -5,7 +5,7 @@ import { import { KeyboardArrowDown as ExpandIcon } from '@mui/icons-material'; import { useLayers } from "@context/map-context"; import PropTypes from 'prop-types'; -import {ActionButton} from "@components/buttons"; +import { ActionButton } from "@components/buttons"; // set component prop types ExternalLayerItems.propTypes = { @@ -215,7 +215,7 @@ export default function ExternalLayerItems(data) { expanded={ getAccordionState(layer['source']) } onChange={ () => toggleAccordionView(layer['source']) }> - + { layer['source'] } @@ -243,7 +243,7 @@ export default function ExternalLayerItems(data) { { layer['name'] } } + label={ { layer['row_num'] }: { layer['name'] } } onChange={ () => toggleLayerVisibility(layer['name']) } /> diff --git a/src/components/trays/remove/index.js b/src/components/trays/remove/index.js index 9542fbc2..5be34922 100644 --- a/src/components/trays/remove/index.js +++ b/src/components/trays/remove/index.js @@ -7,6 +7,7 @@ import { RemoveAllObservations } from "./remove-observations"; import { RemoveModels } from "./remove-models"; import { ResetStyles } from "./reset-styles"; import { ResetCompare } from "./reset-compare"; +import { RemoveAdditionalLayers } from "./remove-addl-layers"; // get an icon for the tray export const icon = ; @@ -25,5 +26,6 @@ export const trayContents = () => ( + ); diff --git a/src/components/trays/remove/remove-addl-layers.js b/src/components/trays/remove/remove-addl-layers.js new file mode 100644 index 00000000..47eca499 --- /dev/null +++ b/src/components/trays/remove/remove-addl-layers.js @@ -0,0 +1,21 @@ +import React, { Fragment } from 'react'; +import { Button } from '@mui/joy'; +import { useLayers } from "@context"; + +/** + * component that handles the removal of Additional layers. + * + * @returns React.ReactElement + * @constructor + */ +export const RemoveAdditionalLayers = () => { + // get the method to remove the observation items in state + const { removeAdditionalLayers } = useLayers(); + + // render the button + return ( + + + + ); +}; diff --git a/src/context/map-context.js b/src/context/map-context.js index a9dc4505..ca7a54fd 100644 --- a/src/context/map-context.js +++ b/src/context/map-context.js @@ -68,6 +68,9 @@ export const LayersProvider = ({ children }) => { // state to keep track of the obs dialog that has focus const [topMostDialogIndex, setTopMostDialogIndex] = useState(0); + // state to keep track of the obs dialog that has focus + const [topMostExtLegendIndex, setTopMostExtLegendIndex] = useState(0); + /** * this section is for the side-by-side compare mode items * @type {string} @@ -127,6 +130,26 @@ export const LayersProvider = ({ children }) => { } }; + /** + * hides the selected additional layers + * + */ + const removeAdditionalLayers = () => { + // get a copy of the external layers + const newLayers = [...externalLayers]; + + // loop through the layers + newLayers.map(layer => { + // if this later is set to visible + if (layer.state.visible) { + layer.state.visible = false; + } + }); + + // save the altered list + setExternalLayers([...newLayers]); + }; + /** * clears any captured compare selection data and layers * @@ -452,10 +475,13 @@ export const LayersProvider = ({ children }) => { selectedRightLayer, setSelectedRightLayer, sideBySideLayers, setSideBySideLayers, resetCompare, removeSideBySideLayers, - + removeAdditionalLayers, // tracks the dialog that has focus - topMostDialogIndex, setTopMostDialogIndex + topMostDialogIndex, setTopMostDialogIndex, + + // tracks the topmost additional layer legend index + topMostExtLegendIndex, setTopMostExtLegendIndex }} > {children}