snapshot of https://github.com/TanStack/query.git (main, 2026-09-16; blobs > 131072B omitted)
This commit is contained in:
25
examples/solid/astro/.gitignore
vendored
Normal file
25
examples/solid/astro/.gitignore
vendored
Normal file
@@ -0,0 +1,25 @@
|
||||
# build output
|
||||
dist/
|
||||
.vercel/
|
||||
# generated types
|
||||
.astro/
|
||||
|
||||
# dependencies
|
||||
node_modules/
|
||||
|
||||
# logs
|
||||
npm-debug.log*
|
||||
yarn-debug.log*
|
||||
yarn-error.log*
|
||||
pnpm-debug.log*
|
||||
|
||||
|
||||
# environment variables
|
||||
.env
|
||||
.env.production
|
||||
|
||||
# macOS-specific files
|
||||
.DS_Store
|
||||
|
||||
# jetbrains setting folder
|
||||
.idea/
|
||||
47
examples/solid/astro/README.md
Normal file
47
examples/solid/astro/README.md
Normal file
@@ -0,0 +1,47 @@
|
||||
# Astro Starter Kit: Minimal
|
||||
|
||||
```sh
|
||||
npm create astro@latest -- --template minimal
|
||||
```
|
||||
|
||||
[](https://stackblitz.com/github/withastro/astro/tree/latest/examples/minimal)
|
||||
[](https://codesandbox.io/p/sandbox/github/withastro/astro/tree/latest/examples/minimal)
|
||||
[](https://codespaces.new/withastro/astro?devcontainer_path=.devcontainer/minimal/devcontainer.json)
|
||||
|
||||
> 🧑🚀 **Seasoned astronaut?** Delete this file. Have fun!
|
||||
|
||||
## 🚀 Project Structure
|
||||
|
||||
Inside of your Astro project, you'll see the following folders and files:
|
||||
|
||||
```text
|
||||
/
|
||||
├── public/
|
||||
├── src/
|
||||
│ └── pages/
|
||||
│ └── index.astro
|
||||
└── package.json
|
||||
```
|
||||
|
||||
Astro looks for `.astro` or `.md` files in the `src/pages/` directory. Each page is exposed as a route based on its file name.
|
||||
|
||||
There's nothing special about `src/components/`, but that's where we like to put any Astro/React/Vue/Svelte/Preact components.
|
||||
|
||||
Any static assets, like images, can be placed in the `public/` directory.
|
||||
|
||||
## 🧞 Commands
|
||||
|
||||
All commands are run from the root of the project, from a terminal:
|
||||
|
||||
| Command | Action |
|
||||
| :------------------------ | :----------------------------------------------- |
|
||||
| `npm install` | Installs dependencies |
|
||||
| `npm run dev` | Starts local dev server at `localhost:4321` |
|
||||
| `npm run build` | Build your production site to `./dist/` |
|
||||
| `npm run preview` | Preview your build locally, before deploying |
|
||||
| `npm run astro ...` | Run CLI commands like `astro add`, `astro check` |
|
||||
| `npm run astro -- --help` | Get help using the Astro CLI |
|
||||
|
||||
## 👀 Want to learn more?
|
||||
|
||||
Feel free to check [our documentation](https://docs.astro.build) or jump into our [Discord server](https://astro.build/chat).
|
||||
12
examples/solid/astro/astro.config.mjs
Normal file
12
examples/solid/astro/astro.config.mjs
Normal file
@@ -0,0 +1,12 @@
|
||||
import { defineConfig } from 'astro/config'
|
||||
import solidJs from '@astrojs/solid-js'
|
||||
import tailwind from '@astrojs/tailwind'
|
||||
import node from '@astrojs/node'
|
||||
// https://astro.build/config
|
||||
export default defineConfig({
|
||||
output: 'server',
|
||||
integrations: [solidJs(), tailwind()],
|
||||
adapter: node({
|
||||
mode: 'standalone',
|
||||
}),
|
||||
})
|
||||
25
examples/solid/astro/package.json
Normal file
25
examples/solid/astro/package.json
Normal file
@@ -0,0 +1,25 @@
|
||||
{
|
||||
"name": "@tanstack/query-example-solid-astro",
|
||||
"private": true,
|
||||
"type": "module",
|
||||
"scripts": {
|
||||
"dev": "astro dev",
|
||||
"start": "astro dev",
|
||||
"build": "astro check && astro build",
|
||||
"preview": "astro preview",
|
||||
"astro": "astro"
|
||||
},
|
||||
"dependencies": {
|
||||
"@astrojs/check": "^0.9.4",
|
||||
"@astrojs/node": "^9.1.3",
|
||||
"@astrojs/solid-js": "^5.0.7",
|
||||
"@astrojs/tailwind": "^6.0.2",
|
||||
"@astrojs/vercel": "^8.1.3",
|
||||
"@tanstack/solid-query": "^5.102.8",
|
||||
"@tanstack/solid-query-devtools": "^5.102.8",
|
||||
"astro": "^5.5.6",
|
||||
"solid-js": "^1.9.7",
|
||||
"tailwindcss": "^3.4.7",
|
||||
"typescript": "5.8.3"
|
||||
}
|
||||
}
|
||||
9
examples/solid/astro/public/favicon.svg
Normal file
9
examples/solid/astro/public/favicon.svg
Normal file
@@ -0,0 +1,9 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 128 128">
|
||||
<path d="M50.4 78.5a75.1 75.1 0 0 0-28.5 6.9l24.2-65.7c.7-2 1.9-3.2 3.4-3.2h29c1.5 0 2.7 1.2 3.4 3.2l24.2 65.7s-11.6-7-28.5-7L67 45.5c-.4-1.7-1.6-2.8-2.9-2.8-1.3 0-2.5 1.1-2.9 2.7L50.4 78.5Zm-1.1 28.2Zm-4.2-20.2c-2 6.6-.6 15.8 4.2 20.2a17.5 17.5 0 0 1 .2-.7 5.5 5.5 0 0 1 5.7-4.5c2.8.1 4.3 1.5 4.7 4.7.2 1.1.2 2.3.2 3.5v.4c0 2.7.7 5.2 2.2 7.4a13 13 0 0 0 5.7 4.9v-.3l-.2-.3c-1.8-5.6-.5-9.5 4.4-12.8l1.5-1a73 73 0 0 0 3.2-2.2 16 16 0 0 0 6.8-11.4c.3-2 .1-4-.6-6l-.8.6-1.6 1a37 37 0 0 1-22.4 2.7c-5-.7-9.7-2-13.2-6.2Z" />
|
||||
<style>
|
||||
path { fill: #000; }
|
||||
@media (prefers-color-scheme: dark) {
|
||||
path { fill: #FFF; }
|
||||
}
|
||||
</style>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 749 B |
20
examples/solid/astro/src/components/Link.tsx
Normal file
20
examples/solid/astro/src/components/Link.tsx
Normal file
@@ -0,0 +1,20 @@
|
||||
export const Link = (props: {
|
||||
href: string
|
||||
children: any
|
||||
class?: string
|
||||
}) => {
|
||||
// Links doing client-side navigation
|
||||
return (
|
||||
<a
|
||||
href={props.href}
|
||||
onClick={(e) => {
|
||||
e.preventDefault()
|
||||
history.pushState({}, '', props.href)
|
||||
window.dispatchEvent(new PopStateEvent('popstate'))
|
||||
}}
|
||||
class={props.class}
|
||||
>
|
||||
{props.children}
|
||||
</a>
|
||||
)
|
||||
}
|
||||
250
examples/solid/astro/src/components/SolidApp.tsx
Normal file
250
examples/solid/astro/src/components/SolidApp.tsx
Normal file
@@ -0,0 +1,250 @@
|
||||
import {
|
||||
QueryClient,
|
||||
QueryClientProvider,
|
||||
keepPreviousData,
|
||||
useQuery,
|
||||
} from '@tanstack/solid-query'
|
||||
import { SolidQueryDevtools } from '@tanstack/solid-query-devtools'
|
||||
import {
|
||||
For,
|
||||
Show,
|
||||
Suspense,
|
||||
createContext,
|
||||
createSignal,
|
||||
useContext,
|
||||
} from 'solid-js'
|
||||
import { isServer } from 'solid-js/web'
|
||||
import { getSearchParams, properCase } from '../utils'
|
||||
import { Link } from './Link'
|
||||
|
||||
const PokemonIdContext = createContext<() => string>()
|
||||
|
||||
const usePokemonID = () => {
|
||||
const id = useContext(PokemonIdContext)
|
||||
if (!id) throw new Error('PokemonIdContext not found')
|
||||
return id
|
||||
}
|
||||
|
||||
const MAX_POKEMONS = 100
|
||||
|
||||
export const SolidApp = (props: { pokemon?: string }) => {
|
||||
const client = new QueryClient()
|
||||
|
||||
const search = getSearchParams(props.pokemon || '')
|
||||
|
||||
return (
|
||||
<QueryClientProvider client={client}>
|
||||
<SolidQueryDevtools />
|
||||
<Suspense fallback={'Loading'}>
|
||||
<PokemonIdContext.Provider value={search}>
|
||||
<App />
|
||||
</PokemonIdContext.Provider>
|
||||
</Suspense>
|
||||
</QueryClientProvider>
|
||||
)
|
||||
}
|
||||
|
||||
const App = () => {
|
||||
return (
|
||||
<div class="flex flex-1 overflow-auto">
|
||||
<SideNav />
|
||||
<PokemonDetails />
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
const PokemonDetails = () => {
|
||||
const id = usePokemonID()
|
||||
return (
|
||||
<div class="flex-1 flex">
|
||||
<Show when={id()}>
|
||||
<Suspense fallback={'Loading'}>
|
||||
<PokemonDex id={id()} />
|
||||
</Suspense>
|
||||
</Show>
|
||||
<Show when={!id()}>
|
||||
<div class="justify-center items-center flex-1 flex text-2xl font-medium text-zinc-700">
|
||||
<div class="">Select a pokemon to see its stats</div>
|
||||
</div>
|
||||
</Show>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
const PokemonDex = (props: { id: string }) => {
|
||||
const pokemon = useQuery(() => ({
|
||||
queryKey: ['pokemon', props.id],
|
||||
queryFn: async () => {
|
||||
const res = await fetch(
|
||||
`https://pokeapi.co/api/v2/pokemon/${props.id}`,
|
||||
).then((res) => res.json())
|
||||
return res
|
||||
},
|
||||
placeholderData: keepPreviousData,
|
||||
}))
|
||||
|
||||
const pokemon_stats = useQuery(() => ({
|
||||
queryKey: ['pokemon', props.id],
|
||||
queryFn: async () => {
|
||||
const res = await fetch(
|
||||
`https://pokeapi.co/api/v2/pokemon/${props.id}`,
|
||||
).then((res) => res.json())
|
||||
|
||||
return res
|
||||
},
|
||||
select(data) {
|
||||
const nameMap = {
|
||||
hp: 'HP',
|
||||
attack: 'Attack',
|
||||
defense: 'Defense',
|
||||
'special-attack': 'Special Attack',
|
||||
'special-defense': 'Special Defense',
|
||||
speed: 'Speed',
|
||||
}
|
||||
const stats = data.stats.map((stat: any) => ({
|
||||
name: nameMap[stat.stat.name as keyof typeof nameMap],
|
||||
value: stat.base_stat,
|
||||
}))
|
||||
return stats as { name: string; value: number }[]
|
||||
},
|
||||
placeholderData: keepPreviousData,
|
||||
reconcile: 'name',
|
||||
}))
|
||||
|
||||
const is_server_rendered = useQuery(() => ({
|
||||
queryKey: ['is_server_rendered', props.id],
|
||||
queryFn: () => {
|
||||
if (isServer) return true
|
||||
return false
|
||||
},
|
||||
placeholderData: keepPreviousData,
|
||||
}))
|
||||
|
||||
return (
|
||||
<div class="flex flex-col flex-1">
|
||||
<Show when={pokemon.data}>
|
||||
<div class="flex justify-center text-3xl font-semibold py-4">
|
||||
{properCase(pokemon.data.name)}
|
||||
</div>
|
||||
<div class="flex justify-center">
|
||||
<div class="h-64 w-64 rounded-lg relative overflow-hidden border">
|
||||
<img
|
||||
class="h-[400%] w-[400%] max-w-[400%] object-contain blur-2xl brightness-125 contrast-150 absolute z-10 top-1/2 left-1/2 -translate-x-1/2 -translate-y-1/2"
|
||||
src={`https://raw.githubusercontent.com/PokeAPI/sprites/master/sprites/pokemon/other/official-artwork/${props.id}.png`}
|
||||
alt={properCase(pokemon.data?.name || '')}
|
||||
/>
|
||||
<img
|
||||
class="h-full w-full object-contain relative z-20"
|
||||
src={`https://raw.githubusercontent.com/PokeAPI/sprites/master/sprites/pokemon/other/official-artwork/${props.id}.png`}
|
||||
alt={properCase(pokemon.data?.name || '')}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</Show>
|
||||
|
||||
<div class="flex justify-center py-4">
|
||||
<div>
|
||||
This query was rendered on the{' '}
|
||||
<Show
|
||||
when={is_server_rendered.data}
|
||||
fallback={
|
||||
<>
|
||||
<b>client</b>. Reload this page to see the page rendered on the
|
||||
server.
|
||||
</>
|
||||
}
|
||||
>
|
||||
<b>server. </b>
|
||||
Click on another pokemon to see queries run and render on the
|
||||
client.
|
||||
</Show>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<Show when={pokemon_stats.data}>
|
||||
<div class="flex justify-center flex-col gap-2">
|
||||
<For each={pokemon_stats.data}>
|
||||
{(stat) => (
|
||||
<div class="flex gap-2 justify-center items-center">
|
||||
<div class="w-36">{stat.name}</div>
|
||||
<div class="w-80 h-8 relative bg-zinc-100 rounded overflow-hidden flex justify-center items-center">
|
||||
<div
|
||||
class="absolute z-10 h-full top-0 left-0 bg-lime-300"
|
||||
style={{
|
||||
width: `${(stat.value / 160) * 100}%`,
|
||||
transition: 'width 0.5s',
|
||||
}}
|
||||
></div>
|
||||
<div class="relative z-20 text-sm mix-blend-darken font-semibold">
|
||||
{stat.value}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
</For>
|
||||
</div>
|
||||
</Show>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
const SideNav = () => {
|
||||
const id = usePokemonID()
|
||||
|
||||
const pokemonsList = useQuery(() => ({
|
||||
queryKey: ['pokemons'],
|
||||
queryFn: async () => {
|
||||
const res = await fetch(
|
||||
`https://pokeapi.co/api/v2/pokemon?limit=${MAX_POKEMONS}`,
|
||||
).then((res) => res.json())
|
||||
return res as {
|
||||
results: { name: string; url: string }[]
|
||||
}
|
||||
},
|
||||
select(data) {
|
||||
return data.results.map((p) => {
|
||||
const regex = /\/pokemon\/(\d+)\/$/
|
||||
const match = p.url.match(regex)
|
||||
const id = match ? match[1] : ''
|
||||
return {
|
||||
name: properCase(p.name),
|
||||
id,
|
||||
avatar: `https://raw.githubusercontent.com/PokeAPI/sprites/master/sprites/pokemon/other/official-artwork/${id}.png`,
|
||||
}
|
||||
})
|
||||
},
|
||||
placeholderData: keepPreviousData,
|
||||
reconcile: 'id',
|
||||
}))
|
||||
|
||||
const activeClass = (pokemonID: string) =>
|
||||
id() === pokemonID ? 'bg-gray-50' : 'hover:bg-gray-50'
|
||||
|
||||
return (
|
||||
<div class="w-72 border-r flex flex-col px-2">
|
||||
<div class="pt-3 pb-1 text-sm font-semibold">Pokemons</div>
|
||||
|
||||
<div class="flex-1 overflow-auto flex flex-col gap-1">
|
||||
<For each={pokemonsList.data}>
|
||||
{(pokemon) => (
|
||||
<Link
|
||||
class={`flex gap-2 items-center border rounded p-1 ${activeClass(
|
||||
pokemon.id,
|
||||
)}`}
|
||||
href={`./?id=${pokemon.id}`}
|
||||
>
|
||||
<span class="flex bg-zinc-100 border relative justify-center items-center overflow-hidden rounded-sm w-9 h-9 flex-shrink-0">
|
||||
<img
|
||||
class="w-7 h-7 relative z-20"
|
||||
src={pokemon.avatar}
|
||||
alt={pokemon.name}
|
||||
/>
|
||||
</span>
|
||||
<span>{pokemon.name}</span>
|
||||
</Link>
|
||||
)}
|
||||
</For>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
2
examples/solid/astro/src/env.d.ts
vendored
Normal file
2
examples/solid/astro/src/env.d.ts
vendored
Normal file
@@ -0,0 +1,2 @@
|
||||
/// <reference path="../.astro/types.d.ts" />
|
||||
/// <reference types="astro/client" />
|
||||
25
examples/solid/astro/src/layouts/MainLayout.astro
Normal file
25
examples/solid/astro/src/layouts/MainLayout.astro
Normal file
@@ -0,0 +1,25 @@
|
||||
---
|
||||
import { SolidApp } from "../components/SolidApp";
|
||||
const { id } = Astro.props;
|
||||
|
||||
---
|
||||
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<link rel="icon" type="image/svg+xml" href="/favicon.svg" />
|
||||
<meta name="viewport" content="width=device-width" />
|
||||
<meta name="generator" content={Astro.generator} />
|
||||
<title>Astro</title>
|
||||
</head>
|
||||
<body class="text-zinc-900">
|
||||
<div class="flex h-screen flex-col">
|
||||
<nav class="flex items-center h-12 border-b px-4 flex-shrink-0">
|
||||
<div class="font-semibold text-lg">
|
||||
Solid Query + Astro
|
||||
</div>
|
||||
</nav>
|
||||
<SolidApp pokemon={id} client:load />
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
7
examples/solid/astro/src/pages/index.astro
Normal file
7
examples/solid/astro/src/pages/index.astro
Normal file
@@ -0,0 +1,7 @@
|
||||
---
|
||||
import MainLayout from '../layouts/MainLayout.astro';
|
||||
|
||||
const id = Astro.url.searchParams.get('id') || '';
|
||||
---
|
||||
|
||||
<MainLayout id={id} />
|
||||
16
examples/solid/astro/src/utils/index.ts
Normal file
16
examples/solid/astro/src/utils/index.ts
Normal file
@@ -0,0 +1,16 @@
|
||||
import { createSignal } from 'solid-js'
|
||||
|
||||
export const getSearchParams = (init: string) => {
|
||||
const [search, setSearch] = createSignal(init)
|
||||
if (typeof window !== 'undefined') {
|
||||
window.addEventListener('popstate', () => {
|
||||
const location = window.location
|
||||
const params = new URLSearchParams(location.search)
|
||||
setSearch(params.get('id') || '')
|
||||
})
|
||||
}
|
||||
return search
|
||||
}
|
||||
|
||||
export const properCase = (str: string) =>
|
||||
str.charAt(0).toUpperCase() + str.slice(1)
|
||||
8
examples/solid/astro/tailwind.config.mjs
Normal file
8
examples/solid/astro/tailwind.config.mjs
Normal file
@@ -0,0 +1,8 @@
|
||||
/** @type {import('tailwindcss').Config} */
|
||||
export default {
|
||||
content: ['./src/**/*.{astro,html,js,jsx,md,mdx,svelte,ts,tsx,vue}'],
|
||||
theme: {
|
||||
extend: {},
|
||||
},
|
||||
plugins: [],
|
||||
}
|
||||
7
examples/solid/astro/tsconfig.json
Normal file
7
examples/solid/astro/tsconfig.json
Normal file
@@ -0,0 +1,7 @@
|
||||
{
|
||||
"extends": "astro/tsconfigs/strict",
|
||||
"compilerOptions": {
|
||||
"jsx": "preserve",
|
||||
"jsxImportSource": "solid-js"
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user