Inspector Panel
-
diff --git a/site/src/inspector_panel/SourcesRow.css b/site/src/inspector_panel/SourcesRow.css
new file mode 100644
index 00000000..10c17438
--- /dev/null
+++ b/site/src/inspector_panel/SourcesRow.css
@@ -0,0 +1,14 @@
+.sources-content {
+ margin-left: 20px;
+}
+
+.source-divider {
+ margin: 2px 0;
+ border-top: 1px solid var(--ifm-table-border-width) solid
+ var(--ifm-table-border-color);
+}
+
+.panel-row.sources {
+ overflow: auto;
+ max-height: 200px;
+}
diff --git a/site/src/inspector_panel/SourcesRow.jsx b/site/src/inspector_panel/SourcesRow.jsx
new file mode 100644
index 00000000..033c1e06
--- /dev/null
+++ b/site/src/inspector_panel/SourcesRow.jsx
@@ -0,0 +1,88 @@
+import InfoToolTip from "./InfoToolTip";
+import "./SourcesRow.css";
+
+function SourcesRow({ entity, mode, tips }) {
+ const sources = JSON.parse(entity["sources"]);
+
+ const getSourceLink = (source) => {
+ try {
+ switch (source.dataset) {
+ case "meta":
+ return `https://facebook.com/${source.record_id}`;
+ case "OpenStreetMap": {
+ const match = source.record_id.match(/^([nwr])(\d+)(@\d+)?$/i);
+ if (!match) return null;
+
+ const typeMap = {
+ n: "node",
+ w: "way",
+ r: "relation",
+ };
+
+ const type = typeMap[match[1].toLowerCase()];
+ const id = match[2];
+
+ return `https://www.openstreetmap.org/${type}/${id}`;
+ }
+ default:
+ return null;
+ }
+ } catch (error) {
+ console.error("Error generating source link:", error);
+ return null;
+ }
+ };
+
+ return (
+
+
+
sources:
+
+ {sources.map((source, index) => {
+ const url = getSourceLink(source);
+ return (
+
+
+ dataset:
+ {source.dataset}
+
+
+ {source.update_time && (
+
+ update_time:
+ {source.update_time}
+
+ )}
+ {source.property && (
+
+ property:
+ {source.property}
+
+ )}
+ {index < sources.length - 1 && (
+
+ )}
+
+ );
+ })}
+
+
+
+
+ );
+}
+
+export default SourcesRow;
diff --git a/site/src/inspector_panel/TableRow.css b/site/src/inspector_panel/TableRow.css
index ff2d282d..3894f545 100644
--- a/site/src/inspector_panel/TableRow.css
+++ b/site/src/inspector_panel/TableRow.css
@@ -11,8 +11,8 @@
word-break: break-all;
}
td:first-child {
- text-align: left;
- min-width: 40%;
+ text-align: right;
+ max-width: 40%;
justify-content: space-between;
word-break: normal;
border: none;
diff --git a/site/src/inspector_panel/ThemeIcon.jsx b/site/src/inspector_panel/ThemeIcon.jsx
new file mode 100644
index 00000000..1bfd1233
--- /dev/null
+++ b/site/src/inspector_panel/ThemeIcon.jsx
@@ -0,0 +1,32 @@
+import LocationOnIcon from "@mui/icons-material/LocationOn";
+import HomeIcon from "@mui/icons-material/Home";
+import TerrainIcon from "@mui/icons-material/Terrain";
+import DirectionsIcon from "@mui/icons-material/Directions";
+import FlagIcon from "@mui/icons-material/Flag";
+import ImportContactsIcon from "@mui/icons-material/ImportContacts";
+import PropTypes from "prop-types";
+
+function ThemeIcon({ theme }) {
+ switch (theme) {
+ case "places":
+ return
;
+ case "buildings":
+ return
;
+ case "base":
+ return
;
+ case "transportation":
+ return
;
+ case "divisions":
+ return
;
+ case "addresses":
+ return
;
+ default:
+ return <>>;
+ }
+}
+
+ThemeIcon.propTypes = {
+ theme: PropTypes.string.isRequired,
+};
+
+export default ThemeIcon;
diff --git a/site/src/inspector_panel/ThemePanel.css b/site/src/inspector_panel/ThemePanel.css
index 1159718d..5caf5c1e 100644
--- a/site/src/inspector_panel/ThemePanel.css
+++ b/site/src/inspector_panel/ThemePanel.css
@@ -1,5 +1,6 @@
div.theme-panel {
width: 350px;
+ padding: 9px 0;
}
caption.common-props {
diff --git a/site/src/inspector_panel/ThemePanel.jsx b/site/src/inspector_panel/ThemePanel.jsx
index 0243c94c..dfef4c49 100644
--- a/site/src/inspector_panel/ThemePanel.jsx
+++ b/site/src/inspector_panel/ThemePanel.jsx
@@ -1,12 +1,11 @@
import TableRow from "./TableRow";
import "./ThemePanel.css";
-import ExpandLessIcon from "@mui/icons-material/ExpandLess";
-import ExpandMoreIcon from "@mui/icons-material/ExpandMore";
-import { useState } from "react";
import IndentIcon from "../icons/icon-indent.svg?react";
import InfoToolTip from "./InfoToolTip";
import PushPinIcon from "@mui/icons-material/PushPin";
import PushPinOutlinedIcon from "@mui/icons-material/PushPinOutlined";
+import ThemeIcon from "./ThemeIcon";
+import SourcesRow from "./SourcesRow.jsx";
const sharedProperties = [
"theme",
@@ -19,14 +18,22 @@ const sharedProperties = [
];
function ThemePanel({ mode, entity, tips, activeThemes, setActiveThemes }) {
- const [commonExpanded, setCommonExpanded] = useState(false);
- const [otherExpanded, setOtherExpanded] = useState(false);
-
return (
+ {entity["id"] ? (
+
+
+ id:
+ {entity["id"]}
+
+
+
+ ) : (
+ <>>
+ )}
- Theme:
+ theme:
{entity["theme"]}
@@ -57,7 +64,7 @@ function ThemePanel({ mode, entity, tips, activeThemes, setActiveThemes }) {
- Type:
+ type:
{entity["type"]}
- Subtype:
+ subtype:
{entity["subtype"]}
>
)}
- {entity["id"] ? (
-
-
- ID:
- {entity["id"]}
-
-
-
- ) : (
- <>>
- )}
{entity["sources"] ? (
-
-
- Source(s):{" "}
- {[...new Set(JSON.parse(entity["sources"]).map((source) => source["dataset"]))].join(', ')}
-
-
-
+
) : (
<>>
)}
- {entity["class"] ? (
-
+ {["version"].map((key) => (
+
- Class: {entity["class"]}
+ {key}:
+ {entity[key]}
-
{" "}
- ) : (
- ""
- )}
-
-
-
- setCommonExpanded(!commonExpanded)}
- >
- Common Properties{" "}
- {commonExpanded ? : }
-
- {" "}
- {commonExpanded ? (
-
- {["update_time", "version"].map((key) => (
-
- ))}
-
- ) : (
-
- )}
-
-
-
-
-
- setOtherExpanded(!otherExpanded)}
- >
- Other Properties{" "}
- {otherExpanded ? : }
-
- {" "}
- {otherExpanded ? (
-
- {Object.keys(entity)
- .filter((key) => !key.startsWith("@"))
- .filter((key) => !sharedProperties.includes(key))
- .map((key) => (
-
- ))}
-
- ) : (
-
- )}
-
-
+ ))}
+ {Object.keys(entity)
+ .filter((key) => !key.startsWith("@"))
+ .filter((key) => !sharedProperties.includes(key))
+ .map((key) => (
+
+ ))}
);
}
diff --git a/site/src/navigator/Navigator.jsx b/site/src/navigator/Navigator.jsx
index 29a86ef6..e3626ac4 100644
--- a/site/src/navigator/Navigator.jsx
+++ b/site/src/navigator/Navigator.jsx
@@ -4,6 +4,20 @@ import ArrowForwardIosIcon from "@mui/icons-material/ArrowForwardIos";
import FlightTakeoffIcon from "@mui/icons-material/FlightTakeoff";
import { useMap } from "react-map-gl/maplibre";
import { tours } from "./NavigatorConfig";
+import { useState, useEffect } from "react";
+
+export function useNavigatorState(initialOpen = false) {
+ const [navigatorOpen, setNavigatorOpen] = useState(() => {
+ const stored = localStorage.getItem("navigatorOpen");
+ return stored !== null ? JSON.parse(stored) : !initialOpen;
+ });
+
+ useEffect(() => {
+ localStorage.setItem("navigatorOpen", JSON.stringify(navigatorOpen));
+ }, [navigatorOpen]);
+
+ return [navigatorOpen, setNavigatorOpen];
+}
function Navigator({ open, setOpen, map, setVisibleTypes, setActiveThemes }) {
const { myMap } = useMap();