@promoboxx/use-filter 1.7.0 → 1.8.1
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/README.md +375 -0
- package/dist/useFilter.d.ts +62 -5
- package/dist/useFilter.js +13 -2
- package/dist/useSimpleFilter.d.ts +66 -33
- package/dist/useSimpleFilter.js +12 -1
- package/package.json +1 -1
package/README.md
ADDED
|
@@ -0,0 +1,375 @@
|
|
|
1
|
+
# @promoboxx/use-filter
|
|
2
|
+
|
|
3
|
+
A React hook to easily build filter views, with features like:
|
|
4
|
+
|
|
5
|
+
- persistence
|
|
6
|
+
- debouncing
|
|
7
|
+
- cursor based pagination
|
|
8
|
+
- offset / page based pagination
|
|
9
|
+
|
|
10
|
+
## What's Included
|
|
11
|
+
|
|
12
|
+
The filter system providers you with a few things:
|
|
13
|
+
|
|
14
|
+
- `filter`: A key/value store for arbitrary filter values
|
|
15
|
+
- `filterInfo`: Metadata around your filter with the current page, number of results, total pages, cursor, etc
|
|
16
|
+
- `filterApi`: What is returned from the hook, complete with `filterInfo` and helpers to update values in your `filter`, change the page, etc.
|
|
17
|
+
|
|
18
|
+
## Usage
|
|
19
|
+
|
|
20
|
+
There are two hooks provided. The default, "advanced", is more controlled, where you provide an `onChange` function which is called by the hook whenever you manipulate the filter.
|
|
21
|
+
|
|
22
|
+
The alternative, "simple", works similarly but gives you both `filterInfo` and a `debouncedFilterInfo` for you to use in effects.
|
|
23
|
+
|
|
24
|
+
### Advanced Example
|
|
25
|
+
|
|
26
|
+
```tsx
|
|
27
|
+
const { filterInfo, updateFilter, setPage, resetFilter, isLoading } = useFilter(
|
|
28
|
+
// Since this deals with caching, a namespace is required.
|
|
29
|
+
'advanced',
|
|
30
|
+
{
|
|
31
|
+
// Disable this if you are using the hook's data caching.
|
|
32
|
+
shouldForceRunOnMount: true,
|
|
33
|
+
|
|
34
|
+
defaultFilterInfo: {
|
|
35
|
+
filter: {
|
|
36
|
+
name: '',
|
|
37
|
+
},
|
|
38
|
+
pageSize: 20,
|
|
39
|
+
},
|
|
40
|
+
|
|
41
|
+
// Called by the hook whenever the filter changes.
|
|
42
|
+
async onChange(
|
|
43
|
+
filterInfo,
|
|
44
|
+
// If this is called as a result of the filter changing, the reason will
|
|
45
|
+
// be 'filter', If it.s due to the page changing, it will be 'pagination'.
|
|
46
|
+
updateReason,
|
|
47
|
+
) {
|
|
48
|
+
const { data } = await runQuery({
|
|
49
|
+
name: filterInfo.filter.name,
|
|
50
|
+
page: filterInfo.page,
|
|
51
|
+
})
|
|
52
|
+
|
|
53
|
+
// Here we are telling the filter system how many results there are. Since
|
|
54
|
+
// it knows how many we want to display per-page, it can now figure out
|
|
55
|
+
// how many pages there are in total.
|
|
56
|
+
return {
|
|
57
|
+
filterInfo: {
|
|
58
|
+
totalResults: data?.locations?.info.count,
|
|
59
|
+
},
|
|
60
|
+
}
|
|
61
|
+
},
|
|
62
|
+
},
|
|
63
|
+
)
|
|
64
|
+
|
|
65
|
+
return (
|
|
66
|
+
<>
|
|
67
|
+
<input
|
|
68
|
+
type="text"
|
|
69
|
+
value={filterInfo.filter.name}
|
|
70
|
+
onChange={(event) => {
|
|
71
|
+
updateFilter({ name: event.target.value })
|
|
72
|
+
}}
|
|
73
|
+
/>
|
|
74
|
+
|
|
75
|
+
<button onClick={resetFilter}>Reset</button>
|
|
76
|
+
|
|
77
|
+
{isLoading ? <CircularProgress /> : undefined}
|
|
78
|
+
|
|
79
|
+
{/* Hypothetical list of results would be here */}
|
|
80
|
+
|
|
81
|
+
<Pagination
|
|
82
|
+
totalPages={filterInfo.totalPages}
|
|
83
|
+
page={filterInfo.page}
|
|
84
|
+
onPageChange={(page) => {
|
|
85
|
+
setPage(page)
|
|
86
|
+
}}
|
|
87
|
+
/>
|
|
88
|
+
</>
|
|
89
|
+
)
|
|
90
|
+
```
|
|
91
|
+
|
|
92
|
+
### Simple Example
|
|
93
|
+
|
|
94
|
+
```tsx
|
|
95
|
+
const {
|
|
96
|
+
filterInfo,
|
|
97
|
+
debouncedFilterInfo,
|
|
98
|
+
updateFilter,
|
|
99
|
+
pagingInfo,
|
|
100
|
+
setPage,
|
|
101
|
+
resetFilter,
|
|
102
|
+
isLoading,
|
|
103
|
+
} = useSimpleFilter('simple', {
|
|
104
|
+
defaultFilterInfo: {
|
|
105
|
+
filter: {
|
|
106
|
+
name: '',
|
|
107
|
+
},
|
|
108
|
+
pageSize: 20,
|
|
109
|
+
},
|
|
110
|
+
})
|
|
111
|
+
|
|
112
|
+
// Hypothetical fetch / query / graphql hook.
|
|
113
|
+
const { data, fetch } = useQuery()
|
|
114
|
+
|
|
115
|
+
useEffect(() => {
|
|
116
|
+
fetch({
|
|
117
|
+
name: debouncedFilterInfo.filter.name,
|
|
118
|
+
page: debouncedFilterInfo.page,
|
|
119
|
+
})
|
|
120
|
+
}, [debouncedFilterInfo])
|
|
121
|
+
|
|
122
|
+
// Since the simple mode doesn't know when we're done doing our work, it
|
|
123
|
+
// provides a helper to get us pagination info.
|
|
124
|
+
const { totalPages } = pagingInfo(data?.count)
|
|
125
|
+
|
|
126
|
+
return (
|
|
127
|
+
<>
|
|
128
|
+
<input
|
|
129
|
+
type="text"
|
|
130
|
+
value={filterInfo.filter.name}
|
|
131
|
+
onChange={(event) => {
|
|
132
|
+
updateFilter({ name: event.target.value })
|
|
133
|
+
}}
|
|
134
|
+
/>
|
|
135
|
+
|
|
136
|
+
<button onClick={resetFilter}>Reset</button>
|
|
137
|
+
|
|
138
|
+
{isLoading ? <CircularProgress /> : undefined}
|
|
139
|
+
|
|
140
|
+
{/* Hypothetical list of results would be here */}
|
|
141
|
+
|
|
142
|
+
<Pagination
|
|
143
|
+
totalPages={totalPages}
|
|
144
|
+
page={filterInfo.page}
|
|
145
|
+
onPageChange={(page) => {
|
|
146
|
+
setPage(page)
|
|
147
|
+
}}
|
|
148
|
+
/>
|
|
149
|
+
</>
|
|
150
|
+
)
|
|
151
|
+
```
|
|
152
|
+
|
|
153
|
+
## API
|
|
154
|
+
|
|
155
|
+
### `useFilter(options)`
|
|
156
|
+
|
|
157
|
+
```tsx
|
|
158
|
+
interface UseFilterOptions<TFilter, TResult> {
|
|
159
|
+
/**
|
|
160
|
+
* Default values for your filter. When calling `.reset` your filter will be
|
|
161
|
+
* set to this.
|
|
162
|
+
* Changing these values does not cause a call to happen.
|
|
163
|
+
*/
|
|
164
|
+
defaultFilterInfo?: Partial<FilterInfo<TFilter>>
|
|
165
|
+
|
|
166
|
+
/**
|
|
167
|
+
* Enable this if you are not using the data caching of this hook.
|
|
168
|
+
*/
|
|
169
|
+
shouldForceRunOnMount?: boolean
|
|
170
|
+
|
|
171
|
+
/**
|
|
172
|
+
* Called whenever the filter changes.
|
|
173
|
+
*/
|
|
174
|
+
onChange: (
|
|
175
|
+
filterInfo: FilterInfo<TFilter>,
|
|
176
|
+
reason: UseFilterUpdateReason,
|
|
177
|
+
) => MaybePromise<UseFilterOnChangeResult<TFilter, TResult>>
|
|
178
|
+
|
|
179
|
+
/**
|
|
180
|
+
* In case you want to change the default debounce duration.
|
|
181
|
+
*/
|
|
182
|
+
debounceDuration?: number
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
export interface FilterApi<TFilter, TResult> {
|
|
186
|
+
/**
|
|
187
|
+
* Whether the system is debouncing or waiting for your `onChange` to finish.
|
|
188
|
+
*/
|
|
189
|
+
isLoading: boolean
|
|
190
|
+
/**
|
|
191
|
+
* Any cached data for this filter.
|
|
192
|
+
*/
|
|
193
|
+
data: TResult | null | undefined
|
|
194
|
+
/**
|
|
195
|
+
* Access to the filter and its metadata.
|
|
196
|
+
*/
|
|
197
|
+
filterInfo: FilterInfo<TFilter>
|
|
198
|
+
|
|
199
|
+
/**
|
|
200
|
+
* Whether the filter existed in the system when the component mounted.
|
|
201
|
+
* @deprecated
|
|
202
|
+
*/
|
|
203
|
+
doesFilterExist: boolean
|
|
204
|
+
/**
|
|
205
|
+
* Why the last update happened.
|
|
206
|
+
*/
|
|
207
|
+
updateReason: UseFilterUpdateReason
|
|
208
|
+
|
|
209
|
+
/**
|
|
210
|
+
* Update one or more values in your filter.
|
|
211
|
+
*/
|
|
212
|
+
updateFilter: (
|
|
213
|
+
filter: Partial<TFilter>,
|
|
214
|
+
shouldRunImmediately?: boolean,
|
|
215
|
+
) => void
|
|
216
|
+
|
|
217
|
+
/**
|
|
218
|
+
* Resets the filter back to `defaultFilterInfo`.
|
|
219
|
+
*/
|
|
220
|
+
resetFilter: (shouldRunImmediately?: boolean) => void
|
|
221
|
+
|
|
222
|
+
/**
|
|
223
|
+
* Changes the offset. Will update `page`.
|
|
224
|
+
*/
|
|
225
|
+
setOffset: (offset: number | string, shouldRunImmediately?: boolean) => void
|
|
226
|
+
|
|
227
|
+
/**
|
|
228
|
+
* Changes the page. Will update `offset`.
|
|
229
|
+
*/
|
|
230
|
+
setPage: (page: number | string, shouldRunImmediately?: boolean) => void
|
|
231
|
+
|
|
232
|
+
/**
|
|
233
|
+
* Changes the page size.
|
|
234
|
+
*/
|
|
235
|
+
setPageSize: (
|
|
236
|
+
pageSize: number | string,
|
|
237
|
+
shouldRunImmediately?: boolean,
|
|
238
|
+
) => void
|
|
239
|
+
|
|
240
|
+
/**
|
|
241
|
+
* Change the sort method.
|
|
242
|
+
*/
|
|
243
|
+
setSort: (sort: string | undefined, shouldRunImmediately?: boolean) => void
|
|
244
|
+
|
|
245
|
+
/**
|
|
246
|
+
* Changes the cursor.
|
|
247
|
+
*/
|
|
248
|
+
setCursor: (cursor: string | null | undefined) => void
|
|
249
|
+
|
|
250
|
+
/**
|
|
251
|
+
* Forces a refresh of the filter.
|
|
252
|
+
*/
|
|
253
|
+
forceRefresh: (shouldRunImmediately?: boolean) => void
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
interface FilterInfo<TFilter> {
|
|
257
|
+
filter: TFilter
|
|
258
|
+
offset: number
|
|
259
|
+
page: number
|
|
260
|
+
sort?: string
|
|
261
|
+
pageSize: number
|
|
262
|
+
lastRefreshAt: number
|
|
263
|
+
totalResults: number
|
|
264
|
+
totalPages: number
|
|
265
|
+
shouldRunImmediately: boolean
|
|
266
|
+
cursor?: string | null
|
|
267
|
+
nextCursor?: string | null
|
|
268
|
+
}
|
|
269
|
+
```
|
|
270
|
+
|
|
271
|
+
### `useSimpleFilter(options)`
|
|
272
|
+
|
|
273
|
+
```tsx
|
|
274
|
+
interface UseSimpleFilterOptions<TFilter> {
|
|
275
|
+
/**
|
|
276
|
+
* Default values for your filter. When calling `.reset` your filter will be
|
|
277
|
+
* set to this.
|
|
278
|
+
* Changing these values does not cause a call to happen.
|
|
279
|
+
*/
|
|
280
|
+
defaultFilterInfo?: Partial<SimpleFilterInfo<TFilter>>
|
|
281
|
+
|
|
282
|
+
/**
|
|
283
|
+
* In case you want to change the default debounce duration.
|
|
284
|
+
*/
|
|
285
|
+
debounceDuration?: number
|
|
286
|
+
}
|
|
287
|
+
|
|
288
|
+
export interface SimpleFilterApi<TFilter> {
|
|
289
|
+
/**
|
|
290
|
+
* Really more "is debouncing", since simple mode doesn't know when your code
|
|
291
|
+
* is doing anything.
|
|
292
|
+
*/
|
|
293
|
+
isLoading: boolean
|
|
294
|
+
/**
|
|
295
|
+
* Access to the filter and its metadata.
|
|
296
|
+
*/
|
|
297
|
+
filterInfo: SimpleFilterInfo<TFilter>
|
|
298
|
+
/**
|
|
299
|
+
* Same as the regular `filterInfo`, but updates in a debounced fashion.
|
|
300
|
+
*/
|
|
301
|
+
debouncedFilterInfo: SimpleFilterInfo<TFilter>
|
|
302
|
+
/**
|
|
303
|
+
* Why the last update happened.
|
|
304
|
+
*/
|
|
305
|
+
updateReason: UseFilterUpdateReason
|
|
306
|
+
|
|
307
|
+
/**
|
|
308
|
+
* Update one or more values in your filter.
|
|
309
|
+
*/
|
|
310
|
+
updateFilter: (
|
|
311
|
+
filter: Partial<TFilter>,
|
|
312
|
+
shouldRunImmediately?: boolean,
|
|
313
|
+
) => void
|
|
314
|
+
|
|
315
|
+
/**
|
|
316
|
+
* Does the boring math for you based on your `pageSize` and `totalResults`.
|
|
317
|
+
*/
|
|
318
|
+
pagingInfo: (total: string | number | null | undefined) => {
|
|
319
|
+
totalResults: number
|
|
320
|
+
totalPages: number
|
|
321
|
+
}
|
|
322
|
+
|
|
323
|
+
/**
|
|
324
|
+
* Resets the filter back to `defaultFilterInfo`.
|
|
325
|
+
*/
|
|
326
|
+
resetFilter: (shouldRunImmediately?: boolean) => void
|
|
327
|
+
|
|
328
|
+
/**
|
|
329
|
+
* Changes the offset. Will update `page`.
|
|
330
|
+
*/
|
|
331
|
+
setOffset: (offset: number | string, shouldRunImmediately?: boolean) => void
|
|
332
|
+
|
|
333
|
+
/**
|
|
334
|
+
* Changes the page. Will update `offset`.
|
|
335
|
+
*/
|
|
336
|
+
setPage: (page: number | string, shouldRunImmediately?: boolean) => void
|
|
337
|
+
|
|
338
|
+
/**
|
|
339
|
+
* Changes the page size.
|
|
340
|
+
*/
|
|
341
|
+
setPageSize: (
|
|
342
|
+
pageSize: number | string,
|
|
343
|
+
shouldRunImmediately?: boolean,
|
|
344
|
+
) => void
|
|
345
|
+
|
|
346
|
+
/**
|
|
347
|
+
* Change the sort method.
|
|
348
|
+
*/
|
|
349
|
+
setSort: (sort: string, shouldRunImmediately?: boolean) => void
|
|
350
|
+
|
|
351
|
+
/**
|
|
352
|
+
* Changes the cursor.
|
|
353
|
+
*/
|
|
354
|
+
setCursor: (
|
|
355
|
+
cursor: string | null | undefined,
|
|
356
|
+
shouldRunImmediately?: boolean,
|
|
357
|
+
) => void
|
|
358
|
+
|
|
359
|
+
/**
|
|
360
|
+
* Forces a refresh of the filter.
|
|
361
|
+
*/
|
|
362
|
+
forceRefresh: (shouldRunImmediately?: boolean) => void
|
|
363
|
+
}
|
|
364
|
+
|
|
365
|
+
interface SimpleFilterInfo<TFilter> {
|
|
366
|
+
filter: TFilter
|
|
367
|
+
sort?: string
|
|
368
|
+
offset: number
|
|
369
|
+
page: number
|
|
370
|
+
pageSize: number
|
|
371
|
+
lastRefreshAt: number
|
|
372
|
+
cursor?: string | null
|
|
373
|
+
shouldRunImmediately: boolean
|
|
374
|
+
}
|
|
375
|
+
```
|
package/dist/useFilter.d.ts
CHANGED
|
@@ -1,24 +1,81 @@
|
|
|
1
1
|
declare type MaybePromise<T> = T | Promise<T>;
|
|
2
2
|
interface UseFilterOptions<TFilter, TResult> {
|
|
3
|
+
/**
|
|
4
|
+
* Default values for your filter. When calling `.reset` your filter will be
|
|
5
|
+
* set to this.
|
|
6
|
+
* Changing these values does not cause a call to happen.
|
|
7
|
+
*/
|
|
3
8
|
defaultFilterInfo?: Partial<FilterInfo<TFilter>>;
|
|
9
|
+
/**
|
|
10
|
+
* Enable this if you are not using the data caching of this hook.
|
|
11
|
+
*/
|
|
4
12
|
shouldForceRunOnMount?: boolean;
|
|
5
|
-
|
|
13
|
+
/**
|
|
14
|
+
* Called whenever the filter changes.
|
|
15
|
+
*/
|
|
16
|
+
onChange: (filterInfo: FilterInfo<TFilter>, reason: UseFilterUpdateReason) => MaybePromise<UseFilterOnChangeResult<TFilter, TResult>>;
|
|
17
|
+
/**
|
|
18
|
+
* In case you want to change the default debounce duration.
|
|
19
|
+
*/
|
|
6
20
|
debounceDuration?: number;
|
|
7
21
|
}
|
|
8
|
-
|
|
22
|
+
export interface FilterApi<TFilter, TResult> {
|
|
23
|
+
/**
|
|
24
|
+
* Whether the system is debouncing or waiting for your `onChange` to finish.
|
|
25
|
+
*/
|
|
9
26
|
isLoading: boolean;
|
|
27
|
+
/**
|
|
28
|
+
* Any cached data for this filter.
|
|
29
|
+
*/
|
|
10
30
|
data: TResult | null | undefined;
|
|
11
|
-
|
|
31
|
+
/**
|
|
32
|
+
* Access to the filter and its metadata.
|
|
33
|
+
*/
|
|
12
34
|
filterInfo: FilterInfo<TFilter>;
|
|
35
|
+
/**
|
|
36
|
+
* Whether the filter existed in the system when the component mounted.
|
|
37
|
+
* @deprecated
|
|
38
|
+
*/
|
|
39
|
+
doesFilterExist: boolean;
|
|
40
|
+
/**
|
|
41
|
+
* Why the last update happened.
|
|
42
|
+
*/
|
|
43
|
+
updateReason: UseFilterUpdateReason;
|
|
44
|
+
/**
|
|
45
|
+
* Update one or more values in your filter.
|
|
46
|
+
*/
|
|
13
47
|
updateFilter: (filter: Partial<TFilter>, shouldRunImmediately?: boolean) => void;
|
|
48
|
+
/**
|
|
49
|
+
* Resets the filter back to `defaultFilterInfo`.
|
|
50
|
+
*/
|
|
14
51
|
resetFilter: (shouldRunImmediately?: boolean) => void;
|
|
52
|
+
/**
|
|
53
|
+
* Changes the offset. Will update `page`.
|
|
54
|
+
*/
|
|
15
55
|
setOffset: (offset: number | string, shouldRunImmediately?: boolean) => void;
|
|
56
|
+
/**
|
|
57
|
+
* Changes the page. Will update `offset`.
|
|
58
|
+
*/
|
|
16
59
|
setPage: (page: number | string, shouldRunImmediately?: boolean) => void;
|
|
60
|
+
/**
|
|
61
|
+
* Changes the page size.
|
|
62
|
+
*/
|
|
17
63
|
setPageSize: (pageSize: number | string, shouldRunImmediately?: boolean) => void;
|
|
64
|
+
/**
|
|
65
|
+
* Change the sort method.
|
|
66
|
+
*/
|
|
18
67
|
setSort: (sort: string | undefined, shouldRunImmediately?: boolean) => void;
|
|
19
|
-
|
|
68
|
+
/**
|
|
69
|
+
* Changes the cursor.
|
|
70
|
+
*/
|
|
71
|
+
setCursor: (cursor: string | null | undefined) => void;
|
|
72
|
+
/**
|
|
73
|
+
* Forces a refresh of the filter.
|
|
74
|
+
*/
|
|
20
75
|
forceRefresh: (shouldRunImmediately?: boolean) => void;
|
|
21
|
-
}
|
|
76
|
+
}
|
|
77
|
+
export declare type UseFilterUpdateReason = 'initial' | 'filter' | 'pagination';
|
|
78
|
+
declare function useFilter<TFilter, TResult>(namespace: string, options: UseFilterOptions<TFilter, TResult>): FilterApi<TFilter, TResult>;
|
|
22
79
|
export default useFilter;
|
|
23
80
|
export interface FilterInfo<TFilter> {
|
|
24
81
|
filter: TFilter;
|
package/dist/useFilter.js
CHANGED
|
@@ -64,6 +64,7 @@ function useFilter(namespace, options) {
|
|
|
64
64
|
: filterInfo
|
|
65
65
|
? filterInfo.lastRefreshAt
|
|
66
66
|
: -1);
|
|
67
|
+
var updateReasonRef = react_1.useRef('initial');
|
|
67
68
|
// Call onChange when data changes.
|
|
68
69
|
react_1.useEffect(function () {
|
|
69
70
|
setIsLoading(true);
|
|
@@ -80,7 +81,7 @@ function useFilter(namespace, options) {
|
|
|
80
81
|
}
|
|
81
82
|
var makeRequest = function () {
|
|
82
83
|
lastRefreshAtRef.current = filterInfo.lastRefreshAt;
|
|
83
|
-
var response = ctxRef.current.onChange(filterInfo);
|
|
84
|
+
var response = ctxRef.current.onChange(filterInfo, updateReasonRef.current);
|
|
84
85
|
store_1.getFilterStore().saveFilter(namespace, filterInfo);
|
|
85
86
|
if (!response) {
|
|
86
87
|
setIsLoading(false);
|
|
@@ -148,13 +149,15 @@ function useFilter(namespace, options) {
|
|
|
148
149
|
}
|
|
149
150
|
};
|
|
150
151
|
}, [filterInfo, namespace]);
|
|
151
|
-
|
|
152
|
+
var api = {
|
|
152
153
|
isLoading: isLoading,
|
|
153
154
|
data: data,
|
|
154
155
|
doesFilterExist: doesFilterExist.current,
|
|
155
156
|
filterInfo: filterInfo || buildDefaultFilterInfo_1.default(options.defaultFilterInfo),
|
|
157
|
+
updateReason: updateReasonRef.current,
|
|
156
158
|
updateFilter: react_1.useCallback(function (filter, shouldRunImmediately) {
|
|
157
159
|
if (shouldRunImmediately === void 0) { shouldRunImmediately = false; }
|
|
160
|
+
updateReasonRef.current = 'filter';
|
|
158
161
|
setFilterInfoState(function (previous) {
|
|
159
162
|
invariant(previous != null, 'updateFilter called without filterInfo');
|
|
160
163
|
var nextFilter = __assign(__assign({}, previous.filter), filter);
|
|
@@ -166,10 +169,12 @@ function useFilter(namespace, options) {
|
|
|
166
169
|
}, []),
|
|
167
170
|
resetFilter: react_1.useCallback(function (shouldRunImmediately) {
|
|
168
171
|
if (shouldRunImmediately === void 0) { shouldRunImmediately = false; }
|
|
172
|
+
updateReasonRef.current = 'filter';
|
|
169
173
|
setFilterInfoState(__assign(__assign({}, buildDefaultFilterInfo_1.default(ctxRef.current.defaultFilterInfo)), { lastRefreshAt: new Date().getTime(), shouldRunImmediately: shouldRunImmediately }));
|
|
170
174
|
}, []),
|
|
171
175
|
setOffset: react_1.useCallback(function (offset, shouldRunImmediately) {
|
|
172
176
|
if (shouldRunImmediately === void 0) { shouldRunImmediately = false; }
|
|
177
|
+
updateReasonRef.current = 'pagination';
|
|
173
178
|
setFilterInfoState(function (previous) {
|
|
174
179
|
invariant(previous != null, 'setOffset called without filterInfo');
|
|
175
180
|
var offsetNumber = Number(offset);
|
|
@@ -178,6 +183,7 @@ function useFilter(namespace, options) {
|
|
|
178
183
|
}, []),
|
|
179
184
|
setPage: react_1.useCallback(function (page, shouldRunImmediately) {
|
|
180
185
|
if (shouldRunImmediately === void 0) { shouldRunImmediately = false; }
|
|
186
|
+
updateReasonRef.current = 'pagination';
|
|
181
187
|
setFilterInfoState(function (previous) {
|
|
182
188
|
invariant(previous != null, 'setPage called without filterInfo');
|
|
183
189
|
var pageNumber = Number(page);
|
|
@@ -186,6 +192,7 @@ function useFilter(namespace, options) {
|
|
|
186
192
|
}, []),
|
|
187
193
|
setPageSize: react_1.useCallback(function (pageSize, shouldRunImmediately) {
|
|
188
194
|
if (shouldRunImmediately === void 0) { shouldRunImmediately = false; }
|
|
195
|
+
updateReasonRef.current = 'pagination';
|
|
189
196
|
setFilterInfoState(function (previous) {
|
|
190
197
|
invariant(previous != null, 'setPageSize called without filterInfo');
|
|
191
198
|
var pageSizeNumber = Number(pageSize);
|
|
@@ -196,6 +203,7 @@ function useFilter(namespace, options) {
|
|
|
196
203
|
}, []),
|
|
197
204
|
setSort: react_1.useCallback(function (sort, shouldRunImmediately) {
|
|
198
205
|
if (shouldRunImmediately === void 0) { shouldRunImmediately = false; }
|
|
206
|
+
updateReasonRef.current = 'filter';
|
|
199
207
|
setFilterInfoState(function (previous) {
|
|
200
208
|
invariant(previous != null, 'setSort called without filterInfo');
|
|
201
209
|
return __assign(__assign({}, previous), { sort: sort, lastRefreshAt: new Date().getTime(), shouldRunImmediately: shouldRunImmediately });
|
|
@@ -204,18 +212,21 @@ function useFilter(namespace, options) {
|
|
|
204
212
|
setCursor: react_1.useCallback(function (cursor, shouldRunImmediately) {
|
|
205
213
|
if (shouldRunImmediately === void 0) { shouldRunImmediately = false; }
|
|
206
214
|
setFilterInfoState(function (previous) {
|
|
215
|
+
updateReasonRef.current = 'pagination';
|
|
207
216
|
invariant(previous != null, 'setCursor called without filterInfo');
|
|
208
217
|
return __assign(__assign({}, previous), { cursor: cursor, lastRefreshAt: new Date().getTime(), shouldRunImmediately: shouldRunImmediately });
|
|
209
218
|
});
|
|
210
219
|
}, []),
|
|
211
220
|
forceRefresh: react_1.useCallback(function (shouldRunImmediately) {
|
|
212
221
|
if (shouldRunImmediately === void 0) { shouldRunImmediately = false; }
|
|
222
|
+
updateReasonRef.current = 'filter';
|
|
213
223
|
setFilterInfoState(function (previous) {
|
|
214
224
|
invariant(previous != null, 'forceRefresh called without filterInfo');
|
|
215
225
|
return __assign(__assign({}, previous), { lastRefreshAt: new Date().getTime(), shouldRunImmediately: shouldRunImmediately });
|
|
216
226
|
});
|
|
217
227
|
}, []),
|
|
218
228
|
};
|
|
229
|
+
return api;
|
|
219
230
|
}
|
|
220
231
|
exports.default = useFilter;
|
|
221
232
|
function invariant(input, message) {
|
|
@@ -1,50 +1,83 @@
|
|
|
1
|
-
|
|
2
|
-
filter: TFilter;
|
|
3
|
-
sort?: string;
|
|
4
|
-
offset: number;
|
|
5
|
-
page: number;
|
|
6
|
-
pageSize: number;
|
|
7
|
-
lastRefreshAt: number;
|
|
8
|
-
cursor?: string | null;
|
|
9
|
-
shouldRunImmediately: boolean;
|
|
10
|
-
}
|
|
1
|
+
import { UseFilterUpdateReason } from './useFilter';
|
|
11
2
|
interface UseSimpleFilterOptions<TFilter> {
|
|
3
|
+
/**
|
|
4
|
+
* Default values for your filter. When calling `.reset` your filter will be
|
|
5
|
+
* set to this.
|
|
6
|
+
* Changing these values does not cause a call to happen.
|
|
7
|
+
*/
|
|
12
8
|
defaultFilterInfo?: Partial<SimpleFilterInfo<TFilter>>;
|
|
9
|
+
/**
|
|
10
|
+
* In case you want to change the default debounce duration.
|
|
11
|
+
*/
|
|
13
12
|
debounceDuration?: number;
|
|
14
13
|
}
|
|
15
|
-
|
|
14
|
+
export interface SimpleFilterApi<TFilter> {
|
|
15
|
+
/**
|
|
16
|
+
* Really more "is debouncing", since simple mode doesn't know when your code
|
|
17
|
+
* is doing anything.
|
|
18
|
+
*/
|
|
16
19
|
isLoading: boolean;
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
};
|
|
37
|
-
updateFilter: (filter: Partial<TFilter>, shouldRunImmediately?: any) => void;
|
|
20
|
+
/**
|
|
21
|
+
* Access to the filter and its metadata.
|
|
22
|
+
*/
|
|
23
|
+
filterInfo: SimpleFilterInfo<TFilter>;
|
|
24
|
+
/**
|
|
25
|
+
* Same as the regular `filterInfo`, but updates in a debounced fashion.
|
|
26
|
+
*/
|
|
27
|
+
debouncedFilterInfo: SimpleFilterInfo<TFilter>;
|
|
28
|
+
/**
|
|
29
|
+
* Why the last update happened.
|
|
30
|
+
*/
|
|
31
|
+
updateReason: UseFilterUpdateReason;
|
|
32
|
+
/**
|
|
33
|
+
* Update one or more values in your filter.
|
|
34
|
+
*/
|
|
35
|
+
updateFilter: (filter: Partial<TFilter>, shouldRunImmediately?: boolean) => void;
|
|
36
|
+
/**
|
|
37
|
+
* Does the boring math for you based on your `pageSize` and `totalResults`.
|
|
38
|
+
*/
|
|
38
39
|
pagingInfo: (total: string | number | null | undefined) => {
|
|
39
40
|
totalResults: number;
|
|
40
41
|
totalPages: number;
|
|
41
42
|
};
|
|
43
|
+
/**
|
|
44
|
+
* Resets the filter back to `defaultFilterInfo`.
|
|
45
|
+
*/
|
|
42
46
|
resetFilter: (shouldRunImmediately?: boolean) => void;
|
|
47
|
+
/**
|
|
48
|
+
* Changes the offset. Will update `page`.
|
|
49
|
+
*/
|
|
43
50
|
setOffset: (offset: number | string, shouldRunImmediately?: boolean) => void;
|
|
51
|
+
/**
|
|
52
|
+
* Changes the page. Will update `offset`.
|
|
53
|
+
*/
|
|
44
54
|
setPage: (page: number | string, shouldRunImmediately?: boolean) => void;
|
|
55
|
+
/**
|
|
56
|
+
* Changes the page size.
|
|
57
|
+
*/
|
|
45
58
|
setPageSize: (pageSize: number | string, shouldRunImmediately?: boolean) => void;
|
|
59
|
+
/**
|
|
60
|
+
* Change the sort method.
|
|
61
|
+
*/
|
|
46
62
|
setSort: (sort: string, shouldRunImmediately?: boolean) => void;
|
|
63
|
+
/**
|
|
64
|
+
* Changes the cursor.
|
|
65
|
+
*/
|
|
47
66
|
setCursor: (cursor: string | null | undefined, shouldRunImmediately?: boolean) => void;
|
|
67
|
+
/**
|
|
68
|
+
* Forces a refresh of the filter.
|
|
69
|
+
*/
|
|
48
70
|
forceRefresh: (shouldRunImmediately?: boolean) => void;
|
|
49
|
-
}
|
|
71
|
+
}
|
|
72
|
+
export interface SimpleFilterInfo<TFilter> {
|
|
73
|
+
filter: TFilter;
|
|
74
|
+
sort?: string;
|
|
75
|
+
offset: number;
|
|
76
|
+
page: number;
|
|
77
|
+
pageSize: number;
|
|
78
|
+
lastRefreshAt: number;
|
|
79
|
+
cursor?: string | null;
|
|
80
|
+
shouldRunImmediately: boolean;
|
|
81
|
+
}
|
|
82
|
+
declare function useSimpleFilter<TFilter>(namespace: string, options: UseSimpleFilterOptions<TFilter>): SimpleFilterApi<TFilter>;
|
|
50
83
|
export default useSimpleFilter;
|
package/dist/useSimpleFilter.js
CHANGED
|
@@ -37,6 +37,7 @@ function useSimpleFilter(namespace, options) {
|
|
|
37
37
|
var _c = react_1.useState(filterInfo), debouncedFilterInfo = _c[0], setDebouncedFilterInfo = _c[1];
|
|
38
38
|
// Not sure this is needed in simple mode?
|
|
39
39
|
var lastRefreshAtRef = react_1.useRef(-1);
|
|
40
|
+
var updateReasonRef = react_1.useRef('initial');
|
|
40
41
|
react_1.useEffect(function () {
|
|
41
42
|
var _a;
|
|
42
43
|
setIsLoading(true);
|
|
@@ -65,12 +66,14 @@ function useSimpleFilter(namespace, options) {
|
|
|
65
66
|
};
|
|
66
67
|
}, [filterInfo]);
|
|
67
68
|
var previousTotalRef = react_1.useRef(0);
|
|
68
|
-
|
|
69
|
+
var api = {
|
|
69
70
|
isLoading: isLoading,
|
|
70
71
|
filterInfo: filterInfo,
|
|
71
72
|
debouncedFilterInfo: debouncedFilterInfo,
|
|
73
|
+
updateReason: updateReasonRef.current,
|
|
72
74
|
updateFilter: react_1.useCallback(function (filter, shouldRunImmediately) {
|
|
73
75
|
if (shouldRunImmediately === void 0) { shouldRunImmediately = false; }
|
|
76
|
+
updateReasonRef.current = 'filter';
|
|
74
77
|
setFilterInfoState(function (previous) {
|
|
75
78
|
var nextFilter = __assign(__assign({}, previous.filter), filter);
|
|
76
79
|
if (shallowEqual_1.default(previous.filter, nextFilter)) {
|
|
@@ -92,20 +95,24 @@ function useSimpleFilter(namespace, options) {
|
|
|
92
95
|
}, [filterInfo.pageSize]),
|
|
93
96
|
resetFilter: react_1.useCallback(function (shouldRunImmediately) {
|
|
94
97
|
if (shouldRunImmediately === void 0) { shouldRunImmediately = false; }
|
|
98
|
+
updateReasonRef.current = 'filter';
|
|
95
99
|
setFilterInfoState(__assign(__assign({}, buildDefaultFilterInfo(ctxRef.current.defaultFilterInfo)), { shouldRunImmediately: shouldRunImmediately }));
|
|
96
100
|
}, []),
|
|
97
101
|
setOffset: react_1.useCallback(function (offset, shouldRunImmediately) {
|
|
98
102
|
if (shouldRunImmediately === void 0) { shouldRunImmediately = false; }
|
|
103
|
+
updateReasonRef.current = 'pagination';
|
|
99
104
|
var offsetNumber = Number(offset);
|
|
100
105
|
setFilterInfoState(function (previous) { return (__assign(__assign({}, previous), { offset: offsetNumber, page: getPageFromOffset_1.default(offsetNumber, previous.pageSize), lastRefreshAt: new Date().getTime(), shouldRunImmediately: shouldRunImmediately })); });
|
|
101
106
|
}, []),
|
|
102
107
|
setPage: react_1.useCallback(function (page, shouldRunImmediately) {
|
|
103
108
|
if (shouldRunImmediately === void 0) { shouldRunImmediately = false; }
|
|
109
|
+
updateReasonRef.current = 'pagination';
|
|
104
110
|
var pageNumber = Number(page);
|
|
105
111
|
setFilterInfoState(function (previous) { return (__assign(__assign({}, previous), { offset: getOffsetFromPage_1.default(pageNumber, previous.pageSize), page: pageNumber, lastRefreshAt: new Date().getTime(), shouldRunImmediately: shouldRunImmediately })); });
|
|
106
112
|
}, []),
|
|
107
113
|
setPageSize: react_1.useCallback(function (pageSize, shouldRunImmediately) {
|
|
108
114
|
if (shouldRunImmediately === void 0) { shouldRunImmediately = false; }
|
|
115
|
+
updateReasonRef.current = 'pagination';
|
|
109
116
|
setFilterInfoState(function (previous) {
|
|
110
117
|
var pageSizeNumber = Number(pageSize);
|
|
111
118
|
var page = getPageFromOffset_1.default(previous.offset, pageSizeNumber);
|
|
@@ -115,17 +122,21 @@ function useSimpleFilter(namespace, options) {
|
|
|
115
122
|
}, []),
|
|
116
123
|
setSort: react_1.useCallback(function (sort, shouldRunImmediately) {
|
|
117
124
|
if (shouldRunImmediately === void 0) { shouldRunImmediately = false; }
|
|
125
|
+
updateReasonRef.current = 'filter';
|
|
118
126
|
setFilterInfoState(function (previous) { return (__assign(__assign({}, previous), { sort: sort, lastRefreshAt: new Date().getTime(), shouldRunImmediately: shouldRunImmediately })); });
|
|
119
127
|
}, []),
|
|
120
128
|
setCursor: react_1.useCallback(function (cursor, shouldRunImmediately) {
|
|
121
129
|
if (shouldRunImmediately === void 0) { shouldRunImmediately = false; }
|
|
130
|
+
updateReasonRef.current = 'pagination';
|
|
122
131
|
setFilterInfoState(function (previous) { return (__assign(__assign({}, previous), { cursor: cursor, lastRefreshAt: new Date().getTime(), shouldRunImmediately: shouldRunImmediately })); });
|
|
123
132
|
}, []),
|
|
124
133
|
forceRefresh: react_1.useCallback(function (shouldRunImmediately) {
|
|
125
134
|
if (shouldRunImmediately === void 0) { shouldRunImmediately = false; }
|
|
135
|
+
updateReasonRef.current = 'filter';
|
|
126
136
|
setFilterInfoState(function (previous) { return (__assign(__assign({}, previous), { lastRefreshAt: new Date().getTime(), shouldRunImmediately: shouldRunImmediately })); });
|
|
127
137
|
}, []),
|
|
128
138
|
};
|
|
139
|
+
return api;
|
|
129
140
|
}
|
|
130
141
|
function buildDefaultFilterInfo(filterInfo) {
|
|
131
142
|
if (filterInfo === void 0) { filterInfo = {}; }
|