Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 2 additions & 3 deletions src/components/DocsLayout.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -20,9 +20,8 @@
const getFrameworkTextColor = (frameworkValue: string | undefined) => {
if (!frameworkValue) return 'text-gray-500'
const framework = frameworkOptions.find((f) => f.value === frameworkValue)
if (!framework) return 'text-gray-500'
// Convert bg-* to text-* color class
return framework.color.replace('bg-', 'text-')

return framework?.fontColor ?? 'text-gray-500'
}

// Create context for width toggle state
Expand Down Expand Up @@ -183,7 +182,7 @@
const prevItem = flatMenu[index - 1]
const nextItem = flatMenu[index + 1]

const [showBytes, setShowBytes] = useLocalStorage('showBytes', true)

Check warning on line 185 in src/components/DocsLayout.tsx

View workflow job for this annotation

GitHub Actions / PR

'setShowBytes' is assigned a value but never used

Check warning on line 185 in src/components/DocsLayout.tsx

View workflow job for this annotation

GitHub Actions / PR

'showBytes' is assigned a value but never used
const [isFullWidth, setIsFullWidth] = useLocalStorage('docsFullWidth', false)

const activePartners = partners.filter(
Expand Down
134 changes: 134 additions & 0 deletions src/components/FrameworkCard.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,134 @@
import { Link } from '@tanstack/react-router'
import { twMerge } from 'tailwind-merge'
import { FaCheck, FaCopy } from 'react-icons/fa'
import { Library } from '~/libraries'
import { getFrameworkOptions } from '~/libraries/frameworks'
import { useCopyButton } from '~/components/CopyMarkdownButton'
import { useToast } from '~/components/ToastProvider'

export function FrameworkCard({
framework,
libraryId,
packageName,
index,
library,
}: {
framework: ReturnType<typeof getFrameworkOptions>[number]
libraryId: string
packageName: string
index: number
library: Library
}) {
const { notify } = useToast()
const [copied, onCopyClick] = useCopyButton(async () => {
await navigator.clipboard.writeText(packageName)
notify(
<div>
<div className="font-medium">Copied package name</div>
<div className="text-gray-500 dark:text-gray-400 text-xs">
{packageName} copied to clipboard
</div>
</div>
)
})

const hasCustomInstallPath = !!library.installPath
const installationPath = library.installPath
? library.installPath
.replace('$framework', framework.value)
.replace('$libraryId', libraryId)
: 'installation'

// Add framework hash fragment only for default installation pages (when installPath is not defined)
// Link component adds the # automatically, so we just pass the value without #
const installationHash = !hasCustomInstallPath ? framework.value : undefined

return (
<div
className={twMerge(
'border-2 border-gray-200 dark:border-gray-800/50 rounded-xl',
'shadow-md p-6 transition-all duration-300 ease-out',
'bg-white/90 dark:bg-black/40 backdrop-blur-sm',
'hover:shadow-xl hover:-translate-y-1',
'flex flex-col gap-4 group',
'min-h-[180px]',
'relative'
)}
style={{
zIndex: index,
willChange: 'transform',
}}
>
<Link
from="/$libraryId/$version/docs"
to="./$"
params={{
_splat: installationPath,
}}
hash={installationHash}
className="flex flex-col flex-1 gap-4"
>
{/* Framework Logo */}
<div className="flex-shrink-0 flex justify-center">
<img
src={framework.logo}
alt={framework.label}
className="w-16 h-16 object-contain transition-transform duration-300 group-hover:scale-110"
/>
</div>

{/* Framework Name */}
<div className="text-center flex-1 flex items-center justify-center">
<div className="text-lg font-semibold text-gray-900 dark:text-gray-100">
{framework.label}
</div>
</div>
</Link>

{/* Package Name with Copy Button - Bottom of Card */}
<div className="pt-2 border-t border-gray-200 dark:border-gray-800 space-y-2">
<div className="flex items-center justify-center gap-2">
<code className="text-xs text-gray-600 dark:text-gray-400 font-mono">
{packageName}
</code>
<button
onClick={(e) => {
e.preventDefault()
e.stopPropagation()
onCopyClick(e)
}}
className="flex-shrink-0 p-1.5 text-gray-500 hover:text-gray-700 dark:text-gray-400 dark:hover:text-gray-200 transition-colors rounded hover:bg-gray-100 dark:hover:bg-gray-800"
title="Copy package name"
>
{copied ? (
<FaCheck className="w-3 h-3 text-green-600 dark:text-green-400" />
) : (
<FaCopy className="w-3 h-3" />
)}
</button>
</div>
<Link
from="/$libraryId/$version/docs"
to="./$"
params={{
_splat: installationPath,
}}
hash={installationHash}
className="block w-full text-center text-xs text-gray-600 dark:text-gray-400 hover:text-gray-900 dark:hover:text-gray-200 underline transition-colors"
>
Full install instructions
</Link>
</div>

{/* Accent indicator */}
<div
className={twMerge(
'absolute bottom-0 left-0 right-0 h-1 rounded-b-xl',
'transition-opacity duration-300',
'opacity-0 group-hover:opacity-100',
framework.color
)}
/>
</div>
)
}
68 changes: 59 additions & 9 deletions src/libraries/frameworks.tsx
Original file line number Diff line number Diff line change
@@ -1,28 +1,78 @@
import reactLogo from '../images/react-logo.svg'
import vueLogo from '../images/vue-logo.svg'
import angularLogo from '../images/angular-logo.svg'
import svelteLogo from '../images/svelte-logo.svg'
import solidLogo from '../images/solid-logo.svg'
import jsLogo from '../images/js-logo.svg'
import litLogo from '../images/lit-logo.svg'
import qwikLogo from '../images/qwik-logo.svg'
import preactLogo from '../images/preact-logo.svg'
import reactLogo from '../images/react-logo.svg'
import solidLogo from '../images/solid-logo.svg'
import svelteLogo from '../images/svelte-logo.svg'
import vueLogo from '../images/vue-logo.svg'
import type { Framework } from './types'

export const frameworkOptions = [
{ label: 'React', value: 'react', logo: reactLogo, color: 'bg-blue-500' },
{ label: 'Vue', value: 'vue', logo: vueLogo, color: 'bg-green-500' },
{
label: 'React',
value: 'react',
logo: reactLogo,
color: 'bg-blue-500',
fontColor: 'text-sky-500',
},
{
label: 'Preact',
value: 'preact',
logo: preactLogo,
color: 'bg-purple-500',
fontColor: 'text-purple-500',
},
{
label: 'Vue',
value: 'vue',
logo: vueLogo,
color: 'bg-green-500',
fontColor: 'text-green-500',
},
{
label: 'Angular',
value: 'angular',
logo: angularLogo,
color: 'bg-red-500',
fontColor: 'text-fuchsia-500',
},
{
label: 'Solid',
value: 'solid',
logo: solidLogo,
color: 'bg-blue-600',
fontColor: 'text-blue-600',
},
{
label: 'Lit',
value: 'lit',
logo: litLogo,
color: 'bg-emerald-500',
fontColor: 'text-emerald-500',
},
{
label: 'Svelte',
value: 'svelte',
logo: svelteLogo,
color: 'bg-orange-500',
color: 'bg-orange-600',
fontColor: 'text-orange-600',
},
{
label: 'Qwik',
value: 'qwik',
logo: qwikLogo,
color: 'bg-indigo-500',
fontColor: 'text-indigo-500',
},
{
label: 'Vanilla',
value: 'vanilla',
logo: jsLogo,
color: 'bg-yellow-500',
fontColor: 'text-yellow-500',
},
{ label: 'Solid', value: 'solid', logo: solidLogo, color: 'bg-blue-600' },
{ label: 'Vanilla', value: 'vanilla', logo: jsLogo, color: 'bg-yellow-500' },
] as const

export function getFrameworkOptions(frameworkStrs: Framework[]) {
Expand Down
29 changes: 27 additions & 2 deletions src/libraries/maintainers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -114,7 +114,7 @@ export const allMaintainers: Maintainer[] = [
isCoreMaintainer: true,
avatar: 'https://github.com/jherr.png',
github: 'jherr',
creatorOf: ['create-tsrouter-app'],
creatorOf: ['ai', 'create-tsrouter-app'],
frameworkExpertise: ['react'],
specialties: ['Templates'],
workshopsAvailable: true,
Expand Down Expand Up @@ -168,7 +168,7 @@ export const allMaintainers: Maintainer[] = [
name: 'Alem Tuzlak',
avatar: 'https://github.com/AlemTuzlak.png',
github: 'AlemTuzlak',
creatorOf: ['devtools'],
creatorOf: ['ai', 'devtools'],
contributorOf: ['pacer', 'form'],
frameworkExpertise: ['react'],
specialties: ['DevTools', 'Routers', 'Vite Plugins'],
Expand Down Expand Up @@ -273,6 +273,31 @@ export const allMaintainers: Maintainer[] = [
website: 'https://www.linkedin.com/in/jonghyeonko',
},
},
{
name: 'Sarah Gerrard',
avatar: 'https://github.com/ladybluenotes.png',
github: 'ladybluenotes',
contributorOf: [
'ai',
'config',
'db',
'devtools',
'form',
'pacer',
'query',
'ranger',
'router',
'start',
'store',
'table',
'virtual',
],
frameworkExpertise: ['react', 'solid'],
specialties: ['Documentation'],
social: {
bluesky: 'https://bsky.app/profile/ladybluenotes.dev',
},
},
{
name: 'Riccardo Perra',
avatar:
Expand Down
1 change: 1 addition & 0 deletions src/libraries/query.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ export const queryProject = {
frameworks: ['react', 'solid', 'vue', 'svelte', 'angular'],
scarfId: '53afb586-3934-4624-a37a-e680c1528e17',
defaultDocs: 'framework/react/overview',
installPath: 'framework/$framework/installation',
legacyPackages: ['react-query'],
handleRedirects: (href: string) => {
handleRedirects(
Expand Down
1 change: 1 addition & 0 deletions src/libraries/router.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ export const routerProject = {
frameworks: ['react', 'solid'],
scarfId: '3d14fff2-f326-4929-b5e1-6ecf953d24f4',
defaultDocs: 'framework/react/overview',
installPath: 'framework/$framework/installation',
legacyPackages: ['react-location'],
hideCodesandboxUrl: true,
showVercelUrl: false,
Expand Down
1 change: 1 addition & 0 deletions src/libraries/start.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ export const startProject = {
embedEditor: 'codesandbox',
frameworks: ['react', 'solid'],
defaultDocs: 'framework/react/overview',
installPath: 'framework/$framework/build-from-scratch',
scarfId: 'b6e2134f-e805-401d-95c3-2a7765d49a3d',
showNetlifyUrl: true,
showCloudflareUrl: true,
Expand Down
2 changes: 1 addition & 1 deletion src/libraries/store.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ export const storeProject = {
colorFrom: 'from-twine-500',
colorTo: 'to-twine-700',
textColor: 'text-twine-700',
frameworks: ['react', 'solid', 'svelte', 'vue', 'angular'],
frameworks: ['react', 'preact', 'solid', 'svelte', 'vue', 'angular'],
scarfId: '302d0fef-cb3f-43c6-b45c-f055b9745edb',
defaultDocs: 'overview',
menu: [
Expand Down
1 change: 1 addition & 0 deletions src/libraries/table.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@ export const tableProject = {
],
scarfId: 'dc8b39e1-3fe9-4f3a-8e56-d4e2cf420a9e',
defaultDocs: 'introduction',
corePackageName: 'table-core',
legacyPackages: ['react-table'],
handleRedirects: (href) => {
handleRedirects(
Expand Down
11 changes: 8 additions & 3 deletions src/libraries/types.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,15 @@
import * as React from 'react'

export type Framework =
| 'react'
| 'vue'
| 'angular'
| 'svelte'
| 'lit'
| 'preact'
| 'qwik'
| 'react'
| 'solid'
| 'svelte'
| 'vanilla'
| 'vue'

export type Library = {
id:
Expand Down Expand Up @@ -62,6 +65,8 @@ export type Library = {
visible?: boolean
// Legacy npm packages (non-@tanstack scope) to include in stats
legacyPackages?: string[]
installPath?: string
corePackageName?: string
}

export type LibraryMenuItem = {
Expand Down
Loading
Loading