@promoboxx/use-filter 1.8.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 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
+ ```
@@ -1,26 +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;
13
+ /**
14
+ * Called whenever the filter changes.
15
+ */
5
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
- export declare type UseFilterUpdateReason = 'initial' | 'filter' | 'pagination';
9
- declare function useFilter<TFilter, TResult>(namespace: string, options: UseFilterOptions<TFilter, TResult>): {
22
+ export interface FilterApi<TFilter, TResult> {
23
+ /**
24
+ * Whether the system is debouncing or waiting for your `onChange` to finish.
25
+ */
10
26
  isLoading: boolean;
27
+ /**
28
+ * Any cached data for this filter.
29
+ */
11
30
  data: TResult | null | undefined;
12
- doesFilterExist: boolean;
31
+ /**
32
+ * Access to the filter and its metadata.
33
+ */
13
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
+ */
14
43
  updateReason: UseFilterUpdateReason;
44
+ /**
45
+ * Update one or more values in your filter.
46
+ */
15
47
  updateFilter: (filter: Partial<TFilter>, shouldRunImmediately?: boolean) => void;
48
+ /**
49
+ * Resets the filter back to `defaultFilterInfo`.
50
+ */
16
51
  resetFilter: (shouldRunImmediately?: boolean) => void;
52
+ /**
53
+ * Changes the offset. Will update `page`.
54
+ */
17
55
  setOffset: (offset: number | string, shouldRunImmediately?: boolean) => void;
56
+ /**
57
+ * Changes the page. Will update `offset`.
58
+ */
18
59
  setPage: (page: number | string, shouldRunImmediately?: boolean) => void;
60
+ /**
61
+ * Changes the page size.
62
+ */
19
63
  setPageSize: (pageSize: number | string, shouldRunImmediately?: boolean) => void;
64
+ /**
65
+ * Change the sort method.
66
+ */
20
67
  setSort: (sort: string | undefined, shouldRunImmediately?: boolean) => void;
21
- setCursor: (cursor: string | null | undefined, shouldRunImmediately?: boolean) => void;
68
+ /**
69
+ * Changes the cursor.
70
+ */
71
+ setCursor: (cursor: string | null | undefined) => void;
72
+ /**
73
+ * Forces a refresh of the filter.
74
+ */
22
75
  forceRefresh: (shouldRunImmediately?: boolean) => void;
23
- };
76
+ }
77
+ export declare type UseFilterUpdateReason = 'initial' | 'filter' | 'pagination';
78
+ declare function useFilter<TFilter, TResult>(namespace: string, options: UseFilterOptions<TFilter, TResult>): FilterApi<TFilter, TResult>;
24
79
  export default useFilter;
25
80
  export interface FilterInfo<TFilter> {
26
81
  filter: TFilter;
package/dist/useFilter.js CHANGED
@@ -149,7 +149,7 @@ function useFilter(namespace, options) {
149
149
  }
150
150
  };
151
151
  }, [filterInfo, namespace]);
152
- return {
152
+ var api = {
153
153
  isLoading: isLoading,
154
154
  data: data,
155
155
  doesFilterExist: doesFilterExist.current,
@@ -226,6 +226,7 @@ function useFilter(namespace, options) {
226
226
  });
227
227
  }, []),
228
228
  };
229
+ return api;
229
230
  }
230
231
  exports.default = useFilter;
231
232
  function invariant(input, message) {
@@ -1,52 +1,83 @@
1
1
  import { UseFilterUpdateReason } from './useFilter';
2
- export interface SimpleFilterInfo<TFilter> {
3
- filter: TFilter;
4
- sort?: string;
5
- offset: number;
6
- page: number;
7
- pageSize: number;
8
- lastRefreshAt: number;
9
- cursor?: string | null;
10
- shouldRunImmediately: boolean;
11
- }
12
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
+ */
13
8
  defaultFilterInfo?: Partial<SimpleFilterInfo<TFilter>>;
9
+ /**
10
+ * In case you want to change the default debounce duration.
11
+ */
14
12
  debounceDuration?: number;
15
13
  }
16
- declare function useSimpleFilter<TFilter>(namespace: string, options: UseSimpleFilterOptions<TFilter>): {
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
+ */
17
19
  isLoading: boolean;
18
- filterInfo: {
19
- shouldRunImmediately: boolean;
20
- filter: TFilter;
21
- sort?: string | undefined;
22
- offset: number;
23
- page: number;
24
- pageSize: number;
25
- lastRefreshAt: number;
26
- cursor?: string | null | undefined;
27
- };
28
- debouncedFilterInfo: {
29
- shouldRunImmediately: boolean;
30
- filter: TFilter;
31
- sort?: string | undefined;
32
- offset: number;
33
- page: number;
34
- pageSize: number;
35
- lastRefreshAt: number;
36
- cursor?: string | null | undefined;
37
- };
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
+ */
38
31
  updateReason: UseFilterUpdateReason;
39
- updateFilter: (filter: Partial<TFilter>, shouldRunImmediately?: any) => void;
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
+ */
40
39
  pagingInfo: (total: string | number | null | undefined) => {
41
40
  totalResults: number;
42
41
  totalPages: number;
43
42
  };
43
+ /**
44
+ * Resets the filter back to `defaultFilterInfo`.
45
+ */
44
46
  resetFilter: (shouldRunImmediately?: boolean) => void;
47
+ /**
48
+ * Changes the offset. Will update `page`.
49
+ */
45
50
  setOffset: (offset: number | string, shouldRunImmediately?: boolean) => void;
51
+ /**
52
+ * Changes the page. Will update `offset`.
53
+ */
46
54
  setPage: (page: number | string, shouldRunImmediately?: boolean) => void;
55
+ /**
56
+ * Changes the page size.
57
+ */
47
58
  setPageSize: (pageSize: number | string, shouldRunImmediately?: boolean) => void;
59
+ /**
60
+ * Change the sort method.
61
+ */
48
62
  setSort: (sort: string, shouldRunImmediately?: boolean) => void;
63
+ /**
64
+ * Changes the cursor.
65
+ */
49
66
  setCursor: (cursor: string | null | undefined, shouldRunImmediately?: boolean) => void;
67
+ /**
68
+ * Forces a refresh of the filter.
69
+ */
50
70
  forceRefresh: (shouldRunImmediately?: boolean) => void;
51
- };
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>;
52
83
  export default useSimpleFilter;
@@ -66,7 +66,7 @@ function useSimpleFilter(namespace, options) {
66
66
  };
67
67
  }, [filterInfo]);
68
68
  var previousTotalRef = react_1.useRef(0);
69
- return {
69
+ var api = {
70
70
  isLoading: isLoading,
71
71
  filterInfo: filterInfo,
72
72
  debouncedFilterInfo: debouncedFilterInfo,
@@ -136,6 +136,7 @@ function useSimpleFilter(namespace, options) {
136
136
  setFilterInfoState(function (previous) { return (__assign(__assign({}, previous), { lastRefreshAt: new Date().getTime(), shouldRunImmediately: shouldRunImmediately })); });
137
137
  }, []),
138
138
  };
139
+ return api;
139
140
  }
140
141
  function buildDefaultFilterInfo(filterInfo) {
141
142
  if (filterInfo === void 0) { filterInfo = {}; }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@promoboxx/use-filter",
3
- "version": "1.8.0",
3
+ "version": "1.8.1",
4
4
  "description": "",
5
5
  "main": "dist/useFilter.js",
6
6
  "keywords": [],