Loading app/src/components/container/App.jsx +48 −11 Original line number Diff line number Diff line Loading @@ -10,12 +10,26 @@ import AppBar from '@mui/material/AppBar'; import Container from '@mui/material/Container'; import GeoTiffViewer from "../../js/geoTiffViewer"; import FootprintResults from "../presentational/FootprintResults.jsx"; import { getFeatures } from "../../js/ApiJsonCollection"; const css = { shown: { display: "block", expanded: { height: "100vh", display: "flex", flexDirection: "row", alignItems: "flex-start", background: "#f8f9fa" }, stacked: { height: "100vh", display: "flex", flexDirection: "column", alignItems: "flex-start", background: "#f8f9fa" }, shown: { display: "block" }, hidden: { display: "none" } Loading @@ -30,13 +44,29 @@ const css = { */ export default function App() { const [targetPlanet, setTargetPlanet] = React.useState("Mars"); const [showSortBar, setShowSortBar] = React.useState(true); const [sortBarStyle, setSortBarStyle] = React.useState(css.hidden); const [showSidePanel, setShowSidePanel] = React.useState(false); const [sidePanelVisStyle, setSidePanelVisStyle] = React.useState(css.shown); const [expandResults, setExpandResults] = React.useState(true); const [resultsExpandStyle, setResultsExpandStyle] = React.useState(css.stacked); const geoTiffViewer = new GeoTiffViewer("geoTiff-Container"); const ShowHideSort = () => { setShowSortBar(!showSortBar); setSortBarStyle(showSortBar ? css.shown : css.hidden); const [footprintData, setFootprintData] = React.useState([]); const showHideSort = () => { console.log(showSidePanel + " -> ") console.log("Show/Hide"); setShowSidePanel(!showSidePanel); setSidePanelVisStyle(showSidePanel ? css.shown : css.hidden); console.log(" -> " + showSidePanel); } const handlePanelLayout = (event) => { console.log("Expand/Collapse"); setExpandResults(expandResults => !expandResults); setResultsExpandStyle(expandResults ? css.expanded : css.stacked); } /** Loading @@ -47,6 +77,11 @@ export default function App() { setTargetPlanet(value); }; const handleFootprintClick = () => { setFootprintData(getFeatures); console.log(footprintData); }; return ( <div id="app-container"> <div id="main-column"> Loading @@ -60,14 +95,16 @@ export default function App() { </div> </div> <div id="right-bar"> <div id="sort-filter-collapsed" onClick={ShowHideSort} > <div id="sort-filter-collapsed" onClick={showHideSort} > <ArrowLeftIcon/> Sort and Filter <ArrowLeftIcon/> </div> <div style={sortBarStyle}> <SearchAndFilterInput target={targetPlanet}/> <FootprintResults/> <div style={sidePanelVisStyle}> <div style={resultsExpandStyle}> <SearchAndFilterInput target={targetPlanet} footprintNavClick={handleFootprintClick} /> <FootprintResults changeLayout={handlePanelLayout}/> </div> </div> </div> Loading app/src/components/container/MapContainer.jsx +18 −26 Original line number Diff line number Diff line import React, { Component } from "react"; import React, { useEffect } from "react"; import AstroMap from "../../js/AstroMap"; import AstroControlManager from "../../js/AstroControlManager"; Loading @@ -8,36 +8,30 @@ import AstroControlManager from "../../js/AstroControlManager"; * for the map. * * * @class MapContainer * @extends {Component} * @component MapContainer * @param {target, map, mapChange} */ export default class MapContainer extends Component { /** * * @param {*} props target - target body name */ constructor(props) { super(props); this.state = {oldTarget: ""}; } export default function MapContainer(props) { const [oldTarget, setOldTarget] = React.useState(""); /** * Invoked when the component is successfully mounted to the DOM, then * handles all of the map intialization and creation. */ componentDidMount() { let map = new AstroMap("map-container", this.props.target, {}); useEffect( () => { let map = new AstroMap("map-container", props.target, {}); let controlManager = new AstroControlManager(map); controlManager.addTo(map); this.setState({oldTarget: this.props.target}) } setOldTarget(props.target) }, []); /** * Invoked after the component's state has changed when the * target selector passes down a new target name from props. */ componentDidUpdate() { if (this.props.target != this.state.oldTarget ) { useEffect( () => { if (props.target != oldTarget ) { // remove old map container and append new container to its parent let oldContainer = document.getElementById("map-container"); let parent = oldContainer.parentNode; Loading @@ -54,16 +48,14 @@ export default class MapContainer extends Component { document.getElementById("projectionSouthPole").classList.remove("disabled"); // create new map with updated target let map = new AstroMap("map-container", this.props.target, {}); let map = new AstroMap("map-container", props.target, {}); let controlManager = new AstroControlManager(map); controlManager.addTo(map); this.setState({oldTarget: this.props.target}) } setOldTarget(props.target) } }); render() { return ( <div id="map-container" /> ); } } app/src/components/presentational/FootprintResults.jsx +78 −53 Original line number Diff line number Diff line import React, {useEffect} from "react"; // CSS import { alpha } from "@mui/material/styles"; // Lists import List from '@mui/material/List'; import ListItem from '@mui/material/ListItem'; import Checkbox from '@mui/material/Checkbox'; // result action links import Chip from '@mui/material/Chip'; import Stack from '@mui/material/Stack'; // icons import PreviewIcon from '@mui/icons-material/Preview'; import LaunchIcon from '@mui/icons-material/Launch'; import OpenInFullIcon from '@mui/icons-material/OpenInFull'; import CloseFullscreenIcon from '@mui/icons-material/CloseFullscreen'; // object with results import { getFeatures } from "../../js/ApiJsonCollection"; import { autocompleteClasses } from "@mui/material"; /** Loading @@ -16,44 +24,14 @@ let css = { backgroundColor: "#f8f9fa", overflow: "hidden", display: "flex", alignItems: "flex-start" }, container: { padding: "1rem", height: "100vh", width: 225, display: "flex", alignItems: "flex-start", flexDirection: "column", margin: "auto", padding: 0 }, textbox: { backgroundColor: "#e9ecef", "&:focus": { borderColor: "#1971c2" } }, button: { width: "auto", color: "#fff", backgroundColor: "#1971c2", "&:hover": { backgroundColor: alpha("#1971c2", 0.7) } }, buttonRemove: { width: "auto", color: "#fff", backgroundColor: "#64748B", "&:hover": { backgroundColor: alpha("#64748B", 0.7) } }, title: { padding: "0.2rem", color: "#343a40", fontSize: 18, fontWeight: 600 width: 275, maxHeight: "100vh", wordWrap: "break-word", flexShrink: 1, padding: 0, borderLeft: "2px solid lightgray" } }; Loading @@ -78,17 +56,64 @@ export default function FootprintResults(props) { return ( <div style={css.root}> <div style={css.container}> <div className="panelSection panelHeader"> <div className="resultHeader"> <span id="panelSectionTitle"> Footprint Results </span> <span className="resultHeaderCheck"> <Checkbox defaultChecked onChange={props.changeLayout} icon={<CloseFullscreenIcon/>} checkedIcon={<OpenInFullIcon/>} sx={{ color: "#64748B", '&.Mui-checked': { color: "#64748B", }, }} /> </span> </div> <List> <div className="resultsList"> {features.map((feature) => ( <div className="panelSection" key={feature.id}> <ListItem>{feature.id}</ListItem> <div className="resultContainer" key={feature.id}> <div className="resultImgDiv"> <img className="resultImg" src={feature.assets.thumbnail.href}/> </div> <div className="resultData"> <div className="resultSub"> <strong>Collection:</strong> {feature.collection} </div> <div className="resultSub"> <strong>ID:</strong> {feature.id} </div> <div className="resultSub"> <strong>Date:</strong> {feature.properties.datetime} </div> </div> <div className="resultLinks"> <Stack direction="row" spacing={1}> <Chip label="Metadata" icon={<PreviewIcon/>} size="small" component="a" href="#" variant="outlined" clickable /> <Chip label="STAC Browser" icon={<LaunchIcon/>} size="small" component="a" href="#" variant="outlined" clickable /> </Stack> </div> </div> ))} </List> </div> </div> ); Loading app/src/components/presentational/SearchAndFilterInput.jsx +12 −6 Original line number Diff line number Diff line Loading @@ -35,12 +35,13 @@ let css = { backgroundColor: "#f8f9fa", overflow: "hidden", display: "flex", alignItems: "flex-start" alignItems: "flex-start", flexShrink: 0, }, container: { padding: "1rem", height: "100vh", width: 225, maxHeight: "100vh", width: 275, display: "flex", flexDirection: "column", margin: "auto", Loading Loading @@ -100,9 +101,9 @@ export default function SearchAndFilterInput(props) { const [dateCheckVal, setDateCheckVal] = React.useState(false); const [dateFromVal, setDateFromVal] = React.useState(null); const [dateToVal, setDateToVal] = React.useState(null); const [maxPages, setMaxPages] = React.useState(0); const [maxNumberFootprints, setMaxNumberFootprints] = React.useState(0); const [limitVal, setLimitVal] = React.useState(0); const [maxPages, setMaxPages] = React.useState(10); const [maxNumberFootprints, setMaxNumberFootprints] = React.useState(10); const [limitVal, setLimitVal] = React.useState(10); // Clear all values const handleClear = (event) => { Loading Loading @@ -168,6 +169,7 @@ export default function SearchAndFilterInput(props) { setLimitVal(value); setTimeout(() => { setMaxPages(getMaxNumberPages); props.footprintNavClick(); }, 1000); } Loading @@ -177,12 +179,16 @@ export default function SearchAndFilterInput(props) { setMaxNumberFootprints(getNumberMatched); setLimitVal(10); setMaxPages(getMaxNumberPages); props.footprintNavClick(); }, 1000); }, [props.target]); // Pagination const handlePageChange = (event, value) => { setCurrentPage(value); setTimeout(() => { props.footprintNavClick(); }, 1000) }; Loading app/src/styles.css +65 −4 Original line number Diff line number Diff line Loading @@ -228,21 +228,20 @@ Controls the CSS for projection buttons when there is no available projection } .panelSection { border-bottom-style: solid; border-bottom-width: 1px; border-bottom-color: rgba(0, 0, 0, 0.12); border-bottom: 1px solid rgba(0, 0, 0, 0.12); padding: 10px; } .panelHeader { font-family: sans-serif; font-weight: bold; padding: 10px; display: block; } .panelSectionHeader { display: inline; font-family: sans-serif; width: auto; } .panelItem { Loading @@ -268,6 +267,68 @@ Controls the CSS for projection buttons when there is no available projection margin-right: -10px; } .resultHeader { display: inline-flex; flex-direction: row; justify-content: space-between; align-content: baseline; font-family: sans-serif; font-weight: bold; padding: 10px; width: 275px; } .resultHeaderCheck { justify-content: flex-end; margin-top: -10px; margin-bottom: -10px; margin-right: 10px; } .resultsList { border-top: 1px solid rgba(0, 0, 0, 0.12); width: 275px; flex-shrink: 1; height: auto; overflow-y: scroll; } .resultContainer { display: grid; grid-template: "ra rb" "rc rc" / 20% 80%; border-bottom: 3px double rgba(0, 0, 0, 0.4); padding: 10px; } .resultSub { font-family: Roboto, Arial, Helvetica, sans-serif; font-size: small; padding-bottom: 2px; border-bottom: 1px solid rgba(0, 0, 0, 0.12); margin-bottom: 3px; word-wrap: break-word; } .resultImgDiv { grid-area: ra; } .resultData { grid-area: rb; } .resultLinks { grid-area: rc; } .resultImg { max-width: 32px; max-height: 96px; } summary { cursor: pointer; width: auto; Loading Loading
app/src/components/container/App.jsx +48 −11 Original line number Diff line number Diff line Loading @@ -10,12 +10,26 @@ import AppBar from '@mui/material/AppBar'; import Container from '@mui/material/Container'; import GeoTiffViewer from "../../js/geoTiffViewer"; import FootprintResults from "../presentational/FootprintResults.jsx"; import { getFeatures } from "../../js/ApiJsonCollection"; const css = { shown: { display: "block", expanded: { height: "100vh", display: "flex", flexDirection: "row", alignItems: "flex-start", background: "#f8f9fa" }, stacked: { height: "100vh", display: "flex", flexDirection: "column", alignItems: "flex-start", background: "#f8f9fa" }, shown: { display: "block" }, hidden: { display: "none" } Loading @@ -30,13 +44,29 @@ const css = { */ export default function App() { const [targetPlanet, setTargetPlanet] = React.useState("Mars"); const [showSortBar, setShowSortBar] = React.useState(true); const [sortBarStyle, setSortBarStyle] = React.useState(css.hidden); const [showSidePanel, setShowSidePanel] = React.useState(false); const [sidePanelVisStyle, setSidePanelVisStyle] = React.useState(css.shown); const [expandResults, setExpandResults] = React.useState(true); const [resultsExpandStyle, setResultsExpandStyle] = React.useState(css.stacked); const geoTiffViewer = new GeoTiffViewer("geoTiff-Container"); const ShowHideSort = () => { setShowSortBar(!showSortBar); setSortBarStyle(showSortBar ? css.shown : css.hidden); const [footprintData, setFootprintData] = React.useState([]); const showHideSort = () => { console.log(showSidePanel + " -> ") console.log("Show/Hide"); setShowSidePanel(!showSidePanel); setSidePanelVisStyle(showSidePanel ? css.shown : css.hidden); console.log(" -> " + showSidePanel); } const handlePanelLayout = (event) => { console.log("Expand/Collapse"); setExpandResults(expandResults => !expandResults); setResultsExpandStyle(expandResults ? css.expanded : css.stacked); } /** Loading @@ -47,6 +77,11 @@ export default function App() { setTargetPlanet(value); }; const handleFootprintClick = () => { setFootprintData(getFeatures); console.log(footprintData); }; return ( <div id="app-container"> <div id="main-column"> Loading @@ -60,14 +95,16 @@ export default function App() { </div> </div> <div id="right-bar"> <div id="sort-filter-collapsed" onClick={ShowHideSort} > <div id="sort-filter-collapsed" onClick={showHideSort} > <ArrowLeftIcon/> Sort and Filter <ArrowLeftIcon/> </div> <div style={sortBarStyle}> <SearchAndFilterInput target={targetPlanet}/> <FootprintResults/> <div style={sidePanelVisStyle}> <div style={resultsExpandStyle}> <SearchAndFilterInput target={targetPlanet} footprintNavClick={handleFootprintClick} /> <FootprintResults changeLayout={handlePanelLayout}/> </div> </div> </div> Loading
app/src/components/container/MapContainer.jsx +18 −26 Original line number Diff line number Diff line import React, { Component } from "react"; import React, { useEffect } from "react"; import AstroMap from "../../js/AstroMap"; import AstroControlManager from "../../js/AstroControlManager"; Loading @@ -8,36 +8,30 @@ import AstroControlManager from "../../js/AstroControlManager"; * for the map. * * * @class MapContainer * @extends {Component} * @component MapContainer * @param {target, map, mapChange} */ export default class MapContainer extends Component { /** * * @param {*} props target - target body name */ constructor(props) { super(props); this.state = {oldTarget: ""}; } export default function MapContainer(props) { const [oldTarget, setOldTarget] = React.useState(""); /** * Invoked when the component is successfully mounted to the DOM, then * handles all of the map intialization and creation. */ componentDidMount() { let map = new AstroMap("map-container", this.props.target, {}); useEffect( () => { let map = new AstroMap("map-container", props.target, {}); let controlManager = new AstroControlManager(map); controlManager.addTo(map); this.setState({oldTarget: this.props.target}) } setOldTarget(props.target) }, []); /** * Invoked after the component's state has changed when the * target selector passes down a new target name from props. */ componentDidUpdate() { if (this.props.target != this.state.oldTarget ) { useEffect( () => { if (props.target != oldTarget ) { // remove old map container and append new container to its parent let oldContainer = document.getElementById("map-container"); let parent = oldContainer.parentNode; Loading @@ -54,16 +48,14 @@ export default class MapContainer extends Component { document.getElementById("projectionSouthPole").classList.remove("disabled"); // create new map with updated target let map = new AstroMap("map-container", this.props.target, {}); let map = new AstroMap("map-container", props.target, {}); let controlManager = new AstroControlManager(map); controlManager.addTo(map); this.setState({oldTarget: this.props.target}) } setOldTarget(props.target) } }); render() { return ( <div id="map-container" /> ); } }
app/src/components/presentational/FootprintResults.jsx +78 −53 Original line number Diff line number Diff line import React, {useEffect} from "react"; // CSS import { alpha } from "@mui/material/styles"; // Lists import List from '@mui/material/List'; import ListItem from '@mui/material/ListItem'; import Checkbox from '@mui/material/Checkbox'; // result action links import Chip from '@mui/material/Chip'; import Stack from '@mui/material/Stack'; // icons import PreviewIcon from '@mui/icons-material/Preview'; import LaunchIcon from '@mui/icons-material/Launch'; import OpenInFullIcon from '@mui/icons-material/OpenInFull'; import CloseFullscreenIcon from '@mui/icons-material/CloseFullscreen'; // object with results import { getFeatures } from "../../js/ApiJsonCollection"; import { autocompleteClasses } from "@mui/material"; /** Loading @@ -16,44 +24,14 @@ let css = { backgroundColor: "#f8f9fa", overflow: "hidden", display: "flex", alignItems: "flex-start" }, container: { padding: "1rem", height: "100vh", width: 225, display: "flex", alignItems: "flex-start", flexDirection: "column", margin: "auto", padding: 0 }, textbox: { backgroundColor: "#e9ecef", "&:focus": { borderColor: "#1971c2" } }, button: { width: "auto", color: "#fff", backgroundColor: "#1971c2", "&:hover": { backgroundColor: alpha("#1971c2", 0.7) } }, buttonRemove: { width: "auto", color: "#fff", backgroundColor: "#64748B", "&:hover": { backgroundColor: alpha("#64748B", 0.7) } }, title: { padding: "0.2rem", color: "#343a40", fontSize: 18, fontWeight: 600 width: 275, maxHeight: "100vh", wordWrap: "break-word", flexShrink: 1, padding: 0, borderLeft: "2px solid lightgray" } }; Loading @@ -78,17 +56,64 @@ export default function FootprintResults(props) { return ( <div style={css.root}> <div style={css.container}> <div className="panelSection panelHeader"> <div className="resultHeader"> <span id="panelSectionTitle"> Footprint Results </span> <span className="resultHeaderCheck"> <Checkbox defaultChecked onChange={props.changeLayout} icon={<CloseFullscreenIcon/>} checkedIcon={<OpenInFullIcon/>} sx={{ color: "#64748B", '&.Mui-checked': { color: "#64748B", }, }} /> </span> </div> <List> <div className="resultsList"> {features.map((feature) => ( <div className="panelSection" key={feature.id}> <ListItem>{feature.id}</ListItem> <div className="resultContainer" key={feature.id}> <div className="resultImgDiv"> <img className="resultImg" src={feature.assets.thumbnail.href}/> </div> <div className="resultData"> <div className="resultSub"> <strong>Collection:</strong> {feature.collection} </div> <div className="resultSub"> <strong>ID:</strong> {feature.id} </div> <div className="resultSub"> <strong>Date:</strong> {feature.properties.datetime} </div> </div> <div className="resultLinks"> <Stack direction="row" spacing={1}> <Chip label="Metadata" icon={<PreviewIcon/>} size="small" component="a" href="#" variant="outlined" clickable /> <Chip label="STAC Browser" icon={<LaunchIcon/>} size="small" component="a" href="#" variant="outlined" clickable /> </Stack> </div> </div> ))} </List> </div> </div> ); Loading
app/src/components/presentational/SearchAndFilterInput.jsx +12 −6 Original line number Diff line number Diff line Loading @@ -35,12 +35,13 @@ let css = { backgroundColor: "#f8f9fa", overflow: "hidden", display: "flex", alignItems: "flex-start" alignItems: "flex-start", flexShrink: 0, }, container: { padding: "1rem", height: "100vh", width: 225, maxHeight: "100vh", width: 275, display: "flex", flexDirection: "column", margin: "auto", Loading Loading @@ -100,9 +101,9 @@ export default function SearchAndFilterInput(props) { const [dateCheckVal, setDateCheckVal] = React.useState(false); const [dateFromVal, setDateFromVal] = React.useState(null); const [dateToVal, setDateToVal] = React.useState(null); const [maxPages, setMaxPages] = React.useState(0); const [maxNumberFootprints, setMaxNumberFootprints] = React.useState(0); const [limitVal, setLimitVal] = React.useState(0); const [maxPages, setMaxPages] = React.useState(10); const [maxNumberFootprints, setMaxNumberFootprints] = React.useState(10); const [limitVal, setLimitVal] = React.useState(10); // Clear all values const handleClear = (event) => { Loading Loading @@ -168,6 +169,7 @@ export default function SearchAndFilterInput(props) { setLimitVal(value); setTimeout(() => { setMaxPages(getMaxNumberPages); props.footprintNavClick(); }, 1000); } Loading @@ -177,12 +179,16 @@ export default function SearchAndFilterInput(props) { setMaxNumberFootprints(getNumberMatched); setLimitVal(10); setMaxPages(getMaxNumberPages); props.footprintNavClick(); }, 1000); }, [props.target]); // Pagination const handlePageChange = (event, value) => { setCurrentPage(value); setTimeout(() => { props.footprintNavClick(); }, 1000) }; Loading
app/src/styles.css +65 −4 Original line number Diff line number Diff line Loading @@ -228,21 +228,20 @@ Controls the CSS for projection buttons when there is no available projection } .panelSection { border-bottom-style: solid; border-bottom-width: 1px; border-bottom-color: rgba(0, 0, 0, 0.12); border-bottom: 1px solid rgba(0, 0, 0, 0.12); padding: 10px; } .panelHeader { font-family: sans-serif; font-weight: bold; padding: 10px; display: block; } .panelSectionHeader { display: inline; font-family: sans-serif; width: auto; } .panelItem { Loading @@ -268,6 +267,68 @@ Controls the CSS for projection buttons when there is no available projection margin-right: -10px; } .resultHeader { display: inline-flex; flex-direction: row; justify-content: space-between; align-content: baseline; font-family: sans-serif; font-weight: bold; padding: 10px; width: 275px; } .resultHeaderCheck { justify-content: flex-end; margin-top: -10px; margin-bottom: -10px; margin-right: 10px; } .resultsList { border-top: 1px solid rgba(0, 0, 0, 0.12); width: 275px; flex-shrink: 1; height: auto; overflow-y: scroll; } .resultContainer { display: grid; grid-template: "ra rb" "rc rc" / 20% 80%; border-bottom: 3px double rgba(0, 0, 0, 0.4); padding: 10px; } .resultSub { font-family: Roboto, Arial, Helvetica, sans-serif; font-size: small; padding-bottom: 2px; border-bottom: 1px solid rgba(0, 0, 0, 0.12); margin-bottom: 3px; word-wrap: break-word; } .resultImgDiv { grid-area: ra; } .resultData { grid-area: rb; } .resultLinks { grid-area: rc; } .resultImg { max-width: 32px; max-height: 96px; } summary { cursor: pointer; width: auto; Loading