diff --git a/app/(main)/ClientComponents/main/MapTooltip.tsx b/app/(main)/ClientComponents/main/MapTooltip.tsx index b684d27..7a1a424 100644 --- a/app/(main)/ClientComponents/main/MapTooltip.tsx +++ b/app/(main)/ClientComponents/main/MapTooltip.tsx @@ -1,6 +1,5 @@ "use client" -import { AnimatePresence, m } from "framer-motion" import { useTranslations } from "next-intl" import { memo } from "react" @@ -17,50 +16,45 @@ const MapTooltip = memo(function MapTooltip() { }) return ( - - { + e.stopPropagation() + }} + > +
+

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

+

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

+
+
{ - e.stopPropagation() + maxHeight: "200px", + overflowY: "auto", }} > -
-

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

-

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

-
-
- {sortedServers.map((server, index) => ( -
- - {server.name} -
- ))} -
- - + {sortedServers.map((server, index) => ( +
+ + {server.name} +
+ ))} +
+ ) }) diff --git a/app/globals.css b/app/globals.css new file mode 100644 index 0000000..e69de29 diff --git a/app/layout.tsx b/app/layout.tsx index e476cd5..deddd14 100644 --- a/app/layout.tsx +++ b/app/layout.tsx @@ -1,6 +1,5 @@ // @auto-i18n-check. Please do not delete the line. import { ThemeColorManager } from "@/components/ThemeColorManager" -import { MotionProvider } from "@/components/motion/motion-provider" import getEnv from "@/lib/env-entry" import { FilterProvider } from "@/lib/network-filter-context" import { StatusProvider } from "@/lib/status-context" @@ -64,23 +63,21 @@ export default async function LocaleLayout({ children }: { children: React.React /> - - - - - - - {children} - - - - - + + + + + + {children} + + + + ) diff --git a/bun.lockb b/bun.lockb index 995cfe2..536bc6a 100755 Binary files a/bun.lockb and b/bun.lockb differ diff --git a/components/Switch.tsx b/components/Switch.tsx index 81213cd..f066307 100644 --- a/components/Switch.tsx +++ b/components/Switch.tsx @@ -2,9 +2,8 @@ import getEnv from "@/lib/env-entry" import { cn } from "@/lib/utils" -import { m } from "framer-motion" import { useTranslations } from "next-intl" -import React, { createRef, useEffect, useRef } from "react" +import React, { createRef, useEffect, useRef, useState } from "react" export default function Switch({ allTag, @@ -20,6 +19,7 @@ export default function Switch({ const scrollRef = useRef(null) const tagRefs = useRef(allTag.map(() => createRef())) const t = useTranslations("ServerListClient") + const [indicator, setIndicator] = useState<{ x: number; w: number }>({ x: 0, w: 0 }) useEffect(() => { const savedTag = sessionStorage.getItem("selectedTag") @@ -48,42 +48,49 @@ export default function Switch({ }, []) useEffect(() => { - const currentTagRef = tagRefs.current[allTag.indexOf(nowTag)] - if (currentTagRef && currentTagRef.current) { - currentTagRef.current.scrollIntoView({ - behavior: "smooth", - block: "nearest", - inline: "center", + const currentTagElement = tagRefs.current[allTag.indexOf(nowTag)]?.current + if (currentTagElement) { + const parentPadding = 1 + setIndicator({ + x: + allTag.indexOf(nowTag) !== 0 + ? currentTagElement.offsetLeft - parentPadding + : currentTagElement.offsetLeft, + w: currentTagElement.offsetWidth, }) } - }, [nowTag]) + }, [nowTag, allTag]) return (
-
+
+ {indicator.w > 0 && ( +
+ )} {allTag.map((tag, index) => (
onTagChange(tag)} className={cn( - "relative cursor-pointer rounded-3xl px-2.5 py-[8px] text-[13px] font-[600] transition-all duration-500", - nowTag === tag ? "text-black dark:text-white" : "text-stone-400 dark:text-stone-500", + "relative cursor-pointer rounded-3xl px-2.5 py-[8px] text-[13px] font-[600]", + "transition-all duration-500 ease-in-out text-stone-400 dark:text-stone-500", + { + "text-stone-950 dark:text-stone-50": nowTag === tag, + }, )} > - {nowTag === tag && ( - - )}
{tag === "defaultTag" ? t("defaultTag") : tag}{" "} diff --git a/components/TabSwitch.tsx b/components/TabSwitch.tsx index f6a1039..5e14e03 100644 --- a/components/TabSwitch.tsx +++ b/components/TabSwitch.tsx @@ -1,9 +1,8 @@ "use client" import { cn } from "@/lib/utils" -import { m } from "framer-motion" import { useTranslations } from "next-intl" -import React from "react" +import React, { useEffect, useRef, useState } from "react" export default function TabSwitch({ tabs, @@ -15,30 +14,53 @@ export default function TabSwitch({ setCurrentTab: (tab: string) => void }) { const t = useTranslations("TabSwitch") + const [indicator, setIndicator] = useState<{ x: number; w: number }>({ x: 0, w: 0 }) + const tabRefs = useRef<(HTMLDivElement | null)[]>([]) + + useEffect(() => { + const currentTabElement = tabRefs.current[tabs.indexOf(currentTab)] + if (currentTabElement) { + // 考虑父元素的padding和gap + const parentPadding = 1 // p-[3px] + setIndicator({ + x: + tabs.indexOf(currentTab) !== 0 + ? currentTabElement.offsetLeft - parentPadding + : currentTabElement.offsetLeft, + w: currentTabElement.offsetWidth, + }) + } + }, [currentTab, tabs]) + return (
-
- {tabs.map((tab: string) => ( +
+ {indicator.w > 0 && ( +
+ )} + {tabs.map((tab: string, index) => (
{ + tabRefs.current[index] = el + }} onClick={() => setCurrentTab(tab)} className={cn( - "relative cursor-pointer rounded-3xl px-2.5 py-[8px] text-[13px] font-[600] transition-all duration-500", - currentTab === tab - ? "text-black dark:text-white" - : "text-stone-400 dark:text-stone-500", + "relative cursor-pointer rounded-3xl px-2.5 py-[8px] text-[13px] font-[600]", + "transition-all duration-500 ease-in-out text-stone-400 dark:text-stone-500", + { + "text-stone-950 dark:text-stone-50": currentTab === tab, + }, )} > - {currentTab === tab && ( - - )}

{t(tab)}

diff --git a/components/motion/framer-lazy-feature.ts b/components/motion/framer-lazy-feature.ts deleted file mode 100644 index b61a58f..0000000 --- a/components/motion/framer-lazy-feature.ts +++ /dev/null @@ -1 +0,0 @@ -export { domMax as default } from "framer-motion" diff --git a/components/motion/motion-provider.tsx b/components/motion/motion-provider.tsx deleted file mode 100644 index 7c9aaa5..0000000 --- a/components/motion/motion-provider.tsx +++ /dev/null @@ -1,13 +0,0 @@ -"use client" - -import { LazyMotion } from "framer-motion" - -const loadFeatures = () => import("./framer-lazy-feature").then((res) => res.default) - -export const MotionProvider = ({ children }: { children: React.ReactNode }) => { - return ( - - {children} - - ) -} diff --git a/package.json b/package.json index 82baba6..3a6f560 100644 --- a/package.json +++ b/package.json @@ -40,7 +40,6 @@ "d3-selection": "^3.0.0", "eslint-plugin-simple-import-sort": "^12.1.1", "flag-icons": "^7.2.3", - "framer-motion": "^12.0.0-alpha.2", "lucide-react": "^0.454.0", "luxon": "^3.5.0", "maxmind": "^4.3.23", diff --git a/styles/globals.css b/styles/globals.css index 215284e..7b66eab 100644 --- a/styles/globals.css +++ b/styles/globals.css @@ -317,3 +317,20 @@ .scrollbar-hidden::-webkit-scrollbar { display: none; /* Chrome, Safari 和 Opera */ } + +.tooltip-animate { + opacity: 0; + filter: blur(10px); + animation: tooltip-fade-in 0.2s ease-in-out forwards; +} + +@keyframes tooltip-fade-in { + from { + opacity: 0; + filter: blur(10px); + } + to { + opacity: 1; + filter: blur(0px); + } +}