@trackunit/react-graphql-hooks 0.0.1-alpha-11d90c6ca6.0

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.
Files changed (44) hide show
  1. package/README.md +19 -0
  2. package/index.cjs.d.ts +1 -0
  3. package/index.cjs.js +283 -0
  4. package/index.esm.js +257 -0
  5. package/package.json +17 -0
  6. package/src/index.d.ts +3 -0
  7. package/src/translation.d.ts +34 -0
  8. package/src/unionEdgesByNodeKey.d.ts +12 -0
  9. package/src/usePaginationQuery.d.ts +60 -0
  10. package/src/useRelayPagination.d.ts +41 -0
  11. package/translation.cjs.js +7 -0
  12. package/translation.cjs10.js +7 -0
  13. package/translation.cjs11.js +7 -0
  14. package/translation.cjs12.js +7 -0
  15. package/translation.cjs13.js +7 -0
  16. package/translation.cjs14.js +7 -0
  17. package/translation.cjs15.js +7 -0
  18. package/translation.cjs16.js +7 -0
  19. package/translation.cjs17.js +7 -0
  20. package/translation.cjs2.js +7 -0
  21. package/translation.cjs3.js +7 -0
  22. package/translation.cjs4.js +7 -0
  23. package/translation.cjs5.js +7 -0
  24. package/translation.cjs6.js +7 -0
  25. package/translation.cjs7.js +7 -0
  26. package/translation.cjs8.js +7 -0
  27. package/translation.cjs9.js +7 -0
  28. package/translation.esm.js +5 -0
  29. package/translation.esm10.js +5 -0
  30. package/translation.esm11.js +5 -0
  31. package/translation.esm12.js +5 -0
  32. package/translation.esm13.js +5 -0
  33. package/translation.esm14.js +5 -0
  34. package/translation.esm15.js +5 -0
  35. package/translation.esm16.js +5 -0
  36. package/translation.esm17.js +5 -0
  37. package/translation.esm2.js +5 -0
  38. package/translation.esm3.js +5 -0
  39. package/translation.esm4.js +5 -0
  40. package/translation.esm5.js +5 -0
  41. package/translation.esm6.js +5 -0
  42. package/translation.esm7.js +5 -0
  43. package/translation.esm8.js +5 -0
  44. package/translation.esm9.js +5 -0
package/README.md ADDED
@@ -0,0 +1,19 @@
1
+ > **⚠️ Beta**
2
+ >
3
+ > This is a beta version and subject to change without notice.
4
+
5
+ # Trackunit GraphQL Hooks
6
+ The purpose of this library is to contain the graphql-hooks library and its dependencies.
7
+ These are just helper hooks for making working with GraphQL easier.
8
+
9
+ For more info and a full guide on Iris App SDK Development, please visit our [Developer Hub](https://developers.trackunit.com/).
10
+
11
+ ## Development
12
+ At this point this library is only developed by Trackunit Employees.
13
+ For development related information see the [development readme](https://github.com/Trackunit/manager/blob/master/libs/react/components/DEVELOPMENT.md).
14
+
15
+ ## Trackunit
16
+ This package was developed by Trackunit ApS.
17
+ Trackunit is the leading SaaS-based IoT solution for the construction industry, offering an ecosystem of hardware, fleet management software & telematics.
18
+
19
+ ![The Trackunit logo](https://trackunit.com/wp-content/uploads/2022/03/top-logo.svg)
package/index.cjs.d.ts ADDED
@@ -0,0 +1 @@
1
+ export * from "./src/index";
package/index.cjs.js ADDED
@@ -0,0 +1,283 @@
1
+ 'use strict';
2
+
3
+ Object.defineProperty(exports, '__esModule', { value: true });
4
+
5
+ require('react/jsx-runtime');
6
+ var i18nLibraryTranslation = require('@trackunit/i18n-library-translation');
7
+ var sharedUtils = require('@trackunit/shared-utils');
8
+ var ApolloClient = require('@apollo/client');
9
+ var react = require('react');
10
+
11
+ function _interopNamespace(e) {
12
+ if (e && e.__esModule) return e;
13
+ var n = Object.create(null);
14
+ if (e) {
15
+ Object.keys(e).forEach(function (k) {
16
+ if (k !== 'default') {
17
+ var d = Object.getOwnPropertyDescriptor(e, k);
18
+ Object.defineProperty(n, k, d.get ? d : {
19
+ enumerable: true,
20
+ get: function () { return e[k]; }
21
+ });
22
+ }
23
+ });
24
+ }
25
+ n["default"] = e;
26
+ return Object.freeze(n);
27
+ }
28
+
29
+ var ApolloClient__namespace = /*#__PURE__*/_interopNamespace(ApolloClient);
30
+
31
+ var defaultTranslations = {
32
+
33
+ };
34
+
35
+ /** The translation namespace for this library */
36
+ const namespace = "react.graphql-hooks";
37
+ /**
38
+ * The TranslationResource for this Library.
39
+ * Holds lazy loaded imports for all languages supported by the library.
40
+ *
41
+ * This is used to register the translations for this library before initializing i18next.
42
+ */
43
+ const translations = {
44
+ ns: namespace,
45
+ default: defaultTranslations,
46
+ languages: {
47
+ de: () => Promise.resolve().then(function () { return require('./translation.cjs.js'); }),
48
+ da: () => Promise.resolve().then(function () { return require('./translation.cjs2.js'); }),
49
+ cs: () => Promise.resolve().then(function () { return require('./translation.cjs3.js'); }),
50
+ nl: () => Promise.resolve().then(function () { return require('./translation.cjs4.js'); }),
51
+ fr: () => Promise.resolve().then(function () { return require('./translation.cjs5.js'); }),
52
+ fi: () => Promise.resolve().then(function () { return require('./translation.cjs6.js'); }),
53
+ hu: () => Promise.resolve().then(function () { return require('./translation.cjs7.js'); }),
54
+ it: () => Promise.resolve().then(function () { return require('./translation.cjs8.js'); }),
55
+ nb: () => Promise.resolve().then(function () { return require('./translation.cjs9.js'); }),
56
+ pl: () => Promise.resolve().then(function () { return require('./translation.cjs10.js'); }),
57
+ pt: () => Promise.resolve().then(function () { return require('./translation.cjs11.js'); }),
58
+ ru: () => Promise.resolve().then(function () { return require('./translation.cjs12.js'); }),
59
+ ro: () => Promise.resolve().then(function () { return require('./translation.cjs13.js'); }),
60
+ es: () => Promise.resolve().then(function () { return require('./translation.cjs14.js'); }),
61
+ sv: () => Promise.resolve().then(function () { return require('./translation.cjs15.js'); }),
62
+ ja: () => Promise.resolve().then(function () { return require('./translation.cjs16.js'); }),
63
+ th: () => Promise.resolve().then(function () { return require('./translation.cjs17.js'); }),
64
+ },
65
+ };
66
+ /**
67
+ * Registers the translations for this library
68
+ */
69
+ const setupLibraryTranslations = () => {
70
+ i18nLibraryTranslation.registerTranslations(translations);
71
+ };
72
+
73
+ /**
74
+ * Creates a union of two arrays of edges by a key on the node.
75
+ *
76
+ * @template TEdge
77
+ * @param key The key to use to determine uniqueness
78
+ * @param previousEdges The previous array of edges to merge with the new array
79
+ * @param newEdges The new array of edges to merge with the previous array
80
+ * @returns {TEdge[]} A new array with the edges from the previous array and the new array, but only if the item's key is unique.
81
+ */
82
+ const unionEdgesByNodeKey = (key, previousEdges, newEdges) => {
83
+ const previousIds = (previousEdges === null || previousEdges === void 0 ? void 0 : previousEdges.map(edge => edge.node[key])) || [];
84
+ const newIds = (newEdges === null || newEdges === void 0 ? void 0 : newEdges.map(edge => edge.node[key])) || [];
85
+ const mergedIds = [...previousIds, ...newIds];
86
+ const uniqueIds = [...new Set(mergedIds)];
87
+ return uniqueIds
88
+ .map(id => {
89
+ const previousEdge = previousEdges === null || previousEdges === void 0 ? void 0 : previousEdges.find(edge => edge.node[key] === id);
90
+ const newEdge = newEdges === null || newEdges === void 0 ? void 0 : newEdges.find(edge => edge.node[key] === id);
91
+ return newEdge || previousEdge;
92
+ })
93
+ .filter(sharedUtils.truthy);
94
+ };
95
+
96
+ /**
97
+ * Custom hook for handling Relay pagination in tables.
98
+ *
99
+ * @param {RelayPaginationProps} props - The props object containing pagination configuration.
100
+ * @returns {RelayPaginationSupport} An object containing functions and state for managing Relay pagination.
101
+ */
102
+ const useRelayPagination = (props = { pageSize: 50 }) => {
103
+ const [variables, setVariables] = react.useState({ first: props.pageSize });
104
+ const [pageInfo, setPageInfo] = react.useState();
105
+ const [isLoading, setIsLoading] = react.useState(false);
106
+ const nextPage = react.useCallback(() => {
107
+ if (pageInfo === null || pageInfo === void 0 ? void 0 : pageInfo.hasNextPage) {
108
+ setVariables({
109
+ after: pageInfo.endCursor === null ? undefined : pageInfo.endCursor,
110
+ first: props.pageSize,
111
+ });
112
+ }
113
+ }, [pageInfo === null || pageInfo === void 0 ? void 0 : pageInfo.endCursor, pageInfo === null || pageInfo === void 0 ? void 0 : pageInfo.hasNextPage, props.pageSize]);
114
+ const previousPage = react.useCallback(() => {
115
+ if (pageInfo === null || pageInfo === void 0 ? void 0 : pageInfo.hasPreviousPage) {
116
+ setVariables({
117
+ before: pageInfo.startCursor === null ? undefined : pageInfo.startCursor,
118
+ last: props.pageSize,
119
+ });
120
+ }
121
+ }, [pageInfo === null || pageInfo === void 0 ? void 0 : pageInfo.hasPreviousPage, pageInfo === null || pageInfo === void 0 ? void 0 : pageInfo.startCursor, props.pageSize]);
122
+ const reset = react.useCallback(() => {
123
+ setVariables({
124
+ last: undefined,
125
+ before: undefined,
126
+ after: undefined,
127
+ first: props.pageSize,
128
+ });
129
+ if (props.onReset) {
130
+ props.onReset();
131
+ }
132
+ // eslint-disable-next-line react-hooks/exhaustive-deps
133
+ }, [props.onReset, props.pageSize]);
134
+ return react.useMemo(() => {
135
+ return {
136
+ variables,
137
+ table: {
138
+ nextPage,
139
+ previousPage,
140
+ isLoading,
141
+ setIsLoading,
142
+ reset,
143
+ pageInfo: pageInfo === null ? undefined : pageInfo,
144
+ setPageInfo,
145
+ },
146
+ };
147
+ }, [variables, nextPage, previousPage, isLoading, reset, pageInfo]);
148
+ };
149
+
150
+ /**
151
+ * This hook is used to fetch data from a GraphQL query and support pagination, it will help maintain the data and the pagination.
152
+ *
153
+ * Data will be the combined data from all pages, if you merge in the updateQuery.
154
+ *
155
+ * It will use pagination.fetchNext to fetch next page and call updateQuery where you can merge the data together.
156
+ *
157
+ * lastFetchedData is used to store the last fetched data, so you can update data according to last page.
158
+ *
159
+ * @example
160
+ *
161
+ * const {
162
+ * data,
163
+ * loading,
164
+ * pagination,
165
+ * lastFetchedData,
166
+ *} = usePaginationQuery({
167
+ * query: myLazyQuery, // <-- myLazyQuery is the graphql LazyQuery to run pagination for.
168
+ * updateQuery: (previous, newData) => {
169
+ * // Here you can use the unionEdgesByNodeKey utils to merge your edges together.
170
+ * if (newData?.assets?.edges) {
171
+ * return {
172
+ * data: {
173
+ * ...newData,
174
+ * assets: {
175
+ * ...newData.assets,
176
+ * edges: unionEdgesByNodeKey("id", previous?.assets?.edges, newData.assets.edges) || [],
177
+ * },
178
+ * },
179
+ * pageInfo: newData.assets.pageInfo,
180
+ * };
181
+ * }
182
+ * return { data: newData, pageInfo: newData.assets.pageInfo };
183
+ * },
184
+ * });
185
+ * @template TData
186
+ * @template TVariables
187
+ * @param {ApolloClient.TypedDocumentNode<TData, TVariables>} document - The GraphQL document (query or mutation).
188
+ * @param {PaginationQueryProps<TData, TVariables>} props - The properties for configuring the query. This includes the `updateQuery` function for merging new and existing data, and options for pagination such as `pageSize`. Also includes other lazy query hook options from Apollo Client.
189
+ * @returns {*} {PaginationQuery}
190
+ */
191
+ const usePaginationQuery = (document, props) => {
192
+ const [lastFetchedData, setLastFetchedData] = react.useState();
193
+ const [resetTrigger, setResetTrigger] = react.useState(0);
194
+ const [, { fetchMore, networkStatus }] = ApolloClient__namespace.useLazyQuery(document, Object.assign(Object.assign({}, props), { notifyOnNetworkStatusChange: true, onCompleted: () => {
195
+ if (networkStatus === ApolloClient__namespace.NetworkStatus.refetch) {
196
+ // trigger reset for refetchQueries for the provided document.
197
+ setResetTrigger(prev => prev + 1);
198
+ }
199
+ },
200
+ // This is safe since we have no cache
201
+ // and without it it will make a magic extra call to the query check this
202
+ // https://stackoverflow.com/questions/66441463/fetchmore-request-executed-twice-every-time
203
+ nextFetchPolicy: "network-only", initialFetchPolicy: "network-only" }));
204
+ const [data, setData] = react.useState();
205
+ const onReset = react.useCallback(() => {
206
+ setResetTrigger(prev => prev + 1);
207
+ }, []);
208
+ const { table: { setIsLoading, isLoading, setPageInfo, pageInfo, reset, nextPage, previousPage }, variables: { first, after }, } = useRelayPagination({
209
+ pageSize: props.pageSize || 50,
210
+ onReset,
211
+ });
212
+ const doFetchMore = react.useCallback((variables, prev) => {
213
+ setIsLoading(true);
214
+ const abortController = new AbortController();
215
+ /**
216
+ * To support pagination with page and pageSize, we need to convert the variables from first and after.
217
+ */
218
+ const fetchMoreVariables = Object.assign(Object.assign({}, props.variables), (props.variables && "page" in props.variables && "pageSize" in props.variables
219
+ ? // eslint-disable-next-line local-rules/no-typescript-assertion
220
+ { pageSize: variables.first, page: Number(variables.after) || 0 }
221
+ : Object.assign({}, variables)));
222
+ fetchMore({
223
+ variables: fetchMoreVariables,
224
+ updateQuery: (_, { fetchMoreResult }) => {
225
+ if (abortController.signal.aborted) {
226
+ // if prev does not hold any data we don't want to return it,
227
+ // since it will make the cache output an error to the console.
228
+ // https://github.com/apollographql/apollo-client/issues/8677
229
+ if (prev) {
230
+ // eslint-disable-next-line local-rules/no-typescript-assertion
231
+ return Object.keys(prev).length === 0 ? undefined : prev;
232
+ }
233
+ // eslint-disable-next-line local-rules/no-typescript-assertion
234
+ return undefined;
235
+ }
236
+ setLastFetchedData(fetchMoreResult);
237
+ const result = props.updateQuery(prev, fetchMoreResult);
238
+ setPageInfo(result.pageInfo || null);
239
+ setData(result.data);
240
+ setIsLoading(false);
241
+ return result.data;
242
+ },
243
+ });
244
+ return abortController;
245
+ }, [setIsLoading, props, fetchMore, setPageInfo]);
246
+ react.useEffect(() => {
247
+ setData(undefined);
248
+ }, [props.variables]);
249
+ react.useEffect(() => {
250
+ const abortController = doFetchMore({ first: first, last: undefined, before: undefined, after: undefined }, undefined);
251
+ return () => {
252
+ abortController.abort();
253
+ };
254
+ // eslint-disable-next-line react-hooks/exhaustive-deps
255
+ }, [props.variables, resetTrigger]);
256
+ react.useEffect(() => {
257
+ if (after) {
258
+ doFetchMore({ first, after }, data);
259
+ }
260
+ // eslint-disable-next-line react-hooks/exhaustive-deps
261
+ }, [after]);
262
+ return react.useMemo(() => {
263
+ return {
264
+ data,
265
+ loading: isLoading,
266
+ pagination: { setIsLoading, isLoading, setPageInfo, pageInfo, reset, nextPage, previousPage },
267
+ lastFetchedData,
268
+ };
269
+ }, [data, isLoading, setIsLoading, setPageInfo, pageInfo, reset, nextPage, previousPage, lastFetchedData]);
270
+ };
271
+
272
+ /*
273
+ * ----------------------------
274
+ * | SETUP TRANSLATIONS START |
275
+ * ----------------------------
276
+ * This import and function call is needed to register translations for this library.
277
+ * Do not remove this if this library has translations.
278
+ */
279
+ setupLibraryTranslations();
280
+
281
+ exports.unionEdgesByNodeKey = unionEdgesByNodeKey;
282
+ exports.usePaginationQuery = usePaginationQuery;
283
+ exports.useRelayPagination = useRelayPagination;
package/index.esm.js ADDED
@@ -0,0 +1,257 @@
1
+ import 'react/jsx-runtime';
2
+ import { registerTranslations } from '@trackunit/i18n-library-translation';
3
+ import { truthy } from '@trackunit/shared-utils';
4
+ import * as ApolloClient from '@apollo/client';
5
+ import { useState, useCallback, useMemo, useEffect } from 'react';
6
+
7
+ var defaultTranslations = {
8
+
9
+ };
10
+
11
+ /** The translation namespace for this library */
12
+ const namespace = "react.graphql-hooks";
13
+ /**
14
+ * The TranslationResource for this Library.
15
+ * Holds lazy loaded imports for all languages supported by the library.
16
+ *
17
+ * This is used to register the translations for this library before initializing i18next.
18
+ */
19
+ const translations = {
20
+ ns: namespace,
21
+ default: defaultTranslations,
22
+ languages: {
23
+ de: () => import('./translation.esm.js'),
24
+ da: () => import('./translation.esm2.js'),
25
+ cs: () => import('./translation.esm3.js'),
26
+ nl: () => import('./translation.esm4.js'),
27
+ fr: () => import('./translation.esm5.js'),
28
+ fi: () => import('./translation.esm6.js'),
29
+ hu: () => import('./translation.esm7.js'),
30
+ it: () => import('./translation.esm8.js'),
31
+ nb: () => import('./translation.esm9.js'),
32
+ pl: () => import('./translation.esm10.js'),
33
+ pt: () => import('./translation.esm11.js'),
34
+ ru: () => import('./translation.esm12.js'),
35
+ ro: () => import('./translation.esm13.js'),
36
+ es: () => import('./translation.esm14.js'),
37
+ sv: () => import('./translation.esm15.js'),
38
+ ja: () => import('./translation.esm16.js'),
39
+ th: () => import('./translation.esm17.js'),
40
+ },
41
+ };
42
+ /**
43
+ * Registers the translations for this library
44
+ */
45
+ const setupLibraryTranslations = () => {
46
+ registerTranslations(translations);
47
+ };
48
+
49
+ /**
50
+ * Creates a union of two arrays of edges by a key on the node.
51
+ *
52
+ * @template TEdge
53
+ * @param key The key to use to determine uniqueness
54
+ * @param previousEdges The previous array of edges to merge with the new array
55
+ * @param newEdges The new array of edges to merge with the previous array
56
+ * @returns {TEdge[]} A new array with the edges from the previous array and the new array, but only if the item's key is unique.
57
+ */
58
+ const unionEdgesByNodeKey = (key, previousEdges, newEdges) => {
59
+ const previousIds = (previousEdges === null || previousEdges === void 0 ? void 0 : previousEdges.map(edge => edge.node[key])) || [];
60
+ const newIds = (newEdges === null || newEdges === void 0 ? void 0 : newEdges.map(edge => edge.node[key])) || [];
61
+ const mergedIds = [...previousIds, ...newIds];
62
+ const uniqueIds = [...new Set(mergedIds)];
63
+ return uniqueIds
64
+ .map(id => {
65
+ const previousEdge = previousEdges === null || previousEdges === void 0 ? void 0 : previousEdges.find(edge => edge.node[key] === id);
66
+ const newEdge = newEdges === null || newEdges === void 0 ? void 0 : newEdges.find(edge => edge.node[key] === id);
67
+ return newEdge || previousEdge;
68
+ })
69
+ .filter(truthy);
70
+ };
71
+
72
+ /**
73
+ * Custom hook for handling Relay pagination in tables.
74
+ *
75
+ * @param {RelayPaginationProps} props - The props object containing pagination configuration.
76
+ * @returns {RelayPaginationSupport} An object containing functions and state for managing Relay pagination.
77
+ */
78
+ const useRelayPagination = (props = { pageSize: 50 }) => {
79
+ const [variables, setVariables] = useState({ first: props.pageSize });
80
+ const [pageInfo, setPageInfo] = useState();
81
+ const [isLoading, setIsLoading] = useState(false);
82
+ const nextPage = useCallback(() => {
83
+ if (pageInfo === null || pageInfo === void 0 ? void 0 : pageInfo.hasNextPage) {
84
+ setVariables({
85
+ after: pageInfo.endCursor === null ? undefined : pageInfo.endCursor,
86
+ first: props.pageSize,
87
+ });
88
+ }
89
+ }, [pageInfo === null || pageInfo === void 0 ? void 0 : pageInfo.endCursor, pageInfo === null || pageInfo === void 0 ? void 0 : pageInfo.hasNextPage, props.pageSize]);
90
+ const previousPage = useCallback(() => {
91
+ if (pageInfo === null || pageInfo === void 0 ? void 0 : pageInfo.hasPreviousPage) {
92
+ setVariables({
93
+ before: pageInfo.startCursor === null ? undefined : pageInfo.startCursor,
94
+ last: props.pageSize,
95
+ });
96
+ }
97
+ }, [pageInfo === null || pageInfo === void 0 ? void 0 : pageInfo.hasPreviousPage, pageInfo === null || pageInfo === void 0 ? void 0 : pageInfo.startCursor, props.pageSize]);
98
+ const reset = useCallback(() => {
99
+ setVariables({
100
+ last: undefined,
101
+ before: undefined,
102
+ after: undefined,
103
+ first: props.pageSize,
104
+ });
105
+ if (props.onReset) {
106
+ props.onReset();
107
+ }
108
+ // eslint-disable-next-line react-hooks/exhaustive-deps
109
+ }, [props.onReset, props.pageSize]);
110
+ return useMemo(() => {
111
+ return {
112
+ variables,
113
+ table: {
114
+ nextPage,
115
+ previousPage,
116
+ isLoading,
117
+ setIsLoading,
118
+ reset,
119
+ pageInfo: pageInfo === null ? undefined : pageInfo,
120
+ setPageInfo,
121
+ },
122
+ };
123
+ }, [variables, nextPage, previousPage, isLoading, reset, pageInfo]);
124
+ };
125
+
126
+ /**
127
+ * This hook is used to fetch data from a GraphQL query and support pagination, it will help maintain the data and the pagination.
128
+ *
129
+ * Data will be the combined data from all pages, if you merge in the updateQuery.
130
+ *
131
+ * It will use pagination.fetchNext to fetch next page and call updateQuery where you can merge the data together.
132
+ *
133
+ * lastFetchedData is used to store the last fetched data, so you can update data according to last page.
134
+ *
135
+ * @example
136
+ *
137
+ * const {
138
+ * data,
139
+ * loading,
140
+ * pagination,
141
+ * lastFetchedData,
142
+ *} = usePaginationQuery({
143
+ * query: myLazyQuery, // <-- myLazyQuery is the graphql LazyQuery to run pagination for.
144
+ * updateQuery: (previous, newData) => {
145
+ * // Here you can use the unionEdgesByNodeKey utils to merge your edges together.
146
+ * if (newData?.assets?.edges) {
147
+ * return {
148
+ * data: {
149
+ * ...newData,
150
+ * assets: {
151
+ * ...newData.assets,
152
+ * edges: unionEdgesByNodeKey("id", previous?.assets?.edges, newData.assets.edges) || [],
153
+ * },
154
+ * },
155
+ * pageInfo: newData.assets.pageInfo,
156
+ * };
157
+ * }
158
+ * return { data: newData, pageInfo: newData.assets.pageInfo };
159
+ * },
160
+ * });
161
+ * @template TData
162
+ * @template TVariables
163
+ * @param {ApolloClient.TypedDocumentNode<TData, TVariables>} document - The GraphQL document (query or mutation).
164
+ * @param {PaginationQueryProps<TData, TVariables>} props - The properties for configuring the query. This includes the `updateQuery` function for merging new and existing data, and options for pagination such as `pageSize`. Also includes other lazy query hook options from Apollo Client.
165
+ * @returns {*} {PaginationQuery}
166
+ */
167
+ const usePaginationQuery = (document, props) => {
168
+ const [lastFetchedData, setLastFetchedData] = useState();
169
+ const [resetTrigger, setResetTrigger] = useState(0);
170
+ const [, { fetchMore, networkStatus }] = ApolloClient.useLazyQuery(document, Object.assign(Object.assign({}, props), { notifyOnNetworkStatusChange: true, onCompleted: () => {
171
+ if (networkStatus === ApolloClient.NetworkStatus.refetch) {
172
+ // trigger reset for refetchQueries for the provided document.
173
+ setResetTrigger(prev => prev + 1);
174
+ }
175
+ },
176
+ // This is safe since we have no cache
177
+ // and without it it will make a magic extra call to the query check this
178
+ // https://stackoverflow.com/questions/66441463/fetchmore-request-executed-twice-every-time
179
+ nextFetchPolicy: "network-only", initialFetchPolicy: "network-only" }));
180
+ const [data, setData] = useState();
181
+ const onReset = useCallback(() => {
182
+ setResetTrigger(prev => prev + 1);
183
+ }, []);
184
+ const { table: { setIsLoading, isLoading, setPageInfo, pageInfo, reset, nextPage, previousPage }, variables: { first, after }, } = useRelayPagination({
185
+ pageSize: props.pageSize || 50,
186
+ onReset,
187
+ });
188
+ const doFetchMore = useCallback((variables, prev) => {
189
+ setIsLoading(true);
190
+ const abortController = new AbortController();
191
+ /**
192
+ * To support pagination with page and pageSize, we need to convert the variables from first and after.
193
+ */
194
+ const fetchMoreVariables = Object.assign(Object.assign({}, props.variables), (props.variables && "page" in props.variables && "pageSize" in props.variables
195
+ ? // eslint-disable-next-line local-rules/no-typescript-assertion
196
+ { pageSize: variables.first, page: Number(variables.after) || 0 }
197
+ : Object.assign({}, variables)));
198
+ fetchMore({
199
+ variables: fetchMoreVariables,
200
+ updateQuery: (_, { fetchMoreResult }) => {
201
+ if (abortController.signal.aborted) {
202
+ // if prev does not hold any data we don't want to return it,
203
+ // since it will make the cache output an error to the console.
204
+ // https://github.com/apollographql/apollo-client/issues/8677
205
+ if (prev) {
206
+ // eslint-disable-next-line local-rules/no-typescript-assertion
207
+ return Object.keys(prev).length === 0 ? undefined : prev;
208
+ }
209
+ // eslint-disable-next-line local-rules/no-typescript-assertion
210
+ return undefined;
211
+ }
212
+ setLastFetchedData(fetchMoreResult);
213
+ const result = props.updateQuery(prev, fetchMoreResult);
214
+ setPageInfo(result.pageInfo || null);
215
+ setData(result.data);
216
+ setIsLoading(false);
217
+ return result.data;
218
+ },
219
+ });
220
+ return abortController;
221
+ }, [setIsLoading, props, fetchMore, setPageInfo]);
222
+ useEffect(() => {
223
+ setData(undefined);
224
+ }, [props.variables]);
225
+ useEffect(() => {
226
+ const abortController = doFetchMore({ first: first, last: undefined, before: undefined, after: undefined }, undefined);
227
+ return () => {
228
+ abortController.abort();
229
+ };
230
+ // eslint-disable-next-line react-hooks/exhaustive-deps
231
+ }, [props.variables, resetTrigger]);
232
+ useEffect(() => {
233
+ if (after) {
234
+ doFetchMore({ first, after }, data);
235
+ }
236
+ // eslint-disable-next-line react-hooks/exhaustive-deps
237
+ }, [after]);
238
+ return useMemo(() => {
239
+ return {
240
+ data,
241
+ loading: isLoading,
242
+ pagination: { setIsLoading, isLoading, setPageInfo, pageInfo, reset, nextPage, previousPage },
243
+ lastFetchedData,
244
+ };
245
+ }, [data, isLoading, setIsLoading, setPageInfo, pageInfo, reset, nextPage, previousPage, lastFetchedData]);
246
+ };
247
+
248
+ /*
249
+ * ----------------------------
250
+ * | SETUP TRANSLATIONS START |
251
+ * ----------------------------
252
+ * This import and function call is needed to register translations for this library.
253
+ * Do not remove this if this library has translations.
254
+ */
255
+ setupLibraryTranslations();
256
+
257
+ export { unionEdgesByNodeKey, usePaginationQuery, useRelayPagination };
package/package.json ADDED
@@ -0,0 +1,17 @@
1
+ {
2
+ "name": "@trackunit/react-graphql-hooks",
3
+ "version": "0.0.1-alpha-11d90c6ca6.0",
4
+ "repository": "https://github.com/Trackunit/manager",
5
+ "license": "SEE LICENSE IN LICENSE.txt",
6
+ "engines": {
7
+ "node": ">=18.x"
8
+ },
9
+ "dependencies": {
10
+ "@apollo/client": "^3.7.10",
11
+ "react": "^18.2.0",
12
+ "@trackunit/i18n-library-translation": "*",
13
+ "@trackunit/shared-utils": "*"
14
+ },
15
+ "module": "./index.esm.js",
16
+ "main": "./index.cjs.js"
17
+ }
package/src/index.d.ts ADDED
@@ -0,0 +1,3 @@
1
+ export * from "./unionEdgesByNodeKey";
2
+ export * from "./usePaginationQuery";
3
+ export * from "./useRelayPagination";
@@ -0,0 +1,34 @@
1
+ /// <reference types="react" />
2
+ import { NamespaceTransProps, TransForLibs, TranslationResource } from "@trackunit/i18n-library-translation";
3
+ import defaultTranslations from "./locales/en/translation.json";
4
+ /** A type for all available translation keys in this library */
5
+ export type TranslationKeys = keyof typeof defaultTranslations;
6
+ /** The translation namespace for this library */
7
+ export declare const namespace = "react.graphql-hooks";
8
+ /**
9
+ * The TranslationResource for this Library.
10
+ * Holds lazy loaded imports for all languages supported by the library.
11
+ *
12
+ * This is used to register the translations for this library before initializing i18next.
13
+ */
14
+ export declare const translations: TranslationResource<TranslationKeys>;
15
+ /**
16
+ * Local useTranslation for this specific library
17
+ */
18
+ export declare const useTranslation: () => [TransForLibs<never>, import("i18next").i18n, boolean] & {
19
+ t: TransForLibs<never>;
20
+ i18n: import("i18next").i18n;
21
+ ready: boolean;
22
+ };
23
+ /**
24
+ * Type of the t function for the local useTranslation for this specific library
25
+ */
26
+ export type TranslationFunction = TransForLibs<TranslationKeys>;
27
+ /**
28
+ * Trans for this specific library.
29
+ */
30
+ export declare const Trans: (props: NamespaceTransProps<TranslationKeys>) => JSX.Element;
31
+ /**
32
+ * Registers the translations for this library
33
+ */
34
+ export declare const setupLibraryTranslations: () => void;
@@ -0,0 +1,12 @@
1
+ /**
2
+ * Creates a union of two arrays of edges by a key on the node.
3
+ *
4
+ * @template TEdge
5
+ * @param key The key to use to determine uniqueness
6
+ * @param previousEdges The previous array of edges to merge with the new array
7
+ * @param newEdges The new array of edges to merge with the previous array
8
+ * @returns {TEdge[]} A new array with the edges from the previous array and the new array, but only if the item's key is unique.
9
+ */
10
+ export declare const unionEdgesByNodeKey: <TEdge extends {
11
+ node: { [K in keyof TEdge["node"]]: unknown; };
12
+ }>(key: keyof TEdge["node"], previousEdges: TEdge[] | null | undefined, newEdges: TEdge[] | null | undefined) => TEdge[];
@@ -0,0 +1,60 @@
1
+ import * as ApolloClient from "@apollo/client";
2
+ import { RelayPageInfo, RelayTableSupport } from "./useRelayPagination";
3
+ /**
4
+ * This type is used to return the data from the query, the pagination object and the last fetched data.
5
+ */
6
+ export type PaginationQuery<TData> = {
7
+ data: TData | undefined;
8
+ loading: boolean;
9
+ lastFetchedData?: TData;
10
+ pagination: RelayTableSupport;
11
+ };
12
+ export type PaginationQueryProps<TData, TVariables extends ApolloClient.OperationVariables = ApolloClient.OperationVariables> = {
13
+ updateQuery: (previous: TData | undefined, newData: TData) => {
14
+ data: TData;
15
+ pageInfo?: RelayPageInfo | null;
16
+ };
17
+ pageSize?: number;
18
+ } & ApolloClient.LazyQueryHookOptions<TData, TVariables>;
19
+ /**
20
+ * This hook is used to fetch data from a GraphQL query and support pagination, it will help maintain the data and the pagination.
21
+ *
22
+ * Data will be the combined data from all pages, if you merge in the updateQuery.
23
+ *
24
+ * It will use pagination.fetchNext to fetch next page and call updateQuery where you can merge the data together.
25
+ *
26
+ * lastFetchedData is used to store the last fetched data, so you can update data according to last page.
27
+ *
28
+ * @example
29
+ *
30
+ * const {
31
+ * data,
32
+ * loading,
33
+ * pagination,
34
+ * lastFetchedData,
35
+ *} = usePaginationQuery({
36
+ * query: myLazyQuery, // <-- myLazyQuery is the graphql LazyQuery to run pagination for.
37
+ * updateQuery: (previous, newData) => {
38
+ * // Here you can use the unionEdgesByNodeKey utils to merge your edges together.
39
+ * if (newData?.assets?.edges) {
40
+ * return {
41
+ * data: {
42
+ * ...newData,
43
+ * assets: {
44
+ * ...newData.assets,
45
+ * edges: unionEdgesByNodeKey("id", previous?.assets?.edges, newData.assets.edges) || [],
46
+ * },
47
+ * },
48
+ * pageInfo: newData.assets.pageInfo,
49
+ * };
50
+ * }
51
+ * return { data: newData, pageInfo: newData.assets.pageInfo };
52
+ * },
53
+ * });
54
+ * @template TData
55
+ * @template TVariables
56
+ * @param {ApolloClient.TypedDocumentNode<TData, TVariables>} document - The GraphQL document (query or mutation).
57
+ * @param {PaginationQueryProps<TData, TVariables>} props - The properties for configuring the query. This includes the `updateQuery` function for merging new and existing data, and options for pagination such as `pageSize`. Also includes other lazy query hook options from Apollo Client.
58
+ * @returns {*} {PaginationQuery}
59
+ */
60
+ export declare const usePaginationQuery: <TData, TVariables extends ApolloClient.OperationVariables>(document: ApolloClient.TypedDocumentNode<TData, TVariables>, props: PaginationQueryProps<TData, TVariables>) => PaginationQuery<TData>;
@@ -0,0 +1,41 @@
1
+ import { Dispatch, SetStateAction } from "react";
2
+ export interface RelayPaginationProps {
3
+ pageSize?: number;
4
+ onReset?: () => void;
5
+ }
6
+ export interface RelayPageInfo {
7
+ count?: number | null;
8
+ endCursor?: string | null;
9
+ hasNextPage?: boolean;
10
+ hasPreviousPage?: boolean;
11
+ startCursor?: string | null;
12
+ }
13
+ export interface RelayPagination {
14
+ nextPage: () => void;
15
+ previousPage: () => void;
16
+ pageInfo?: RelayPageInfo;
17
+ isLoading: boolean;
18
+ }
19
+ export interface RelayPaginationQueryVariables {
20
+ first?: number | null;
21
+ last?: number | null;
22
+ before?: string | null;
23
+ after?: string | null;
24
+ }
25
+ export interface RelayTableSupport extends RelayPagination {
26
+ isLoading: boolean;
27
+ setIsLoading: Dispatch<SetStateAction<boolean>>;
28
+ reset: () => void;
29
+ setPageInfo: Dispatch<SetStateAction<RelayPageInfo | null | undefined>>;
30
+ }
31
+ export interface RelayPaginationSupport {
32
+ variables: RelayPaginationQueryVariables;
33
+ table: RelayTableSupport;
34
+ }
35
+ /**
36
+ * Custom hook for handling Relay pagination in tables.
37
+ *
38
+ * @param {RelayPaginationProps} props - The props object containing pagination configuration.
39
+ * @returns {RelayPaginationSupport} An object containing functions and state for managing Relay pagination.
40
+ */
41
+ export declare const useRelayPagination: (props?: RelayPaginationProps) => RelayPaginationSupport;
@@ -0,0 +1,7 @@
1
+ 'use strict';
2
+
3
+ var translation = {
4
+
5
+ };
6
+
7
+ exports["default"] = translation;
@@ -0,0 +1,7 @@
1
+ 'use strict';
2
+
3
+ var translation = {
4
+
5
+ };
6
+
7
+ exports["default"] = translation;
@@ -0,0 +1,7 @@
1
+ 'use strict';
2
+
3
+ var translation = {
4
+
5
+ };
6
+
7
+ exports["default"] = translation;
@@ -0,0 +1,7 @@
1
+ 'use strict';
2
+
3
+ var translation = {
4
+
5
+ };
6
+
7
+ exports["default"] = translation;
@@ -0,0 +1,7 @@
1
+ 'use strict';
2
+
3
+ var translation = {
4
+
5
+ };
6
+
7
+ exports["default"] = translation;
@@ -0,0 +1,7 @@
1
+ 'use strict';
2
+
3
+ var translation = {
4
+
5
+ };
6
+
7
+ exports["default"] = translation;
@@ -0,0 +1,7 @@
1
+ 'use strict';
2
+
3
+ var translation = {
4
+
5
+ };
6
+
7
+ exports["default"] = translation;
@@ -0,0 +1,7 @@
1
+ 'use strict';
2
+
3
+ var translation = {
4
+
5
+ };
6
+
7
+ exports["default"] = translation;
@@ -0,0 +1,7 @@
1
+ 'use strict';
2
+
3
+ var translation = {
4
+
5
+ };
6
+
7
+ exports["default"] = translation;
@@ -0,0 +1,7 @@
1
+ 'use strict';
2
+
3
+ var translation = {
4
+
5
+ };
6
+
7
+ exports["default"] = translation;
@@ -0,0 +1,7 @@
1
+ 'use strict';
2
+
3
+ var translation = {
4
+
5
+ };
6
+
7
+ exports["default"] = translation;
@@ -0,0 +1,7 @@
1
+ 'use strict';
2
+
3
+ var translation = {
4
+
5
+ };
6
+
7
+ exports["default"] = translation;
@@ -0,0 +1,7 @@
1
+ 'use strict';
2
+
3
+ var translation = {
4
+
5
+ };
6
+
7
+ exports["default"] = translation;
@@ -0,0 +1,7 @@
1
+ 'use strict';
2
+
3
+ var translation = {
4
+
5
+ };
6
+
7
+ exports["default"] = translation;
@@ -0,0 +1,7 @@
1
+ 'use strict';
2
+
3
+ var translation = {
4
+
5
+ };
6
+
7
+ exports["default"] = translation;
@@ -0,0 +1,7 @@
1
+ 'use strict';
2
+
3
+ var translation = {
4
+
5
+ };
6
+
7
+ exports["default"] = translation;
@@ -0,0 +1,7 @@
1
+ 'use strict';
2
+
3
+ var translation = {
4
+
5
+ };
6
+
7
+ exports["default"] = translation;
@@ -0,0 +1,5 @@
1
+ var translation = {
2
+
3
+ };
4
+
5
+ export { translation as default };
@@ -0,0 +1,5 @@
1
+ var translation = {
2
+
3
+ };
4
+
5
+ export { translation as default };
@@ -0,0 +1,5 @@
1
+ var translation = {
2
+
3
+ };
4
+
5
+ export { translation as default };
@@ -0,0 +1,5 @@
1
+ var translation = {
2
+
3
+ };
4
+
5
+ export { translation as default };
@@ -0,0 +1,5 @@
1
+ var translation = {
2
+
3
+ };
4
+
5
+ export { translation as default };
@@ -0,0 +1,5 @@
1
+ var translation = {
2
+
3
+ };
4
+
5
+ export { translation as default };
@@ -0,0 +1,5 @@
1
+ var translation = {
2
+
3
+ };
4
+
5
+ export { translation as default };
@@ -0,0 +1,5 @@
1
+ var translation = {
2
+
3
+ };
4
+
5
+ export { translation as default };
@@ -0,0 +1,5 @@
1
+ var translation = {
2
+
3
+ };
4
+
5
+ export { translation as default };
@@ -0,0 +1,5 @@
1
+ var translation = {
2
+
3
+ };
4
+
5
+ export { translation as default };
@@ -0,0 +1,5 @@
1
+ var translation = {
2
+
3
+ };
4
+
5
+ export { translation as default };
@@ -0,0 +1,5 @@
1
+ var translation = {
2
+
3
+ };
4
+
5
+ export { translation as default };
@@ -0,0 +1,5 @@
1
+ var translation = {
2
+
3
+ };
4
+
5
+ export { translation as default };
@@ -0,0 +1,5 @@
1
+ var translation = {
2
+
3
+ };
4
+
5
+ export { translation as default };
@@ -0,0 +1,5 @@
1
+ var translation = {
2
+
3
+ };
4
+
5
+ export { translation as default };
@@ -0,0 +1,5 @@
1
+ var translation = {
2
+
3
+ };
4
+
5
+ export { translation as default };
@@ -0,0 +1,5 @@
1
+ var translation = {
2
+
3
+ };
4
+
5
+ export { translation as default };