diff --git a/src/app.js b/src/app.js index b101fb59..74371385 100644 --- a/src/app.js +++ b/src/app.js @@ -23,8 +23,11 @@ const Content = () => { { // for each observation selected - selectedObservations.map (function (obs) { - // render the observation + selectedObservations.map (function (obs, idx) { + // add in an index used to cascade the dialogs + obs['index']=idx; + + // render the observation draggable dialog return ; }) } diff --git a/src/components/control-panel/control-panel.js b/src/components/control-panel/control-panel.js index ae6d7cf2..b7d6d140 100644 --- a/src/components/control-panel/control-panel.js +++ b/src/components/control-panel/control-panel.js @@ -277,7 +277,7 @@ export const ControlPanel = () => { '&:hover': { filter: 'opacity(1.0)' }, height: 'auto', width: '300px', - zIndex: 401, + zIndex: 410, borderRadius: 'sm', }} > diff --git a/src/components/dialog/base-floating-dialog.js b/src/components/dialog/base-floating-dialog.js index 47554172..41b09dc3 100644 --- a/src/components/dialog/base-floating-dialog.js +++ b/src/components/dialog/base-floating-dialog.js @@ -15,6 +15,7 @@ import CloseOutlinedIcon from '@mui/icons-material/CloseOutlined'; // define the properties of this component's input BaseFloatingDialog.propTypes = { title: PropTypes.string, + index: PropTypes.any, dialogObject: PropTypes.any, dataKey: PropTypes.any, dataList: PropTypes.any, @@ -27,13 +28,14 @@ BaseFloatingDialog.propTypes = { * Note: this component * * @param title - the name of the dialog: string + * @param index - the index of the data * @param dialogObject the object to render in the dialog: {JSX.Element} * @param dataKey - the key to the data list elements in state: string * @param dataList - a data list in state: array * @param setDataList - method to update a data list in state: function * @param map - a reference to the map state: object */ -export default function BaseFloatingDialog({ title, dialogObject, dataKey, dataList, setDataList, map} ) { +export default function BaseFloatingDialog({ title, index, dialogObject, dataKey, dataList, setDataList, map} ) { /** * the close dialog handler */ @@ -62,8 +64,9 @@ export default function BaseFloatingDialog({ title, dialogObject, dataKey, dataL TransitionComponent={ Transition } disableEnforceFocus style={{ pointerEvents: 'none' }} - PaperProps={{ sx: { width: 750, height: 465, pointerEvents: 'auto' } }} - sx={{ zIndex: 402, width: 750, height: 465, '.MuiBackdrop-root': { backgroundColor: 'transparent' }}} + PaperProps={{ sx: { width: 800, height: 465, pointerEvents: 'auto' } }} + sx={{ zIndex: 405, width: 800, height: 465, '.MuiBackdrop-root': { backgroundColor: 'transparent' }, + left: index * 20, top: 20 + index * 43 }} > { title } @@ -73,7 +76,7 @@ export default function BaseFloatingDialog({ title, dialogObject, dataKey, dataL - { dialogObject } + { dialogObject } ); @@ -87,15 +90,19 @@ export default function BaseFloatingDialog({ title, dialogObject, dataKey, dataL * @constructor */ function PaperComponent(props) { + // create a reference to avoid the findDOMNode deprecation issue + const nodeRef = React.useRef(null); + + // render the component return ( - - + + ); } /** -* This creates an animated transition for the dialog that pops up + * This creates an animated transition for the dialog that pops up * * @type {React.ForwardRefExoticComponent & React.RefAttributes>} */ diff --git a/src/components/dialog/observation-chart.js b/src/components/dialog/observation-chart.js index 4a65b8e0..55e6dcb7 100644 --- a/src/components/dialog/observation-chart.js +++ b/src/components/dialog/observation-chart.js @@ -2,6 +2,7 @@ import React from 'react'; import axios from 'axios'; import { LineChart, Line, XAxis, YAxis, CartesianGrid, Legend, ResponsiveContainer, Tooltip } from 'recharts'; import { useQuery } from '@tanstack/react-query'; +import { useSettings } from "@context"; /** * renders the observations as a chart @@ -57,6 +58,9 @@ function getObsChartData(url) { * @constructor */ function CreateObsChart(url) { + // get the settings for the Y-axis min/max values + const { obsChartY } = useSettings(); + // call to get the data. expect back some information too const { status, data, error } = getObsChartData(url.url); @@ -66,12 +70,12 @@ function CreateObsChart(url) { { status === 'pending' ? (
Loading...
) : status === 'error' ? ( -
Error: {error.message}
+
Error: { error.message }
) : ( - + - - + + diff --git a/src/components/dialog/observation-dialog.js b/src/components/dialog/observation-dialog.js index 82d6f008..76f96412 100644 --- a/src/components/dialog/observation-dialog.js +++ b/src/components/dialog/observation-dialog.js @@ -1,6 +1,6 @@ -import React, {Fragment} from 'react'; +import React, { Fragment } from 'react'; import BaseFloatingDialog from "@dialog/base-floating-dialog"; -import {useLayers} from "@context"; +import { useLayers } from "@context"; import ObservationChart from "@dialog/observation-chart"; /** @@ -23,7 +23,7 @@ export const ObservationDialog = (obs_data) => { // create the chart return ( - + ); }; @@ -31,7 +31,8 @@ export const ObservationDialog = (obs_data) => { // create a data object for the base dialog to use to render const floaterArgs = { title: obs_data.obs['location_name'], - dialogObject: {...graphObj(obs_data.obs['csvurl'])}, + index: obs_data.obs['index'], + dialogObject: { ...graphObj(obs_data.obs['csvurl']) }, dataKey: obs_data.obs['id'], dataList: selectedObservations, setDataList: setSelectedObservations, @@ -41,7 +42,7 @@ export const ObservationDialog = (obs_data) => { // render the dialog return ( - + ); }; \ No newline at end of file diff --git a/src/components/legend/legend.js b/src/components/legend/legend.js index 86341efc..a9235eea 100644 --- a/src/components/legend/legend.js +++ b/src/components/legend/legend.js @@ -64,7 +64,7 @@ export const MapLegend = () => { height: 'auto', width: '100px', padding: '10px', - zIndex: 401, + zIndex: 410, borderRadius: 'sm', visibility: legendVisibilty, }} diff --git a/src/components/map/default-layers.js b/src/components/map/default-layers.js index 55bff8db..191ff0dc 100644 --- a/src/components/map/default-layers.js +++ b/src/components/map/default-layers.js @@ -29,7 +29,8 @@ export const DefaultLayers = () => { const { defaultModelLayers, setDefaultModelLayers, - setSelectedObservations + setSelectedObservations, + setShowShareComment } = useLayers(); const obsPointToLayer = ((feature, latlng) => { @@ -71,7 +72,6 @@ export const DefaultLayers = () => { layer.on("mouseout", function () { this.closePopup(); }); - layer.on("click", function (e) { // this id is used to remove a selected observation from the selectedObservations list when the dialog is closed feature.properties.id = feature.properties.station_name; @@ -145,8 +145,13 @@ export const DefaultLayers = () => { "&typeName=" + obsLayer.layers; const {data} = await axios.get(obs_url); + + // save the observation data setObsData(data); + // turn on the show comment state + setShowShareComment(true); + // update the selected observations specified on the share link addSharedObservations(map, shared_params['obs'], setSelectedObservations); } diff --git a/src/components/share/buildlink.js b/src/components/share/buildlink.js index 9f75dcb2..62e79a6a 100644 --- a/src/components/share/buildlink.js +++ b/src/components/share/buildlink.js @@ -22,6 +22,7 @@ export const BuildLink = () => { */ const createLink = (comment) => { // get the list of selected layers from state + // this forces the group at the top to be the reproduced in the share line const run_id = defaultModelLayers // get all the distinct groups .filter((val, idx, self) => diff --git a/src/components/share/share-comment.js b/src/components/share/share-comment.js index 109b1d87..2dcb1bc4 100644 --- a/src/components/share/share-comment.js +++ b/src/components/share/share-comment.js @@ -1,6 +1,8 @@ import React, { Fragment } from 'react'; -import { Typography, Card } from '@mui/joy'; -import { parseSharedURL } from "@utils/map-utils"; +import { Typography, Card, IconButton } from '@mui/joy'; +import { parseSharedURL} from "@utils/map-utils"; +import CloseOutlinedIcon from '@mui/icons-material/CloseOutlined'; +import { useLayers } from "@context"; /** * renders the shared content on the app as defined in the query string @@ -9,14 +11,28 @@ import { parseSharedURL } from "@utils/map-utils"; * @constructor */ export const ShareComment = () => { + // get the show shared comment state + const { showShareComment, setShowShareComment } = useLayers(); + // parse the hash of the sharing URL const shared_params = parseSharedURL(); + /** + * the close dialog handler + */ + const handleClose = () => { + setShowShareComment(false); + }; + // if there was a comment, display it - if ( shared_params['comment'] !== '') { + if ( shared_params['comment'] !== '' && showShareComment ) { return ( Share comment: { shared_params['comment']} + + + + ); } diff --git a/src/components/share/share.js b/src/components/share/share.js index 79438539..c1790556 100644 --- a/src/components/share/share.js +++ b/src/components/share/share.js @@ -24,7 +24,7 @@ export const Share = () => { height: 50, width: 80, padding: '0px', - zIndex: 401, + zIndex: 410, borderRadius: 'sm' }}> { position: 'absolute', top: 0, left: 0, height: '100vh', - zIndex: 401, + zIndex: 410, maxWidth: '68px', overflow: 'hidden', p: 0, diff --git a/src/components/sidebar/tray.js b/src/components/sidebar/tray.js index 2f43f896..d9fff7e6 100644 --- a/src/components/sidebar/tray.js +++ b/src/components/sidebar/tray.js @@ -17,7 +17,7 @@ export const Tray = ({ active, Contents, title, closeHandler }) => { transition: 'transform 250ms', height: '100vh', width: TRAY_WIDTH, - zIndex: 401, + zIndex: 410, filter: 'drop-shadow(0 0 8px rgba(0, 0, 0, 0.2))', overflowX: 'hidden', overflowY: 'auto', diff --git a/src/components/trays/help-about/helpAboutTray.js b/src/components/trays/help-about/helpAboutTray.js index 02023bcf..a4352fe2 100644 --- a/src/components/trays/help-about/helpAboutTray.js +++ b/src/components/trays/help-about/helpAboutTray.js @@ -15,15 +15,15 @@ export const HelpAboutTray = () => { return ( - + About - { setIndex(expanded ? 0 : null); }}> - Application version - Version: {process.env.REACT_APP_VERSION} + { setIndex(expanded ? 0 : null); }}> + Application version + Version: { process.env.REACT_APP_VERSION } - { setIndex(expanded ? 1 : null); }}> - Application description + { setIndex(expanded ? 1 : null); }}> + Application description Add some content here... @@ -31,21 +31,21 @@ export const HelpAboutTray = () => { - + FAQs - { setIndex(expanded ? 3 : null); }}> - What FAQs should we put in here? + { setIndex(expanded ? 3 : null); }}> + What FAQs should we put in here? What sort of things should we be putting in the FAQs? Below are some examples... - { setIndex(expanded ? 4 : null); }}> - How do I capture a screenshot ? + { setIndex(expanded ? 4 : null); }}> + How do I capture a screenshot? - { setSubIndex(expanded ? 0 : null); }}> - Edge + { setSubIndex(expanded ? 0 : null); }}> + Edge
  • Right click the browser surface.
  • @@ -56,8 +56,8 @@ export const HelpAboutTray = () => { - { setSubIndex(expanded ? 1 : null); }}> - Firefox + { setSubIndex(expanded ? 1 : null); }}> + Firefox
    • Right click the browser surface
    • @@ -68,8 +68,8 @@ export const HelpAboutTray = () => { - { setSubIndex(expanded ? 2 : null); }}> - Chrome + { setSubIndex(expanded ? 2 : null); }}> + Chrome
      • Install and activate the Chrome Full Page Screen Capture browser extension.
      • @@ -80,8 +80,8 @@ export const HelpAboutTray = () => { - { setSubIndex(expanded ? 3 : null); }}> - Safari + { setSubIndex(expanded ? 3 : null); }}> + Safari
        • Install and activate Awesome Screenshot
        • @@ -97,48 +97,48 @@ export const HelpAboutTray = () => { - { setIndex(expanded ? 5 : null); }}> - What are some features of this application? + { setIndex(expanded ? 5 : null); }}> + What are some features of this application? Add some content here... - { setIndex(expanded ? 6 : null); }}> - How do I add/remove Layers on the map? + { setIndex(expanded ? 6 : null); }}> + How do I add/remove Layers on the map? Add some content here... - { setIndex(expanded ? 7 : null); }}> - How do I move through synoptic cycles? + { setIndex(expanded ? 7 : null); }}> + How do I move through synoptic cycles? Add some content here... - { setIndex(expanded ? 8 : null); }}> - What do the icons on the left mean? + { setIndex(expanded ? 8 : null); }}> + What do the icons on the left mean? Add some content here... - { setIndex(expanded ? 9 : null); }}> - What are some user settings? + { setIndex(expanded ? 9 : null); }}> + What are some user settings? Add some content here... - { setIndex(expanded ? 10 : null); }}> - How do I change the base map? + { setIndex(expanded ? 10 : null); }}> + How do I change the base map? Add some content here... - { setIndex(expanded ? 11 : null); }}> - How do I view observation data? + { setIndex(expanded ? 11 : null); }}> + How do I view observation data? Add some content here... - { setIndex(expanded ? 12 : null); }}> - How do I show/hide layers? + { setIndex(expanded ? 12 : null); }}> + How do I show/hide layers? Add some content here... - { setIndex(expanded ? 13 : null); }}> - How do I reorder layers on the map? + { setIndex(expanded ? 13 : null); }}> + How do I reorder layers on the map? Add some content here... diff --git a/src/components/trays/settings/chart-yaxis.js b/src/components/trays/settings/chart-yaxis.js new file mode 100644 index 00000000..18c914c4 --- /dev/null +++ b/src/components/trays/settings/chart-yaxis.js @@ -0,0 +1,77 @@ +import React, { Fragment } from 'react'; +import { IconButton, Stack, Typography, Slider, Box } from '@mui/joy'; +import { useSettings } from '@context'; +import LineAxisRoundedIcon from '@mui/icons-material/LineAxisRounded'; + +/** + * renders the observation chart Y-axis slider + * + * @returns {JSX.Element} + * @constructor + */ +export const ObsChartYAxis = () => { + // get the settings for the obs chart Y-axis min/max values + const { obsChartY, setObsChartY } = useSettings(); + + // capture the slider values + const handleChange = (event, newValue) => { + // save the value to state + setObsChartY(newValue); + }; + + // mark the line + const marks = [ { value: -20 }, { value: -10 }, { value: 0 }, { value: 10 }, { value: 20 } ]; + + // render the control + return ( + + + +
          + Observation chart Y-axis [{ obsChartY.map(x => x).join(' -> ') }] + + + 'Y-Axis'} + value={ obsChartY } + defaultValue={ obsChartY } + onChange={ handleChange } + valueLabelDisplay="auto" + step={ 0.5 } + marks={ marks } + min={-20} + max={20} + disableSwap + sx={{ + "--Slider-thumbWidth": "8px", + "--Slider-valueLabelArrowSize": "-1px", + "--Slider-trackSize": "3px", + "--Slider-markSize": "3px" + }} + /> + +
          +
          + ); +}; + +/** + * renders the icon + * + * @returns {JSX.Element} + * @constructor + */ +export const YAxisSlider = () => { + // render the control + return ( + + + + + ); +}; diff --git a/src/components/trays/settings/index.js b/src/components/trays/settings/index.js index 7d56494f..2f6c2f2d 100644 --- a/src/components/trays/settings/index.js +++ b/src/components/trays/settings/index.js @@ -4,14 +4,16 @@ import { Tune as SettingsIcon } from '@mui/icons-material'; import { DarkModeToggle } from './dark-mode'; import { BaseMaps } from './basemap'; +import { ObsChartYAxis } from './chart-yaxis'; export const icon = ; export const title = 'Settings'; export const trayContents = () => ( - + + ); \ No newline at end of file diff --git a/src/context/map-context.js b/src/context/map-context.js index 29d276c1..4b90a002 100644 --- a/src/context/map-context.js +++ b/src/context/map-context.js @@ -83,7 +83,8 @@ export const LayersProvider = ({ children }) => { const getAllLayersInvisible = () => { const currentLayers = [...defaultModelLayers]; - const alteredLayers = currentLayers + + return currentLayers .map((layer) => { const opacity = layer.state.opacity; return { @@ -91,8 +92,6 @@ export const LayersProvider = ({ children }) => { state: {visible: false, opacity: opacity} }; }); - - return alteredLayers; }; const swapLayers = (i, j) => { @@ -133,6 +132,8 @@ export const LayersProvider = ({ children }) => { const [baseMap, setBaseMap] = React.useState(); + // used to track the view state of the share comment + const [ showShareComment, setShowShareComment ] = useState(true); return ( { toggleHurricaneLayerVisibility, toggleLayerVisibility, getAllLayersInvisible, - selectedObservations, - setSelectedObservations, + selectedObservations, setSelectedObservations, + showShareComment, setShowShareComment, swapLayers, removeLayer, layerTypes, diff --git a/src/context/settings.js b/src/context/settings.js index d4018f85..ed90d426 100644 --- a/src/context/settings.js +++ b/src/context/settings.js @@ -3,7 +3,9 @@ import React, { useCallback, useContext, useMemo, + useState } from "react"; + import PropTypes from "prop-types"; import { useColorScheme } from '@mui/joy/styles'; import { @@ -25,13 +27,18 @@ export const SettingsProvider = ({ children }) => { setMode(darkMode ? 'light' : 'dark'); }, [mode]); + // used to capture the selected observation chart min/max Y-axis + const [obsChartY, setObsChartY] = useState([-2, 2]); + return ( { children } diff --git a/src/utils/map-utils.js b/src/utils/map-utils.js index 726e2533..d80a51e1 100644 --- a/src/utils/map-utils.js +++ b/src/utils/map-utils.js @@ -30,7 +30,6 @@ export const markClicked = (map, event, id) => { export const markUnclicked = (map, id) => { - map.eachLayer((layer) => { if (layer.options && layer.options.pane === "markerPane") { if (layer._id === id) { @@ -83,14 +82,20 @@ export const parseSharedURL = () => { * @param setSelectedObservations */ export const addSharedObservations = (map, obs, setSelectedObservations )=> { - // put a target icon on the map and observation data in state - obs.forEach(r => { - // put the target icons on the map - markClicked(map, {'latlng': {'lat': r.lat, 'lng': r.lng}}, r.id); - - // add the selected observation data into state - setSelectedObservations(previous => [...previous, r]); - }); + // if there are observations, put them on the map + if(obs) { + // clear out the current selected observation dialogs + setSelectedObservations([]); + + // put a target icon on the map and observation data in state + obs.forEach(r => { + // put the target icons on the map + markClicked(map, {'latlng': {'lat': r.lat, 'lng': r.lng}}, r.id); + + // add the selected observation data into state + setSelectedObservations(previous => [...previous, r]); + }); + } }; // add any new basemaps here