this repo has no description
0
fork

Configure Feed

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

done

+45 -119
+27 -13
src/components/filter-form.tsx
··· 3 3 import { Label } from "~/components/ui/label"; 4 4 import { Button } from "~/components/ui/button"; 5 5 6 - export const FilterSubmitContext = React.createContext<{ 7 - handleSubmit: () => void; 8 - updateNameFilter: (nameFilter: string) => void; 9 - nameFilter: string; 10 - } | null>(null); 6 + type FilterFormProps = { 7 + initialName: string; 8 + onSubmit: (nameFilter: string) => void; 9 + onNameChange?: (nameFilter: string) => void; 10 + description?: string; 11 + }; 11 12 12 - export function FilterForm() { 13 - const submitContext = React.useContext(FilterSubmitContext); 13 + export function FilterForm(props: FilterFormProps) { 14 + const { description, initialName, onNameChange, onSubmit } = props; 15 + const [nameFilter, setNameFilter] = React.useState(initialName); 14 16 15 - if (!submitContext) { 16 - throw new Error("FilterSubmitContext not found"); 17 - } 17 + React.useEffect(() => { 18 + setNameFilter(initialName); 19 + }, [initialName]); 20 + 21 + const updateNameFilter = React.useCallback( 22 + (value: string) => { 23 + setNameFilter(value); 24 + onNameChange?.(value); 25 + }, 26 + [onNameChange], 27 + ); 18 28 19 29 return ( 20 30 <div className="space-y-4"> 31 + <h2 className="text-sm font-semibold text-(--text-primary) uppercase tracking-wider"> 32 + Filters 33 + </h2> 34 + {description ? <p className="text-sm text-(--text-muted)">{description}</p> : null} 21 35 <form 22 36 onSubmit={(e) => { 23 37 e.preventDefault(); 24 - submitContext.handleSubmit(); 38 + onSubmit(nameFilter); 25 39 }} 26 40 > 27 41 <Label ··· 34 48 id="name-filter" 35 49 type="text" 36 50 placeholder="Enter Pokemon name..." 37 - value={submitContext.nameFilter} 38 - onChange={(e) => submitContext.updateNameFilter(e.target.value)} 51 + value={nameFilter} 52 + onChange={(e) => updateNameFilter(e.target.value)} 39 53 className="mt-2 bg-(--bg-secondary) border-(--border-default) focus:border-(--accent-default) focus:ring-0 rounded-none font-mono text-sm" 40 54 /> 41 55 <Button type="submit" className="mt-2">
+10 -44
src/routes/debounced-preload-filters.tsx
··· 1 - import * as React from "react"; 2 1 import { createFileRoute, useRouteContext } from "@tanstack/react-router"; 3 2 import { useDebouncedCallback } from "@tanstack/react-pacer"; 4 3 import { queryOptions, useQueryClient, useSuspenseQuery } from "@tanstack/react-query"; 5 4 import * as v from "valibot"; 6 - import { FilterForm, FilterSubmitContext } from "~/components/filter-form"; 5 + import { FilterForm } from "~/components/filter-form"; 7 6 import { StrategyChapterLayout } from "~/components/strategy-page-layout"; 8 7 import { ConsoleCard } from "~/components/console/console-card"; 9 8 import { ··· 51 50 component: RouteComponent, 52 51 }); 53 52 54 - function PreloadFilterSubmitContextProvider(props: { 55 - initialName: string; 56 - handleSubmit: (nameFilter: string) => void; 57 - children: React.ReactNode; 58 - }) { 53 + function RouteComponent() { 54 + const { offset: currentOffset, name: nameFilter } = Route.useSearch(); 55 + const { Article } = Route.useLoaderData(); 59 56 const queryClient = useQueryClient(); 60 57 const { pokemonListOptions: serverPokemonListOptions } = Route.useRouteContext(); 61 - const [nameFilter, setNameFilter] = React.useState(props.initialName); 62 - 58 + const navigate = Route.useNavigate(); 63 59 const debouncedNameFilter = useDebouncedCallback( 64 60 (newNameFilter: string) => { 65 61 void queryClient.prefetchQuery({ ··· 76 72 }, 77 73 ); 78 74 79 - const updateNameFilter = React.useCallback( 80 - (value: string) => { 81 - debouncedNameFilter(value); 82 - setNameFilter(value); 83 - }, 84 - [debouncedNameFilter], 85 - ); 86 - 87 - const handleSubmit = React.useCallback(() => { 88 - props.handleSubmit(nameFilter); 89 - }, [nameFilter, props]); 90 - 91 - return ( 92 - <FilterSubmitContext.Provider value={{ handleSubmit, nameFilter, updateNameFilter }}> 93 - {props.children} 94 - </FilterSubmitContext.Provider> 95 - ); 96 - } 97 - 98 - function RouteComponent() { 99 - const { offset: currentOffset, name: nameFilter } = Route.useSearch(); 100 - const { Article } = Route.useLoaderData(); 101 - const navigate = Route.useNavigate(); 102 - 103 75 return ( 104 76 <StrategyChapterLayout 105 77 headerTitle="06_debounced" ··· 107 79 sidebar={Article} 108 80 > 109 81 <ConsoleCard className="mb-6"> 110 - <h2 className="text-sm font-semibold mb-4 text-(--text-primary) uppercase tracking-wider"> 111 - Filters 112 - </h2> 113 - <p className="text-sm text-(--text-muted) mb-4"> 114 - Preloads results while typing (debounced 100ms) 115 - </p> 116 - <PreloadFilterSubmitContextProvider 82 + <FilterForm 117 83 initialName={nameFilter} 118 - handleSubmit={(newNameFilter) => { 84 + description="Preloads results while typing (debounced 100ms)" 85 + onNameChange={(newNameFilter) => debouncedNameFilter(newNameFilter)} 86 + onSubmit={(newNameFilter) => { 119 87 void navigate({ 120 88 search: { name: newNameFilter }, 121 89 }); 122 90 }} 123 - > 124 - <FilterForm /> 125 - </PreloadFilterSubmitContextProvider> 91 + /> 126 92 </ConsoleCard> 127 93 128 94 <ConsoleCard>
+4 -31
src/routes/filters.tsx
··· 1 - import * as React from "react"; 2 1 import { createFileRoute, useRouteContext } from "@tanstack/react-router"; 3 2 import { queryOptions, useSuspenseQuery } from "@tanstack/react-query"; 4 3 import * as v from "valibot"; 5 - import { FilterForm, FilterSubmitContext } from "~/components/filter-form"; 4 + import { FilterForm } from "~/components/filter-form"; 6 5 import { StrategyChapterLayout } from "~/components/strategy-page-layout"; 7 6 import { ConsoleCard } from "~/components/console/console-card"; 8 7 import { ··· 18 17 name: v.optional(v.string(), ""), 19 18 }); 20 19 21 - function FilterSubmitContextProvider(props: { 22 - initialName: string; 23 - handleSubmit: (nameFilter: string) => void; 24 - children: React.ReactNode; 25 - }) { 26 - const [nameFilter, setNameFilter] = React.useState(props.initialName); 27 - 28 - const handleSubmit = React.useCallback(() => { 29 - props.handleSubmit(nameFilter); 30 - }, [nameFilter, props]); 31 - 32 - return ( 33 - <FilterSubmitContext.Provider 34 - value={{ handleSubmit, nameFilter, updateNameFilter: setNameFilter }} 35 - > 36 - {props.children} 37 - </FilterSubmitContext.Provider> 38 - ); 39 - } 40 - 41 20 export const Route = createFileRoute("/filters")({ 42 21 validateSearch: searchParamsSchema, 43 22 loaderDeps: ({ search }) => ({ ··· 78 57 sidebar={Article} 79 58 > 80 59 <ConsoleCard className="mb-6"> 81 - <h2 className="text-sm font-semibold mb-4 text-(--text-primary) uppercase tracking-wider"> 82 - Filters 83 - </h2> 84 - <FilterSubmitContextProvider 85 - key={`filter-submit-context-provider-${nameFilter}`} 60 + <FilterForm 86 61 initialName={nameFilter} 87 - handleSubmit={(newNameFilter) => { 62 + onSubmit={(newNameFilter) => { 88 63 void navigate({ 89 64 search: { name: newNameFilter }, 90 65 }); 91 66 }} 92 - > 93 - <FilterForm /> 94 - </FilterSubmitContextProvider> 67 + /> 95 68 </ConsoleCard> 96 69 97 70 <ConsoleCard>
+4 -31
src/routes/live-query-filters.tsx
··· 1 - import * as React from "react"; 2 1 import { createFileRoute } from "@tanstack/react-router"; 3 2 import { useLiveSuspenseQuery, eq, ilike, toArray } from "@tanstack/react-db"; 4 3 import * as v from "valibot"; 5 4 import { StrategyChapterLayout } from "~/components/strategy-page-layout"; 6 5 import { ConsoleCard } from "~/components/console/console-card"; 7 - import { FilterForm, FilterSubmitContext } from "~/components/filter-form"; 6 + import { FilterForm } from "~/components/filter-form"; 8 7 import { 9 8 PokedexPagination, 10 9 PokedexTableResults, ··· 27 26 name: v.optional(v.string(), ""), 28 27 }); 29 28 30 - function FilterSubmitContextProvider(props: { 31 - initialName: string; 32 - handleSubmit: (nameFilter: string) => void; 33 - children: React.ReactNode; 34 - }) { 35 - const [nameFilter, setNameFilter] = React.useState(props.initialName); 36 - 37 - const handleSubmit = React.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 - } 49 - 50 29 export const Route = createFileRoute("/live-query-filters")({ 51 30 ssr: false, 52 31 validateSearch: searchParamsSchema, ··· 72 51 > 73 52 <div> 74 53 <ConsoleCard className="mb-6"> 75 - <h2 className="text-sm font-semibold mb-4 text-(--text-primary) uppercase tracking-wider"> 76 - Filters 77 - </h2> 78 - <FilterSubmitContextProvider 79 - key={`live-filter-submit-context-provider-${nameFilter}`} 54 + <FilterForm 80 55 initialName={nameFilter} 81 - handleSubmit={(newNameFilter) => { 56 + onSubmit={(newNameFilter) => { 82 57 void navigate({ 83 58 search: { offset: 0, name: newNameFilter }, 84 59 }); 85 60 }} 86 - > 87 - <FilterForm /> 88 - </FilterSubmitContextProvider> 61 + /> 89 62 </ConsoleCard> 90 63 91 64 <ConsoleCard>