this repo has no description
0
fork

Configure Feed

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

dependency injection

+105 -86
+1
package.json
··· 16 16 "@radix-ui/react-slot": "^1.2.3", 17 17 "@t3-oss/env-core": "^0.13.4", 18 18 "@tailwindcss/vite": "^4.1.7", 19 + "@tanstack/pacer": "^0.7.0", 19 20 "@tanstack/react-query": "^5.76.2", 20 21 "@tanstack/react-query-devtools": "^5.76.2", 21 22 "@tanstack/react-router": "1.121.0-alpha.11",
+9
pnpm-lock.yaml
··· 23 23 '@tailwindcss/vite': 24 24 specifier: ^4.1.7 25 25 version: 4.1.7(vite@6.3.5(@types/node@22.15.21)(jiti@2.4.2)(lightningcss@1.30.1)(terser@5.39.2)(tsx@4.19.4)) 26 + '@tanstack/pacer': 27 + specifier: ^0.7.0 28 + version: 0.7.0 26 29 '@tanstack/react-query': 27 30 specifier: ^5.76.2 28 31 version: 5.76.2(react@19.1.0) ··· 1226 1229 '@tanstack/history@1.121.0-alpha.1': 1227 1230 resolution: {integrity: sha512-/nKiPTyUIqSlCS0hGOL/hjQUI/TJ/i5w+dAFdgq0bhhiGpLvv4s/SAxbHb+/6Sm1WGkule6+taaRamDmrK3SYQ==} 1228 1231 engines: {node: '>=12'} 1232 + 1233 + '@tanstack/pacer@0.7.0': 1234 + resolution: {integrity: sha512-/CB3dN05Vd6CxiuiqRYdVQDu9wO61kN9flQiPcbdojYF2hWhhuzwbqS+lbKxZjObWuiTQPsC559lnZn1lbpM9Q==} 1235 + engines: {node: '>=18'} 1229 1236 1230 1237 '@tanstack/query-core@5.76.2': 1231 1238 resolution: {integrity: sha512-PFGwWh5ss9cJQ67l6bZ7hqXbisX2gy13G2jP+VGY1bgdbCfOMWh6UBVnN62QbFXro6CCoX9hYzTnZHr6Rz00YQ==} ··· 5010 5017 - supports-color 5011 5018 5012 5019 '@tanstack/history@1.121.0-alpha.1': {} 5020 + 5021 + '@tanstack/pacer@0.7.0': {} 5013 5022 5014 5023 '@tanstack/query-core@5.76.2': {} 5015 5024
+40
src/components/filter-form.tsx
··· 1 + import { createContext, useContext } from "react"; 2 + import { Input } from "~/components/ui/input"; 3 + import { Label } from "~/components/ui/label"; 4 + 5 + export const FilterSubmitContext = createContext<{ 6 + handleSubmit: () => void; 7 + updateNameFilter: (nameFilter: string) => void; 8 + nameFilter: string; 9 + } | null>(null); 10 + 11 + export function FilterForm() { 12 + const submitContext = useContext(FilterSubmitContext); 13 + 14 + if (!submitContext) { 15 + throw new Error("FilterSubmitContext not found"); 16 + } 17 + 18 + return ( 19 + <div className="space-y-4"> 20 + <form 21 + onSubmit={(e) => { 22 + e.preventDefault(); 23 + submitContext.handleSubmit(); 24 + }} 25 + > 26 + <Label htmlFor="name-filter" className="text-sm font-medium"> 27 + Filter by Name 28 + </Label> 29 + <Input 30 + id="name-filter" 31 + type="text" 32 + placeholder="Enter Pokemon name..." 33 + value={submitContext.nameFilter} 34 + onChange={(e) => submitContext.updateNameFilter(e.target.value)} 35 + className="mt-1" 36 + /> 37 + </form> 38 + </div> 39 + ); 40 + }
+29 -44
src/routes/debounced-preload-filters.tsx
··· 4 4 useSuspenseQuery, 5 5 } from "@tanstack/react-query"; 6 6 import { useServerFn } from "@tanstack/react-start"; 7 - import { useCallback, useState } from "react"; 7 + import { useCallback } from "react"; 8 + import { useState } from "react"; 8 9 import * as v from "valibot"; 10 + import { FilterForm, FilterSubmitContext } from "~/components/filter-form"; 9 11 import { PaginationNav } from "~/components/pagination-nav"; 10 - import { Input } from "~/components/ui/input"; 11 - import { Label } from "~/components/ui/label"; 12 12 import { 13 13 Table, 14 14 TableBody, ··· 28 28 name: v.optional(v.string(), ""), 29 29 }); 30 30 31 + function FilterSubmitContextProvider(props: { 32 + initialName: string; 33 + handleSubmit: (nameFilter: string) => void; 34 + children: React.ReactNode; 35 + }) { 36 + const [nameFilter, setNameFilter] = useState(props.initialName); 37 + 38 + const handleSubmit = useCallback(() => { 39 + props.handleSubmit(nameFilter); 40 + }, [nameFilter, props]); 41 + 42 + return ( 43 + <FilterSubmitContext.Provider 44 + value={{ handleSubmit, nameFilter, updateNameFilter: setNameFilter }} 45 + > 46 + {props.children} 47 + </FilterSubmitContext.Provider> 48 + ); 49 + } 50 + 31 51 export const Route = createFileRoute({ 32 52 validateSearch: searchParamsSchema, 33 53 loaderDeps: ({ search }) => ({ ··· 36 56 }), 37 57 context: ({ deps }) => { 38 58 const newKey = getFilteredPokemonListQueryKey( 39 - "filters", 59 + "debounced-preload-filters", 40 60 deps.offset, 41 61 deps.name, 42 62 ); ··· 103 123 {/* Filter UI */} 104 124 <div className="mb-6 p-4 border rounded-lg bg-gray-50"> 105 125 <h2 className="text-lg font-semibold mb-3">Filters</h2> 106 - <FilterForm 107 - key={`filter-form-${nameFilter}`} 126 + <FilterSubmitContextProvider 127 + initialName={nameFilter} 108 128 handleSubmit={(nameFilter) => { 109 129 navigate({ 110 130 search: { name: nameFilter }, 111 131 }); 112 132 }} 113 - initialName={nameFilter} 114 - /> 133 + > 134 + <FilterForm /> 135 + </FilterSubmitContextProvider> 115 136 </div> 116 137 117 138 <Table> ··· 156 177 </div> 157 178 ); 158 179 } 159 - 160 - function FilterForm(props: { 161 - handleSubmit: (nameFilter: string) => void; 162 - initialName: string; 163 - }) { 164 - const [nameFilter, setNameFilter] = useState(props.initialName); 165 - 166 - const onSubmit = useCallback( 167 - (e: React.FormEvent<HTMLFormElement>) => { 168 - e.preventDefault(); 169 - props.handleSubmit(nameFilter); 170 - }, 171 - [nameFilter, props], 172 - ); 173 - 174 - return ( 175 - <div className="space-y-4"> 176 - <form onSubmit={onSubmit}> 177 - <Label htmlFor="name-filter" className="text-sm font-medium"> 178 - Filter by Name 179 - </Label> 180 - <Input 181 - id="name-filter" 182 - type="text" 183 - placeholder="Enter Pokemon name..." 184 - value={nameFilter} 185 - onChange={(e) => setNameFilter(e.target.value)} 186 - className="mt-1" 187 - /> 188 - <p className="text-xs text-gray-500 mt-1"> 189 - Current filter: "{nameFilter}" (dummy UI - not functional yet) 190 - </p> 191 - </form> 192 - </div> 193 - ); 194 - }
+26 -42
src/routes/filters.tsx
··· 6 6 import { useServerFn } from "@tanstack/react-start"; 7 7 import { useCallback, useState } from "react"; 8 8 import * as v from "valibot"; 9 + import { FilterForm, FilterSubmitContext } from "~/components/filter-form"; 9 10 import { PaginationNav } from "~/components/pagination-nav"; 10 - import { Input } from "~/components/ui/input"; 11 - import { Label } from "~/components/ui/label"; 12 11 import { 13 12 Table, 14 13 TableBody, ··· 28 27 name: v.optional(v.string(), ""), 29 28 }); 30 29 30 + function FilterSubmitContextProvider(props: { 31 + initialName: string; 32 + handleSubmit: (nameFilter: string) => void; 33 + children: React.ReactNode; 34 + }) { 35 + const [nameFilter, setNameFilter] = useState(props.initialName); 36 + 37 + const handleSubmit = useCallback(() => { 38 + props.handleSubmit(nameFilter); 39 + }, [nameFilter, props]); 40 + 41 + return ( 42 + <FilterSubmitContext.Provider 43 + value={{ handleSubmit, nameFilter, updateNameFilter: setNameFilter }} 44 + > 45 + {props.children} 46 + </FilterSubmitContext.Provider> 47 + ); 48 + } 31 49 export const Route = createFileRoute({ 32 50 validateSearch: searchParamsSchema, 33 51 loaderDeps: ({ search }) => ({ ··· 103 121 {/* Filter UI */} 104 122 <div className="mb-6 p-4 border rounded-lg bg-gray-50"> 105 123 <h2 className="text-lg font-semibold mb-3">Filters</h2> 106 - <FilterForm 107 - key={`filter-form-${nameFilter}`} 124 + <FilterSubmitContextProvider 125 + key={`filter-submit-context-provider-${nameFilter}`} 126 + initialName={nameFilter} 108 127 handleSubmit={(nameFilter) => { 109 128 navigate({ 110 129 search: { name: nameFilter }, 111 130 }); 112 131 }} 113 - initialName={nameFilter} 114 - /> 132 + > 133 + <FilterForm /> 134 + </FilterSubmitContextProvider> 115 135 </div> 116 136 117 137 <Table> ··· 156 176 </div> 157 177 ); 158 178 } 159 - 160 - function FilterForm(props: { 161 - handleSubmit: (nameFilter: string) => void; 162 - initialName: string; 163 - }) { 164 - const [nameFilter, setNameFilter] = useState(props.initialName); 165 - 166 - const onSubmit = useCallback( 167 - (e: React.FormEvent<HTMLFormElement>) => { 168 - e.preventDefault(); 169 - props.handleSubmit(nameFilter); 170 - }, 171 - [nameFilter, props], 172 - ); 173 - 174 - return ( 175 - <div className="space-y-4"> 176 - <form onSubmit={onSubmit}> 177 - <Label htmlFor="name-filter" className="text-sm font-medium"> 178 - Filter by Name 179 - </Label> 180 - <Input 181 - id="name-filter" 182 - type="text" 183 - placeholder="Enter Pokemon name..." 184 - value={nameFilter} 185 - onChange={(e) => setNameFilter(e.target.value)} 186 - className="mt-1" 187 - /> 188 - <p className="text-xs text-gray-500 mt-1"> 189 - Current filter: "{nameFilter}" (dummy UI - not functional yet) 190 - </p> 191 - </form> 192 - </div> 193 - ); 194 - }