From f043f5e3a9f1091ec9e9b120748d16126effb6f1 Mon Sep 17 00:00:00 2001 From: hamster1963 <1410514192@qq.com> Date: Mon, 25 Nov 2024 17:51:01 +0800 Subject: [PATCH] feat(global): tooltip --- app/(main)/ClientComponents/Global.tsx | 73 +++--------- .../ClientComponents/InteractiveMap.tsx | 108 ++++++++++++++++++ messages/en.json | 3 +- messages/ja.json | 3 +- messages/zh-t.json | 3 +- messages/zh.json | 3 +- 6 files changed, 129 insertions(+), 64 deletions(-) create mode 100644 app/(main)/ClientComponents/InteractiveMap.tsx diff --git a/app/(main)/ClientComponents/Global.tsx b/app/(main)/ClientComponents/Global.tsx index 0870a33..1982db0 100644 --- a/app/(main)/ClientComponents/Global.tsx +++ b/app/(main)/ClientComponents/Global.tsx @@ -1,91 +1,44 @@ -import { countryCodeMapping } from "@/lib/geo"; import { GetNezhaData } from "@/lib/serverFetch"; -import { geoEqualEarth, geoPath } from "d3-geo"; import { geoJsonString } from "../../../lib/geo-json-string"; import GlobalInfo from "./GlobalInfo"; - -interface GlobalProps { - countries?: string[]; -} +import { InteractiveMap } from "./InteractiveMap"; export default async function ServerGlobal() { const nezhaServerList = await GetNezhaData(); const countrytList: string[] = []; + const serverCounts: { [key: string]: number } = {}; + nezhaServerList.result.forEach((server) => { if (server.host.CountryCode) { - server.host.CountryCode = server.host.CountryCode.toUpperCase(); - if (!countrytList.includes(server.host.CountryCode)) { - countrytList.push(server.host.CountryCode); + const countryCode = server.host.CountryCode.toUpperCase(); + if (!countrytList.includes(countryCode)) { + countrytList.push(countryCode); } + serverCounts[countryCode] = (serverCounts[countryCode] || 0) + 1; } }); - return ; -} - -export async function Global({ countries = [] }: GlobalProps) { const width = 900; const height = 500; - const projection = geoEqualEarth() - .scale(180) - .translate([width / 2, height / 2]) - .rotate([0, 0]); // 调整旋转以优化显示效果 - - const path = geoPath().projection(projection); - const geoJson = JSON.parse(geoJsonString); - const countries_alpha3 = countries - .map((code) => countryCodeMapping[code]) - .filter((code) => code !== undefined); - const filteredFeatures = geoJson.features.filter( (feature: any) => feature.properties.iso_a3 !== "", ); return (
- +
- - - - - - - - {/* @ts-ignore */} - {filteredFeatures.map((feature, index) => { - const isHighlighted = countries_alpha3.includes( - feature.properties.iso_a3, - ); - return ( - - ); - })} - - + filteredFeatures={filteredFeatures} + />
); diff --git a/app/(main)/ClientComponents/InteractiveMap.tsx b/app/(main)/ClientComponents/InteractiveMap.tsx new file mode 100644 index 0000000..c6cf93f --- /dev/null +++ b/app/(main)/ClientComponents/InteractiveMap.tsx @@ -0,0 +1,108 @@ +"use client"; + +import { countryCodeMapping } from "@/lib/geo"; +import { geoEqualEarth, geoPath } from "d3-geo"; +import { useTranslations } from "next-intl"; +import { useState } from "react"; + +interface InteractiveMapProps { + countries: string[]; + serverCounts: { [key: string]: number }; + width: number; + height: number; + filteredFeatures: any[]; +} + +export function InteractiveMap({ + countries, + serverCounts, + width, + height, + filteredFeatures, +}: InteractiveMapProps) { + const t = useTranslations("Global"); + + const [tooltipData, setTooltipData] = useState<{ + centroid: [number, number]; + country: string; + count: number; + } | null>(null); + + const countries_alpha3 = countries + .map((code) => countryCodeMapping[code]) + .filter((code) => code !== undefined); + + const projection = geoEqualEarth() + .scale(180) + .translate([width / 2, height / 2]); + + const path = geoPath().projection(projection); + + return ( +
+ + + + + + + + {filteredFeatures.map((feature, index) => { + const isHighlighted = countries_alpha3.includes( + feature.properties.iso_a3, + ); + const countryCode = Object.entries(countryCodeMapping).find( + ([_, alpha3]) => alpha3 === feature.properties.iso_a3, + )?.[0]; + const serverCount = countryCode + ? serverCounts[countryCode] || 0 + : 0; + + return ( + { + if (isHighlighted && path.centroid(feature)) { + setTooltipData({ + centroid: path.centroid(feature), + country: feature.properties.name, + count: serverCount, + }); + } + }} + onMouseLeave={() => setTooltipData(null)} + /> + ); + })} + + + {tooltipData && ( +
+

{tooltipData.country}

+

+ {tooltipData.count} {t("Servers")} +

+
+ )} +
+ ); +} diff --git a/messages/en.json b/messages/en.json index 1d11b51..6818ff4 100644 --- a/messages/en.json +++ b/messages/en.json @@ -93,7 +93,8 @@ "Global": { "Loading": "Loading...", "Distributions": "Servers are distributed in", - "Regions": "Regions" + "Regions": "Regions", + "Servers": "servers" }, "NotFoundPage": { "h1_490-590_404NotFound": "404 Not Found", diff --git a/messages/ja.json b/messages/ja.json index 82a6e03..7c337ed 100644 --- a/messages/ja.json +++ b/messages/ja.json @@ -93,7 +93,8 @@ "Global": { "Loading": "Loading...", "Distributions": "サーバーは", - "Regions": "つの地域に分散されています" + "Regions": "つの地域に分散されています", + "Servers": "サーバー" }, "NotFoundPage": { "h1_490-590_404NotFound": "404 見つかりませんでした", diff --git a/messages/zh-t.json b/messages/zh-t.json index 9066286..fc63ce7 100644 --- a/messages/zh-t.json +++ b/messages/zh-t.json @@ -93,7 +93,8 @@ "Global": { "Loading": "載入中...", "Distributions": "伺服器分佈在", - "Regions": "個地區" + "Regions": "個地區", + "Servers": "個伺服器" }, "NotFoundPage": { "h1_490-590_404NotFound": "404 未找到", diff --git a/messages/zh.json b/messages/zh.json index 6a40e29..bdff0cb 100644 --- a/messages/zh.json +++ b/messages/zh.json @@ -93,7 +93,8 @@ "Global": { "Loading": "加载中...", "Distributions": "服务器分布在", - "Regions": "个地区" + "Regions": "个地区", + "Servers": "个服务器" }, "NotFoundPage": { "h1_490-590_404NotFound": "404 未找到",