This commit is contained in:
2025-05-06 02:13:23 +08:00
commit 22baba9e7b
52 changed files with 13258 additions and 0 deletions

View File

@@ -0,0 +1,25 @@
import { cache } from "react"
import { getPokemon } from "@/registry/new-york/complex-component/lib/pokemon"
import { Card, CardContent } from "@/components/ui/card"
import { PokemonImage } from "@/registry/new-york/complex-component/components/pokemon-image"
const cachedGetPokemon = cache(getPokemon)
export async function PokemonCard({ name }: { name: string }) {
const pokemon = await cachedGetPokemon(name)
if (!pokemon) {
return null
}
return (
<Card>
<CardContent className="flex flex-col items-center p-2">
<div>
<PokemonImage name={pokemon.name} number={pokemon.id} />
</div>
<div className="text-center font-medium">{pokemon.name}</div>
</CardContent>
</Card>
)
}

View File

@@ -0,0 +1,20 @@
"use client"
/* eslint-disable @next/next/no-img-element */
import { usePokemonImage } from "@/registry/new-york/complex-component/hooks/use-pokemon"
export function PokemonImage({
name,
number,
}: {
name: string
number: number
}) {
const imageUrl = usePokemonImage(number)
if (!imageUrl) {
return null
}
return <img src={imageUrl} alt={name} />
}

View File

@@ -0,0 +1,7 @@
"use client"
// Totally unnecessary hook, but it's a good example of how to use a hook in a custom registry.
export function usePokemonImage(number: number) {
return `https://raw.githubusercontent.com/PokeAPI/sprites/master/sprites/pokemon/${number}.png`
}

View File

@@ -0,0 +1,48 @@
import { z } from "zod"
export async function getPokemonList({ limit = 10 }: { limit?: number }) {
try {
const response = await fetch(
`https://pokeapi.co/api/v2/pokemon?limit=${limit}`
)
return z
.object({
results: z.array(z.object({ name: z.string() })),
})
.parse(await response.json())
} catch (error) {
console.error(error)
return null
}
}
export async function getPokemon(name: string) {
try {
const response = await fetch(`https://pokeapi.co/api/v2/pokemon/${name}`)
if (!response.ok) {
throw new Error("Failed to fetch pokemon")
}
return z
.object({
name: z.string(),
id: z.number(),
sprites: z.object({
front_default: z.string(),
}),
stats: z.array(
z.object({
base_stat: z.number(),
stat: z.object({
name: z.string(),
}),
})
),
})
.parse(await response.json())
} catch (error) {
console.error(error)
return null
}
}

View File

@@ -0,0 +1,23 @@
import { cache } from "react"
import { PokemonCard } from "@/registry/new-york/complex-component/components/pokemon-card"
import { getPokemonList } from "@/registry/new-york/complex-component/lib/pokemon"
const getCachedPokemonList = cache(getPokemonList)
export default async function Page() {
const pokemons = await getCachedPokemonList({ limit: 12 })
if (!pokemons) {
return null
}
return (
<div className="mx-auto w-full max-w-2xl px-4">
<div className="grid grid-cols-2 gap-4 py-10 sm:grid-cols-3 md:grid-cols-4">
{pokemons.results.map((p) => (
<PokemonCard key={p.name} name={p.name} />
))}
</div>
</div>
)
}