Skip to content

Commit 801c132

Browse files
committed
feat(project-card): optimized project card display logic
1 parent 54c948a commit 801c132

File tree

1 file changed

+83
-100
lines changed

1 file changed

+83
-100
lines changed

components/cards/project/index.tsx

Lines changed: 83 additions & 100 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,6 @@
22

33
import clsx from 'clsx'
44
import { Github, Download } from 'lucide-react'
5-
import { Fragment } from 'react'
65
import useSWR from 'swr'
76
import type { BrandsMap } from '~/components/ui/brand'
87
import { Brand } from '~/components/ui/brand'
@@ -15,21 +14,85 @@ import type { PROJECTS } from '~/data/projects'
1514
import type { GithubRepository, NpmPackage } from '~/types/data'
1615
import { fetcher } from '~/utils/misc'
1716

17+
function NpmStats({ npmPackageName, downloads }: { npmPackageName: string; downloads: number }) {
18+
return (
19+
<div className="space-y-1.5">
20+
<div className="text-xs text-gray-600 dark:text-gray-400">
21+
<span className="hidden sm:inline">Monthly downloads</span>
22+
<span className="sm:hidden">Downloads</span>
23+
</div>
24+
<div className="flex items-center gap-2">
25+
<Link
26+
href={`https://www.npmjs.com/package/${npmPackageName}`}
27+
className="flex items-center gap-1.5"
28+
>
29+
<GrowingUnderline data-umami-event="project-npm-link">
30+
<div className="flex items-center space-x-1.5">
31+
<Download size={16} strokeWidth={1.5} />
32+
<span className="font-medium">{downloads}</span>
33+
</div>
34+
</GrowingUnderline>
35+
</Link>
36+
</div>
37+
</div>
38+
)
39+
}
40+
41+
function GithubStats({ repository }: { repository: GithubRepository }) {
42+
return (
43+
<div className="space-y-1.5">
44+
<div className="text-xs text-gray-600 dark:text-gray-400">
45+
<span className="hidden sm:inline">Github stars</span>
46+
<span className="sm:hidden">Stars</span>
47+
</div>
48+
<div className="flex items-center gap-2">
49+
<Link href={repository.url} className="flex items-center gap-1.5">
50+
<GrowingUnderline data-umami-event="project-github-link">
51+
<div className="flex items-center space-x-1.5">
52+
<Github size={16} strokeWidth={1.5} />
53+
<span className="font-medium">{repository.stargazerCount}</span>
54+
</div>
55+
</GrowingUnderline>
56+
</Link>
57+
</div>
58+
</div>
59+
)
60+
}
61+
62+
function Stack({ builtWith }: { builtWith: string[] }) {
63+
return (
64+
<div className="space-y-1.5">
65+
<div className="text-xs text-gray-600 dark:text-gray-400">Stack</div>
66+
<div className="flex h-6 flex-wrap items-center gap-1.5">
67+
{builtWith?.map((tool) => {
68+
return (
69+
<Brand
70+
key={tool}
71+
name={tool as keyof typeof BrandsMap}
72+
iconClassName={clsx(tool === 'Pygame' ? 'h-4' : 'h-4 w-4')}
73+
/>
74+
)
75+
})}
76+
</div>
77+
</div>
78+
)
79+
}
80+
1881
export function ProjectCard({ project }: { project: (typeof PROJECTS)[0] }) {
19-
const { title, description, imgSrc, url, repo, npm, builtWith, links } = project
82+
const { title, description, imgSrc, url, repo, npmPackageName, builtWith, links } = project
2083

2184
const { data: repository } = useSWR<GithubRepository>(
22-
repo && typeof repo === 'string' ? `/api/github?repo=${repo}` : null,
85+
repo ? `/api/github?repo=${repo}` : null,
2386
fetcher
2487
)
2588

2689
const { data: npmPackage } = useSWR<NpmPackage>(
27-
npm && typeof npm === 'string' ? `/api/npm?package=${npm}` : null,
90+
npmPackageName ? `/api/npm?package=${npmPackageName}` : null,
2891
fetcher
2992
)
30-
3193
const href = url || repository?.url
32-
const lang = repository?.languages?.[0]
94+
95+
const propertyCount = (npmPackage ? 1 : 0) + (repository ? 1 : 0) + (builtWith.length > 0 ? 1 : 0)
3396

3497
return (
3598
<GradientBorder
@@ -51,101 +114,21 @@ export function ProjectCard({ project }: { project: (typeof PROJECTS)[0] }) {
51114
</h2>
52115
</div>
53116
</div>
54-
<p className="mb-16 line-clamp-3 grow text-lg">{repository?.description || description}</p>
55-
<div className={clsx('mt-auto flex gap-6 sm:gap-9 md:grid md:gap-0', `grid-cols-3`)}>
56-
{npmPackage
57-
? // NPM Downloads
58-
(npmPackage || (npm && typeof npm === 'object')) && (
59-
<div className="space-y-1.5">
60-
<div className="text-xs text-gray-600 dark:text-gray-400">
61-
<span className="hidden sm:inline">Monthly downloads</span>
62-
<span className="sm:hidden">Downloads</span>
63-
</div>
64-
<div className="flex items-center gap-2">
65-
<div className="flex items-center space-x-1.5">
66-
<Download size={16} strokeWidth={1.5} />
67-
<span className="font-medium">
68-
{npmPackage?.downloads ||
69-
(npm && typeof npm === 'object' ? npm.downloads : 0)}
70-
</span>
71-
</div>
72-
</div>
73-
</div>
74-
)
75-
: repository
76-
? // GitHub Stars
77-
(repository || (repo && typeof repo === 'object')) && (
78-
<div className="space-y-1.5">
79-
<div className="text-xs text-gray-600 dark:text-gray-400">
80-
<span className="hidden sm:inline">Github stars</span>
81-
<span className="sm:hidden">Stars</span>
82-
</div>
83-
<div className="flex items-center gap-2">
84-
<div className="flex items-center space-x-1.5">
85-
<Github size={16} strokeWidth={1.5} />
86-
<span className="font-medium">
87-
{repository?.stargazerCount ||
88-
(repo && typeof repo === 'object' ? repo.stargazerCount : 0)}
89-
</span>
90-
</div>
91-
</div>
92-
</div>
93-
)
94-
: null}
95-
96-
{/* Links (only show if no repo and no npm) */}
97-
{!repository &&
98-
!npmPackage &&
99-
!(repo && typeof repo === 'object') &&
100-
!(npm && typeof npm === 'object') &&
101-
links && (
102-
<div className="space-y-1.5">
103-
<div className="text-xs text-gray-600 dark:text-gray-400">Links</div>
104-
<div className="flex flex-col items-start gap-0.5 sm:flex-row sm:items-center sm:gap-1.5">
105-
{links?.map(({ title, url }, idx) => (
106-
<Fragment key={url}>
107-
<Link href={url} className="flex items-center gap-1.5">
108-
<GrowingUnderline className="font-medium" data-umami-event="project-link">
109-
{title}
110-
</GrowingUnderline>
111-
</Link>
112-
{idx !== links.length - 1 && (
113-
<span className="hidden text-gray-400 dark:text-gray-500 md:inline">|</span>
114-
)}
115-
</Fragment>
116-
))}
117-
</div>
118-
</div>
119-
)}
120-
121-
{/* Stack */}
122-
{builtWith && builtWith.length > 0 && (
123-
<div className="space-y-1.5">
124-
<div className="text-xs text-gray-600 dark:text-gray-400">Stack</div>
125-
<div className="flex h-6 flex-wrap items-center gap-1.5">
126-
{builtWith?.map((tool) => {
127-
return (
128-
<Brand
129-
key={tool}
130-
name={tool as keyof typeof BrandsMap}
131-
iconClassName={clsx(tool === 'Pygame' ? 'h-4' : 'h-4 w-4')}
132-
/>
133-
)
134-
})}
135-
</div>
136-
</div>
137-
)}
138-
139-
{/* Language */}
140-
{lang && (
141-
<div className="space-y-1.5">
142-
<div className="text-xs text-gray-600 dark:text-gray-400">Language</div>
143-
<div className="flex items-center gap-1.5">
144-
<Brand name={lang.name as keyof typeof BrandsMap} as="icon" className="h-4 w-4" />
145-
<span className="font-medium">{lang.name}</span>
146-
</div>
147-
</div>
117+
<p className="mb-16 line-clamp-3 grow text-lg">{description}</p>
118+
<div
119+
className={clsx('mt-auto flex gap-6 sm:gap-9 md:grid md:gap-0', {
120+
'md:grid-cols-1': propertyCount === 1,
121+
'md:grid-cols-2': propertyCount === 2,
122+
'md:grid-cols-3': propertyCount === 3,
123+
'md:grid-cols-4': propertyCount === 4,
124+
})}
125+
>
126+
{/* {projectProperty} */}
127+
{npmPackageName && (
128+
<NpmStats npmPackageName={npmPackageName} downloads={npmPackage?.downloads || 0} />
148129
)}
130+
{repository && <GithubStats repository={repository} />}
131+
{builtWith.length > 0 && <Stack builtWith={builtWith} />}
149132
</div>
150133
</GradientBorder>
151134
)

0 commit comments

Comments
 (0)