From 42f99a6f843c567b5944007104473d0991211d4b Mon Sep 17 00:00:00 2001
From: hamster1963 <1410514192@qq.com>
Date: Sun, 28 Dec 2025 16:11:52 +0800
Subject: [PATCH] feat: implement animated count component and update header
for real-time display
---
bun.lock | 9 +---
package.json | 3 +-
src/components/AnimatedCount.tsx | 79 ++++++++++++++++++++++++++++++++
src/components/Header.tsx | 69 +++++++++++++++++++---------
src/index.css | 51 +++++++++++++++++++++
5 files changed, 179 insertions(+), 32 deletions(-)
create mode 100644 src/components/AnimatedCount.tsx
diff --git a/bun.lock b/bun.lock
index 3ea41aa..587cfc3 100644
--- a/bun.lock
+++ b/bun.lock
@@ -6,7 +6,6 @@
"dependencies": {
"@fontsource/inter": "5.1.1",
"@heroicons/react": "2.2.0",
- "@number-flow/react": "0.5.5",
"@radix-ui/react-accordion": "1.2.3",
"@radix-ui/react-checkbox": "1.1.4",
"@radix-ui/react-dialog": "1.1.6",
@@ -44,7 +43,7 @@
"recharts": "2.15.1",
"sonner": "1.7.4",
"tailwind-merge": "2.6.0",
- "tailwindcss-animate": "1.0.7",
+ "tailwindcss-animate": "^1.0.7",
},
"devDependencies": {
"@eslint/js": "9.20.0",
@@ -190,8 +189,6 @@
"@nodelib/fs.walk": ["@nodelib/fs.walk@1.2.8", "", { "dependencies": { "@nodelib/fs.scandir": "2.1.5", "fastq": "^1.6.0" } }, "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg=="],
- "@number-flow/react": ["@number-flow/react@0.5.5", "", { "dependencies": { "esm-env": "^1.1.4", "number-flow": "0.5.3" }, "peerDependencies": { "react": "^18 || ^19", "react-dom": "^18 || ^19" } }, "sha512-Zdju5n0osxrb+7jbcpUJ9L2VJ2+9ptwjz5+A+2wq9Q32hs3PW/noPJjHtLTrtGINM9mEw76DcDg0ac/dx6j1aA=="],
-
"@radix-ui/number": ["@radix-ui/number@1.1.0", "", {}, "sha512-V3gRzhVNU1ldS5XhAPTom1fOIo4ccrjjJgmE+LI2h/WaFpHmx0MQApT+KZHnx8abG6Avtfcz4WoEciMnpFT3HQ=="],
"@radix-ui/primitive": ["@radix-ui/primitive@1.1.1", "", {}, "sha512-SJ31y+Q/zAyShtXJc8x83i9TYdbAfHZ++tUZnvjJJqFjzsdUnKsxPL6IEtBlxKkU7yzer//GQtZSV4GbldL3YA=="],
@@ -530,8 +527,6 @@
"eslint-visitor-keys": ["eslint-visitor-keys@4.2.0", "", {}, "sha512-UyLnSehNt62FFhSwjZlHmeokpRK59rcz29j+F1/aDgbkbRTk7wIc9XzdoasMUbRNKDM0qQt/+BJ4BrpFeABemw=="],
- "esm-env": ["esm-env@1.2.2", "", {}, "sha512-Epxrv+Nr/CaL4ZcFGPJIYLWFom+YeV1DqMLHJoEd9SYRxNbaFruBwfEX/kkHUJf55j2+TUbmDcmuilbP1TmXHA=="],
-
"espree": ["espree@10.3.0", "", { "dependencies": { "acorn": "^8.14.0", "acorn-jsx": "^5.3.2", "eslint-visitor-keys": "^4.2.0" } }, "sha512-0QYC8b24HWY8zjRnDTL6RiHfDbAWn63qb4LMj1Z4b076A4une81+z03Kg7l7mn/48PUTqoLptSXez8oknU8Clg=="],
"esquery": ["esquery@1.6.0", "", { "dependencies": { "estraverse": "^5.1.0" } }, "sha512-ca9pw9fomFcKPvFLXhBKUK90ZvGibiGOvRJNbjljY7s7uq/5YO4BOzcYtJqExdx99rF6aAcnRxHmcUHcz6sQsg=="],
@@ -678,8 +673,6 @@
"natural-compare": ["natural-compare@1.4.0", "", {}, "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw=="],
- "number-flow": ["number-flow@0.5.3", "", { "dependencies": { "esm-env": "^1.1.4" } }, "sha512-iLKyssImNWQmJ41rza9K7P5lHRZTyishi/9FarWPLQHYY2Ydtl6eiXINEjZ1fa8dHeY0O7+YOD+Py3ZsJddYkg=="],
-
"object-assign": ["object-assign@4.1.1", "", {}, "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg=="],
"optionator": ["optionator@0.9.4", "", { "dependencies": { "deep-is": "^0.1.3", "fast-levenshtein": "^2.0.6", "levn": "^0.4.1", "prelude-ls": "^1.2.1", "type-check": "^0.4.0", "word-wrap": "^1.2.5" } }, "sha512-6IpQ7mKUxRcZNLIObR0hz7lxsapSSIYNZJwXPGeF0mTVqGKFIXj1DQcMoT22S3ROcLyY/rz0PWaWZ9ayWmad9g=="],
diff --git a/package.json b/package.json
index d735459..e4f619c 100644
--- a/package.json
+++ b/package.json
@@ -14,7 +14,6 @@
"dependencies": {
"@fontsource/inter": "5.1.1",
"@heroicons/react": "2.2.0",
- "@number-flow/react": "0.5.5",
"@radix-ui/react-accordion": "1.2.3",
"@radix-ui/react-checkbox": "1.1.4",
"@radix-ui/react-dialog": "1.1.6",
@@ -52,7 +51,7 @@
"recharts": "2.15.1",
"sonner": "1.7.4",
"tailwind-merge": "2.6.0",
- "tailwindcss-animate": "1.0.7"
+ "tailwindcss-animate": "^1.0.7"
},
"devDependencies": {
"@eslint/js": "9.20.0",
diff --git a/src/components/AnimatedCount.tsx b/src/components/AnimatedCount.tsx
new file mode 100644
index 0000000..fb623cd
--- /dev/null
+++ b/src/components/AnimatedCount.tsx
@@ -0,0 +1,79 @@
+import { cn } from "@/lib/utils"
+import { useEffect, useState } from "react"
+
+export function AnimateCountClient({ count, className, minDigits }: { count: number; className?: string; minDigits?: number }) {
+ const [previousCount, setPreviousCount] = useState(count)
+
+ useEffect(() => {
+ if (count !== previousCount) {
+ setTimeout(() => {
+ setPreviousCount(count)
+ }, 300)
+ }
+ }, [count])
+ return (
+
👋 {t("overview")}
-{t("whereTheTimeIs")}
-:{time.ss.toString().padStart(2, "0")}
+ {mounted ? ( +