Files
openclaw-market/components/map/heatmap-layer.tsx
richarjiang fa4c458eda init
2026-03-13 11:00:01 +08:00

67 lines
1.9 KiB
TypeScript

"use client";
import { motion } from "framer-motion";
import type { HeatmapPoint } from "@/hooks/use-heatmap-data";
interface HeatmapLayerProps {
points: HeatmapPoint[];
projection: (coords: [number, number]) => [number, number] | null;
onPointClick?: (point: HeatmapPoint) => void;
}
export function HeatmapLayer({ points, projection, onPointClick }: HeatmapLayerProps) {
return (
<g>
{points.map((point, i) => {
const coords = projection([point.lng, point.lat]);
if (!coords) return null;
const [x, y] = coords;
const radius = Math.max(point.weight * 3, 4);
return (
<g key={`${point.city}-${point.country}-${i}`}>
{/* Glow */}
<motion.circle
cx={x}
cy={y}
r={radius * 2}
fill="rgba(0, 240, 255, 0.1)"
initial={{ scale: 0 }}
animate={{ scale: [1, 1.3, 1] }}
transition={{ duration: 2, repeat: Infinity, delay: i * 0.1 }}
/>
{/* Main dot */}
<motion.circle
cx={x}
cy={y}
r={radius}
fill="rgba(0, 240, 255, 0.6)"
stroke="rgba(0, 240, 255, 0.8)"
strokeWidth={1}
className="cursor-pointer"
initial={{ scale: 0 }}
animate={{ scale: 1 }}
transition={{ duration: 0.5, delay: i * 0.05 }}
whileHover={{ scale: 1.5 }}
onClick={() => onPointClick?.(point)}
/>
{/* Count label */}
{point.lobsterCount > 1 && (
<text
x={x}
y={y - radius - 4}
textAnchor="middle"
fill="#00f0ff"
fontSize={9}
fontFamily="var(--font-mono)"
>
{point.lobsterCount}
</text>
)}
</g>
);
})}
</g>
);
}