v1.1: Add CIDR subnet filtering and Agent filter in Devices page
This commit is contained in:
commit
966058e2fe
422 files changed
+54656
No files matched your search
@@ -0,0 +1,152 @@
|
||||
"use client";
|
||||
|
||||
import React, { useMemo, useState } from 'react';
|
||||
import { ComposableMap, Geographies, Geography, Line, Marker, ZoomableGroup } from "react-simple-maps";
|
||||
import { useTheme } from "next-themes";
|
||||
import { useGlobeData, GlobeDataInput } from './useGlobeData';
|
||||
|
||||
const geoUrl = "/ne_110m_admin_0_countries.geojson";
|
||||
|
||||
interface GlobeMapProps {
|
||||
data: GlobeDataInput[];
|
||||
}
|
||||
|
||||
export const GlobeMap = ({ data }: GlobeMapProps) => {
|
||||
const { theme } = useTheme();
|
||||
const isLight = theme === 'light';
|
||||
|
||||
const [position, setPosition] = useState({ coordinates: [0, 20], zoom: 1 });
|
||||
function handleMoveEnd(newPosition: any) {
|
||||
setPosition(newPosition);
|
||||
}
|
||||
|
||||
const { arcsData, pointsData, labelsData } = useGlobeData(data, isLight);
|
||||
|
||||
const mapBgColor = "transparent";
|
||||
const mapBorderColor = isLight ? "#ffffff" : "#0ea5e9";
|
||||
const mapFillColor = isLight ? "#4ade80" : "#0f172a";
|
||||
const mapHoverFill = isLight ? "#22c55e" : "#1e293b";
|
||||
|
||||
return (
|
||||
<div className="relative w-full h-[500px] rounded-xl overflow-hidden bg-transparent flex items-center justify-center cursor-grab active:cursor-grabbing">
|
||||
<style>{`
|
||||
@keyframes flowDashGlobe {
|
||||
to {
|
||||
stroke-dashoffset: -100;
|
||||
}
|
||||
}
|
||||
.globe-animated-line {
|
||||
animation: flowDashGlobe 1.5s linear infinite;
|
||||
}
|
||||
`}</style>
|
||||
<ComposableMap
|
||||
projection="geoMercator"
|
||||
projectionConfig={{
|
||||
scale: 120,
|
||||
center: [0, 20]
|
||||
}}
|
||||
width={800}
|
||||
height={500}
|
||||
style={{ width: "100%", height: "100%", outline: "none" }}
|
||||
>
|
||||
<ZoomableGroup
|
||||
zoom={position.zoom}
|
||||
center={position.coordinates as [number, number]}
|
||||
onMoveEnd={handleMoveEnd}
|
||||
minZoom={1}
|
||||
maxZoom={8}
|
||||
>
|
||||
<Geographies geography={geoUrl}>
|
||||
{({ geographies }) =>
|
||||
geographies.map((geo) => (
|
||||
<Geography
|
||||
key={geo.rsmKey}
|
||||
geography={geo}
|
||||
fill={mapFillColor}
|
||||
stroke={mapBorderColor}
|
||||
strokeWidth={0.5}
|
||||
style={{
|
||||
default: { outline: "none" },
|
||||
hover: { fill: mapHoverFill, outline: "none" },
|
||||
pressed: { outline: "none" },
|
||||
}}
|
||||
/>
|
||||
))
|
||||
}
|
||||
</Geographies>
|
||||
|
||||
{arcsData.map((arc: any, i: number) => {
|
||||
const isLocal = arc.startLng === arc.endLng && arc.startLat === arc.endLat;
|
||||
const color = Array.isArray(arc.color) ? arc.color[0] : arc.color || '#0ea5e9';
|
||||
|
||||
if (isLocal) {
|
||||
return (
|
||||
<Marker key={`arc-${i}`} coordinates={[arc.startLng, arc.startLat]}>
|
||||
<circle r={8} fill="none" stroke={color} strokeWidth={1.5} opacity={0.6} strokeDasharray="2,2" />
|
||||
</Marker>
|
||||
);
|
||||
}
|
||||
|
||||
// Force the line to go straight across the map (the "front")
|
||||
// by interpolating multiple points. This prevents d3-geo from
|
||||
// wrapping around the dateline for long distances.
|
||||
const coordinates = [];
|
||||
const steps = 30;
|
||||
for (let j = 0; j <= steps; j++) {
|
||||
const t = j / steps;
|
||||
let lng = arc.startLng + (arc.endLng - arc.startLng) * t;
|
||||
|
||||
// Dynamic arc height based on distance
|
||||
const dist = Math.sqrt(
|
||||
Math.pow(arc.endLng - arc.startLng, 2) +
|
||||
Math.pow(arc.endLat - arc.startLat, 2)
|
||||
);
|
||||
const height = Math.min(dist * 0.15, 25);
|
||||
let lat = arc.startLat + (arc.endLat - arc.startLat) * t + Math.sin(t * Math.PI) * height;
|
||||
|
||||
coordinates.push([lng, lat]);
|
||||
}
|
||||
|
||||
return (
|
||||
<Line
|
||||
key={`arc-${i}`}
|
||||
coordinates={coordinates}
|
||||
stroke={color}
|
||||
strokeWidth={1.5}
|
||||
strokeLinecap="round"
|
||||
strokeOpacity={0.9}
|
||||
strokeDasharray="2 4 4 4 8 78"
|
||||
className="globe-animated-line"
|
||||
style={{ pointerEvents: 'none' }}
|
||||
/>
|
||||
);
|
||||
})}
|
||||
|
||||
{pointsData.map((pt: any, i: number) => (
|
||||
<Marker key={`marker-${i}`} coordinates={[pt.lng, pt.lat]}>
|
||||
<circle r={pt.size * 2} fill={pt.color} opacity={0.8} />
|
||||
</Marker>
|
||||
))}
|
||||
|
||||
{labelsData.map((lbl: any, i: number) => (
|
||||
<Marker key={`label-${i}`} coordinates={[lbl.lng, lbl.lat]}>
|
||||
<text
|
||||
textAnchor="middle"
|
||||
y={-10}
|
||||
style={{
|
||||
fontFamily: "system-ui",
|
||||
fill: lbl.color,
|
||||
fontSize: `${lbl.size * 6}px`,
|
||||
fontWeight: "bold",
|
||||
pointerEvents: "none"
|
||||
}}
|
||||
>
|
||||
{lbl.name}
|
||||
</text>
|
||||
</Marker>
|
||||
))}
|
||||
</ZoomableGroup>
|
||||
</ComposableMap>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
Reference in new issue
Block a user