diff --git a/src/components/ContributorsWall.tsx b/src/components/ContributorsWall.tsx new file mode 100644 index 000000000..920c9bec4 --- /dev/null +++ b/src/components/ContributorsWall.tsx @@ -0,0 +1,40 @@ +import { type Library } from '~/libraries' + +export function ContributorsWall({ library }: { library: Library }) { + return ( +
+ + GitHub Contributors + +
+ Powered by{' '} + + contrib.rocks + +
+
+ + View all contributors on GitHub + +
+
+ ) +} diff --git a/src/components/DocsLayout.tsx b/src/components/DocsLayout.tsx index 5a352eaab..f80ffa3da 100644 --- a/src/components/DocsLayout.tsx +++ b/src/components/DocsLayout.tsx @@ -197,6 +197,10 @@ const useMenuConfig = ({ }, ] : []), + { + label: 'Contributors', + to: '/$libraryId/$version/docs/contributors', + }, { label: (
diff --git a/src/components/MaintainerCard.tsx b/src/components/MaintainerCard.tsx new file mode 100644 index 000000000..f727f2869 --- /dev/null +++ b/src/components/MaintainerCard.tsx @@ -0,0 +1,282 @@ +import { Library, Framework, frameworkOptions } from '~/libraries' +import { + getRoleInLibrary, + Maintainer, + getPersonsMaintainerOf, +} from '~/libraries/maintainers' +import { useState } from 'react' + +function RoleBadge({ + maintainer, + libraryId, +}: { + maintainer: Maintainer + libraryId?: Library['id'] +}) { + const role = libraryId ? getRoleInLibrary(maintainer, libraryId) : '' + const isCreator = role.toLowerCase().includes('creator') + const isMaintainer = role.toLowerCase().includes('maintainer') + const isCoreMaintainer = isMaintainer && maintainer.isCoreMaintainer + + if (isCreator) { + return ( + + {role} + + ) + } + + if (isMaintainer) { + return ( + + {role} + + ) + } + + return {role} +} + +function FrameworkChip({ framework }: { framework: Framework }) { + const frameworkOption = frameworkOptions.find((f) => f.value === framework) + const bgColor = frameworkOption?.color || 'bg-gray-500' + return ( + + + {frameworkOption?.label || framework} + + ) +} + +function SpecialtyChip({ specialty }: { specialty: string }) { + return ( + + + {specialty} + + ) +} + +function LibraryBadge({ library }: { library: Library }) { + if (library.to) { + return ( + e.stopPropagation()} + title={`View all contributors for ${library.name}`} + > + {library.name.replace('TanStack', '🌴')} + + ) + } + return ( + + {library.name.replace('TanStack', '🌴')} + + ) +} + +interface MaintainerCardProps { + maintainer: Maintainer + libraryId?: Library['id'] +} + +export function MaintainerCard({ maintainer, libraryId }: MaintainerCardProps) { + const libraries = getPersonsMaintainerOf(maintainer) + const [showAllLibraries, setShowAllLibraries] = useState(false) + + return ( +
+ + {`Avatar +
+
+
+ {maintainer.frameworkExpertise && + maintainer.frameworkExpertise.length > 0 && ( +
+ {maintainer.frameworkExpertise.map((framework) => ( + + ))} +
+ )} + {maintainer.specialties && maintainer.specialties.length > 0 && ( +
+ {maintainer.specialties.map((specialty) => ( + + ))} +
+ )} +
+
+
+
+
+ + {maintainer.name} + +
+ {libraryId && ( + + )} +
+
+ {!libraryId && libraries.length > 0 && ( +
+ {libraries + .slice(0, showAllLibraries ? undefined : 2) + .map((library) => ( + + ))} + {!showAllLibraries && libraries.length > 2 && ( + + )} +
+ )} +
+ e.stopPropagation()} + > + + + {maintainer.social?.twitter && ( + e.stopPropagation()} + > + + + )} + {maintainer.social?.bluesky && ( + e.stopPropagation()} + > + + + )} + {maintainer.social?.website && ( + e.stopPropagation()} + > + + + )} +
+
+
+ ) +} diff --git a/src/libraries/index.tsx b/src/libraries/index.tsx index 0c146a92b..c9e49ad90 100644 --- a/src/libraries/index.tsx +++ b/src/libraries/index.tsx @@ -57,9 +57,10 @@ export type Library = { | 'db' | 'config' | 'react-charts' + | 'create-tsrouter-app' name: string cardStyles: string - to: string + to?: string tagline: string description: string ogImage?: string @@ -89,6 +90,7 @@ export type Library = { }[] docsRoot?: string embedEditor?: 'codesandbox' | 'stackblitz' + visible?: boolean } export type LibraryMenuItem = { @@ -109,6 +111,16 @@ export const libraries = [ rangerProject, dbProject, configProject, + { + id: 'react-charts', + name: 'React Charts', + repo: 'tanstack/react-charts', + } as Library, + { + id: 'create-tsrouter-app', + name: 'Create TS Router App', + repo: 'tanstack/create-tsrouter-app', + } as Library, ] satisfies Library[] export const librariesByGroup = { @@ -125,7 +137,6 @@ export const librariesByGroup = { } export const librariesGroupNamesMap = { - app: 'Application Building', state: 'Data and State Management', headlessUI: 'Headless UI', other: 'Other', diff --git a/src/libraries/maintainers.ts b/src/libraries/maintainers.ts new file mode 100644 index 000000000..d7564bf8f --- /dev/null +++ b/src/libraries/maintainers.ts @@ -0,0 +1,453 @@ +import { Framework, getLibrary, Library } from '.' + +export interface Maintainer { + name: string + avatar: string + github: string + isCoreMaintainer?: boolean + creatorOf?: Library['id'][] + maintainerOf?: Library['id'][] // inherits from creatorOf + contributorOf?: Library['id'][] // inherits from maintainerOf + consultantOf?: Library['id'][] // inherits from maintainerOf + frameworkExpertise?: Framework[] + specialties?: string[] + social?: { + twitter?: string + bluesky?: string + website?: string + } +} + +// order matters +export const allMaintainers: Maintainer[] = [ + { + name: 'Tanner Linsley', + isCoreMaintainer: true, + avatar: 'https://github.com/tannerlinsley.png', + github: 'tannerlinsley', + creatorOf: [ + 'start', + 'router', + 'query', + 'table', + 'form', + 'virtual', + 'ranger', + 'store', + 'pacer', + 'react-charts', + ], + frameworkExpertise: ['react', 'solid'], + specialties: ['Architecture', 'Core API', 'Documentation'], + social: { + twitter: 'https://x.com/tannerlinsley', + bluesky: 'https://bsky.app/profile/tannerlinsley.com', + website: 'https://tannerlinsley.com', + }, + }, + { + name: 'Dominik Dorfmeister', + isCoreMaintainer: true, + avatar: 'https://github.com/tkdodo.png', + github: 'tkdodo', + maintainerOf: ['query'], + contributorOf: ['router'], + frameworkExpertise: ['react'], + specialties: ['Core API', 'TypeScript', 'Documentation'], + social: { + bluesky: 'https://bsky.app/profile/tkdodo.eu', + website: 'https://tkdodo.eu', + }, + }, + { + name: 'Corbin Crutchley', + isCoreMaintainer: true, + avatar: 'https://github.com/crutchcorn.png', + github: 'crutchcorn', + creatorOf: ['form'], + maintainerOf: ['store', 'config'], + frameworkExpertise: ['react', 'solid', 'vue', 'angular'], + specialties: ['Forms', 'Validation', 'State Management'], + social: { + twitter: 'https://x.com/crutchcorn', + bluesky: 'https://bsky.app/profile/crutchcorn.dev', + website: 'https://playfulprogramming.com/people/crutchcorn', + }, + }, + { + name: 'Manuel Schiller', + isCoreMaintainer: true, + avatar: 'https://github.com/schiller-manuel.png', + github: 'schiller-manuel', + maintainerOf: ['start', 'router'], + frameworkExpertise: ['react'], + specialties: ['Architecture', 'Core API', 'Documentation'], + social: { + twitter: 'https://x.com/schanuelmiller', + bluesky: 'https://bsky.app/profile/manuelschiller.bsky.social', + }, + }, + { + name: 'Kevin Van Cott', + isCoreMaintainer: true, + avatar: 'https://github.com/kevinvandy.png', + github: 'kevinvandy', + creatorOf: ['pacer'], + maintainerOf: ['table'], + contributorOf: ['virtual'], + consultantOf: ['query'], + frameworkExpertise: ['react', 'solid', 'svelte'], + specialties: ['Tables', 'Data Grids', 'Dashboards'], + social: { + twitter: 'https://x.com/kevinvancott', + bluesky: 'https://bsky.app/profile/kevinvancott.dev', + website: 'https://kevinvancott.dev', + }, + }, + { + name: 'Sean Cassiere', + isCoreMaintainer: true, + avatar: 'https://github.com/seancassiere.png', + github: 'seancassiere', + maintainerOf: ['start', 'router'], + frameworkExpertise: ['react'], + specialties: ['Architecture', 'Core API', 'Documentation'], + social: { + twitter: 'https://x.com/seancassiere', + bluesky: 'https://bsky.app/profile/seancassiere.com', + website: 'https://seancassiere.com', + }, + }, + { + name: 'Chris Horobin', + isCoreMaintainer: true, + avatar: 'https://github.com/chorobin.png', + github: 'chorobin', + maintainerOf: ['start', 'router'], + frameworkExpertise: ['react'], + specialties: ['TypeScript'], + social: { + twitter: 'https://x.com/c_horobin', + bluesky: 'https://bsky.app/profile/chorobin.bsky.social', + }, + }, + { + name: 'Damian Pieczynski', + isCoreMaintainer: true, + avatar: 'https://github.com/piecyk.png', + github: 'piecyk', + maintainerOf: ['virtual'], + frameworkExpertise: ['react'], + specialties: ['Virtualization', 'Performance'], + }, + { + name: 'Jack Herrington', + isCoreMaintainer: true, + avatar: 'https://github.com/jherr.png', + github: 'jherr', + creatorOf: ['create-tsrouter-app'], + frameworkExpertise: ['react'], + specialties: ['Templates'], + }, + { + name: 'Kyle Mathews', + isCoreMaintainer: true, + avatar: 'https://github.com/KyleAMathews.png', + github: 'KyleAMathews', + creatorOf: ['db'], + frameworkExpertise: ['react'], + specialties: ['Sync Engines'], + }, + { + name: 'Lachlan Collins', + isCoreMaintainer: true, + avatar: 'https://github.com/lachlancollins.png', + github: 'lachlancollins', + maintainerOf: ['config', 'query'], + frameworkExpertise: ['react', 'svelte'], + specialties: ['Architecture'], + }, + { + name: 'Leonardo Montini', + avatar: 'https://github.com/Balastrong.png', + github: 'Balastrong', + maintainerOf: ['form'], + frameworkExpertise: ['react'], + social: { + website: 'https://leonardomontini.dev/', + twitter: 'https://x.com/Balastrong', + bluesky: 'https://bsky.app/profile/leonardomontini.dev', + }, + }, + { + name: 'Fredrik Höglund', + avatar: 'https://github.com/ephem.png', + github: 'ephem', + maintainerOf: ['query'], + frameworkExpertise: ['react'], + specialties: ['Data Management', 'SSR', 'Hydration'], + social: { + bluesky: 'https://bsky.app/profile/ephem.dev', + twitter: 'https://x.com/ephemjs', + website: 'https://www.ephem.dev', + }, + }, + { + name: 'Aadit Olkar', + avatar: 'https://github.com/aadito123.png', + github: 'aadito123', + maintainerOf: ['form'], + frameworkExpertise: ['solid'], + social: { + twitter: 'https://x.com/swagdoctor19', + }, + }, + { + name: 'Luca Jakob', + avatar: 'https://github.com/LeCarbonator.png', + github: 'LeCarbonator', + maintainerOf: ['form'], + frameworkExpertise: ['react'], + }, + { + name: 'Jonghyeon Ko', + avatar: 'https://github.com/manudeli.png', + github: 'manudeli', + maintainerOf: ['query'], + frameworkExpertise: ['react'], + specialties: ['TypeScript', 'Backport', 'Test'], + social: { + twitter: 'https://x.com/manudeli_', + bluesky: 'https://bsky.app/profile/manudeli.bsky.social', + website: 'https://www.linkedin.com/in/jonghyeonko', + }, + }, + { + name: 'Riccardo Perra', + avatar: + 'https://cdn.bsky.app/img/avatar/plain/did:plc:gtnigsmgu7jyrc4tnkvn62qw/bafkreiceysbj4o6jrbbniudtwj3tcsns6rvwcxyjsqiaumeojurwbkki5a@jpeg', + github: 'riccardoperra', + maintainerOf: ['table'], + frameworkExpertise: ['angular', 'solid'], + specialties: [], + social: { + twitter: 'https://x.com/riccardoperra0', + bluesky: 'https://bsky.app/profile/riccardoperra.bsky.social', + website: 'https://riccardoperra.com', + }, + }, + { + name: 'Birk Skyum', + avatar: 'https://github.com/birkskyum.png', + github: 'birkskyum', + maintainerOf: ['start'], + contributorOf: ['pacer'], + frameworkExpertise: ['solid'], + specialties: [], + social: { + twitter: 'https://x.com/birkskyum', + bluesky: 'https://bsky.app/profile/bskyum.bsky.social', + }, + }, + { + name: 'Arnoud de Vries', + avatar: 'https://github.com/arnoud-dv.png', + github: 'arnoud-dv', + maintainerOf: ['query'], + frameworkExpertise: ['angular', 'react'], + specialties: [ + 'Architecture', + 'Developer Experience', + 'TypeScript', + 'Reactivity', + ], + social: { + twitter: 'https://x.com/Arnoud_dv', + bluesky: 'https://bsky.app/profile/arnoud.dev', + website: 'https://www.linkedin.com/in/arnouddv/', + }, + }, + { + name: 'Fülöp Kovács', + avatar: 'https://github.com/fulopkovacs.png', + github: 'fulopkovacs', + maintainerOf: ['form'], + frameworkExpertise: ['react'], + social: { + website: 'https://fulop.dev/', + twitter: 'https://x.com/notacheetah', + bluesky: 'https://bsky.app/profile/notacheetah.bsky.social', + }, + }, + { + name: 'Aryan Deora', + avatar: 'https://github.com/ardeora.png', + github: 'ardeora', + maintainerOf: ['query'], + frameworkExpertise: ['solid'], + specialties: ['Dev Tools'], + social: { + twitter: 'https://x.com/aryan__deora', + website: 'https://www.aryandeora.com/', + }, + }, + { + name: 'Mokshit Jain', + avatar: 'https://github.com/Mokshit06.png', + contributorOf: ['table'], + frameworkExpertise: ['svelte', 'solid', 'vue'], + github: 'Mokshit06', + social: { + twitter: 'https://x.com/mokshit06', + website: 'https://mokshitjain.co', + }, + }, + { + name: 'Walker Lockard', + github: 'walker-tx', + avatar: 'https://github.com/walker-tx.png', + contributorOf: ['table'], + frameworkExpertise: ['svelte', 'react'], + social: { + twitter: 'https://x.com/walker_lockard', + website: 'https://walker.dev', + }, + }, + { + name: 'Tom', + github: 'tombuntus', + avatar: 'https://github.com/tombuntus.png', + contributorOf: ['table'], + frameworkExpertise: ['react'], + social: {}, + }, +] + +export const coreMaintainers = allMaintainers.filter( + (maintainer) => maintainer.isCoreMaintainer +) + +export function getLibraryCreators(libraryId: string): Maintainer[] { + return allMaintainers.filter((maintainer) => + maintainer.creatorOf?.includes(libraryId as Library['id']) + ) +} + +export function getLibraryMaintainers( + libraryId: string, + includeCreators = true +): Maintainer[] { + const creators = getLibraryCreators(libraryId) + const maintainers = allMaintainers.filter((maintainer) => + maintainer.maintainerOf?.includes(libraryId as Library['id']) + ) + + // Use Set to dedupe while preserving order + return includeCreators + ? [...new Set([...creators, ...maintainers])] + : maintainers +} + +export function getLibraryContributors( + libraryId: string, + includeMaintainers = true +): Maintainer[] { + const maintainers = getLibraryMaintainers(libraryId) + const contributors = allMaintainers.filter((maintainer) => + maintainer.contributorOf?.includes(libraryId as Library['id']) + ) + + return includeMaintainers + ? [...new Set([...maintainers, ...contributors])] + : contributors +} + +export function getLibraryConsultants( + libraryId: string, + includeMaintainers = true +): Maintainer[] { + const maintainers = getLibraryMaintainers(libraryId) + const consultants = allMaintainers.filter((maintainer) => + maintainer.consultantOf?.includes(libraryId as Library['id']) + ) + + return includeMaintainers + ? [...new Set([...maintainers, ...consultants])] + : consultants +} + +export function getPersonsCreatorOf(person: Maintainer): Library[] { + return person.creatorOf?.map((libraryId) => getLibrary(libraryId)) || [] +} + +export function getPersonsMaintainerOf( + person: Maintainer, + includeCreatorOf = true +): Library[] { + const creatorOf = getPersonsCreatorOf(person) + const maintainerOf = + person.maintainerOf?.map((libraryId) => getLibrary(libraryId)) || [] + + return includeCreatorOf + ? [...new Set([...creatorOf, ...maintainerOf])] + : maintainerOf +} + +export function getPersonsContributorOf( + person: Maintainer, + includeMaintainers = true +): Library[] { + const maintainers = getPersonsMaintainerOf(person) + const contributors = + person.contributorOf?.map((libraryId) => getLibrary(libraryId)) || [] + + return includeMaintainers + ? [...new Set([...maintainers, ...contributors])] + : contributors +} + +export function getPersonsConsultantOf( + person: Maintainer, + includeMaintainers = true +): Library[] { + const maintainers = getPersonsMaintainerOf(person) + const consultants = + person.consultantOf?.map((libraryId) => getLibrary(libraryId)) || [] + + return includeMaintainers + ? [...new Set([...maintainers, ...consultants])] + : consultants +} + +export function getIsCreatorOfLibrary(person: Maintainer, libraryId: string) { + return person.creatorOf?.includes(libraryId as Library['id']) +} + +export function getIsMaintainerOfLibrary( + person: Maintainer, + libraryId: string +) { + return person.maintainerOf?.includes(libraryId as Library['id']) +} + +export function getIsContributorOfLibrary( + person: Maintainer, + libraryId: string +) { + return person.contributorOf?.includes(libraryId as Library['id']) +} + +export function getIsConsultantOfLibrary( + person: Maintainer, + libraryId: string +) { + return person.consultantOf?.includes(libraryId as Library['id']) +} + +export function getRoleInLibrary(person: Maintainer, libraryId: string) { + if (getIsCreatorOfLibrary(person, libraryId)) return 'Creator' + if (getIsMaintainerOfLibrary(person, libraryId)) return 'Maintainer' + if (getIsContributorOfLibrary(person, libraryId)) return 'Contributor' + if (getIsConsultantOfLibrary(person, libraryId)) return 'Consultant' + return 'Contributor' +} diff --git a/src/routeTree.gen.ts b/src/routeTree.gen.ts index 15f7e0cc8..5603f1dd3 100644 --- a/src/routeTree.gen.ts +++ b/src/routeTree.gen.ts @@ -46,6 +46,7 @@ import { Route as LibrariesFormVersionIndexRouteImport } from './routes/_librari import { Route as LibrariesDbVersionIndexRouteImport } from './routes/_libraries/db.$version.index' import { Route as LibrariesConfigVersionIndexRouteImport } from './routes/_libraries/config.$version.index' import { Route as LibraryIdVersionDocsIndexRouteImport } from './routes/$libraryId/$version.docs.index' +import { Route as LibraryIdVersionDocsContributorsRouteImport } from './routes/$libraryId/$version.docs.contributors' import { Route as LibraryIdVersionDocsSplatRouteImport } from './routes/$libraryId/$version.docs.$' import { Route as LibraryIdVersionDocsFrameworkIndexRouteImport } from './routes/$libraryId/$version.docs.framework.index' import { Route as LibraryIdVersionDocsFrameworkFrameworkIndexRouteImport } from './routes/$libraryId/$version.docs.framework.$framework.index' @@ -263,6 +264,13 @@ const LibraryIdVersionDocsIndexRoute = getParentRoute: () => LibraryIdVersionDocsRoute, } as any) +const LibraryIdVersionDocsContributorsRoute = + LibraryIdVersionDocsContributorsRouteImport.update({ + id: '/contributors', + path: '/contributors', + getParentRoute: () => LibraryIdVersionDocsRoute, + } as any) + const LibraryIdVersionDocsSplatRoute = LibraryIdVersionDocsSplatRouteImport.update({ id: '/$', @@ -456,6 +464,13 @@ declare module '@tanstack/react-router' { preLoaderRoute: typeof LibraryIdVersionDocsSplatRouteImport parentRoute: typeof LibraryIdVersionDocsRouteImport } + '/$libraryId/$version/docs/contributors': { + id: '/$libraryId/$version/docs/contributors' + path: '/contributors' + fullPath: '/$libraryId/$version/docs/contributors' + preLoaderRoute: typeof LibraryIdVersionDocsContributorsRouteImport + parentRoute: typeof LibraryIdVersionDocsRouteImport + } '/$libraryId/$version/docs/': { id: '/$libraryId/$version/docs/' path: '/' @@ -771,6 +786,15 @@ declare module './routes/$libraryId/$version.docs.$' { FileRoutesByPath['/$libraryId/$version/docs/$']['fullPath'] > } +declare module './routes/$libraryId/$version.docs.contributors' { + const createFileRoute: CreateFileRoute< + '/$libraryId/$version/docs/contributors', + FileRoutesByPath['/$libraryId/$version/docs/contributors']['parentRoute'], + FileRoutesByPath['/$libraryId/$version/docs/contributors']['id'], + FileRoutesByPath['/$libraryId/$version/docs/contributors']['path'], + FileRoutesByPath['/$libraryId/$version/docs/contributors']['fullPath'] + > +} declare module './routes/$libraryId/$version.docs.index' { const createFileRoute: CreateFileRoute< '/$libraryId/$version/docs/', @@ -920,6 +944,7 @@ declare module './routes/$libraryId/$version.docs.framework.$framework.examples. interface LibraryIdVersionDocsRouteChildren { LibraryIdVersionDocsSplatRoute: typeof LibraryIdVersionDocsSplatRoute + LibraryIdVersionDocsContributorsRoute: typeof LibraryIdVersionDocsContributorsRoute LibraryIdVersionDocsIndexRoute: typeof LibraryIdVersionDocsIndexRoute LibraryIdVersionDocsFrameworkIndexRoute: typeof LibraryIdVersionDocsFrameworkIndexRoute LibraryIdVersionDocsFrameworkFrameworkSplatRoute: typeof LibraryIdVersionDocsFrameworkFrameworkSplatRoute @@ -929,6 +954,7 @@ interface LibraryIdVersionDocsRouteChildren { const LibraryIdVersionDocsRouteChildren: LibraryIdVersionDocsRouteChildren = { LibraryIdVersionDocsSplatRoute: LibraryIdVersionDocsSplatRoute, + LibraryIdVersionDocsContributorsRoute: LibraryIdVersionDocsContributorsRoute, LibraryIdVersionDocsIndexRoute: LibraryIdVersionDocsIndexRoute, LibraryIdVersionDocsFrameworkIndexRoute: LibraryIdVersionDocsFrameworkIndexRoute, @@ -1053,6 +1079,7 @@ export interface FileRoutesByFullPath { '/blog/': typeof LibrariesBlogIndexRoute '/stats/npm': typeof StatsNpmIndexRoute '/$libraryId/$version/docs/$': typeof LibraryIdVersionDocsSplatRoute + '/$libraryId/$version/docs/contributors': typeof LibraryIdVersionDocsContributorsRoute '/$libraryId/$version/docs/': typeof LibraryIdVersionDocsIndexRoute '/config/$version': typeof LibrariesConfigVersionIndexRoute '/db/$version': typeof LibrariesDbVersionIndexRoute @@ -1090,6 +1117,7 @@ export interface FileRoutesByTo { '/blog': typeof LibrariesBlogIndexRoute '/stats/npm': typeof StatsNpmIndexRoute '/$libraryId/$version/docs/$': typeof LibraryIdVersionDocsSplatRoute + '/$libraryId/$version/docs/contributors': typeof LibraryIdVersionDocsContributorsRoute '/$libraryId/$version/docs': typeof LibraryIdVersionDocsIndexRoute '/config/$version': typeof LibrariesConfigVersionIndexRoute '/db/$version': typeof LibrariesDbVersionIndexRoute @@ -1132,6 +1160,7 @@ export interface FileRoutesById { '/_libraries/blog/': typeof LibrariesBlogIndexRoute '/stats/npm/': typeof StatsNpmIndexRoute '/$libraryId/$version/docs/$': typeof LibraryIdVersionDocsSplatRoute + '/$libraryId/$version/docs/contributors': typeof LibraryIdVersionDocsContributorsRoute '/$libraryId/$version/docs/': typeof LibraryIdVersionDocsIndexRoute '/_libraries/config/$version/': typeof LibrariesConfigVersionIndexRoute '/_libraries/db/$version/': typeof LibrariesDbVersionIndexRoute @@ -1175,6 +1204,7 @@ export interface FileRouteTypes { | '/blog/' | '/stats/npm' | '/$libraryId/$version/docs/$' + | '/$libraryId/$version/docs/contributors' | '/$libraryId/$version/docs/' | '/config/$version' | '/db/$version' @@ -1211,6 +1241,7 @@ export interface FileRouteTypes { | '/blog' | '/stats/npm' | '/$libraryId/$version/docs/$' + | '/$libraryId/$version/docs/contributors' | '/$libraryId/$version/docs' | '/config/$version' | '/db/$version' @@ -1251,6 +1282,7 @@ export interface FileRouteTypes { | '/_libraries/blog/' | '/stats/npm/' | '/$libraryId/$version/docs/$' + | '/$libraryId/$version/docs/contributors' | '/$libraryId/$version/docs/' | '/_libraries/config/$version/' | '/_libraries/db/$version/' @@ -1410,6 +1442,7 @@ export const routeTree = rootRoute "parent": "/$libraryId/$version", "children": [ "/$libraryId/$version/docs/$", + "/$libraryId/$version/docs/contributors", "/$libraryId/$version/docs/", "/$libraryId/$version/docs/framework/", "/$libraryId/$version/docs/framework/$framework/$", @@ -1432,6 +1465,10 @@ export const routeTree = rootRoute "filePath": "$libraryId/$version.docs.$.tsx", "parent": "/$libraryId/$version/docs" }, + "/$libraryId/$version/docs/contributors": { + "filePath": "$libraryId/$version.docs.contributors.tsx", + "parent": "/$libraryId/$version/docs" + }, "/$libraryId/$version/docs/": { "filePath": "$libraryId/$version.docs.index.tsx", "parent": "/$libraryId/$version/docs" diff --git a/src/routes/$libraryId/$version.docs.contributors.tsx b/src/routes/$libraryId/$version.docs.contributors.tsx new file mode 100644 index 000000000..40c2d1f56 --- /dev/null +++ b/src/routes/$libraryId/$version.docs.contributors.tsx @@ -0,0 +1,55 @@ +import { twMerge } from 'tailwind-merge' +import { DocContainer } from '~/components/DocContainer' +import { DocTitle } from '~/components/DocTitle' +import { getLibrary } from '~/libraries' +import { ContributorsWall } from '~/components/ContributorsWall' +import {} from '@tanstack/react-router' +import { getLibraryContributors } from '~/libraries/maintainers' +import { MaintainerCard } from '~/components/MaintainerCard' + +export const Route = createFileRoute({ + component: RouteComponent, +}) + +function RouteComponent() { + const { libraryId } = Route.useParams() + const library = getLibrary(libraryId) + + // Get the maintainers for this library + const libraryContributors = getLibraryContributors(libraryId) + + return ( + +
+
+ {library.name} Maintainers and Contributors +
+
+
+ {libraryContributors.map((maintainer) => ( + + ))} +
+
+ +
+
+ +

All-Time Contributors

+ +
+
+
+ + ) +} diff --git a/src/routes/_libraries/index.tsx b/src/routes/_libraries/index.tsx index d613a6807..deba766b8 100644 --- a/src/routes/_libraries/index.tsx +++ b/src/routes/_libraries/index.tsx @@ -15,6 +15,8 @@ import splashLightImg from '~/images/splash-light.png' import splashDarkImg from '~/images/splash-dark.png' import { GadFooter } from '~/components/GoogleScripts' import LandingPageGad from '~/components/LandingPageGad' +import { MaintainerCard } from '~/components/MaintainerCard' +import { coreMaintainers } from '~/libraries/maintainers' export const textColors = [ `text-rose-500`, @@ -382,6 +384,15 @@ function Index() {
+
+

Core Maintainers

+
+ {coreMaintainers.map((maintainer) => ( + + ))} +
+
+
diff --git a/src/routes/_libraries/route.tsx b/src/routes/_libraries/route.tsx index 941919fab..923b88a86 100644 --- a/src/routes/_libraries/route.tsx +++ b/src/routes/_libraries/route.tsx @@ -5,7 +5,13 @@ import { MdLibraryBooks, MdLineAxis, MdSupport } from 'react-icons/md' import { twMerge } from 'tailwind-merge' import { sortBy } from '~/utils/utils' import logoColor100w from '~/images/logo-color-100w.png' -import { FaDiscord, FaGithub, FaInstagram, FaTshirt } from 'react-icons/fa' +import { + FaDiscord, + FaGithub, + FaInstagram, + FaTshirt, + FaUsers, +} from 'react-icons/fa' import { getSponsorsForSponsorPack } from '~/server/sponsors' import { libraries } from '~/libraries' import { Scarf } from '~/components/Scarf' @@ -28,7 +34,7 @@ function LibrariesLayout() { const activeLibrary = useLocation({ select: (location) => { return libraries.find((library) => { - return location.pathname.startsWith(library.to) + return location.pathname.startsWith(library.to!) }) }, }) @@ -38,110 +44,123 @@ function LibrariesLayout() { const items = ( <> - {sortBy(libraries, (d) => !d.name.includes('TanStack')).map( - (library, i) => { - const [prefix, name] = library.name.split(' ') + {sortBy( + libraries.filter((d) => d.to), + (d) => !d.name.includes('TanStack') + ).map((library, i) => { + const [prefix, name] = library.name.split(' ') - return ( -
- {library.to.startsWith('http') ? ( - - - - {prefix} - {' '} - {name} - - - ) : ( -
- { - detailsRef.current.removeAttribute('open') - }} - > - {(props) => { - return ( -
+ {library.to?.startsWith('http') ? ( + + + + {prefix} + {' '} + {name} + + + ) : ( +
+ { + detailsRef.current.removeAttribute('open') + }} + > + {(props) => { + return ( +
+ + {prefix} + {' '} + + {name} + + + {library.badge ? ( + - - {prefix} - {' '} - - {name} - + {library.badge} - {library.badge ? ( - - {library.badge} - - ) : null} -
- ) + ) : null} +
+ ) + }} + +
+ {library.menu?.map((item, i) => { + return ( + + {item.icon} + {item.label} + + ) + })} + -
- {library.menu?.map((item, i) => { - return ( - - {item.icon} - {item.label} - - ) - })} -
+ + Contributors +
- )} -
- ) - } - )} +
+ )} +
+ ) + })}