@leancodepl/antd-table-hooks 10.1.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +19 -0
- package/LICENSE +201 -0
- package/README.md +470 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +722 -0
- package/dist/lib/filters/index.d.ts +37 -0
- package/dist/lib/filters/index.d.ts.map +1 -0
- package/dist/lib/filters/types.d.ts +37 -0
- package/dist/lib/filters/types.d.ts.map +1 -0
- package/dist/lib/filters/useFilters.d.ts +41 -0
- package/dist/lib/filters/useFilters.d.ts.map +1 -0
- package/dist/lib/index.d.ts +5 -0
- package/dist/lib/index.d.ts.map +1 -0
- package/dist/lib/pagination/index.d.ts +53 -0
- package/dist/lib/pagination/index.d.ts.map +1 -0
- package/dist/lib/sorting/index.d.ts +77 -0
- package/dist/lib/sorting/index.d.ts.map +1 -0
- package/dist/lib/table/index.d.ts +113 -0
- package/dist/lib/table/index.d.ts.map +1 -0
- package/dist/lib/types.d.ts +6 -0
- package/dist/lib/types.d.ts.map +1 -0
- package/package.json +64 -0
package/README.md
ADDED
|
@@ -0,0 +1,470 @@
|
|
|
1
|
+
# @leancodepl/antd-table-hooks
|
|
2
|
+
|
|
3
|
+
React hooks for managing Ant Design table state — sorting, pagination, and filters — with optional URL query parameter
|
|
4
|
+
persistence via Zod schemas.
|
|
5
|
+
|
|
6
|
+
## Installation
|
|
7
|
+
|
|
8
|
+
```bash
|
|
9
|
+
npm install @leancodepl/antd-table-hooks
|
|
10
|
+
```
|
|
11
|
+
|
|
12
|
+
```bash
|
|
13
|
+
yarn add @leancodepl/antd-table-hooks
|
|
14
|
+
```
|
|
15
|
+
|
|
16
|
+
## API
|
|
17
|
+
|
|
18
|
+
### `useTable(props)`
|
|
19
|
+
|
|
20
|
+
Orchestrates table sorting, pagination, and filter state through URL query parameters. Reads current state from
|
|
21
|
+
`queryParams` and writes updates via `setQueryParams`, enabling shareable links, browser navigation, and state
|
|
22
|
+
restoration on page reload.
|
|
23
|
+
|
|
24
|
+
**Parameters:**
|
|
25
|
+
|
|
26
|
+
| Name | Type | Description |
|
|
27
|
+
| ---------------------------- | ------------------------------------------- | --------------------------------------------------------------------------------- |
|
|
28
|
+
| `props.queryParams` | `Record<string, unknown>` | Current URL search params object (e.g. from TanStack Router's `useSearch`) |
|
|
29
|
+
| `props.setQueryParams` | `(params: Record<string, unknown>) => void` | Callback to update URL search params |
|
|
30
|
+
| `props.definedFilters` | `FilterDefinition[]` | Filter definitions created by `defineFilters` |
|
|
31
|
+
| `props.tableId` | `string` | Unique prefix for URL params to avoid collisions between tables on the same route |
|
|
32
|
+
| `props.defaultSortKey` | `Key` | Initial sort column key |
|
|
33
|
+
| `props.defaultSortDirection` | `SortOrder` | Initial sort direction (`"ascend"` or `"descend"`) |
|
|
34
|
+
| `props.defaultPageSize` | `number` (optional) | Initial page size (defaults to `100`) |
|
|
35
|
+
|
|
36
|
+
**Returns:** `{ filters, pagination, sorting }` — state objects to pass to `useFilters`, `usePagination`, and
|
|
37
|
+
`useSorting`.
|
|
38
|
+
|
|
39
|
+
### `useSorting(props)`
|
|
40
|
+
|
|
41
|
+
Manages table column sorting state. Supports two modes: URL-driven (via `useTable().sorting`) or standalone with local
|
|
42
|
+
state.
|
|
43
|
+
|
|
44
|
+
**Parameters (URL-driven mode):**
|
|
45
|
+
|
|
46
|
+
| Name | Type | Description |
|
|
47
|
+
| --------------------- | ---------------------------------------------------- | -------------------------- |
|
|
48
|
+
| `props.sortKey` | `Key` | Current sort column key |
|
|
49
|
+
| `props.sortDirection` | `SortOrder` | Current sort direction |
|
|
50
|
+
| `props.onSortUpdate` | `(sortKey?: Key, sortDirection?: SortOrder) => void` | Callback when sort changes |
|
|
51
|
+
|
|
52
|
+
**Parameters (standalone mode):**
|
|
53
|
+
|
|
54
|
+
| Name | Type | Description |
|
|
55
|
+
| ---------------------------- | --------------------------------------------------------------- | -------------------------- |
|
|
56
|
+
| `props.defaultSortKey` | `Key` (optional) | Initial sort column key |
|
|
57
|
+
| `props.defaultSortDirection` | `SortOrder` (optional) | Initial sort direction |
|
|
58
|
+
| `props.onSortUpdate` | `(sortKey?: Key, sortDirection?: SortOrder) => void` (optional) | Callback when sort changes |
|
|
59
|
+
|
|
60
|
+
**Returns:** `{ sortData, sortKey, sortDirection, isDescending }`
|
|
61
|
+
|
|
62
|
+
- `sortData` — pass to an Ant Design table's `sort` prop
|
|
63
|
+
- `sortKey` — current sort column key
|
|
64
|
+
- `sortDirection` — current sort direction (`"ascend"` | `"descend"`)
|
|
65
|
+
- `isDescending` — `true` when `sortDirection` is `"descend"`
|
|
66
|
+
|
|
67
|
+
### `usePagination(props)`
|
|
68
|
+
|
|
69
|
+
Manages table pagination state with zero-indexed page output for API calls. Returns a `getTablePagination` helper that
|
|
70
|
+
produces an Ant Design `TablePaginationConfig`.
|
|
71
|
+
|
|
72
|
+
**Parameters (URL-driven mode):**
|
|
73
|
+
|
|
74
|
+
| Name | Type | Description |
|
|
75
|
+
| -------------------------- | ------------------------------------------------------------ | -------------------------------- |
|
|
76
|
+
| `props.displayPage` | `number` | Current display page (1-indexed) |
|
|
77
|
+
| `props.pageSize` | `number` | Current page size |
|
|
78
|
+
| `props.onPaginationChange` | `(props: { displayPage: number; pageSize: number }) => void` | Callback when pagination changes |
|
|
79
|
+
|
|
80
|
+
**Parameters (standalone mode, all optional):**
|
|
81
|
+
|
|
82
|
+
| Name | Type | Description |
|
|
83
|
+
| -------------------------- | ----------------------------------------------------------------------- | -------------------------------------- |
|
|
84
|
+
| `props.initialDisplayPage` | `number` (optional) | Initial display page (defaults to `1`) |
|
|
85
|
+
| `props.initialPageSize` | `number` (optional) | Initial page size (defaults to `100`) |
|
|
86
|
+
| `props.onPaginationChange` | `(props: { displayPage: number; pageSize: number }) => void` (optional) | Callback when pagination changes |
|
|
87
|
+
|
|
88
|
+
**Returns:** `{ page, pageSize, getTablePagination, resetPage }`
|
|
89
|
+
|
|
90
|
+
- `page` — zero-indexed page number for API calls
|
|
91
|
+
- `pageSize` — current page size
|
|
92
|
+
- `getTablePagination(total?)` — returns an Ant Design `TablePaginationConfig`
|
|
93
|
+
- `resetPage()` — resets to page 1
|
|
94
|
+
|
|
95
|
+
### `defineFilters()`
|
|
96
|
+
|
|
97
|
+
Creates a typed filter definition factory along with a combined Zod search schema for URL param validation. Call with a
|
|
98
|
+
query type generic, then pass an array of filter definitions (or a factory function for context-dependent filters).
|
|
99
|
+
|
|
100
|
+
**Returns:** A function that accepts filter definitions and returns `{ searchSchema, filters }`.
|
|
101
|
+
|
|
102
|
+
- `searchSchema` — Zod schema for filter-related URL params, used with `tableSearchSchema`
|
|
103
|
+
- `filters` — the filter definitions array (or factory function) to pass to `useFilters`
|
|
104
|
+
|
|
105
|
+
### `useFilters(props)`
|
|
106
|
+
|
|
107
|
+
Manages filter state and produces an `applyFilters` function that applies all active filters to a query object.
|
|
108
|
+
|
|
109
|
+
**Parameters:**
|
|
110
|
+
|
|
111
|
+
| Name | Type | Description |
|
|
112
|
+
| ----------------------- | -------------------------------------- | ------------------------------------------------------------- |
|
|
113
|
+
| `props.filters` | `FilterDefinition[]` | Array of filter definitions (from `defineFilters`) |
|
|
114
|
+
| `props.onFiltersChange` | `(filters, values) => void` (optional) | Callback invoked when any filter value changes |
|
|
115
|
+
| `props.initialValues` | `Partial<TValues>` (optional) | Initial filter values (e.g. from `useTable().filters.values`) |
|
|
116
|
+
|
|
117
|
+
**Returns:** `{ filters, applyFilters, filterComponents, resetFilters, anyFilterSet }`
|
|
118
|
+
|
|
119
|
+
- `filters` — convenience wrapper object containing `applyFilters`, `filterComponents`, `resetFilters`, and `anyFilterSet`
|
|
120
|
+
- `applyFilters(query)` — applies all active filter transforms to the query object
|
|
121
|
+
- `filterComponents` — array of React nodes to render filter UI
|
|
122
|
+
- `resetFilters()` — clears all filters
|
|
123
|
+
- `anyFilterSet` — `true` when at least one filter is active
|
|
124
|
+
|
|
125
|
+
### `tableSearchSchema(filtersSearchSchema, tableId, sortKeySchema)`
|
|
126
|
+
|
|
127
|
+
Combines filter, pagination, and sorting schemas into a single Zod schema for route validation.
|
|
128
|
+
|
|
129
|
+
**Parameters:**
|
|
130
|
+
|
|
131
|
+
| Name | Type | Description |
|
|
132
|
+
| --------------------- | --------------------- | ----------------------------------------------------------- |
|
|
133
|
+
| `filtersSearchSchema` | `DefinedSearchSchema` | Search schema returned by `defineFilters` |
|
|
134
|
+
| `tableId` | `string` | Unique table identifier used to prefix parameter names |
|
|
135
|
+
| `sortKeySchema` | `ZodType` | Zod schema for the sort key type (e.g. `z.coerce.number()`) |
|
|
136
|
+
|
|
137
|
+
**Returns:** Combined Zod object schema covering all table URL parameters.
|
|
138
|
+
|
|
139
|
+
### `buildPaginationSearchSchema(tableId)`
|
|
140
|
+
|
|
141
|
+
Creates a Zod schema for pagination URL parameters (`{tableId}-displayPage`, `{tableId}-pageSize`).
|
|
142
|
+
|
|
143
|
+
**Parameters:**
|
|
144
|
+
|
|
145
|
+
| Name | Type | Description |
|
|
146
|
+
| --------- | -------- | ------------------------------------------------------ |
|
|
147
|
+
| `tableId` | `string` | Unique table identifier used to prefix parameter names |
|
|
148
|
+
|
|
149
|
+
**Returns:** Zod object schema for pagination params.
|
|
150
|
+
|
|
151
|
+
### `buildSortingSearchSchema(tableId, sortKeySchema)`
|
|
152
|
+
|
|
153
|
+
Creates a Zod schema for sorting URL parameters (`{tableId}-sortKey`, `{tableId}-sortDescending`).
|
|
154
|
+
|
|
155
|
+
**Parameters:**
|
|
156
|
+
|
|
157
|
+
| Name | Type | Description |
|
|
158
|
+
| --------------- | --------- | ----------------------------------------------------------- |
|
|
159
|
+
| `tableId` | `string` | Unique table identifier used to prefix parameter names |
|
|
160
|
+
| `sortKeySchema` | `ZodType` | Zod schema for the sort key type (e.g. `z.coerce.number()`) |
|
|
161
|
+
|
|
162
|
+
**Returns:** Zod object schema for sorting params.
|
|
163
|
+
|
|
164
|
+
## Usage Examples
|
|
165
|
+
|
|
166
|
+
### Full Table with URL Query State
|
|
167
|
+
|
|
168
|
+
The primary pattern — persists sorting, pagination, and filters in URL search params for shareable links and state
|
|
169
|
+
restoration:
|
|
170
|
+
|
|
171
|
+
```tsx
|
|
172
|
+
import { useSearch, useNavigate } from "@tanstack/react-router"
|
|
173
|
+
import { keepPreviousData } from "@tanstack/react-query"
|
|
174
|
+
import {
|
|
175
|
+
useTable,
|
|
176
|
+
useSorting,
|
|
177
|
+
usePagination,
|
|
178
|
+
useFilters,
|
|
179
|
+
defineFilters,
|
|
180
|
+
tableSearchSchema,
|
|
181
|
+
InferFiltersSchema,
|
|
182
|
+
} from "@leancodepl/antd-table-hooks"
|
|
183
|
+
import z from "zod"
|
|
184
|
+
|
|
185
|
+
const tableId = "reviews"
|
|
186
|
+
|
|
187
|
+
const { searchSchema: filtersSearchSchema, filters: reviewFilters } = defineFilters<SearchQuery>()([
|
|
188
|
+
emailFilter,
|
|
189
|
+
statusFilter,
|
|
190
|
+
])
|
|
191
|
+
|
|
192
|
+
export const searchSchema = tableSearchSchema(filtersSearchSchema, tableId, z.coerce.number())
|
|
193
|
+
|
|
194
|
+
export function ReviewsTable() {
|
|
195
|
+
const search = useSearch({ from: "/reviews" })
|
|
196
|
+
const navigate = useNavigate({ from: "/reviews" })
|
|
197
|
+
|
|
198
|
+
const queryState = useTable({
|
|
199
|
+
queryParams: search,
|
|
200
|
+
setQueryParams: params => navigate({ search: params }),
|
|
201
|
+
definedFilters: reviewFilters,
|
|
202
|
+
tableId,
|
|
203
|
+
defaultSortKey: SortKey.DateCreated,
|
|
204
|
+
defaultSortDirection: "descend",
|
|
205
|
+
})
|
|
206
|
+
|
|
207
|
+
const { sortData, sortKey, isDescending } = useSorting(queryState.sorting)
|
|
208
|
+
const { page, pageSize, getTablePagination, resetPage } = usePagination(queryState.pagination)
|
|
209
|
+
|
|
210
|
+
const { applyFilters, filters } = useFilters<SearchQuery, InferFiltersSchema<typeof reviewFilters>>({
|
|
211
|
+
filters: reviewFilters,
|
|
212
|
+
initialValues: queryState.filters.values,
|
|
213
|
+
onFiltersChange: (_, values) => {
|
|
214
|
+
queryState.filters.onFiltersChange(values)
|
|
215
|
+
resetPage()
|
|
216
|
+
},
|
|
217
|
+
})
|
|
218
|
+
|
|
219
|
+
const { data, isPending, isPlaceholderData } = api.useSearchReviews(
|
|
220
|
+
applyFilters({ PageNumber: page, PageSize: pageSize, SortBy: sortKey, SortByDescending: isDescending }),
|
|
221
|
+
{ placeholderData: keepPreviousData },
|
|
222
|
+
)
|
|
223
|
+
|
|
224
|
+
return (
|
|
225
|
+
<Table
|
|
226
|
+
columns={columns}
|
|
227
|
+
dataSource={data?.items}
|
|
228
|
+
loading={isPending || isPlaceholderData}
|
|
229
|
+
pagination={getTablePagination(data?.totalCount)}
|
|
230
|
+
/>
|
|
231
|
+
)
|
|
232
|
+
}
|
|
233
|
+
```
|
|
234
|
+
|
|
235
|
+
### Route Configuration
|
|
236
|
+
|
|
237
|
+
Use the combined search schema as `validateSearch` in a TanStack Router route:
|
|
238
|
+
|
|
239
|
+
```tsx
|
|
240
|
+
import { createFileRoute } from "@tanstack/react-router"
|
|
241
|
+
import { searchSchema } from "./ReviewsTable"
|
|
242
|
+
|
|
243
|
+
export const Route = createFileRoute("/reviews")({
|
|
244
|
+
component: ReviewsPage,
|
|
245
|
+
validateSearch: searchSchema,
|
|
246
|
+
})
|
|
247
|
+
```
|
|
248
|
+
|
|
249
|
+
### Standalone Table (Without URL State)
|
|
250
|
+
|
|
251
|
+
Use `useSorting`, `usePagination`, and `useFilters` directly when URL persistence is not needed:
|
|
252
|
+
|
|
253
|
+
```tsx
|
|
254
|
+
import { useSorting, usePagination, useFilters } from "@leancodepl/antd-table-hooks"
|
|
255
|
+
import { keepPreviousData } from "@tanstack/react-query"
|
|
256
|
+
|
|
257
|
+
export function SimpleTable() {
|
|
258
|
+
const { sortData, sortKey, isDescending } = useSorting({
|
|
259
|
+
defaultSortDirection: "descend",
|
|
260
|
+
defaultSortKey: SortKey.CreatedAt,
|
|
261
|
+
})
|
|
262
|
+
|
|
263
|
+
const { page, pageSize, getTablePagination, resetPage } = usePagination()
|
|
264
|
+
|
|
265
|
+
const { applyFilters, filters } = useFilters({
|
|
266
|
+
filters: simpleFilters,
|
|
267
|
+
onFiltersChange: resetPage,
|
|
268
|
+
})
|
|
269
|
+
|
|
270
|
+
const { data, isPending } = api.useSearch(
|
|
271
|
+
applyFilters({ PageNumber: page, PageSize: pageSize, SortBy: sortKey, SortByDescending: isDescending }),
|
|
272
|
+
{ placeholderData: keepPreviousData },
|
|
273
|
+
)
|
|
274
|
+
|
|
275
|
+
return (
|
|
276
|
+
<Table
|
|
277
|
+
columns={columns}
|
|
278
|
+
dataSource={data?.items}
|
|
279
|
+
loading={isPending}
|
|
280
|
+
pagination={getTablePagination(data?.totalCount)}
|
|
281
|
+
/>
|
|
282
|
+
)
|
|
283
|
+
}
|
|
284
|
+
```
|
|
285
|
+
|
|
286
|
+
### Defining Filters
|
|
287
|
+
|
|
288
|
+
Filters are defined outside the component using `defineFilters`. Each filter is a `FilterDefinition` object describing
|
|
289
|
+
its ID, component, search schema entries, and how it transforms the query:
|
|
290
|
+
|
|
291
|
+
```tsx
|
|
292
|
+
import { defineFilters, FilterDefinition } from "@leancodepl/antd-table-hooks"
|
|
293
|
+
|
|
294
|
+
const tableId = "users"
|
|
295
|
+
|
|
296
|
+
const { searchSchema: usersFiltersSchema, filters: userFilters } = defineFilters<SearchQuery>()([
|
|
297
|
+
emailTextFilter,
|
|
298
|
+
stateSelectFilter,
|
|
299
|
+
createdAtDateRangeFilter,
|
|
300
|
+
])
|
|
301
|
+
```
|
|
302
|
+
|
|
303
|
+
For context-dependent filters (e.g. internationalization):
|
|
304
|
+
|
|
305
|
+
```tsx
|
|
306
|
+
type FiltersContext = { intl: IntlShape }
|
|
307
|
+
|
|
308
|
+
const { searchSchema, filters: filtersFn } = defineFilters<SearchQuery, FiltersContext>()(context => [
|
|
309
|
+
nameFilter(context?.intl),
|
|
310
|
+
])
|
|
311
|
+
|
|
312
|
+
// In the component:
|
|
313
|
+
const intl = useIntl()
|
|
314
|
+
const filtersDefinition = useMemo(() => filtersFn({ intl }), [intl])
|
|
315
|
+
```
|
|
316
|
+
|
|
317
|
+
### Implementing Custom Filter Definitions
|
|
318
|
+
|
|
319
|
+
This library does not ship filter UI components — you implement them in your application to match your design system. A
|
|
320
|
+
filter definition is an object satisfying the `FilterDefinition<TQuery, TValue, TId>` type:
|
|
321
|
+
|
|
322
|
+
| Property | Type | Description |
|
|
323
|
+
| ------------------ | -------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------- |
|
|
324
|
+
| `id` | `string` | Unique identifier for the filter within the table |
|
|
325
|
+
| `component` | `ComponentType<FilterProps>` | React component receiving `{ applyFilter, reset$, initialValue }` |
|
|
326
|
+
| `searchSchema` | `[key, ZodType][]` (optional) | URL param key/schema pairs for serialization |
|
|
327
|
+
| `buildApplyFilter` | `(value) => ((query) => query) \| undefined` (optional) | Reconstructs the filter function from a stored value (needed for initial/restored values) |
|
|
328
|
+
| `toSearchParams` | `(value) => Record<string, unknown>` (required when `searchSchema` is set) | Serializes the filter value to URL params |
|
|
329
|
+
| `fromSearchParams` | `(params) => value \| undefined` (required when `searchSchema` is set) | Deserializes URL params back to a filter value |
|
|
330
|
+
|
|
331
|
+
Each filter component receives three props from `useFilters`:
|
|
332
|
+
|
|
333
|
+
- `applyFilter(filterFn, value)` — call with a query transform function when the value changes, or `undefined` to clear
|
|
334
|
+
- `reset$` — an RxJS Observable that emits when filters are reset; subscribe to clear local state
|
|
335
|
+
- `initialValue` — restored value when hydrating from URL params
|
|
336
|
+
|
|
337
|
+
#### Minimal Example
|
|
338
|
+
|
|
339
|
+
A simple text input filter showing the essential structure:
|
|
340
|
+
|
|
341
|
+
```tsx
|
|
342
|
+
import { useState, useEffect, useCallback } from "react"
|
|
343
|
+
import z from "zod"
|
|
344
|
+
import { FilterDefinition } from "@leancodepl/antd-table-hooks"
|
|
345
|
+
|
|
346
|
+
const schema = z.string().optional().catch(undefined)
|
|
347
|
+
|
|
348
|
+
export function textFilter<TQuery, const TId extends string, const TTableId extends string>({
|
|
349
|
+
id,
|
|
350
|
+
tableId,
|
|
351
|
+
label,
|
|
352
|
+
filter,
|
|
353
|
+
}: {
|
|
354
|
+
id: TId
|
|
355
|
+
tableId: TTableId
|
|
356
|
+
label: string
|
|
357
|
+
filter: (value: string, query: TQuery) => TQuery
|
|
358
|
+
}) {
|
|
359
|
+
const paramKey = `${tableId}-${id}` as const
|
|
360
|
+
|
|
361
|
+
return {
|
|
362
|
+
id,
|
|
363
|
+
|
|
364
|
+
component: ({ applyFilter, reset$, initialValue }) => {
|
|
365
|
+
const [value, setValue] = useState(initialValue)
|
|
366
|
+
|
|
367
|
+
const clear = useCallback(() => {
|
|
368
|
+
setValue(undefined)
|
|
369
|
+
applyFilter(undefined)
|
|
370
|
+
}, [applyFilter])
|
|
371
|
+
|
|
372
|
+
useEffect(() => {
|
|
373
|
+
const sub = reset$.subscribe(clear)
|
|
374
|
+
return () => sub.unsubscribe()
|
|
375
|
+
}, [clear, reset$])
|
|
376
|
+
|
|
377
|
+
return (
|
|
378
|
+
<input
|
|
379
|
+
placeholder={label}
|
|
380
|
+
value={value}
|
|
381
|
+
onChange={e => {
|
|
382
|
+
const v = e.target.value
|
|
383
|
+
setValue(v)
|
|
384
|
+
applyFilter(v ? query => filter(v, query) : undefined, v)
|
|
385
|
+
}}
|
|
386
|
+
/>
|
|
387
|
+
)
|
|
388
|
+
},
|
|
389
|
+
|
|
390
|
+
buildApplyFilter: value => (value ? query => filter(value, query) : undefined),
|
|
391
|
+
|
|
392
|
+
searchSchema: [[paramKey, schema]] as const,
|
|
393
|
+
toSearchParams: value => ({ [paramKey]: value }),
|
|
394
|
+
fromSearchParams: params => params[paramKey] as string | undefined,
|
|
395
|
+
} satisfies FilterDefinition<TQuery, string, TId>
|
|
396
|
+
}
|
|
397
|
+
```
|
|
398
|
+
|
|
399
|
+
Usage:
|
|
400
|
+
|
|
401
|
+
```tsx
|
|
402
|
+
textFilter({
|
|
403
|
+
id: "email",
|
|
404
|
+
tableId: "users",
|
|
405
|
+
label: "Email",
|
|
406
|
+
filter: (email, query) => ({ ...query, EmailFilter: email }),
|
|
407
|
+
})
|
|
408
|
+
```
|
|
409
|
+
|
|
410
|
+
#### Key Implementation Patterns
|
|
411
|
+
|
|
412
|
+
**Single URL param** (text, select): use one `searchSchema` entry keyed as `{tableId}-{id}`.
|
|
413
|
+
|
|
414
|
+
```tsx
|
|
415
|
+
searchSchema: [[`${tableId}-${id}`, z.string().optional()]] as const
|
|
416
|
+
```
|
|
417
|
+
|
|
418
|
+
**Multiple URL params** (date ranges): use multiple `searchSchema` entries with distinct keys.
|
|
419
|
+
|
|
420
|
+
```tsx
|
|
421
|
+
searchSchema: [
|
|
422
|
+
[`${tableId}-${id}-from`, z.string().optional()],
|
|
423
|
+
[`${tableId}-${id}-to`, z.string().optional()],
|
|
424
|
+
] as const
|
|
425
|
+
```
|
|
426
|
+
|
|
427
|
+
**Clearing**: call `applyFilter(undefined)` when the filter value is empty or cleared. The `reset$` observable fires
|
|
428
|
+
when the user resets all filters — subscribe to it and clear local state.
|
|
429
|
+
|
|
430
|
+
**Restoring from URL**: `fromSearchParams` deserializes raw URL values back into the filter's value type.
|
|
431
|
+
`buildApplyFilter` reconstructs the query transform function from a deserialized value, enabling filter restoration on
|
|
432
|
+
page load.
|
|
433
|
+
|
|
434
|
+
### Reusable Table with Query Params from Parent
|
|
435
|
+
|
|
436
|
+
Pass query params as props to share the same table component across multiple pages:
|
|
437
|
+
|
|
438
|
+
```tsx
|
|
439
|
+
import { useSearch, useNavigate } from "@tanstack/react-router"
|
|
440
|
+
import z from "zod"
|
|
441
|
+
|
|
442
|
+
type UsersTableQueryParams = z.infer<typeof usersSearchSchema>
|
|
443
|
+
|
|
444
|
+
type UsersTableProps = {
|
|
445
|
+
name: string
|
|
446
|
+
queryParams: UsersTableQueryParams
|
|
447
|
+
setQueryParams: (params: UsersTableQueryParams) => void
|
|
448
|
+
}
|
|
449
|
+
|
|
450
|
+
export function UsersTable({ name, queryParams, setQueryParams }: UsersTableProps) {
|
|
451
|
+
const queryState = useTable({
|
|
452
|
+
queryParams,
|
|
453
|
+
setQueryParams,
|
|
454
|
+
definedFilters: usersFilters,
|
|
455
|
+
tableId: "users",
|
|
456
|
+
defaultSortKey: SortKey.CreatedAt,
|
|
457
|
+
defaultSortDirection: "descend",
|
|
458
|
+
})
|
|
459
|
+
|
|
460
|
+
// ... same pattern as full table example
|
|
461
|
+
}
|
|
462
|
+
|
|
463
|
+
// Parent page
|
|
464
|
+
export function UsersPage() {
|
|
465
|
+
const search = useSearch({ from: "/users" })
|
|
466
|
+
const navigate = useNavigate({ from: "/users" })
|
|
467
|
+
|
|
468
|
+
return <UsersTable name="users" queryParams={search} setQueryParams={params => navigate({ search: params })} />
|
|
469
|
+
}
|
|
470
|
+
```
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,OAAO,CAAA"}
|