feat: add GeoMap component for visualizing country-specific scan data.
This commit is contained in:
parent
57d6e3a449
commit
036500f6d1
|
|
@ -144,8 +144,16 @@ const GeoMap: React.FC<GeoMapProps> = ({ countryStats, totalScans }) => {
|
||||||
.domain([0, maxCount || 1])
|
.domain([0, maxCount || 1])
|
||||||
.range(['#E0F2FE', '#1E40AF']);
|
.range(['#E0F2FE', '#1E40AF']);
|
||||||
|
|
||||||
|
const [tooltipContent, setTooltipContent] = React.useState<{ name: string; count: number } | null>(null);
|
||||||
|
const [tooltipPos, setTooltipPos] = React.useState({ x: 0, y: 0 });
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="w-full h-full">
|
<div
|
||||||
|
className="w-full h-full relative group"
|
||||||
|
onMouseMove={(evt) => {
|
||||||
|
setTooltipPos({ x: evt.clientX, y: evt.clientY });
|
||||||
|
}}
|
||||||
|
>
|
||||||
<ComposableMap
|
<ComposableMap
|
||||||
projection="geoMercator"
|
projection="geoMercator"
|
||||||
projectionConfig={{
|
projectionConfig={{
|
||||||
|
|
@ -179,6 +187,13 @@ const GeoMap: React.FC<GeoMapProps> = ({ countryStats, totalScans }) => {
|
||||||
},
|
},
|
||||||
pressed: { outline: 'none' },
|
pressed: { outline: 'none' },
|
||||||
}}
|
}}
|
||||||
|
onMouseEnter={() => {
|
||||||
|
const { name } = geo.properties;
|
||||||
|
setTooltipContent({ name, count: scanCount });
|
||||||
|
}}
|
||||||
|
onMouseLeave={() => {
|
||||||
|
setTooltipContent(null);
|
||||||
|
}}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
})
|
})
|
||||||
|
|
@ -186,6 +201,24 @@ const GeoMap: React.FC<GeoMapProps> = ({ countryStats, totalScans }) => {
|
||||||
</Geographies>
|
</Geographies>
|
||||||
</ZoomableGroup>
|
</ZoomableGroup>
|
||||||
</ComposableMap>
|
</ComposableMap>
|
||||||
|
|
||||||
|
{tooltipContent && (
|
||||||
|
<div
|
||||||
|
className="fixed z-50 px-3 py-2 text-sm font-medium text-white bg-gray-900 rounded-lg shadow-xl pointer-events-none transform -translate-x-1/2 -translate-y-full"
|
||||||
|
style={{
|
||||||
|
left: tooltipPos.x,
|
||||||
|
top: tooltipPos.y - 10,
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<div className="flex items-center gap-2">
|
||||||
|
<span>{tooltipContent.name}</span>
|
||||||
|
<span className="text-gray-400">|</span>
|
||||||
|
<span className="font-bold text-blue-400">{tooltipContent.count} scans</span>
|
||||||
|
</div>
|
||||||
|
{/* Arrow */}
|
||||||
|
<div className="absolute bottom-0 left-1/2 -translate-x-1/2 translate-y-full w-0 h-0 border-l-4 border-r-4 border-t-4 border-l-transparent border-r-transparent border-t-gray-900"></div>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue