feat: 用 react-map-gl + MapLibre 替换 react-simple-maps 实现 2D 地图
- 替换 react-simple-maps/d3-geo/topojson-client 为 react-map-gl + maplibre-gl - 使用 CARTO dark-matter 免费暗色瓦片,自带国家/城市名标注 - 基于 locale 动态切换地图标注语言(name:zh / name_en) - MapLibre 原生 heatmap + circle 双层渲染替代 SVG 热力图 - 提取 MapPopup 组件,配合 react-map-gl Popup 实现点击弹窗 - continent page 改为 dynamic import (ssr: false) - dev 模式去掉 Turbopack 以兼容 maplibre-gl - 删除 heatmap-layer.tsx 和 react-simple-maps 类型声明
This commit is contained in:
60
components/map/map-popup.tsx
Normal file
60
components/map/map-popup.tsx
Normal file
@@ -0,0 +1,60 @@
|
||||
"use client";
|
||||
|
||||
import { useTranslations } from "next-intl";
|
||||
import type { HeatmapPoint } from "@/hooks/use-heatmap-data";
|
||||
|
||||
interface MapPopupProps {
|
||||
point: HeatmapPoint;
|
||||
onClose: () => void;
|
||||
}
|
||||
|
||||
export function MapPopup({ point, onClose }: MapPopupProps) {
|
||||
const t = useTranslations("continentMap");
|
||||
const tPopup = useTranslations("clawPopup");
|
||||
|
||||
return (
|
||||
<div className="min-w-[200px]">
|
||||
<div className="flex items-center justify-between">
|
||||
<div>
|
||||
<p className="font-mono text-sm font-medium text-[var(--accent-cyan)]">
|
||||
{point.city}
|
||||
</p>
|
||||
<p className="text-xs text-[var(--text-muted)]">{point.country}</p>
|
||||
</div>
|
||||
<button
|
||||
onClick={onClose}
|
||||
className="text-xs text-[var(--text-muted)] hover:text-[var(--text-primary)]"
|
||||
>
|
||||
✕
|
||||
</button>
|
||||
</div>
|
||||
<div className="mt-2 flex gap-2">
|
||||
<span className="rounded-full bg-[var(--accent-cyan)]/10 px-2 py-0.5 text-xs text-[var(--accent-cyan)]">
|
||||
{tPopup("total", { count: point.clawCount })}
|
||||
</span>
|
||||
<span className="rounded-full bg-green-500/10 px-2 py-0.5 text-xs text-green-400">
|
||||
{tPopup("online", { count: point.onlineCount })}
|
||||
</span>
|
||||
</div>
|
||||
{point.claws.length > 0 && (
|
||||
<div className="mt-3 max-h-40 space-y-1.5 overflow-y-auto">
|
||||
{point.claws.map((claw) => (
|
||||
<div key={claw.id} className="flex items-center gap-2">
|
||||
<span
|
||||
className={`h-1.5 w-1.5 rounded-full ${
|
||||
claw.isOnline ? "bg-green-400" : "bg-gray-500"
|
||||
}`}
|
||||
/>
|
||||
<span className="truncate font-mono text-xs text-[var(--text-secondary)]">
|
||||
{claw.name}
|
||||
</span>
|
||||
<span className="ml-auto text-[10px] text-[var(--text-muted)]">
|
||||
{claw.isOnline ? t("online") : t("offline")}
|
||||
</span>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user