diff --git a/src/app/map/[id]/components/inspector/ClusterMarkersList.tsx b/src/app/map/[id]/components/inspector/ClusterMarkersList.tsx
new file mode 100644
index 00000000..3b43e725
--- /dev/null
+++ b/src/app/map/[id]/components/inspector/ClusterMarkersList.tsx
@@ -0,0 +1,48 @@
+import { useInspector } from "@/app/map/[id]/hooks/useInspector";
+import { DataSourceRecordType } from "@/server/models/DataSource";
+import { MarkersList, MembersList } from "./MarkersLists";
+import type { MarkerFeature } from "@/types";
+
+export default function ClusterMarkersList() {
+ const { selectedRecords, inspectorContent } = useInspector();
+ const recordType = inspectorContent?.dataSource?.recordType;
+ const markerFeatures = selectedRecords
+ .map((r): MarkerFeature | null => {
+ if (!r.dataSourceId || !r.geocodePoint) {
+ // Should never happen for markers in a cluster
+ return null;
+ }
+ return {
+ type: "Feature",
+ geometry: {
+ // [0, 0] should never happen because these records are in a cluster on the map
+ coordinates: [r.geocodePoint?.lng || 0, r.geocodePoint?.lat || 0],
+ type: "Point",
+ },
+ properties: {
+ id: r.id,
+ name: r.name,
+ dataSourceId: r.dataSourceId,
+ matched: true,
+ },
+ };
+ })
+ .filter((r) => r !== null);
+
+ return (
+
+ {recordType === DataSourceRecordType.Members ? (
+
+ ) : (
+
+ )}
+
+ );
+}
diff --git a/src/app/map/[id]/components/inspector/InspectorMarkersTab.tsx b/src/app/map/[id]/components/inspector/InspectorMarkersTab.tsx
index 197a65f3..0f984a3f 100644
--- a/src/app/map/[id]/components/inspector/InspectorMarkersTab.tsx
+++ b/src/app/map/[id]/components/inspector/InspectorMarkersTab.tsx
@@ -1,5 +1,6 @@
import { LayerType } from "@/types";
import BoundaryMarkersList from "./BoundaryMarkersList";
+import ClusterMarkersList from "./ClusterMarkersList";
import TurfMarkersList from "./TurfMarkersList";
interface InspectorMarkersTabProps {
@@ -11,6 +12,7 @@ export default function InspectorMarkersTab({
}: InspectorMarkersTabProps) {
return (
+ {type === LayerType.Cluster && }
{type === LayerType.Turf && }
{type === LayerType.Boundary && }
diff --git a/src/app/map/[id]/components/inspector/InspectorPanel.tsx b/src/app/map/[id]/components/inspector/InspectorPanel.tsx
index fc0ca039..5f6318a1 100644
--- a/src/app/map/[id]/components/inspector/InspectorPanel.tsx
+++ b/src/app/map/[id]/components/inspector/InspectorPanel.tsx
@@ -1,5 +1,5 @@
import { ArrowLeftIcon, SettingsIcon, XIcon } from "lucide-react";
-import { useState } from "react";
+import { useMemo, useState } from "react";
import { useInspector } from "@/app/map/[id]/hooks/useInspector";
import { cn } from "@/shadcn/utils";
@@ -26,12 +26,26 @@ export default function InspectorPanel() {
setFocusedRecord,
selectedRecords,
} = useInspector();
+ const { dataSource, properties, type } = inspectorContent ?? {};
+
+ const safeActiveTab = useMemo(() => {
+ if (activeTab === "data" && type === LayerType.Cluster) {
+ return "markers";
+ }
+ const isMarkers = type === LayerType.Marker || type === LayerType.Member;
+ if (activeTab === "markers" && isMarkers) {
+ return "data";
+ }
+ if (activeTab === "config" && type !== LayerType.Boundary) {
+ return type === LayerType.Cluster ? "markers" : "data";
+ }
+ return activeTab;
+ }, [activeTab, type]);
if (!Boolean(inspectorContent)) {
return <>>;
}
- const { dataSource, properties, type } = inspectorContent ?? {};
const isDetailsView = Boolean(
(selectedTurf && type !== LayerType.Turf) ||
(selectedBoundary && type !== LayerType.Boundary),
@@ -49,17 +63,17 @@ export default function InspectorPanel() {
className={cn(
"absolute top-0 bottom-0 right-4 / flex flex-col gap-6 py-5",
"bottom-24", // to avoid clash with bug report button
- activeTab === "config" ? "h-full" : "h-fit max-h-full",
+ safeActiveTab === "config" ? "h-full" : "h-fit max-h-full",
)}
style={{
- minWidth: activeTab === "config" ? "400px" : "250px",
+ minWidth: safeActiveTab === "config" ? "400px" : "250px",
maxWidth: "450px",
}}
>
@@ -98,12 +112,14 @@ export default function InspectorPanel() {
- Data
+ {type !== LayerType.Cluster && (
+ Data
+ )}
Notes 0
-
-
-
+ {type === LayerType.Boundary && (
+
+
+
+ )}
-
-
-
+ {type !== LayerType.Cluster && (
+
+
+
+ )}
-
-
-
+ {type === LayerType.Boundary && (
+
+
+
+ )}
diff --git a/src/app/map/[id]/components/inspector/MarkersLists.tsx b/src/app/map/[id]/components/inspector/MarkersLists.tsx
index c22195b4..e10d5778 100644
--- a/src/app/map/[id]/components/inspector/MarkersLists.tsx
+++ b/src/app/map/[id]/components/inspector/MarkersLists.tsx
@@ -13,8 +13,8 @@ export const MembersList = ({
areaType,
}: {
markers: MarkerFeature[];
- dataSource: DataSource | null;
- areaType: "area" | "boundary";
+ dataSource: DataSource | undefined | null;
+ areaType: "area" | "boundary" | "cluster";
}) => {
const { setSelectedRecords } = useInspector();
@@ -68,7 +68,7 @@ export const MarkersList = ({
dataSource,
}: {
markers: MarkerFeature[];
- dataSource: DataSource | null;
+ dataSource: DataSource | undefined | null;
}) => {
const { setSelectedRecords } = useInspector();
const total = markers.length;
diff --git a/src/app/map/[id]/hooks/useInspector.ts b/src/app/map/[id]/hooks/useInspector.ts
index 8653c7b9..22e4301a 100644
--- a/src/app/map/[id]/hooks/useInspector.ts
+++ b/src/app/map/[id]/hooks/useInspector.ts
@@ -71,26 +71,34 @@ export function useInspector() {
}
const dataSourceId = focusedRecord.dataSourceId;
-
const dataSource = dataSourceId ? getDataSourceById(dataSourceId) : null;
+ if (selectedRecords.length > 1) {
+ return {
+ type: LayerType.Cluster,
+ name: "Cluster",
+ properties: null,
+ dataSource,
+ };
+ }
+
const type =
dataSourceId === mapConfig.membersDataSourceId
? LayerType.Member
: LayerType.Marker;
return {
- type: type,
+ type,
name: focusedRecord.name,
properties: null,
- dataSource: dataSource,
+ dataSource,
};
}, [
focusedRecord,
getDataSourceById,
mapConfig.membersDataSourceId,
selectedBoundary,
- selectedTurf?.id,
- selectedTurf?.name,
+ selectedRecords.length,
+ selectedTurf,
]);
const resetInspector = useCallback(() => {
diff --git a/src/types.ts b/src/types.ts
index cf4066a7..e96179a5 100644
--- a/src/types.ts
+++ b/src/types.ts
@@ -86,6 +86,7 @@ export interface FilterField {
export enum LayerType {
Boundary = "Boundary",
+ Cluster = "Cluster",
Member = "Member",
Marker = "Marker",
Turf = "Turf",