Skip to content

Commit 880cd91

Browse files
authored
fix stats counter, move to separate file (#325)
1 parent c02b47f commit 880cd91

File tree

6 files changed

+259
-184
lines changed

6 files changed

+259
-184
lines changed

app/components/OpenSourceStats.tsx

+149
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,149 @@
1+
import { convexQuery } from '@convex-dev/react-query'
2+
import { useNpmDownloadCounter } from '@erquhart/convex-oss-stats/react'
3+
import NumberFlow from '@number-flow/react'
4+
import { useSuspenseQuery } from '@tanstack/react-query'
5+
import { api } from 'convex/_generated/api'
6+
import { FaCube, FaStar, FaUsers } from 'react-icons/fa'
7+
import { FaDownload } from 'react-icons/fa'
8+
import convexImageWhite from '~/images/convex-white.svg'
9+
import convexImageDark from '~/images/convex-dark.svg'
10+
11+
const counterIntervalMs = 500
12+
13+
const StableCounter = ({ value }: { value?: number }) => {
14+
const dummyString = Number(
15+
Array(value?.toString().length ?? 1)
16+
.fill('8')
17+
.join('')
18+
).toLocaleString()
19+
20+
return (
21+
<>
22+
{/* Dummy span to prevent layout shift */}
23+
<span className="opacity-0">{dummyString}</span>
24+
<span className="absolute -top-0.5 left-0">
25+
<NumberFlow
26+
transformTiming={{
27+
duration: counterIntervalMs,
28+
easing: 'linear',
29+
}}
30+
value={value}
31+
trend={1}
32+
continuous
33+
isolate
34+
willChange
35+
/>
36+
</span>
37+
</>
38+
)
39+
}
40+
41+
const NpmDownloadCounter = ({
42+
npmData,
43+
}: {
44+
npmData: Parameters<typeof useNpmDownloadCounter>[0]
45+
}) => {
46+
const liveNpmDownloadCount = useNpmDownloadCounter(npmData, {
47+
intervalMs: counterIntervalMs,
48+
})
49+
return <StableCounter value={liveNpmDownloadCount} />
50+
}
51+
52+
export default function OssStats() {
53+
const { data: github } = useSuspenseQuery(
54+
convexQuery(api.stats.getGithubOwner, {
55+
owner: 'tanstack',
56+
})
57+
)
58+
const { data: npm } = useSuspenseQuery(
59+
convexQuery(api.stats.getNpmOrg, {
60+
name: 'tanstack',
61+
})
62+
)
63+
64+
return (
65+
<div>
66+
<div className="p-8 grid grid-cols-1 sm:grid-cols-2 xl:grid-cols-4 gap-8 items-center justify-center xl:place-items-center bg-white/50 dark:bg-gray-700/30 dark:shadow-none rounded-xl shadow-xl">
67+
<a
68+
href="https://www.npmjs.com/org/tanstack"
69+
target="_blank"
70+
rel="noreferrer"
71+
className="group flex gap-4 items-center"
72+
>
73+
<FaDownload className="text-2xl group-hover:text-emerald-500 transition-colors duration-200" />
74+
<div>
75+
<div className="text-2xl font-bold opacity-80 relative group-hover:text-emerald-500 transition-colors duration-200">
76+
<NpmDownloadCounter npmData={npm} />
77+
</div>
78+
<div className="text-sm opacity-50 font-medium italic group-hover:text-emerald-500 transition-colors duration-200">
79+
NPM Downloads
80+
</div>
81+
</div>
82+
</a>
83+
<a
84+
href="https://github.com/orgs/TanStack/repositories?q=sort:stars"
85+
target="_blank"
86+
rel="noreferrer"
87+
className="group flex gap-4 items-center"
88+
>
89+
<FaStar className="group-hover:text-yellow-500 text-2xl transition-colors duration-200" />
90+
<div>
91+
<div className="text-2xl font-bold opacity-80 leading-none group-hover:text-yellow-500 transition-colors duration-200">
92+
<NumberFlow value={github?.starCount} />
93+
</div>
94+
<div className="text-sm opacity-50 font-medium italic -mt-1 group-hover:text-yellow-500 transition-colors duration-200">
95+
Stars on Github
96+
</div>
97+
</div>
98+
</a>
99+
<div className="flex gap-4 items-center">
100+
<FaUsers className="text-2xl" />
101+
<div className="">
102+
<div className="text-2xl font-bold opacity-80">
103+
<NumberFlow value={github?.contributorCount} />
104+
</div>
105+
<div className="text-sm opacity-50 font-medium italic -mt-1">
106+
Contributors on GitHub
107+
</div>
108+
</div>
109+
</div>
110+
<div className="flex gap-4 items-center">
111+
<FaCube className="text-2xl" />
112+
<div className="">
113+
<div className="text-2xl font-bold opacity-80 relative">
114+
<NumberFlow value={github?.dependentCount} />
115+
</div>
116+
<div className="text-sm opacity-50 font-medium italic -mt-1">
117+
Dependents on GitHub
118+
</div>
119+
</div>
120+
</div>
121+
</div>
122+
<div className="px-4 py-2 flex justify-end">
123+
<a
124+
href="https://www.convex.dev/?utm_source=tanstack"
125+
className="group flex items-center gap-2"
126+
>
127+
<div className="h-2 w-2 animate-pulse rounded-full bg-green-500"></div>
128+
<div className="flex items-center gap-1">
129+
<span className="text-[.75rem] opacity-30 relative -top-px">
130+
Powered by
131+
</span>
132+
<img
133+
className="dark:hidden opacity-30 group-hover:opacity-50"
134+
src={convexImageDark}
135+
alt="Convex Logo"
136+
width={80}
137+
/>
138+
<img
139+
className="hidden dark:block opacity-30 group-hover:opacity-50"
140+
src={convexImageWhite}
141+
alt="Convex Logo"
142+
width={80}
143+
/>
144+
</div>
145+
</a>
146+
</div>
147+
</div>
148+
)
149+
}

app/routes/_libraries/index.tsx

+2-146
Original file line numberDiff line numberDiff line change
@@ -5,19 +5,12 @@ import {
55
createFileRoute,
66
getRouteApi,
77
} from '@tanstack/react-router'
8-
import { useSuspenseQuery } from '@tanstack/react-query'
9-
import { convexQuery } from '@convex-dev/react-query'
10-
import { useNpmDownloadCounter } from '@erquhart/convex-oss-stats/react'
11-
import NumberFlow from '@number-flow/react'
12-
import { api } from '../../../convex/_generated/api'
138
import { Carbon } from '~/components/Carbon'
149
import { twMerge } from 'tailwind-merge'
1510
import { CgSpinner } from 'react-icons/cg'
1611
import { Footer } from '~/components/Footer'
1712
import SponsorPack from '~/components/SponsorPack'
1813
import discordImage from '~/images/discord-logo-white.svg'
19-
import convexImageWhite from '~/images/convex-white.svg'
20-
import convexImageDark from '~/images/convex-dark.svg'
2114
import { useMutation } from '~/hooks/useMutation'
2215
import { sample } from '~/utils/utils'
2316
import { libraries } from '~/libraries'
@@ -27,7 +20,7 @@ import bytesImage from '~/images/bytes.svg'
2720
// import waves from '~/images/waves.png'
2821
// import background from '~/images/background.jpg'
2922
import { partners } from '../../utils/partners'
30-
import { FaCube, FaDownload, FaStar, FaUsers } from 'react-icons/fa'
23+
import OpenSourceStats from '~/components/OpenSourceStats'
3124

3225
export const textColors = [
3326
`text-rose-500`,
@@ -79,143 +72,6 @@ async function bytesSignupServerFn({ email }: { email: string }) {
7972

8073
const librariesRouteApi = getRouteApi('/_libraries')
8174

82-
const StableCounter = ({ value }: { value?: number }) => {
83-
const dummyString = Number(
84-
Array(value?.toString().length ?? 1)
85-
.fill('8')
86-
.join('')
87-
).toLocaleString()
88-
89-
return (
90-
<>
91-
{/* Dummy span to prevent layout shift */}
92-
<span className="opacity-0">{dummyString}</span>
93-
<span className="absolute -top-0.5 left-0">
94-
<NumberFlow
95-
transformTiming={{
96-
duration: 1000,
97-
easing: 'linear',
98-
}}
99-
value={value}
100-
trend={1}
101-
continuous
102-
isolate
103-
willChange
104-
/>
105-
</span>
106-
</>
107-
)
108-
}
109-
110-
const NpmDownloadCounter = ({
111-
npmData,
112-
}: {
113-
npmData: Parameters<typeof useNpmDownloadCounter>[0]
114-
}) => {
115-
const liveNpmDownloadCount = useNpmDownloadCounter(npmData)
116-
return <StableCounter value={liveNpmDownloadCount} />
117-
}
118-
119-
const OssStats = () => {
120-
const { data: github } = useSuspenseQuery(
121-
convexQuery(api.stats.getGithubOwner, {
122-
owner: 'tanstack',
123-
})
124-
)
125-
console.log('github', github)
126-
const { data: npm } = useSuspenseQuery(
127-
convexQuery(api.stats.getNpmOrg, {
128-
name: 'tanstack',
129-
})
130-
)
131-
132-
return (
133-
<div>
134-
<div className="p-8 grid grid-cols-1 sm:grid-cols-2 xl:grid-cols-4 gap-8 items-center justify-center xl:place-items-center bg-white/50 dark:bg-gray-700/30 dark:shadow-none rounded-xl shadow-xl">
135-
<a
136-
href="https://www.npmjs.com/org/tanstack"
137-
target="_blank"
138-
rel="noreferrer"
139-
className="group flex gap-4 items-center"
140-
>
141-
<FaDownload className="text-2xl group-hover:text-emerald-500 transition-colors duration-200" />
142-
<div>
143-
<div className="text-2xl font-bold opacity-80 relative group-hover:text-emerald-500 transition-colors duration-200">
144-
<NpmDownloadCounter npmData={npm} />
145-
</div>
146-
<div className="text-sm opacity-50 font-medium italic group-hover:text-emerald-500 transition-colors duration-200">
147-
NPM Downloads
148-
</div>
149-
</div>
150-
</a>
151-
<a
152-
href="https://github.com/orgs/TanStack/repositories?q=sort:stars"
153-
target="_blank"
154-
rel="noreferrer"
155-
className="group flex gap-4 items-center"
156-
>
157-
<FaStar className="group-hover:text-yellow-500 text-2xl transition-colors duration-200" />
158-
<div>
159-
<div className="text-2xl font-bold opacity-80 leading-none group-hover:text-yellow-500 transition-colors duration-200">
160-
<NumberFlow value={github?.starCount} />
161-
</div>
162-
<div className="text-sm opacity-50 font-medium italic -mt-1 group-hover:text-yellow-500 transition-colors duration-200">
163-
Stars on Github
164-
</div>
165-
</div>
166-
</a>
167-
<div className="flex gap-4 items-center">
168-
<FaUsers className="text-2xl" />
169-
<div className="">
170-
<div className="text-2xl font-bold opacity-80">
171-
<NumberFlow value={github?.contributorCount} />
172-
</div>
173-
<div className="text-sm opacity-50 font-medium italic -mt-1">
174-
Contributors on GitHub
175-
</div>
176-
</div>
177-
</div>
178-
<div className="flex gap-4 items-center">
179-
<FaCube className="text-2xl" />
180-
<div className="">
181-
<div className="text-2xl font-bold opacity-80 relative">
182-
<NumberFlow value={github?.dependentCount} />
183-
</div>
184-
<div className="text-sm opacity-50 font-medium italic -mt-1">
185-
Dependents on GitHub
186-
</div>
187-
</div>
188-
</div>
189-
</div>
190-
<div className="px-4 py-2 flex justify-end">
191-
<a
192-
href="https://www.convex.dev/?utm_source=tanstack"
193-
className="group flex items-center gap-2"
194-
>
195-
<div className="h-2 w-2 animate-pulse rounded-full bg-green-500"></div>
196-
<div className="flex items-center gap-1">
197-
<span className="text-[.75rem] opacity-30 relative -top-px">
198-
Powered by
199-
</span>
200-
<img
201-
className="dark:hidden opacity-30 group-hover:opacity-50"
202-
src={convexImageDark}
203-
alt="Convex Logo"
204-
width={80}
205-
/>
206-
<img
207-
className="hidden dark:block opacity-30 group-hover:opacity-50"
208-
src={convexImageWhite}
209-
alt="Convex Logo"
210-
width={80}
211-
/>
212-
</div>
213-
</a>
214-
</div>
215-
</div>
216-
)
217-
}
218-
21975
function Index() {
22076
const bytesSignupMutation = useMutation({
22177
fn: bytesSignupServerFn,
@@ -281,7 +137,7 @@ function Index() {
281137
</div>
282138
<div className="h-8" />
283139
<div className="w-fit mx-auto">
284-
<OssStats />
140+
<OpenSourceStats />
285141
</div>
286142
<div className="h-24" />
287143
<div className="px-4 lg:max-w-screen-lg md:mx-auto">

0 commit comments

Comments
 (0)