this repo has no description
0
fork

Configure Feed

Select the types of activity you want to include in your feed.

filters

+269 -1
+1
package.json
··· 12 12 }, 13 13 "dependencies": { 14 14 "@libsql/client": "^0.15.7", 15 + "@radix-ui/react-label": "^2.1.7", 15 16 "@radix-ui/react-slot": "^1.2.3", 16 17 "@t3-oss/env-core": "^0.13.4", 17 18 "@tailwindcss/vite": "^4.1.7",
+47
pnpm-lock.yaml
··· 11 11 '@libsql/client': 12 12 specifier: ^0.15.7 13 13 version: 0.15.7 14 + '@radix-ui/react-label': 15 + specifier: ^2.1.7 16 + version: 2.1.7(@types/react-dom@19.1.5(@types/react@19.1.5))(@types/react@19.1.5)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) 14 17 '@radix-ui/react-slot': 15 18 specifier: ^1.2.3 16 19 version: 1.2.3(@types/react@19.1.5)(react@19.1.0) ··· 886 889 react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc 887 890 peerDependenciesMeta: 888 891 '@types/react': 892 + optional: true 893 + 894 + '@radix-ui/react-label@2.1.7': 895 + resolution: {integrity: sha512-YT1GqPSL8kJn20djelMX7/cTRp/Y9w5IZHvfxQTVHrOqa2yMl7i/UfMqKRU5V7mEyKTrUVgJXhNQPVCG8PBLoQ==} 896 + peerDependencies: 897 + '@types/react': '*' 898 + '@types/react-dom': '*' 899 + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc 900 + react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc 901 + peerDependenciesMeta: 902 + '@types/react': 903 + optional: true 904 + '@types/react-dom': 905 + optional: true 906 + 907 + '@radix-ui/react-primitive@2.1.3': 908 + resolution: {integrity: sha512-m9gTwRkhy2lvCPe6QJp4d3G1TYEUHn/FzJUtq9MjH46an1wJU+GdoGC5VLof8RX8Ft/DlpshApkhswDLZzHIcQ==} 909 + peerDependencies: 910 + '@types/react': '*' 911 + '@types/react-dom': '*' 912 + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc 913 + react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc 914 + peerDependenciesMeta: 915 + '@types/react': 916 + optional: true 917 + '@types/react-dom': 889 918 optional: true 890 919 891 920 '@radix-ui/react-slot@1.2.3': ··· 4728 4757 react: 19.1.0 4729 4758 optionalDependencies: 4730 4759 '@types/react': 19.1.5 4760 + 4761 + '@radix-ui/react-label@2.1.7(@types/react-dom@19.1.5(@types/react@19.1.5))(@types/react@19.1.5)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)': 4762 + dependencies: 4763 + '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.1.5(@types/react@19.1.5))(@types/react@19.1.5)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) 4764 + react: 19.1.0 4765 + react-dom: 19.1.0(react@19.1.0) 4766 + optionalDependencies: 4767 + '@types/react': 19.1.5 4768 + '@types/react-dom': 19.1.5(@types/react@19.1.5) 4769 + 4770 + '@radix-ui/react-primitive@2.1.3(@types/react-dom@19.1.5(@types/react@19.1.5))(@types/react@19.1.5)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)': 4771 + dependencies: 4772 + '@radix-ui/react-slot': 1.2.3(@types/react@19.1.5)(react@19.1.0) 4773 + react: 19.1.0 4774 + react-dom: 19.1.0(react@19.1.0) 4775 + optionalDependencies: 4776 + '@types/react': 19.1.5 4777 + '@types/react-dom': 19.1.5(@types/react@19.1.5) 4731 4778 4732 4779 '@radix-ui/react-slot@1.2.3(@types/react@19.1.5)(react@19.1.0)': 4733 4780 dependencies:
+22
src/components/ui/label.tsx
··· 1 + import * as React from "react" 2 + import * as LabelPrimitive from "@radix-ui/react-label" 3 + 4 + import { cn } from "~/lib/utils" 5 + 6 + function Label({ 7 + className, 8 + ...props 9 + }: React.ComponentProps<typeof LabelPrimitive.Root>) { 10 + return ( 11 + <LabelPrimitive.Root 12 + data-slot="label" 13 + className={cn( 14 + "flex items-center gap-2 text-sm leading-none font-medium select-none group-data-[disabled=true]:pointer-events-none group-data-[disabled=true]:opacity-50 peer-disabled:cursor-not-allowed peer-disabled:opacity-50", 15 + className 16 + )} 17 + {...props} 18 + /> 19 + ) 20 + } 21 + 22 + export { Label }
+41 -1
src/routeTree.gen.ts
··· 16 16 import { Route as PreloadingRouteImport } from './routes/preloading' 17 17 import { Route as PaginationRouteImport } from './routes/pagination' 18 18 import { Route as IntentPreloadingRouteImport } from './routes/intent-preloading' 19 + import { Route as FiltersRouteImport } from './routes/filters' 19 20 import { Route as BasicRouteImport } from './routes/basic' 20 21 import { Route as IndexRouteImport } from './routes/index' 21 22 ··· 36 37 const IntentPreloadingRoute = IntentPreloadingRouteImport.update({ 37 38 id: '/intent-preloading', 38 39 path: '/intent-preloading', 40 + getParentRoute: () => rootRoute, 41 + } as any) 42 + 43 + const FiltersRoute = FiltersRouteImport.update({ 44 + id: '/filters', 45 + path: '/filters', 39 46 getParentRoute: () => rootRoute, 40 47 } as any) 41 48 ··· 69 76 preLoaderRoute: typeof BasicRouteImport 70 77 parentRoute: typeof rootRoute 71 78 } 79 + '/filters': { 80 + id: '/filters' 81 + path: '/filters' 82 + fullPath: '/filters' 83 + preLoaderRoute: typeof FiltersRouteImport 84 + parentRoute: typeof rootRoute 85 + } 72 86 '/intent-preloading': { 73 87 id: '/intent-preloading' 74 88 path: '/intent-preloading' ··· 113 127 FileRoutesByPath['/basic']['fullPath'] 114 128 > 115 129 } 130 + declare module './routes/filters' { 131 + const createFileRoute: CreateFileRoute< 132 + '/filters', 133 + FileRoutesByPath['/filters']['parentRoute'], 134 + FileRoutesByPath['/filters']['id'], 135 + FileRoutesByPath['/filters']['path'], 136 + FileRoutesByPath['/filters']['fullPath'] 137 + > 138 + } 116 139 declare module './routes/intent-preloading' { 117 140 const createFileRoute: CreateFileRoute< 118 141 '/intent-preloading', ··· 146 169 export interface FileRoutesByFullPath { 147 170 '/': typeof IndexRoute 148 171 '/basic': typeof BasicRoute 172 + '/filters': typeof FiltersRoute 149 173 '/intent-preloading': typeof IntentPreloadingRoute 150 174 '/pagination': typeof PaginationRoute 151 175 '/preloading': typeof PreloadingRoute ··· 154 178 export interface FileRoutesByTo { 155 179 '/': typeof IndexRoute 156 180 '/basic': typeof BasicRoute 181 + '/filters': typeof FiltersRoute 157 182 '/intent-preloading': typeof IntentPreloadingRoute 158 183 '/pagination': typeof PaginationRoute 159 184 '/preloading': typeof PreloadingRoute ··· 163 188 __root__: typeof rootRoute 164 189 '/': typeof IndexRoute 165 190 '/basic': typeof BasicRoute 191 + '/filters': typeof FiltersRoute 166 192 '/intent-preloading': typeof IntentPreloadingRoute 167 193 '/pagination': typeof PaginationRoute 168 194 '/preloading': typeof PreloadingRoute ··· 173 199 fullPaths: 174 200 | '/' 175 201 | '/basic' 202 + | '/filters' 176 203 | '/intent-preloading' 177 204 | '/pagination' 178 205 | '/preloading' 179 206 fileRoutesByTo: FileRoutesByTo 180 - to: '/' | '/basic' | '/intent-preloading' | '/pagination' | '/preloading' 207 + to: 208 + | '/' 209 + | '/basic' 210 + | '/filters' 211 + | '/intent-preloading' 212 + | '/pagination' 213 + | '/preloading' 181 214 id: 182 215 | '__root__' 183 216 | '/' 184 217 | '/basic' 218 + | '/filters' 185 219 | '/intent-preloading' 186 220 | '/pagination' 187 221 | '/preloading' ··· 191 225 export interface RootRouteChildren { 192 226 IndexRoute: typeof IndexRoute 193 227 BasicRoute: typeof BasicRoute 228 + FiltersRoute: typeof FiltersRoute 194 229 IntentPreloadingRoute: typeof IntentPreloadingRoute 195 230 PaginationRoute: typeof PaginationRoute 196 231 PreloadingRoute: typeof PreloadingRoute ··· 199 234 const rootRouteChildren: RootRouteChildren = { 200 235 IndexRoute: IndexRoute, 201 236 BasicRoute: BasicRoute, 237 + FiltersRoute: FiltersRoute, 202 238 IntentPreloadingRoute: IntentPreloadingRoute, 203 239 PaginationRoute: PaginationRoute, 204 240 PreloadingRoute: PreloadingRoute, ··· 216 252 "children": [ 217 253 "/", 218 254 "/basic", 255 + "/filters", 219 256 "/intent-preloading", 220 257 "/pagination", 221 258 "/preloading" ··· 226 263 }, 227 264 "/basic": { 228 265 "filePath": "basic.tsx" 266 + }, 267 + "/filters": { 268 + "filePath": "filters.tsx" 229 269 }, 230 270 "/intent-preloading": { 231 271 "filePath": "intent-preloading.tsx"
+158
src/routes/filters.tsx
··· 1 + import { 2 + queryOptions, 3 + useQueryClient, 4 + useSuspenseQuery, 5 + } from "@tanstack/react-query"; 6 + import { useServerFn } from "@tanstack/react-start"; 7 + import * as v from "valibot"; 8 + import { PaginationNav } from "~/components/pagination-nav"; 9 + import { Input } from "~/components/ui/input"; 10 + import { Label } from "~/components/ui/label"; 11 + import { 12 + Table, 13 + TableBody, 14 + TableCell, 15 + TableHead, 16 + TableHeader, 17 + TableRow, 18 + } from "~/components/ui/table"; 19 + import { 20 + POKEMON_LIMIT, 21 + getPokemonListQueryKey, 22 + getServerPokemonListQueryFn, 23 + } from "~/util/pokemon"; 24 + 25 + const searchParamsSchema = v.object({ 26 + offset: v.optional(v.number(), 0), 27 + name: v.optional(v.string(), ""), 28 + }); 29 + 30 + export const Route = createFileRoute({ 31 + validateSearch: searchParamsSchema, 32 + loaderDeps: ({ search }) => ({ 33 + offset: search.offset, 34 + name: search.name, 35 + }), 36 + context: ({ deps }) => { 37 + const newKey = getPokemonListQueryKey("filters", deps.offset); 38 + 39 + const pokemonListOptions = queryOptions({ 40 + queryKey: newKey, 41 + queryFn: getServerPokemonListQueryFn, 42 + }); 43 + 44 + return { 45 + pokemonListOptions, 46 + }; 47 + }, 48 + loader: ({ context }) => { 49 + context.queryClient.prefetchQuery(context.pokemonListOptions); 50 + }, 51 + component: RouteComponent, 52 + }); 53 + 54 + function RouteComponent() { 55 + const { offset: currentOffset, name: nameFilter } = Route.useSearch(); 56 + const { pokemonListOptions: serverPokemonListOptions } = 57 + Route.useRouteContext(); 58 + const queryClient = useQueryClient(); 59 + 60 + const { data } = useSuspenseQuery({ 61 + ...serverPokemonListOptions, 62 + queryFn: useServerFn(getServerPokemonListQueryFn), 63 + }); 64 + 65 + if (data.prevOffset !== null) { 66 + void queryClient.prefetchQuery({ 67 + ...serverPokemonListOptions, 68 + queryKey: getPokemonListQueryKey("filters", data.prevOffset), 69 + }); 70 + } 71 + 72 + if (data.nextOffset !== null) { 73 + void queryClient.prefetchQuery({ 74 + ...serverPokemonListOptions, 75 + queryKey: getPokemonListQueryKey("filters", data.nextOffset), 76 + }); 77 + } 78 + 79 + // Filter Pokemon by name (dummy implementation for now) 80 + const filteredPokemon = data.pokemon.filter((pokemon) => 81 + pokemon.name.toLowerCase().includes(nameFilter.toLowerCase()), 82 + ); 83 + 84 + return ( 85 + <div className="p-4"> 86 + <h1 className="text-2xl font-bold mb-4"> 87 + National Pokédex: Pokémon {currentOffset + 1}- 88 + {currentOffset + POKEMON_LIMIT} (Filtered) 89 + </h1> 90 + 91 + {/* Filter UI */} 92 + <div className="mb-6 p-4 border rounded-lg bg-gray-50"> 93 + <h2 className="text-lg font-semibold mb-3">Filters</h2> 94 + <div className="space-y-4"> 95 + <div> 96 + <Label htmlFor="name-filter" className="text-sm font-medium"> 97 + Filter by Name 98 + </Label> 99 + <Input 100 + id="name-filter" 101 + type="text" 102 + placeholder="Enter Pokemon name..." 103 + value={nameFilter} 104 + onChange={(e) => { 105 + // This is dummy UI - in a real implementation, this would update the URL search params 106 + console.log("Filter changed:", e.target.value); 107 + }} 108 + className="mt-1" 109 + /> 110 + <p className="text-xs text-gray-500 mt-1"> 111 + Current filter: "{nameFilter}" (dummy UI - not functional yet) 112 + </p> 113 + </div> 114 + </div> 115 + </div> 116 + 117 + <Table> 118 + <TableHeader> 119 + <TableRow> 120 + <TableHead>#</TableHead> 121 + <TableHead>Name</TableHead> 122 + <TableHead>Details</TableHead> 123 + </TableRow> 124 + </TableHeader> 125 + <TableBody> 126 + {filteredPokemon.map((pokemon) => ( 127 + <TableRow key={pokemon.name}> 128 + <TableCell>{pokemon.id}</TableCell> 129 + <TableCell className="capitalize">{pokemon.name}</TableCell> 130 + <TableCell> 131 + {pokemon.types.map((type) => ( 132 + <span 133 + key={type.type.name} 134 + className="inline-block px-2 py-1 mr-1 text-sm font-medium rounded-full bg-gray-100" 135 + > 136 + {type.type.name} 137 + </span> 138 + ))} 139 + </TableCell> 140 + </TableRow> 141 + ))} 142 + </TableBody> 143 + </Table> 144 + 145 + {filteredPokemon.length === 0 && nameFilter && ( 146 + <div className="text-center py-8 text-gray-500"> 147 + No Pokemon found matching "{nameFilter}" 148 + </div> 149 + )} 150 + 151 + <PaginationNav 152 + prevOffset={data.prevOffset ?? undefined} 153 + nextOffset={data.nextOffset ?? undefined} 154 + to="/filters" 155 + /> 156 + </div> 157 + ); 158 + }