+ Lots of work was done but specifically within the GlobalMap.jsx
+ Two new component files were also added + Some slight adjustments to Theme.jsx
This commit is contained in:
Generated
+10
@@ -14,6 +14,7 @@
|
|||||||
"@tailwindcss/vite": "^4.1.11",
|
"@tailwindcss/vite": "^4.1.11",
|
||||||
"framer-motion": "^12.23.0",
|
"framer-motion": "^12.23.0",
|
||||||
"leaflet": "^1.9.4",
|
"leaflet": "^1.9.4",
|
||||||
|
"lucide-react": "^0.539.0",
|
||||||
"next-themes": "^0.4.6",
|
"next-themes": "^0.4.6",
|
||||||
"react": "^19.1.0",
|
"react": "^19.1.0",
|
||||||
"react-dom": "^19.1.0",
|
"react-dom": "^19.1.0",
|
||||||
@@ -3894,6 +3895,15 @@
|
|||||||
"integrity": "sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==",
|
"integrity": "sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==",
|
||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
|
"node_modules/lucide-react": {
|
||||||
|
"version": "0.539.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/lucide-react/-/lucide-react-0.539.0.tgz",
|
||||||
|
"integrity": "sha512-VVISr+VF2krO91FeuCrm1rSOLACQUYVy7NQkzrOty52Y8TlTPcXcMdQFj9bYzBgXbWCiywlwSZ3Z8u6a+6bMlg==",
|
||||||
|
"license": "ISC",
|
||||||
|
"peerDependencies": {
|
||||||
|
"react": "^16.5.1 || ^17.0.0 || ^18.0.0 || ^19.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/magic-string": {
|
"node_modules/magic-string": {
|
||||||
"version": "0.30.17",
|
"version": "0.30.17",
|
||||||
"resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.17.tgz",
|
"resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.17.tgz",
|
||||||
|
|||||||
@@ -16,6 +16,7 @@
|
|||||||
"@tailwindcss/vite": "^4.1.11",
|
"@tailwindcss/vite": "^4.1.11",
|
||||||
"framer-motion": "^12.23.0",
|
"framer-motion": "^12.23.0",
|
||||||
"leaflet": "^1.9.4",
|
"leaflet": "^1.9.4",
|
||||||
|
"lucide-react": "^0.539.0",
|
||||||
"next-themes": "^0.4.6",
|
"next-themes": "^0.4.6",
|
||||||
"react": "^19.1.0",
|
"react": "^19.1.0",
|
||||||
"react-dom": "^19.1.0",
|
"react-dom": "^19.1.0",
|
||||||
|
|||||||
@@ -0,0 +1,47 @@
|
|||||||
|
import { useMap } from "react-leaflet";
|
||||||
|
import L from "leaflet";
|
||||||
|
import { useEffect, useState } from "react";
|
||||||
|
import { createPortal } from "react-dom";
|
||||||
|
|
||||||
|
// props contain title of btn (which can be an icon), the onClick fn (what should happen)
|
||||||
|
// when the settings btn is clicked, and it's positioning with the default being topright
|
||||||
|
export default function MapButtonControls({settingsBtn, position = "topright"}) {
|
||||||
|
const map = useMap(); // retrieves the map instance that is mounted
|
||||||
|
const [container, setContainer] = useState(null);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
// extending the leaflet control class
|
||||||
|
const customControl = L.Control.extend({
|
||||||
|
onAdd: () => {
|
||||||
|
// create button element with className 'leaflet-bar'
|
||||||
|
const dv = L.DomUtil.create("div", "layer-selector");
|
||||||
|
dv.style.cursor = "pointer";
|
||||||
|
|
||||||
|
// prevent map interactions when clicking on settings btn
|
||||||
|
L.DomEvent.disableClickPropagation(dv);
|
||||||
|
// L.DomEvent.on(dv, "click", onClick); // pass your own onClick fn
|
||||||
|
|
||||||
|
// pass button to container for react to render
|
||||||
|
setContainer(dv);
|
||||||
|
return dv;
|
||||||
|
},
|
||||||
|
onRemove: () => {
|
||||||
|
setContainer(null);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// instantiate new customControl object
|
||||||
|
const control = new customControl({ position });
|
||||||
|
// method inside og class that we extended above
|
||||||
|
control.addTo(map);
|
||||||
|
|
||||||
|
return () => {
|
||||||
|
control.remove();
|
||||||
|
};
|
||||||
|
}, [map, position, settingsBtn ]);
|
||||||
|
|
||||||
|
return container ? createPortal(
|
||||||
|
settingsBtn,
|
||||||
|
container)
|
||||||
|
: null;
|
||||||
|
}
|
||||||
@@ -0,0 +1,84 @@
|
|||||||
|
import { Box, Checkbox, Separator , CloseButton, Text, chakra, Field, IconButton, Icon, Input, Fieldset, Flex } from "@chakra-ui/react";
|
||||||
|
import { useState } from "react";
|
||||||
|
import { FiSettings } from "react-icons/fi";
|
||||||
|
import { IoIosArrowDown } from "react-icons/io";
|
||||||
|
import { IoIosArrowUp } from "react-icons/io";
|
||||||
|
|
||||||
|
export default function MapSettingsButton() {
|
||||||
|
const [open, setOpen] = useState(false);
|
||||||
|
|
||||||
|
return open ? (
|
||||||
|
<Box width="240px" display="flex" flexDir="column" gap="2" p="2" bg="white" borderRadius="5px" boxShadow="0 0 0 2px rgba(119, 119, 119, 0.4)">
|
||||||
|
<Header {...{open, setOpen}}/>
|
||||||
|
<Separator />
|
||||||
|
<ThermalAnomalies />
|
||||||
|
<Lightning />
|
||||||
|
</Box>
|
||||||
|
):(
|
||||||
|
<IconButton boxShadow="0 0 0 2px rgba(119, 119, 119, 0.4)" size="sm" variant="subtle" onClick={() => setOpen(true)}>
|
||||||
|
<FiSettings />
|
||||||
|
</IconButton>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
function Header(props) {
|
||||||
|
const { setOpen } = props;
|
||||||
|
return (
|
||||||
|
<chakra.div display="flex" dir="row" alignItems="center" justifyContent="space-between" pl="12">
|
||||||
|
<Text textStyle="lg">Select Layers</Text>
|
||||||
|
<CloseButton size="sm" colorPalette='red' onClick={() => setOpen(false)}/>
|
||||||
|
</chakra.div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
function ThermalAnomalies() {
|
||||||
|
const [checked, setChecked] = useState(false);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Box p="2" borderRadius="3px" bg={checked ? "blue.200":"blue.100"} color={checked ? "blue.700":"text"} border="1px solid" borderColor={checked ? "blue.600":"border"} >
|
||||||
|
<Checkbox.Root colorPalette="blue">
|
||||||
|
<Checkbox.HiddenInput onClick={() => setChecked(!checked)}/>
|
||||||
|
<Checkbox.Control bg={checked ? undefined:"white"}/>
|
||||||
|
<Checkbox.Label >Thermal Anomalies</Checkbox.Label>
|
||||||
|
</Checkbox.Root>
|
||||||
|
</Box>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
function Lightning() {
|
||||||
|
const [open, setOpen] = useState(false);
|
||||||
|
const [checked, setChecked] = useState(false);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Box p="2" borderRadius="3px" bg={checked ? "blue.200":"blue.100"} color={checked ? "blue.700":"text"} border="1px solid" borderColor={checked ? "blue.600":"border"} >
|
||||||
|
<Checkbox.Root colorPalette="blue">
|
||||||
|
<Checkbox.HiddenInput onClick={() => {setChecked(!checked); console.log("I'm clicked!")}}/>
|
||||||
|
<Checkbox.Control bg={checked ? undefined:"white"}/>
|
||||||
|
<Checkbox.Label >Lightning</Checkbox.Label>
|
||||||
|
</Checkbox.Root>
|
||||||
|
<Icon pt="1" size="md" variant="none" onClick={() => setOpen(!open)}>
|
||||||
|
{open ? (<IoIosArrowUp />):(<IoIosArrowDown />)}
|
||||||
|
</Icon>
|
||||||
|
{open && (<LightningOptions />)}
|
||||||
|
</Box>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
function LightningOptions() {
|
||||||
|
return (
|
||||||
|
<Fieldset.Root size="xs">
|
||||||
|
<Field.Root pl="10" gap={0} >
|
||||||
|
<Field.Label>Radius (km)</Field.Label>
|
||||||
|
<Input height="1.25rem" width="4rem" bg="white" border="none" borderRadius="6px" fontStyle="italic" placeholder="max"/>
|
||||||
|
</Field.Root>
|
||||||
|
<Field.Root pl="10" gap={0} >
|
||||||
|
<Field.Label>Date Range</Field.Label>
|
||||||
|
<Flex dir="row" gap="1">
|
||||||
|
<Input height="1.25rem" width="4rem" bg="white" border="none" borderRadius="6px" fontStyle="italic" placeholder="min"/>
|
||||||
|
<Text fontStyle="italic">to</Text>
|
||||||
|
<Input height="1.25rem" width="4rem" bg="white" border="none" borderRadius="6px" fontStyle="italic" placeholder="max"/>
|
||||||
|
</Flex>
|
||||||
|
</Field.Root>
|
||||||
|
</Fieldset.Root>
|
||||||
|
);
|
||||||
|
}
|
||||||
@@ -1,14 +1,18 @@
|
|||||||
import React from "react";
|
import React from "react";
|
||||||
import { MapContainer, TileLayer, Marker, Popup } from "react-leaflet";
|
import { MapContainer, TileLayer, Marker, Popup } from "react-leaflet";
|
||||||
|
import MapButtonControls from "../CanvasComponents/MapButtonControls";
|
||||||
|
import MapSettingsButton from "../CanvasComponents/MapSettingsButton";
|
||||||
|
|
||||||
export default function GlobalMap() {
|
export default function GlobalMap() {
|
||||||
const position = [45.61142478054226, -122.49630033167544]; // [latitude, longitude]
|
const position = [45.61142478054226, -122.49630033167544]; // [latitude, longitude]
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<MapContainer center={position} zoom={3} style={{ height: "100%", width: "100%" }}>
|
<MapContainer center={position} zoom={3} style={{ height: "100%", width: "100%" }}>
|
||||||
<TileLayer
|
<TileLayer
|
||||||
url="https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png"
|
url="https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png"
|
||||||
attribution='© OpenStreetMap contributors'
|
attribution='© OpenStreetMap contributors'
|
||||||
/>
|
/>
|
||||||
|
<MapButtonControls settingsBtn = {<MapSettingsButton />} position="topright" />
|
||||||
<Marker position={position}>
|
<Marker position={position}>
|
||||||
<Popup>
|
<Popup>
|
||||||
CVO — Home of the volcanoligists
|
CVO — Home of the volcanoligists
|
||||||
|
|||||||
@@ -11,6 +11,7 @@ import { MdOutlineSecurity } from "react-icons/md";
|
|||||||
import { IoPersonCircleSharp } from "react-icons/io5";
|
import { IoPersonCircleSharp } from "react-icons/io5";
|
||||||
import { useState, useEffect, useMemo } from "react";
|
import { useState, useEffect, useMemo } from "react";
|
||||||
import { DASHBOARD_VIEWS } from "@/constants/viewKeys";
|
import { DASHBOARD_VIEWS } from "@/constants/viewKeys";
|
||||||
|
import { KeyRound } from 'lucide-react';
|
||||||
|
|
||||||
export default function Sidebar({ view, onChangeView, darkMode, setDarkMode }) {
|
export default function Sidebar({ view, onChangeView, darkMode, setDarkMode }) {
|
||||||
const [isOpen, setIsOpen] = useState(true);
|
const [isOpen, setIsOpen] = useState(true);
|
||||||
@@ -23,7 +24,7 @@ export default function Sidebar({ view, onChangeView, darkMode, setDarkMode }) {
|
|||||||
if (["settings", "personal", "security"].includes(view)) {
|
if (["settings", "personal", "security"].includes(view)) {
|
||||||
return [
|
return [
|
||||||
{icon: IoPersonCircleSharp, name: "Personal Info", view: "personal"},
|
{icon: IoPersonCircleSharp, name: "Personal Info", view: "personal"},
|
||||||
{icon: MdOutlineSecurity, name: "Security", view: "security"},
|
{icon: KeyRound, name: "Security", view: "security"},
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
return [
|
return [
|
||||||
|
|||||||
+11
-1
@@ -149,10 +149,20 @@ const config = defineConfig({
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
'.leaflet-container': {
|
'.leaflet-container': {
|
||||||
|
fontFamily: "georgia, system-ui, sans-serif !important",
|
||||||
_dark: {
|
_dark: {
|
||||||
filter: "invert(100%) hue-rotate(180deg) brightness(95%) contrast(90%)",
|
filter: "invert(100%) hue-rotate(180deg) brightness(95%) contrast(90%)",
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
".leaflet-popup-content": {
|
||||||
|
fontFamily: "georgia, system-ui, sans-serif !important"
|
||||||
|
},
|
||||||
|
".leaflet-control a": {
|
||||||
|
fontFamily: "georgia, system-ui, sans-serif !important"
|
||||||
|
},
|
||||||
|
".leaflet-tooltip": {
|
||||||
|
fontFamily: "georgia, system-ui, sans-serif !important"
|
||||||
|
},
|
||||||
".react-grid-item": {
|
".react-grid-item": {
|
||||||
transition: "none !important"
|
transition: "none !important"
|
||||||
},
|
},
|
||||||
@@ -166,7 +176,7 @@ const config = defineConfig({
|
|||||||
primary: { value: {base: "#2B98F8", _dark: "#2B98F8"}},
|
primary: { value: {base: "#2B98F8", _dark: "#2B98F8"}},
|
||||||
primaryLt: { value: {base: "#98cdffff", _dark: "#98cdffff"}},
|
primaryLt: { value: {base: "#98cdffff", _dark: "#98cdffff"}},
|
||||||
secondaryBG: { value: {base: "#EDF1F1", _dark: "#121212"}},
|
secondaryBG: { value: {base: "#EDF1F1", _dark: "#121212"}},
|
||||||
border: {value: {base: "#000000", _dark: "#CBCBCB"}},
|
border: {value: {base: "#000000", _dark: "#cbcbcb84"}},
|
||||||
text: {value: {base: "#000000", _dark: "lightgrey"}},
|
text: {value: {base: "#000000", _dark: "lightgrey"}},
|
||||||
textInverted: {value: "white"},
|
textInverted: {value: "white"},
|
||||||
icon: {value: {base: "#000000", _dark: "lightgrey"}},
|
icon: {value: {base: "#000000", _dark: "lightgrey"}},
|
||||||
|
|||||||
Reference in New Issue
Block a user