From ba6186aababb6ad6e6d6f36e5f3565800cc562e2 Mon Sep 17 00:00:00 2001 From: hamster1963 <1410514192@qq.com> Date: Sat, 7 Dec 2024 19:25:36 +0800 Subject: [PATCH] feat(global): new tooltip --- app/(main)/ClientComponents/Global.tsx | 18 ++-- .../ClientComponents/InteractiveMap.tsx | 83 ++++++++++-------- app/(main)/ClientComponents/MapTooltip.tsx | 65 ++++++++++++++ .../ClientComponents/TooltipContext.tsx | 38 ++++++++ bun.lockb | Bin 608672 -> 608672 bytes 5 files changed, 161 insertions(+), 43 deletions(-) create mode 100644 app/(main)/ClientComponents/MapTooltip.tsx create mode 100644 app/(main)/ClientComponents/TooltipContext.tsx diff --git a/app/(main)/ClientComponents/Global.tsx b/app/(main)/ClientComponents/Global.tsx index cb58e6b..5edc482 100644 --- a/app/(main)/ClientComponents/Global.tsx +++ b/app/(main)/ClientComponents/Global.tsx @@ -8,6 +8,7 @@ import { geoJsonString } from "../../../lib/geo-json-string"; import GlobalInfo from "./GlobalInfo"; import GlobalLoading from "./GlobalLoading"; import { InteractiveMap } from "./InteractiveMap"; +import { TooltipProvider } from "./TooltipContext"; export default function ServerGlobal() { const { data: nezhaServerList, error } = useSWR( @@ -51,13 +52,16 @@ export default function ServerGlobal() {
- + + +
); diff --git a/app/(main)/ClientComponents/InteractiveMap.tsx b/app/(main)/ClientComponents/InteractiveMap.tsx index 3f0da5e..8b50e78 100644 --- a/app/(main)/ClientComponents/InteractiveMap.tsx +++ b/app/(main)/ClientComponents/InteractiveMap.tsx @@ -2,9 +2,10 @@ import { countryCoordinates } from "@/lib/geo-limit"; import { geoEquirectangular, geoPath } from "d3-geo"; -import { AnimatePresence, m } from "framer-motion"; import { useTranslations } from "next-intl"; -import { useState } from "react"; + +import MapTooltip from "./MapTooltip"; +import { useTooltip } from "./TooltipContext"; interface InteractiveMapProps { countries: string[]; @@ -12,6 +13,7 @@ interface InteractiveMapProps { width: number; height: number; filteredFeatures: any[]; + nezhaServerList: any; } export function InteractiveMap({ @@ -20,14 +22,10 @@ export function InteractiveMap({ width, height, filteredFeatures, + nezhaServerList, }: InteractiveMapProps) { const t = useTranslations("Global"); - - const [tooltipData, setTooltipData] = useState<{ - centroid: [number, number]; - country: string; - count: number; - } | null>(null); + const { setTooltipData } = useTooltip(); const projection = geoEquirectangular() .scale(140) @@ -37,7 +35,10 @@ export function InteractiveMap({ const path = geoPath().projection(projection); return ( -
+
setTooltipData(null)} + > + {/* Background rect to handle mouse events in empty areas */} + setTooltipData(null)} + /> {filteredFeatures.map((feature, index) => { const isHighlighted = countries.includes( feature.properties.iso_a2_eh, @@ -72,15 +82,30 @@ export function InteractiveMap({ : "fill-neutral-200/50 dark:fill-neutral-800 stroke-neutral-300/40 dark:stroke-neutral-700 stroke-[0.5]" } onMouseEnter={() => { - if (isHighlighted && path.centroid(feature)) { + if (!isHighlighted) { + setTooltipData(null); + return; + } + if (path.centroid(feature)) { + const countryCode = feature.properties.iso_a2_eh; + const countryServers = nezhaServerList.result + .filter( + (server: any) => + server.host.CountryCode?.toUpperCase() === + countryCode, + ) + .map((server: any) => ({ + name: server.name, + status: server.online_status, + })); setTooltipData({ centroid: path.centroid(feature), country: feature.properties.name, count: serverCount, + servers: countryServers, }); } }} - onMouseLeave={() => setTooltipData(null)} /> ); })} @@ -107,13 +132,22 @@ export function InteractiveMap({ { + const countryServers = nezhaServerList.result + .filter( + (server: any) => + server.host.CountryCode?.toUpperCase() === countryCode, + ) + .map((server: any) => ({ + name: server.name, + status: server.online_status, + })); setTooltipData({ centroid: [x, y], country: coords.name, count: serverCount, + servers: countryServers, }); }} - onMouseLeave={() => setTooltipData(null)} className="cursor-pointer" > - - {tooltipData && ( - -

- {tooltipData.country === "China" - ? "Mainland China" - : tooltipData.country} -

-

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

-
- )} -
+
); } diff --git a/app/(main)/ClientComponents/MapTooltip.tsx b/app/(main)/ClientComponents/MapTooltip.tsx new file mode 100644 index 0000000..70eace3 --- /dev/null +++ b/app/(main)/ClientComponents/MapTooltip.tsx @@ -0,0 +1,65 @@ +"use client"; + +import { AnimatePresence, m } from "framer-motion"; +import { useTranslations } from "next-intl"; +import { memo } from "react"; + +import { useTooltip } from "./TooltipContext"; + +const MapTooltip = memo(function MapTooltip() { + const { tooltipData } = useTooltip(); + const t = useTranslations("Global"); + + if (!tooltipData) return null; + + return ( + + { + e.stopPropagation(); + }} + > +
+

+ {tooltipData.country === "China" + ? "Mainland China" + : tooltipData.country} +

+

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

+
+
+ {tooltipData.servers.map((server, index) => ( +
+ + {server.name} +
+ ))} +
+
+
+ ); +}); + +export default MapTooltip; diff --git a/app/(main)/ClientComponents/TooltipContext.tsx b/app/(main)/ClientComponents/TooltipContext.tsx new file mode 100644 index 0000000..6674db9 --- /dev/null +++ b/app/(main)/ClientComponents/TooltipContext.tsx @@ -0,0 +1,38 @@ +"use client"; + +import { ReactNode, createContext, useContext, useState } from "react"; + +export interface TooltipData { + centroid: [number, number]; + country: string; + count: number; + servers: Array<{ + name: string; + status: boolean; + }>; +} + +interface TooltipContextType { + tooltipData: TooltipData | null; + setTooltipData: (data: TooltipData | null) => void; +} + +const TooltipContext = createContext(undefined); + +export function TooltipProvider({ children }: { children: ReactNode }) { + const [tooltipData, setTooltipData] = useState(null); + + return ( + + {children} + + ); +} + +export function useTooltip() { + const context = useContext(TooltipContext); + if (context === undefined) { + throw new Error("useTooltip must be used within a TooltipProvider"); + } + return context; +} diff --git a/bun.lockb b/bun.lockb index 5bda9a4d78fe18dcea6781faf1044961f88f93b3..573b35a679d4181565990c7514355db1b1e6d27d 100755 GIT binary patch delta 56 zcmV-80LTBJlO>>&C4hthgaU*Egaot&u-F4OE-;6)*af$<*aokMmxwnB4!3Y%2MYoL OH-~&;2e*7;2qfogjui|5 delta 53 zcmV-50LuTMlO>>&C4hthgaU*Egaot&u-F4NE;EO-*af$<*aokM0X4T{U