Skip to content

Commit 9f681bb

Browse files
authored
feat: new libraries/framework pages (#872)
1 parent 03a1d36 commit 9f681bb

10 files changed

Lines changed: 304 additions & 29 deletions

File tree

src/components/LibraryCard.tsx

Lines changed: 14 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,24 +1,35 @@
1-
import { Link } from '@tanstack/react-router'
1+
import { Link, useParams } from '@tanstack/react-router'
22
import { Library } from '~/libraries'
3+
import { frameworkOptions } from '~/libraries/frameworks'
34
import { twMerge } from 'tailwind-merge'
45

56
export default function LibraryCard({
67
library,
78
index = 0,
89
isGeneric = false,
10+
namePrefix,
911
}: {
1012
library: Library
1113
index?: number
1214
isGeneric?: boolean
15+
namePrefix?: string
1316
}) {
1417
const isExternal = library.to?.startsWith('http')
1518
const Component = isExternal ? 'a' : Link
1619
const props = isExternal
1720
? { href: library.to, target: '_blank', rel: 'noopener noreferrer' }
1821
: { to: library.to ?? '#' }
22+
const params = useParams({ strict: false })
23+
const frameworkPrefix =
24+
namePrefix ??
25+
frameworkOptions.find((framework) => framework.value === params.framework)
26+
?.label
1927

2028
const hasTanStackPrefix = library.name.startsWith('TanStack ')
2129
const nameWithoutPrefix = library.name.replace('TanStack ', '')
30+
const displayName = frameworkPrefix
31+
? `${frameworkPrefix}-${nameWithoutPrefix}`
32+
: nameWithoutPrefix
2233

2334
return (
2435
<Component
@@ -82,7 +93,7 @@ export default function LibraryCard({
8293
: 'text-current'
8394
}
8495
>
85-
{nameWithoutPrefix}
96+
{displayName}
8697
</span>
8798
</>
8899
) : (
@@ -93,7 +104,7 @@ export default function LibraryCard({
93104
: 'text-current'
94105
}
95106
>
96-
{nameWithoutPrefix}
107+
{displayName}
97108
</span>
98109
)}
99110
</div>

src/images/alpine-logo.svg

Lines changed: 1 addition & 0 deletions
Loading

src/libraries/frameworks.tsx

Lines changed: 15 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
import alpineLogo from '../images/alpine-logo.svg'
12
import angularLogo from '../images/angular-logo.svg'
23
import jsLogo from '../images/js-logo.svg'
34
import litLogo from '../images/lit-logo.svg'
@@ -45,13 +46,6 @@ export const frameworkOptions = [
4546
color: 'bg-blue-600',
4647
fontColor: 'text-blue-600',
4748
},
48-
{
49-
label: 'Lit',
50-
value: 'lit',
51-
logo: litLogo,
52-
color: 'bg-emerald-500',
53-
fontColor: 'text-emerald-500',
54-
},
5549
{
5650
label: 'Svelte',
5751
value: 'svelte',
@@ -66,6 +60,20 @@ export const frameworkOptions = [
6660
color: 'bg-indigo-500',
6761
fontColor: 'text-indigo-500',
6862
},
63+
{
64+
label: 'Lit',
65+
value: 'lit',
66+
logo: litLogo,
67+
color: 'bg-emerald-500',
68+
fontColor: 'text-emerald-500',
69+
},
70+
{
71+
label: 'Alpine',
72+
value: 'alpine',
73+
logo: alpineLogo,
74+
color: 'bg-slate-500',
75+
fontColor: 'text-slate-500',
76+
},
6977
{
7078
label: 'Vanilla',
7179
value: 'vanilla',

src/libraries/libraries.ts

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -321,12 +321,13 @@ export const table: LibrarySlim = {
321321
repo: 'tanstack/table',
322322
frameworks: [
323323
'angular',
324-
'lit',
325-
'qwik',
326324
'react',
327325
'solid',
328326
'svelte',
329327
'vue',
328+
'qwik',
329+
'lit',
330+
'alpine',
330331
'vanilla',
331332
],
332333
latestVersion: 'v8',

src/libraries/types.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ import * as React from 'react'
22

33
export type Framework =
44
| 'angular'
5+
| 'alpine'
56
| 'lit'
67
| 'preact'
78
| 'qwik'

src/routeTree.gen.ts

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,7 @@ import { Route as PartnersPartnerRouteImport } from './routes/partners.$partner'
5757
import { Route as OauthTokenRouteImport } from './routes/oauth/token'
5858
import { Route as OauthRegisterRouteImport } from './routes/oauth/register'
5959
import { Route as OauthAuthorizeRouteImport } from './routes/oauth/authorize'
60+
import { Route as LibrariesFrameworkRouteImport } from './routes/libraries_.$framework'
6061
import { Route as BuilderDocsRouteImport } from './routes/builder.docs'
6162
import { Route as BlogSplatRouteImport } from './routes/blog.$'
6263
import { Route as AuthSignoutRouteImport } from './routes/auth/signout'
@@ -390,6 +391,11 @@ const OauthAuthorizeRoute = OauthAuthorizeRouteImport.update({
390391
path: '/oauth/authorize',
391392
getParentRoute: () => rootRouteImport,
392393
} as any)
394+
const LibrariesFrameworkRoute = LibrariesFrameworkRouteImport.update({
395+
id: '/libraries_/$framework',
396+
path: '/libraries/$framework',
397+
getParentRoute: () => rootRouteImport,
398+
} as any)
393399
const BuilderDocsRoute = BuilderDocsRouteImport.update({
394400
id: '/docs',
395401
path: '/docs',
@@ -925,6 +931,7 @@ export interface FileRoutesByFullPath {
925931
'/auth/signout': typeof AuthSignoutRoute
926932
'/blog/$': typeof BlogSplatRoute
927933
'/builder/docs': typeof BuilderDocsRoute
934+
'/libraries/$framework': typeof LibrariesFrameworkRoute
928935
'/oauth/authorize': typeof OauthAuthorizeRoute
929936
'/oauth/register': typeof OauthRegisterRoute
930937
'/oauth/token': typeof OauthTokenRoute
@@ -1059,6 +1066,7 @@ export interface FileRoutesByTo {
10591066
'/auth/signout': typeof AuthSignoutRoute
10601067
'/blog/$': typeof BlogSplatRoute
10611068
'/builder/docs': typeof BuilderDocsRoute
1069+
'/libraries/$framework': typeof LibrariesFrameworkRoute
10621070
'/oauth/authorize': typeof OauthAuthorizeRoute
10631071
'/oauth/register': typeof OauthRegisterRoute
10641072
'/oauth/token': typeof OauthTokenRoute
@@ -1200,6 +1208,7 @@ export interface FileRoutesById {
12001208
'/auth/signout': typeof AuthSignoutRoute
12011209
'/blog/$': typeof BlogSplatRoute
12021210
'/builder/docs': typeof BuilderDocsRoute
1211+
'/libraries_/$framework': typeof LibrariesFrameworkRoute
12031212
'/oauth/authorize': typeof OauthAuthorizeRoute
12041213
'/oauth/register': typeof OauthRegisterRoute
12051214
'/oauth/token': typeof OauthTokenRoute
@@ -1344,6 +1353,7 @@ export interface FileRouteTypes {
13441353
| '/auth/signout'
13451354
| '/blog/$'
13461355
| '/builder/docs'
1356+
| '/libraries/$framework'
13471357
| '/oauth/authorize'
13481358
| '/oauth/register'
13491359
| '/oauth/token'
@@ -1478,6 +1488,7 @@ export interface FileRouteTypes {
14781488
| '/auth/signout'
14791489
| '/blog/$'
14801490
| '/builder/docs'
1491+
| '/libraries/$framework'
14811492
| '/oauth/authorize'
14821493
| '/oauth/register'
14831494
| '/oauth/token'
@@ -1618,6 +1629,7 @@ export interface FileRouteTypes {
16181629
| '/auth/signout'
16191630
| '/blog/$'
16201631
| '/builder/docs'
1632+
| '/libraries_/$framework'
16211633
| '/oauth/authorize'
16221634
| '/oauth/register'
16231635
| '/oauth/token'
@@ -1747,6 +1759,7 @@ export interface RootRouteChildren {
17471759
AuthCliRoute: typeof AuthCliRoute
17481760
AuthPopupSuccessRoute: typeof AuthPopupSuccessRoute
17491761
AuthSignoutRoute: typeof AuthSignoutRoute
1762+
LibrariesFrameworkRoute: typeof LibrariesFrameworkRoute
17501763
OauthAuthorizeRoute: typeof OauthAuthorizeRoute
17511764
OauthRegisterRoute: typeof OauthRegisterRoute
17521765
OauthTokenRoute: typeof OauthTokenRoute
@@ -2139,6 +2152,13 @@ declare module '@tanstack/react-router' {
21392152
preLoaderRoute: typeof OauthAuthorizeRouteImport
21402153
parentRoute: typeof rootRouteImport
21412154
}
2155+
'/libraries_/$framework': {
2156+
id: '/libraries_/$framework'
2157+
path: '/libraries/$framework'
2158+
fullPath: '/libraries/$framework'
2159+
preLoaderRoute: typeof LibrariesFrameworkRouteImport
2160+
parentRoute: typeof rootRouteImport
2161+
}
21422162
'/builder/docs': {
21432163
id: '/builder/docs'
21442164
path: '/docs'
@@ -3041,6 +3061,7 @@ const rootRouteChildren: RootRouteChildren = {
30413061
AuthCliRoute: AuthCliRoute,
30423062
AuthPopupSuccessRoute: AuthPopupSuccessRoute,
30433063
AuthSignoutRoute: AuthSignoutRoute,
3064+
LibrariesFrameworkRoute: LibrariesFrameworkRoute,
30443065
OauthAuthorizeRoute: OauthAuthorizeRoute,
30453066
OauthRegisterRoute: OauthRegisterRoute,
30463067
OauthTokenRoute: OauthTokenRoute,

src/routes/-libraries-utils.ts

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
import { libraries, type Framework, type LibrarySlim } from '~/libraries'
2+
3+
export function getVisibleLibraries() {
4+
return libraries.filter((library) => library.to && library.visible !== false)
5+
}
6+
7+
export function orderLibrariesForBrowse<TLibrary extends LibrarySlim>(
8+
allLibraries: Array<TLibrary>,
9+
) {
10+
const others = allLibraries.filter(
11+
(library) =>
12+
library.id !== 'ranger' &&
13+
library.id !== 'config' &&
14+
library.id !== 'react-charts',
15+
)
16+
const ranger = allLibraries.filter((library) => library.id === 'ranger')
17+
const config = allLibraries.filter((library) => library.id === 'config')
18+
19+
const devtoolsIndex = others.findIndex((library) => library.id === 'devtools')
20+
21+
if (devtoolsIndex === -1) {
22+
return [...others, ...config, ...ranger]
23+
}
24+
25+
return [
26+
...others.slice(0, devtoolsIndex + 1),
27+
...config,
28+
...others.slice(devtoolsIndex + 1),
29+
...ranger,
30+
]
31+
}
32+
33+
export function getFrameworkLibraryCounts(allLibraries: Array<LibrarySlim>) {
34+
const counts = {} as Partial<Record<Framework, number>>
35+
36+
for (const library of allLibraries) {
37+
for (const framework of library.frameworks) {
38+
counts[framework] = (counts[framework] ?? 0) + 1
39+
}
40+
}
41+
42+
return counts
43+
}

src/routes/libraries.tsx

Lines changed: 44 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,14 @@
1-
import { createFileRoute } from '@tanstack/react-router'
1+
import { Link, createFileRoute } from '@tanstack/react-router'
22
import * as React from 'react'
3-
import { libraries, Library } from '~/libraries'
3+
import { type Library } from '~/libraries'
4+
import { frameworkOptions } from '~/libraries/frameworks'
45
import { reactChartsProject } from '~/libraries/react-charts'
56
import LibraryCard from '~/components/LibraryCard'
7+
import {
8+
getFrameworkLibraryCounts,
9+
getVisibleLibraries,
10+
orderLibrariesForBrowse,
11+
} from './-libraries-utils'
612

713
export const Route = createFileRoute('/libraries')({
814
component: LibrariesPage,
@@ -20,22 +26,12 @@ export const Route = createFileRoute('/libraries')({
2026
})
2127

2228
function LibrariesPage() {
23-
const allLibraries = libraries.filter((d) => d.to && d.visible !== false)
24-
const others = allLibraries.filter(
25-
(l) => l.id !== 'ranger' && l.id !== 'config' && l.id !== 'react-charts',
29+
const allLibraries = getVisibleLibraries()
30+
const ordered = orderLibrariesForBrowse(allLibraries)
31+
const frameworkCounts = getFrameworkLibraryCounts(allLibraries)
32+
const frameworksWithLibraries = frameworkOptions.filter(
33+
(framework) => (frameworkCounts[framework.value] ?? 0) > 0,
2634
)
27-
const ranger = allLibraries.filter((l) => l.id === 'ranger')
28-
const config = allLibraries.filter((l) => l.id === 'config')
29-
30-
// Find devtools index in others to insert config after it
31-
const devtoolsIndex = others.findIndex((l) => l.id === 'devtools')
32-
const ordered = [
33-
...others.slice(0, devtoolsIndex + 1),
34-
...config,
35-
...others.slice(devtoolsIndex + 1),
36-
...ranger,
37-
]
38-
3935
const deprecatedLibraries = [reactChartsProject]
4036

4137
return (
@@ -45,6 +41,37 @@ function LibrariesPage() {
4541
Browse all TanStack libraries.
4642
</p>
4743

44+
<section className="mt-8">
45+
<h2 className="text-xl font-medium">Browse by Your Framework</h2>
46+
<div className="mt-4 flex flex-wrap gap-3">
47+
{frameworksWithLibraries.map((framework) => {
48+
const count = frameworkCounts[framework.value] ?? 0
49+
50+
return (
51+
<Link
52+
key={framework.value}
53+
to="/libraries/$framework"
54+
params={{
55+
framework: framework.value,
56+
}}
57+
className="group inline-flex items-center gap-2 rounded-lg border border-gray-200 bg-white px-3 py-2 text-sm font-medium text-gray-800 shadow-sm transition-all hover:-translate-y-0.5 hover:border-current hover:shadow-md dark:border-gray-800 dark:bg-gray-900 dark:text-gray-100"
58+
>
59+
<img
60+
src={framework.logo}
61+
alt=""
62+
loading="lazy"
63+
className="h-5 w-5 object-contain"
64+
/>
65+
<span>{framework.label}</span>
66+
<span className="text-gray-500 dark:text-gray-400">
67+
{count} {count === 1 ? 'library' : 'libraries'}
68+
</span>
69+
</Link>
70+
)
71+
})}
72+
</div>
73+
</section>
74+
4875
<div
4976
className={`grid grid-cols-1 gap-6 gap-y-8 justify-center mt-8
5077
sm:grid-cols-2 md:grid-cols-3 xl:grid-cols-3`}

0 commit comments

Comments
 (0)