diff --git a/package-lock.json b/package-lock.json index ae68edf9..eebaf534 100644 --- a/package-lock.json +++ b/package-lock.json @@ -9,17 +9,19 @@ "@emotion/react": "^11.11.4", "@emotion/styled": "^11.11.5", "@fontsource/inter": "^5.0.16", - "@mui/icons-material": "^5.15.11", - "@mui/joy": "^5.0.0-beta.32", - "@mui/material": "^5.15.11", + "@mui/icons-material": "^5.15.15", + "@mui/joy": "^5.0.0-beta.36", + "@mui/material": "^5.15.15", "axios": "^1.6.7", "core-js": "^3.36.0", "d3": "^7.8.5", "dotenv": "^16.4.5", "leaflet": "^1.9.4", "mapbox-gl": "^3.1.2", + "prop-types": "^15.8.1", "react": "^18.2.0", "react-dom": "^18.2.0", + "react-draggable": "^4.4.6", "react-leaflet": "^4.2.1", "react-map-gl": "^7.1.7", "react-query": "^3.39.3", @@ -15670,6 +15672,27 @@ "react": "^18.3.1" } }, + "node_modules/react-draggable": { + "version": "4.4.6", + "resolved": "https://registry.npmjs.org/react-draggable/-/react-draggable-4.4.6.tgz", + "integrity": "sha512-LtY5Xw1zTPqHkVmtM3X8MUOxNDOUhv/khTgBgrUvwaS064bwVvxT+q5El0uUFNx5IEPKXuRejr7UqLwBIg5pdw==", + "dependencies": { + "clsx": "^1.1.1", + "prop-types": "^15.8.1" + }, + "peerDependencies": { + "react": ">= 16.3.0", + "react-dom": ">= 16.3.0" + } + }, + "node_modules/react-draggable/node_modules/clsx": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/clsx/-/clsx-1.2.1.tgz", + "integrity": "sha512-EcR6r5a8bj6pu3ycsa/E/cKVGuTgZJZdsyUYHOksG/UHIiKfjxzRxYJpyVBwYaQeOvghal9fcc4PidlgzugAQg==", + "engines": { + "node": ">=6" + } + }, "node_modules/react-is": { "version": "18.3.1", "resolved": "https://registry.npmjs.org/react-is/-/react-is-18.3.1.tgz", diff --git a/package.json b/package.json index ec59f33c..5abcda2f 100644 --- a/package.json +++ b/package.json @@ -47,17 +47,19 @@ "@emotion/react": "^11.11.4", "@emotion/styled": "^11.11.5", "@fontsource/inter": "^5.0.16", - "@mui/icons-material": "^5.15.11", - "@mui/joy": "^5.0.0-beta.32", - "@mui/material": "^5.15.11", + "@mui/icons-material": "^5.15.15", + "@mui/joy": "^5.0.0-beta.36", + "@mui/material": "^5.15.15", "axios": "^1.6.7", "core-js": "^3.36.0", "d3": "^7.8.5", "dotenv": "^16.4.5", "leaflet": "^1.9.4", "mapbox-gl": "^3.1.2", + "prop-types": "^15.8.1", "react": "^18.2.0", "react-dom": "^18.2.0", + "react-draggable": "^4.4.6", "react-leaflet": "^4.2.1", "react-map-gl": "^7.1.7", "react-query": "^3.39.3", diff --git a/src/app.js b/src/app.js index 0794220e..fb5a61dd 100644 --- a/src/app.js +++ b/src/app.js @@ -1,12 +1,26 @@ import React, { Fragment } from 'react'; import { Map } from '@components/map'; +import ObservationDialog from "@components/map/observation-dialog"; +import { useLayers } from '@context'; import { Sidebar } from '@components/sidebar'; export const App = () => { - return ( + // install the selected observation list from the layer context + const { + selectedObservations + } = useLayers(); + + return ( - - - - ); + { + // for each observation selected + selectedObservations.map (function (obs) { + // render the observation + return ; + }) + } + + + + ); }; diff --git a/src/components/map/default-layers.js b/src/components/map/default-layers.js index 36d2aacc..0b3b9f0d 100644 --- a/src/components/map/default-layers.js +++ b/src/components/map/default-layers.js @@ -27,6 +27,7 @@ export const DefaultLayers = () => { const { defaultModelLayers, setDefaultModelLayers, + setSelectedObservations } = useLayers(); // Create the authorization header @@ -81,6 +82,9 @@ export const DefaultLayers = () => { // from the feature.properties.csv_url and create a fancy plot console.log("Observation Station '" + feature.properties.location_name + "' clicked"); markClicked(map, e); + + // populate selectedObservations list with the newly selected observation point + setSelectedObservations(previous => [...previous, feature.properties]); }); } }; diff --git a/src/components/map/observation-dialog.js b/src/components/map/observation-dialog.js new file mode 100644 index 00000000..4d0ab9d6 --- /dev/null +++ b/src/components/map/observation-dialog.js @@ -0,0 +1,33 @@ +import React, {Fragment} from 'react'; +import PropTypes from 'prop-types'; +import BaseFloatingDialog from "@utils/base-floating-dialog"; + +// define the properties of this component +ObservationDialog.propTypes = { + obs_data: PropTypes.object +}; + +export default function ObservationDialog(obs_data) { + // TODO: the url is put in here but it will eventually + // return a graph using data from this url + const graphObj = (url) => { + return ( + +
+ {url} +
+
+ ); + }; + + // create an object for the base dialog + const floaterArgs = {title: obs_data.obs.station_name, description: obs_data.obs.location_name, openDialogImmediately:true, "dialogObject": {...graphObj(obs_data.obs.csvurl)}}; + + // render the dialog. + // the key here will be used to remove the dialog from the selected observation list when the dialog is closed + return ( + + + + ); +}; \ No newline at end of file diff --git a/src/context/map-context.js b/src/context/map-context.js index e185f0e7..c2632def 100644 --- a/src/context/map-context.js +++ b/src/context/map-context.js @@ -7,6 +7,10 @@ export const useLayers = () => useContext(LayersContext); export const LayersProvider = ({ children }) => { const [defaultModelLayers, setDefaultModelLayers] = useState([]); const [filteredModelLayers, setFilteredModelLayers] = useState([]); + + // this object contains data for graph rendering + const [selectedObservations, setSelectedObservations] = useState([]); + const [map, setMap] = useState(null); const toggleLayerVisibility = id => { @@ -35,6 +39,8 @@ export const LayersProvider = ({ children }) => { filteredModelLayers, setFilteredModelLayers, toggleLayerVisibility, + selectedObservations, + setSelectedObservations }} > {children} diff --git a/src/utils/base-floating-dialog.js b/src/utils/base-floating-dialog.js new file mode 100644 index 00000000..8c598e82 --- /dev/null +++ b/src/utils/base-floating-dialog.js @@ -0,0 +1,104 @@ +import React, {Fragment} from 'react'; +import Draggable from 'react-draggable'; +import PropTypes from 'prop-types'; + +import Button from '@mui/material/Button'; +import CssBaseline from '@mui/material/CssBaseline'; +import Dialog from '@mui/material/Dialog'; +import DialogActions from '@mui/material/DialogActions'; +import DialogContent from '@mui/material/DialogActions'; +import DialogContentText from '@mui/material/DialogContentText'; +import DialogTitle from '@mui/material/DialogTitle'; +import Paper from '@mui/material/Paper'; +import Slide from '@mui/material/Slide'; + +import { useLayers } from '@context'; + +// define the properties of this component +BaseFloatingDialog.propTypes = { + title: PropTypes.string, + description: PropTypes.string, + openDialogImmediately: PropTypes.bool, + dialogObject: PropTypes.any +}; + +/** + * This is a component that displays a floating dialog with the content passed + * + * @param title: string + * @param description: string + * @param openDialogImmediately: boolean + * @param dialogObject: {JSX.Element} + * @returns {JSX.Element} + */ +export default function BaseFloatingDialog({ title, description, openDialogImmediately, dialogObject} ) { + // define the dialog open/close session state + const [open, setOpen] = React.useState(openDialogImmediately); + + const { + selectedObservations, + setSelectedObservations + } = useLayers(); + + /** + * the close dialog handler + */ + const handleClose = () => { + // close the dialog + setOpen(false); + + // remove this item from the selected observations list + setSelectedObservations(selectedObservations.filter(item => item.station_name !== title)); + }; + + /** + * configure and render the floating dialog + */ + return ( + + + + { "Station: " + title } + + { "Location: " + description } + + { dialogObject } + + + + + ); +}; + +/** + * This creates a 3D dialog. + * + * @param props + * @returns {JSX.Element} + * @constructor + */ +function PaperComponent(props) { + return ( + + + + ); +} + +/** + * This creates an animated transition for the dialog that pops up + * @type {React.ForwardRefExoticComponent & React.RefAttributes>} + */ +const Transition = React.forwardRef(function Transition(props, ref) { + return ; +}); \ No newline at end of file