@trackunit/react-graphql-hooks 2.3.4 → 2.3.6

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/index.cjs.js CHANGED
@@ -2,9 +2,9 @@
2
2
 
3
3
  require('react/jsx-runtime');
4
4
  var i18nLibraryTranslation = require('@trackunit/i18n-library-translation');
5
+ var react = require('react');
5
6
  var client = require('@apollo/client');
6
7
  var esToolkit = require('es-toolkit');
7
- var react = require('react');
8
8
  var reactComponents = require('@trackunit/react-components');
9
9
  var sharedUtils = require('@trackunit/shared-utils');
10
10
 
@@ -50,6 +50,80 @@ const setupLibraryTranslations = () => {
50
50
  i18nLibraryTranslation.registerTranslations(translations);
51
51
  };
52
52
 
53
+ const isRecord = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
54
+ const toRelayPageInfo = (pageInfo) => {
55
+ if (!isRecord(pageInfo)) {
56
+ return null;
57
+ }
58
+ return {
59
+ hasNextPage: pageInfo.hasNextPage === true,
60
+ hasPreviousPage: pageInfo.hasPreviousPage === true,
61
+ startCursor: typeof pageInfo.startCursor === "string" ? pageInfo.startCursor : null,
62
+ endCursor: typeof pageInfo.endCursor === "string" ? pageInfo.endCursor : null,
63
+ };
64
+ };
65
+ const readConnection = (data, rootField, connectionField) => {
66
+ if (!isRecord(data)) {
67
+ return null;
68
+ }
69
+ const root = data[rootField];
70
+ if (!isRecord(root)) {
71
+ return null;
72
+ }
73
+ const connection = root[connectionField];
74
+ return isRecord(connection) ? connection : null;
75
+ };
76
+ /**
77
+ * Appends a newly fetched Relay connection page to the accumulated one.
78
+ *
79
+ * Backs `useConnectionPageMerger`, which is how consumers reach this. A later page that comes back
80
+ * without `edges` (the field is nullable in most schemas) keeps everything accumulated so far
81
+ * instead of collapsing the list.
82
+ *
83
+ * @param rootField name of the field on the query root that holds the connection
84
+ * @param connectionField name of the connection field on the root field
85
+ * @param previous the accumulated query result, undefined on the first page
86
+ * @param newData the freshly fetched query result
87
+ * @returns {object} the merged result together with the newest page info
88
+ */
89
+ const mergeConnectionPages = (rootField, connectionField, previous, newData) => {
90
+ const newConnection = readConnection(newData, rootField, connectionField);
91
+ const previousConnection = readConnection(previous, rootField, connectionField);
92
+ const pageInfo = toRelayPageInfo(newConnection?.pageInfo);
93
+ const previousEdges = previousConnection?.edges;
94
+ const root = newData[rootField];
95
+ if (newConnection === null || !isRecord(root) || !Array.isArray(previousEdges)) {
96
+ return { data: newData, pageInfo };
97
+ }
98
+ const newEdges = Array.isArray(newConnection.edges) ? newConnection.edges : [];
99
+ return {
100
+ data: {
101
+ ...newData,
102
+ [rootField]: {
103
+ ...root,
104
+ [connectionField]: {
105
+ ...newConnection,
106
+ edges: [...previousEdges, ...newEdges],
107
+ },
108
+ },
109
+ },
110
+ pageInfo,
111
+ };
112
+ };
113
+
114
+ /**
115
+ * Builds a stable `updateQuery` for `usePaginationQuery` that appends each fetched page of a Relay
116
+ * connection to the accumulated one.
117
+ *
118
+ * Use it when the connection sits at a fixed `<rootField>.<connectionField>` path. A later page that
119
+ * comes back without `edges` keeps everything accumulated so far instead of collapsing the list.
120
+ *
121
+ * @param rootField name of the field on the query root that holds the connection
122
+ * @param connectionField name of the connection field on the root field
123
+ * @returns {Function} the `updateQuery` callback
124
+ */
125
+ const useConnectionPageMerger = (rootField, connectionField) => react.useCallback((previous, newData) => mergeConnectionPages(rootField, connectionField, previous, newData), [rootField, connectionField]);
126
+
53
127
  /**
54
128
  * A wrapper around Apollo Client's useLazyQuery that provides stable data by default.
55
129
  *
@@ -624,6 +698,7 @@ const useQuery = (document, options) => {
624
698
  */
625
699
  setupLibraryTranslations();
626
700
 
701
+ exports.useConnectionPageMerger = useConnectionPageMerger;
627
702
  exports.useLazyQuery = useLazyQuery;
628
703
  exports.usePaginationQuery = usePaginationQuery;
629
704
  exports.useQuery = useQuery;
package/index.esm.js CHANGED
@@ -1,8 +1,8 @@
1
1
  import 'react/jsx-runtime';
2
2
  import { registerTranslations } from '@trackunit/i18n-library-translation';
3
+ import { useCallback, useMemo, useState, useReducer, useRef, useEffect } from 'react';
3
4
  import { useLazyQuery as useLazyQuery$1, NetworkStatus, useQuery as useQuery$1 } from '@apollo/client';
4
5
  import { omit, isEqual } from 'es-toolkit';
5
- import { useMemo, useState, useReducer, useRef, useEffect, useCallback } from 'react';
6
6
  import { defaultPageSize, useRelayPagination, useWatch } from '@trackunit/react-components';
7
7
  import { truthy, objectKeys } from '@trackunit/shared-utils';
8
8
 
@@ -48,6 +48,80 @@ const setupLibraryTranslations = () => {
48
48
  registerTranslations(translations);
49
49
  };
50
50
 
51
+ const isRecord = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
52
+ const toRelayPageInfo = (pageInfo) => {
53
+ if (!isRecord(pageInfo)) {
54
+ return null;
55
+ }
56
+ return {
57
+ hasNextPage: pageInfo.hasNextPage === true,
58
+ hasPreviousPage: pageInfo.hasPreviousPage === true,
59
+ startCursor: typeof pageInfo.startCursor === "string" ? pageInfo.startCursor : null,
60
+ endCursor: typeof pageInfo.endCursor === "string" ? pageInfo.endCursor : null,
61
+ };
62
+ };
63
+ const readConnection = (data, rootField, connectionField) => {
64
+ if (!isRecord(data)) {
65
+ return null;
66
+ }
67
+ const root = data[rootField];
68
+ if (!isRecord(root)) {
69
+ return null;
70
+ }
71
+ const connection = root[connectionField];
72
+ return isRecord(connection) ? connection : null;
73
+ };
74
+ /**
75
+ * Appends a newly fetched Relay connection page to the accumulated one.
76
+ *
77
+ * Backs `useConnectionPageMerger`, which is how consumers reach this. A later page that comes back
78
+ * without `edges` (the field is nullable in most schemas) keeps everything accumulated so far
79
+ * instead of collapsing the list.
80
+ *
81
+ * @param rootField name of the field on the query root that holds the connection
82
+ * @param connectionField name of the connection field on the root field
83
+ * @param previous the accumulated query result, undefined on the first page
84
+ * @param newData the freshly fetched query result
85
+ * @returns {object} the merged result together with the newest page info
86
+ */
87
+ const mergeConnectionPages = (rootField, connectionField, previous, newData) => {
88
+ const newConnection = readConnection(newData, rootField, connectionField);
89
+ const previousConnection = readConnection(previous, rootField, connectionField);
90
+ const pageInfo = toRelayPageInfo(newConnection?.pageInfo);
91
+ const previousEdges = previousConnection?.edges;
92
+ const root = newData[rootField];
93
+ if (newConnection === null || !isRecord(root) || !Array.isArray(previousEdges)) {
94
+ return { data: newData, pageInfo };
95
+ }
96
+ const newEdges = Array.isArray(newConnection.edges) ? newConnection.edges : [];
97
+ return {
98
+ data: {
99
+ ...newData,
100
+ [rootField]: {
101
+ ...root,
102
+ [connectionField]: {
103
+ ...newConnection,
104
+ edges: [...previousEdges, ...newEdges],
105
+ },
106
+ },
107
+ },
108
+ pageInfo,
109
+ };
110
+ };
111
+
112
+ /**
113
+ * Builds a stable `updateQuery` for `usePaginationQuery` that appends each fetched page of a Relay
114
+ * connection to the accumulated one.
115
+ *
116
+ * Use it when the connection sits at a fixed `<rootField>.<connectionField>` path. A later page that
117
+ * comes back without `edges` keeps everything accumulated so far instead of collapsing the list.
118
+ *
119
+ * @param rootField name of the field on the query root that holds the connection
120
+ * @param connectionField name of the connection field on the root field
121
+ * @returns {Function} the `updateQuery` callback
122
+ */
123
+ const useConnectionPageMerger = (rootField, connectionField) => useCallback((previous, newData) => mergeConnectionPages(rootField, connectionField, previous, newData), [rootField, connectionField]);
124
+
51
125
  /**
52
126
  * A wrapper around Apollo Client's useLazyQuery that provides stable data by default.
53
127
  *
@@ -622,4 +696,4 @@ const useQuery = (document, options) => {
622
696
  */
623
697
  setupLibraryTranslations();
624
698
 
625
- export { useLazyQuery, usePaginationQuery, useQuery };
699
+ export { useConnectionPageMerger, useLazyQuery, usePaginationQuery, useQuery };
@@ -0,0 +1 @@
1
+ {"version":3,"file":"entry.js","sourceRoot":"","sources":["../../../../../libs/react/graphql-hooks/migrations/entry.ts"],"names":[],"mappings":"","sourcesContent":["export {};\n"]}
package/package.json CHANGED
@@ -1,16 +1,16 @@
1
1
  {
2
2
  "name": "@trackunit/react-graphql-hooks",
3
- "version": "2.3.4",
3
+ "version": "2.3.6",
4
4
  "repository": "https://github.com/Trackunit/manager",
5
5
  "license": "SEE LICENSE IN LICENSE.txt",
6
6
  "engines": {
7
7
  "node": ">=24.x"
8
8
  },
9
9
  "dependencies": {
10
- "@trackunit/i18n-library-translation": "2.2.4",
11
- "@trackunit/shared-utils": "1.15.92",
10
+ "@trackunit/i18n-library-translation": "2.2.5",
11
+ "@trackunit/shared-utils": "1.15.93",
12
12
  "es-toolkit": "^1.39.10",
13
- "@trackunit/react-components": "2.3.4"
13
+ "@trackunit/react-components": "2.4.0"
14
14
  },
15
15
  "peerDependencies": {
16
16
  "@apollo/client": "^3.13.8",
package/src/index.d.ts CHANGED
@@ -1,3 +1,4 @@
1
+ export * from "./useConnectionPageMerger";
1
2
  export * from "./useLazyQuery";
2
3
  export * from "./usePaginationQuery";
3
4
  export * from "./useQuery";
@@ -0,0 +1,17 @@
1
+ import type { RelayPageInfo } from "@trackunit/react-components";
2
+ import { ConnectionQueryShape } from "./utils/mergeConnectionPages";
3
+ /**
4
+ * Builds a stable `updateQuery` for `usePaginationQuery` that appends each fetched page of a Relay
5
+ * connection to the accumulated one.
6
+ *
7
+ * Use it when the connection sits at a fixed `<rootField>.<connectionField>` path. A later page that
8
+ * comes back without `edges` keeps everything accumulated so far instead of collapsing the list.
9
+ *
10
+ * @param rootField name of the field on the query root that holds the connection
11
+ * @param connectionField name of the connection field on the root field
12
+ * @returns {Function} the `updateQuery` callback
13
+ */
14
+ export declare const useConnectionPageMerger: <TRootField extends string, TQuery extends ConnectionQueryShape<TRootField>>(rootField: TRootField, connectionField: string) => ((previous: TQuery | undefined, newData: TQuery) => {
15
+ data: TQuery;
16
+ pageInfo: RelayPageInfo | null;
17
+ });
@@ -0,0 +1,23 @@
1
+ import type { RelayPageInfo } from "@trackunit/react-components";
2
+ export type ConnectionQueryShape<TRootField extends string> = {
3
+ [K in TRootField]?: {
4
+ [key: string]: unknown;
5
+ } | null;
6
+ };
7
+ /**
8
+ * Appends a newly fetched Relay connection page to the accumulated one.
9
+ *
10
+ * Backs `useConnectionPageMerger`, which is how consumers reach this. A later page that comes back
11
+ * without `edges` (the field is nullable in most schemas) keeps everything accumulated so far
12
+ * instead of collapsing the list.
13
+ *
14
+ * @param rootField name of the field on the query root that holds the connection
15
+ * @param connectionField name of the connection field on the root field
16
+ * @param previous the accumulated query result, undefined on the first page
17
+ * @param newData the freshly fetched query result
18
+ * @returns {object} the merged result together with the newest page info
19
+ */
20
+ export declare const mergeConnectionPages: <TRootField extends string, TQuery extends ConnectionQueryShape<TRootField>>(rootField: TRootField, connectionField: string, previous: TQuery | undefined, newData: TQuery) => {
21
+ data: TQuery;
22
+ pageInfo: RelayPageInfo | null;
23
+ };