@graphcommerce/react-hook-form 9.0.0-canary.99 → 9.0.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.
- package/CHANGELOG.md +41 -936
- package/package.json +6 -9
- package/src/ComposedForm/ComposedSubmit.tsx +1 -2
- package/src/ComposedForm/context.ts +1 -1
- package/src/ComposedForm/reducer.ts +1 -1
- package/src/ComposedForm/types.ts +2 -2
- package/src/ComposedForm/useFormCompose.ts +2 -2
- package/src/useFormAutoSubmit.tsx +15 -13
- package/src/useFormGql.tsx +37 -30
- package/src/useFormGqlMutation.tsx +10 -6
- package/src/useFormGqlQuery.tsx +7 -4
- package/src/useFormMuiRegister.tsx +2 -2
- package/src/useFormPersist.tsx +6 -11
- package/src/useFormValidFields.tsx +3 -2
- package/src/useGqlDocumentHandler.tsx +15 -14
- package/src/utils/tryTuple.ts +2 -0
- package/src/utils/debounce.ts +0 -85
- package/src/utils/useDebounceCallback.ts +0 -20
package/package.json
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
"name": "@graphcommerce/react-hook-form",
|
|
3
3
|
"homepage": "https://www.graphcommerce.org/",
|
|
4
4
|
"repository": "github:graphcommerce-org/graphcommerce",
|
|
5
|
-
"version": "9.0.0
|
|
5
|
+
"version": "9.0.0",
|
|
6
6
|
"sideEffects": false,
|
|
7
7
|
"prettier": "@graphcommerce/prettier-config-pwa",
|
|
8
8
|
"eslintConfig": {
|
|
@@ -11,14 +11,11 @@
|
|
|
11
11
|
"project": "./tsconfig.json"
|
|
12
12
|
}
|
|
13
13
|
},
|
|
14
|
-
"devDependencies": {
|
|
15
|
-
"@testing-library/react": "^14.3.1"
|
|
16
|
-
},
|
|
17
14
|
"peerDependencies": {
|
|
18
|
-
"@apollo/client": "
|
|
19
|
-
"@graphcommerce/eslint-config-pwa": "^9.0.0
|
|
20
|
-
"@graphcommerce/prettier-config-pwa": "^9.0.0
|
|
21
|
-
"@graphcommerce/typescript-config-pwa": "^9.0.0
|
|
15
|
+
"@apollo/client": "*",
|
|
16
|
+
"@graphcommerce/eslint-config-pwa": "^9.0.0",
|
|
17
|
+
"@graphcommerce/prettier-config-pwa": "^9.0.0",
|
|
18
|
+
"@graphcommerce/typescript-config-pwa": "^9.0.0",
|
|
22
19
|
"@mui/utils": "^5",
|
|
23
20
|
"graphql": "^16.6.0",
|
|
24
21
|
"react": "^18.2.0",
|
|
@@ -26,7 +23,7 @@
|
|
|
26
23
|
"react-hook-form": "^7"
|
|
27
24
|
},
|
|
28
25
|
"dependencies": {
|
|
29
|
-
"@types/lodash": "^4.17.
|
|
26
|
+
"@types/lodash": "^4.17.13",
|
|
30
27
|
"lodash": "^4.17.21"
|
|
31
28
|
}
|
|
32
29
|
}
|
|
@@ -1,9 +1,8 @@
|
|
|
1
1
|
import { ApolloError } from '@apollo/client'
|
|
2
2
|
import React, { useContext, useEffect, useRef } from 'react'
|
|
3
|
-
import { GlobalError } from 'react-hook-form'
|
|
4
3
|
import { isFormGqlOperation } from '../useFormGqlMutation'
|
|
5
4
|
import { composedFormContext } from './context'
|
|
6
|
-
import { ComposedSubmitRenderComponentProps } from './types'
|
|
5
|
+
import type { ComposedSubmitRenderComponentProps } from './types'
|
|
7
6
|
|
|
8
7
|
export type ComposedSubmitProps = {
|
|
9
8
|
onSubmitSuccessful?: () => void
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { isFormGqlOperation } from '../useFormGqlMutation'
|
|
2
|
-
import { ComposedFormReducer, ComposedFormState } from './types'
|
|
2
|
+
import type { ComposedFormReducer, ComposedFormState } from './types'
|
|
3
3
|
|
|
4
4
|
function updateFormStateIfNecessary(state: ComposedFormState): ComposedFormState {
|
|
5
5
|
const formEntries = Object.entries(state.forms)
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { ApolloError } from '@apollo/client'
|
|
2
|
-
import type { FieldValues, FormState,
|
|
1
|
+
import type { ApolloError } from '@apollo/client'
|
|
2
|
+
import type { FieldValues, FormState, UseFormReturn } from 'react-hook-form'
|
|
3
3
|
import type { SetOptional } from 'type-fest'
|
|
4
4
|
|
|
5
5
|
export type UseFormComposeOptions<V extends FieldValues = FieldValues> = {
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import { useContext, useEffect } from 'react'
|
|
2
|
-
import { FieldValues, UseFormReturn } from 'react-hook-form'
|
|
2
|
+
import type { FieldValues, UseFormReturn } from 'react-hook-form'
|
|
3
3
|
import { isFormGqlOperation } from '../useFormGqlMutation'
|
|
4
4
|
import { composedFormContext } from './context'
|
|
5
|
-
import { UseFormComposeOptions } from './types'
|
|
5
|
+
import type { UseFormComposeOptions } from './types'
|
|
6
6
|
|
|
7
7
|
export function useFormCompose<V extends Record<string, unknown>>(
|
|
8
8
|
fields: UseFormComposeOptions<V>,
|
|
@@ -1,20 +1,20 @@
|
|
|
1
1
|
/* eslint-disable react/no-unused-prop-types */
|
|
2
|
-
import { cloneDeep } from '@apollo/client/utilities'
|
|
3
2
|
// eslint-disable-next-line import/no-extraneous-dependencies
|
|
4
3
|
import { useMemoObject } from '@graphcommerce/next-ui/hooks/useMemoObject'
|
|
4
|
+
import { cloneDeep } from '@apollo/client/utilities'
|
|
5
5
|
// eslint-disable-next-line import/no-extraneous-dependencies
|
|
6
6
|
import { debounce } from '@mui/material'
|
|
7
|
-
import React, { useCallback, useEffect, useRef, useState } from 'react'
|
|
8
|
-
import {
|
|
7
|
+
import React, { startTransition, useCallback, useEffect, useRef, useState } from 'react'
|
|
8
|
+
import type {
|
|
9
9
|
DeepPartialSkipArrayKey,
|
|
10
10
|
FieldPath,
|
|
11
11
|
FieldValues,
|
|
12
12
|
UseFormReturn,
|
|
13
13
|
UseWatchProps,
|
|
14
|
-
useFormState,
|
|
15
|
-
useWatch,
|
|
16
14
|
} from 'react-hook-form'
|
|
17
|
-
import {
|
|
15
|
+
import { useFormState, useWatch } from 'react-hook-form'
|
|
16
|
+
import type { DebounceSettings } from './utils/useDebounce'
|
|
17
|
+
import { useDebounce } from './utils/useDebounce'
|
|
18
18
|
|
|
19
19
|
export type UseFormAutoSubmitOptions<TForm extends UseFormReturn<V>, V extends FieldValues> = {
|
|
20
20
|
/** Instance of current form */
|
|
@@ -48,14 +48,13 @@ export type UseFormAutoSubmitOptions<TForm extends UseFormReturn<V>, V extends F
|
|
|
48
48
|
* Q: The form keeps submitting in loops: A: formState.isDirty should be false after submission Make
|
|
49
49
|
* sure that you call `reset(getValues())` after submission.
|
|
50
50
|
*
|
|
51
|
+
* @deprecated Please use the <FormAutoSubmit /> component instead. This method causes excessive
|
|
52
|
+
* rerenders.
|
|
51
53
|
* @see useFormGqlMutation.tsx for an example implementation
|
|
52
54
|
*
|
|
53
55
|
* Q: How to I resubmit if the form is modified during the request?
|
|
54
56
|
* formState.isDirty should be true after the submission
|
|
55
57
|
* @see useFormGqlMutation.tsx for an example implementation
|
|
56
|
-
*
|
|
57
|
-
*
|
|
58
|
-
* @deprecated Please use the <FormAutoSubmit /> component instead. This method causes excessive rerenders.
|
|
59
58
|
*/
|
|
60
59
|
export function useFormAutoSubmit<
|
|
61
60
|
Form extends UseFormReturn<V>,
|
|
@@ -128,7 +127,7 @@ export type FormAutoSubmitProps<TFieldValues extends FieldValues = FieldValues>
|
|
|
128
127
|
} & Omit<UseWatchProps<TFieldValues>, 'defaultValue'> &
|
|
129
128
|
DebounceSettings
|
|
130
129
|
|
|
131
|
-
|
|
130
|
+
function useAutoSubmitBase<TFieldValues extends FieldValues = FieldValues>(
|
|
132
131
|
props: FormAutoSubmitProps<TFieldValues>,
|
|
133
132
|
) {
|
|
134
133
|
const {
|
|
@@ -152,8 +151,10 @@ export function useAutoSubmitBase<TFieldValues extends FieldValues = FieldValues
|
|
|
152
151
|
const submitDebounced = useDebounce(
|
|
153
152
|
async () => {
|
|
154
153
|
try {
|
|
155
|
-
|
|
156
|
-
|
|
154
|
+
await new Promise((resolve) => {
|
|
155
|
+
oldValues.current = values
|
|
156
|
+
resolve(null)
|
|
157
|
+
}).then(() => submit())
|
|
157
158
|
} catch (e) {
|
|
158
159
|
// We're not interested if the submission actually succeeds, that should be handled by the form itself.
|
|
159
160
|
}
|
|
@@ -173,7 +174,8 @@ export function useAutoSubmitBase<TFieldValues extends FieldValues = FieldValues
|
|
|
173
174
|
}
|
|
174
175
|
|
|
175
176
|
/**
|
|
176
|
-
* This is made a components so the useWatch that is used here doesn't retrigger the rerender of the
|
|
177
|
+
* This is made a components so the useWatch that is used here doesn't retrigger the rerender of the
|
|
178
|
+
* parent component.
|
|
177
179
|
*/
|
|
178
180
|
function FormAutoSubmitBase<TFieldValues extends FieldValues = FieldValues>(
|
|
179
181
|
props: FormAutoSubmitProps<TFieldValues>,
|
package/src/useFormGql.tsx
CHANGED
|
@@ -1,30 +1,35 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import type {
|
|
2
2
|
ApolloError,
|
|
3
3
|
FetchResult,
|
|
4
|
+
LazyQueryHookOptions,
|
|
4
5
|
LazyQueryResultTuple,
|
|
6
|
+
MaybeMasked,
|
|
7
|
+
MutationHookOptions,
|
|
5
8
|
MutationTuple,
|
|
6
9
|
TypedDocumentNode,
|
|
7
|
-
isApolloError,
|
|
8
|
-
MutationHookOptions,
|
|
9
|
-
LazyQueryHookOptions,
|
|
10
10
|
} from '@apollo/client'
|
|
11
|
+
import { isApolloError } from '@apollo/client'
|
|
11
12
|
import { getOperationName } from '@apollo/client/utilities'
|
|
12
13
|
import useEventCallback from '@mui/utils/useEventCallback'
|
|
13
14
|
import { useEffect, useRef } from 'react'
|
|
14
|
-
import { DefaultValues, FieldValues, UseFormProps, UseFormReturn } from 'react-hook-form'
|
|
15
|
+
import type { DefaultValues, FieldValues, UseFormProps, UseFormReturn } from 'react-hook-form'
|
|
15
16
|
import diff from './diff'
|
|
16
|
-
import {
|
|
17
|
+
import type { UseGqlDocumentHandler } from './useGqlDocumentHandler'
|
|
18
|
+
import { useGqlDocumentHandler } from './useGqlDocumentHandler'
|
|
17
19
|
import { tryAsync } from './utils/tryTuple'
|
|
18
20
|
|
|
19
|
-
export type OnCompleteFn<Q, V> = (
|
|
21
|
+
export type OnCompleteFn<Q, V> = (
|
|
22
|
+
data: FetchResult<MaybeMasked<Q>>,
|
|
23
|
+
variables: V,
|
|
24
|
+
) => void | Promise<void>
|
|
20
25
|
|
|
21
26
|
type UseFormGraphQLCallbacks<Q, V> = {
|
|
22
27
|
/**
|
|
23
28
|
* Allows you to modify the variablels computed by the form to make it compatible with the GraphQL
|
|
24
29
|
* Mutation.
|
|
25
30
|
*
|
|
26
|
-
* When returning false, it will silently stop the submission.
|
|
27
|
-
*
|
|
31
|
+
* When returning false, it will silently stop the submission. When an error is thrown, it will be
|
|
32
|
+
* set as an ApolloError
|
|
28
33
|
*/
|
|
29
34
|
onBeforeSubmit?: (variables: V) => V | false | Promise<V | false>
|
|
30
35
|
/**
|
|
@@ -37,26 +42,27 @@ type UseFormGraphQLCallbacks<Q, V> = {
|
|
|
37
42
|
/**
|
|
38
43
|
* @deprecated Not used anymore, is now the default
|
|
39
44
|
*
|
|
40
|
-
*
|
|
41
|
-
*
|
|
42
|
-
*
|
|
43
|
-
*
|
|
45
|
+
* Changes:
|
|
46
|
+
*
|
|
47
|
+
* - Restores `defaultValues` functionality to original functionality, use `values` instead.
|
|
48
|
+
* - Does not reset the form after submission, use `values` instead.
|
|
49
|
+
* - Does not 'encode' the variables, use onBeforeSubmit instead.
|
|
44
50
|
*
|
|
45
|
-
*
|
|
46
|
-
* - Remove the useMutation/useLazyQuery tuple and use a reguler client.mutation() call.
|
|
47
|
-
* - Write graphql errors to setError('root')
|
|
48
|
-
* - Remove onBeforeSubmit, onComplete and the handleSubmit rewrite with a single mutate() callback.
|
|
51
|
+
* Future plans:
|
|
49
52
|
*
|
|
53
|
+
* - Remove the useMutation/useLazyQuery tuple and use a reguler client.mutation() call.
|
|
54
|
+
* - Write graphql errors to setError('root')
|
|
55
|
+
* - Remove onBeforeSubmit, onComplete and the handleSubmit rewrite with a single mutate() callback.
|
|
50
56
|
*
|
|
51
|
-
*
|
|
52
|
-
*
|
|
57
|
+
* ```ts
|
|
58
|
+
* const { handleSubmit } = useFormGqlMutation()
|
|
53
59
|
*
|
|
54
|
-
*
|
|
55
|
-
*
|
|
56
|
-
*
|
|
57
|
-
*
|
|
58
|
-
*
|
|
59
|
-
*
|
|
60
|
+
* const submit = handleSubmit((formValues, mutate) => {
|
|
61
|
+
* // onBeforeSubmit now simply is code before mutate() where you can return early for example or set errors.
|
|
62
|
+
* const result = mutate() // executes the mutation and automatically sets generic errors with setError('root')
|
|
63
|
+
* // onComplete: now simply use the result after the form, to for example reset the form, or do other things.
|
|
64
|
+
* })
|
|
65
|
+
* ```
|
|
60
66
|
*/
|
|
61
67
|
experimental_useV2?: boolean
|
|
62
68
|
/**
|
|
@@ -67,7 +73,8 @@ type UseFormGraphQLCallbacks<Q, V> = {
|
|
|
67
73
|
deprecated_useV1?: boolean
|
|
68
74
|
|
|
69
75
|
/**
|
|
70
|
-
* Only submit the form when there are dirty fields. If all fields are clean, we skip the
|
|
76
|
+
* Only submit the form when there are dirty fields. If all fields are clean, we skip the
|
|
77
|
+
* submission.
|
|
71
78
|
*
|
|
72
79
|
* Form is still set to isSubmitted and isSubmitSuccessful.
|
|
73
80
|
*/
|
|
@@ -82,7 +89,7 @@ export type UseFormGqlMethods<Q, V extends FieldValues> = Omit<
|
|
|
82
89
|
'encode' | 'type'
|
|
83
90
|
> &
|
|
84
91
|
Pick<UseFormReturn<V>, 'handleSubmit'> & {
|
|
85
|
-
data?: Q | null
|
|
92
|
+
data?: MaybeMasked<Q> | null
|
|
86
93
|
error?: ApolloError
|
|
87
94
|
submittedVariables?: V
|
|
88
95
|
}
|
|
@@ -154,7 +161,7 @@ export function useFormGql<Q, V extends FieldValues>(
|
|
|
154
161
|
: true
|
|
155
162
|
|
|
156
163
|
if (skipUnchanged && !hasDirtyFields) {
|
|
157
|
-
console.
|
|
164
|
+
console.info(
|
|
158
165
|
`[useFormGql ${getOperationName(document)}] skipped submission, no dirty fields`,
|
|
159
166
|
)
|
|
160
167
|
await onValid(formValues, event)
|
|
@@ -173,7 +180,7 @@ export function useFormGql<Q, V extends FieldValues>(
|
|
|
173
180
|
if (isApolloError(onBeforeSubmitError)) {
|
|
174
181
|
returnedError.current = onBeforeSubmitError
|
|
175
182
|
} else {
|
|
176
|
-
console.
|
|
183
|
+
console.info(
|
|
177
184
|
'A non ApolloError was thrown during the onBeforeSubmit handler.',
|
|
178
185
|
onBeforeSubmitError,
|
|
179
186
|
)
|
|
@@ -209,7 +216,7 @@ export function useFormGql<Q, V extends FieldValues>(
|
|
|
209
216
|
if (isApolloError(onCompleteError)) {
|
|
210
217
|
returnedError.current = onCompleteError
|
|
211
218
|
} else {
|
|
212
|
-
console.
|
|
219
|
+
console.info(
|
|
213
220
|
'A non ApolloError was thrown during the onComplete handler.',
|
|
214
221
|
onCompleteError,
|
|
215
222
|
)
|
|
@@ -1,8 +1,12 @@
|
|
|
1
|
-
import { MutationHookOptions, TypedDocumentNode
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
4
|
-
import {
|
|
5
|
-
import {
|
|
1
|
+
import type { MutationHookOptions, TypedDocumentNode } from '@apollo/client'
|
|
2
|
+
import { useMutation } from '@apollo/client'
|
|
3
|
+
import type { FieldValues, UseFormReturn } from 'react-hook-form'
|
|
4
|
+
import { useForm } from 'react-hook-form'
|
|
5
|
+
import type { UseFormGqlMethods, UseFormGraphQlOptions } from './useFormGql'
|
|
6
|
+
import { useFormGql } from './useFormGql'
|
|
7
|
+
import type { UseMuiFormRegister } from './useFormMuiRegister'
|
|
8
|
+
import { useFormMuiRegister } from './useFormMuiRegister'
|
|
9
|
+
import type { UseFormValidReturn } from './useFormValidFields'
|
|
6
10
|
|
|
7
11
|
export type UseFormGqlMutationReturn<
|
|
8
12
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
@@ -33,7 +37,7 @@ export function assertFormGqlOperation<
|
|
|
33
37
|
Q extends Record<string, unknown> = Record<string, unknown>,
|
|
34
38
|
>(form: UseFormReturn<V>): asserts form is UseFormGqlMutationReturn<Q, V> {
|
|
35
39
|
if (typeof (form as UseFormGqlMutationReturn<Q, V>).muiRegister !== 'function') {
|
|
36
|
-
throw Error(
|
|
40
|
+
throw Error("form must be one of 'useFromGqlMutation' or 'useFormGqlQuery'")
|
|
37
41
|
}
|
|
38
42
|
}
|
|
39
43
|
|
package/src/useFormGqlQuery.tsx
CHANGED
|
@@ -1,7 +1,10 @@
|
|
|
1
|
-
import { LazyQueryHookOptions, TypedDocumentNode
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
4
|
-
import {
|
|
1
|
+
import type { LazyQueryHookOptions, TypedDocumentNode } from '@apollo/client'
|
|
2
|
+
import { useLazyQuery } from '@apollo/client'
|
|
3
|
+
import type { FieldValues } from 'react-hook-form'
|
|
4
|
+
import { useForm } from 'react-hook-form'
|
|
5
|
+
import type { UseFormGraphQlOptions } from './useFormGql'
|
|
6
|
+
import { useFormGql } from './useFormGql'
|
|
7
|
+
import type { UseFormGqlMutationReturn } from './useFormGqlMutation'
|
|
5
8
|
import { useFormMuiRegister } from './useFormMuiRegister'
|
|
6
9
|
|
|
7
10
|
export type UseFormGqlQueryReturn<
|
package/src/useFormPersist.tsx
CHANGED
|
@@ -1,14 +1,7 @@
|
|
|
1
|
-
|
|
1
|
+
/* eslint-disable react/no-unused-prop-types */
|
|
2
2
|
import { useEffect } from 'react'
|
|
3
|
-
import {
|
|
4
|
-
|
|
5
|
-
UseFormReturn,
|
|
6
|
-
Path,
|
|
7
|
-
FieldPath,
|
|
8
|
-
PathValue,
|
|
9
|
-
useWatch,
|
|
10
|
-
useFormState,
|
|
11
|
-
} from 'react-hook-form'
|
|
3
|
+
import type { FieldPath, FieldValues, Path, PathValue, UseFormReturn } from 'react-hook-form'
|
|
4
|
+
import { useFormState, useWatch } from 'react-hook-form'
|
|
12
5
|
|
|
13
6
|
export type UseFormPersistOptions<
|
|
14
7
|
TFieldValues extends FieldValues = FieldValues,
|
|
@@ -40,6 +33,7 @@ export type UseFormPersistOptions<
|
|
|
40
33
|
* Todo: Use wath callback so it won't trigger a rerender
|
|
41
34
|
*
|
|
42
35
|
* @deprecated Please use the FormPersist component instead. This method causes INP problems.
|
|
36
|
+
* @public
|
|
43
37
|
*/
|
|
44
38
|
export function useFormPersist<V extends FieldValues>(options: UseFormPersistOptions<V>) {
|
|
45
39
|
const { form, name, storage = 'sessionStorage', exclude = [], persist = [] } = options
|
|
@@ -99,7 +93,8 @@ export function useFormPersist<V extends FieldValues>(options: UseFormPersistOpt
|
|
|
99
93
|
}
|
|
100
94
|
|
|
101
95
|
/**
|
|
102
|
-
* Please make sure to always include this component at the end of your form because of useWatch
|
|
96
|
+
* Please make sure to always include this component at the end of your form because of useWatch
|
|
97
|
+
* rules: https://react-hook-form.com/docs/usewatch
|
|
103
98
|
*/
|
|
104
99
|
export function FormPersist<V extends FieldValues>(props: UseFormPersistOptions<V>) {
|
|
105
100
|
useFormPersist(props)
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { FieldValues, Path, UseFormReturn } from 'react-hook-form'
|
|
2
|
-
import { IsRequired } from './useGqlDocumentHandler'
|
|
1
|
+
import type { FieldValues, Path, UseFormReturn } from 'react-hook-form'
|
|
2
|
+
import type { IsRequired } from './useGqlDocumentHandler'
|
|
3
3
|
|
|
4
4
|
export type UseFormValidReturn<TFieldValues> = Partial<Record<Path<TFieldValues>, boolean>>
|
|
5
5
|
|
|
@@ -9,6 +9,7 @@ export type UseFormValidReturn<TFieldValues> = Partial<Record<Path<TFieldValues>
|
|
|
9
9
|
* Record field names as key and boolean as value indicating whether the field is valid
|
|
10
10
|
*
|
|
11
11
|
* @deprecated Please use TextInputElement, SelectElement, etc. with the showValid prop
|
|
12
|
+
* @public
|
|
12
13
|
*/
|
|
13
14
|
export function useFormValidFields<TFieldValues extends FieldValues>(
|
|
14
15
|
form: Pick<UseFormReturn<TFieldValues>, 'watch' | 'formState'>,
|
|
@@ -1,15 +1,16 @@
|
|
|
1
1
|
import type { TypedDocumentNode } from '@apollo/client'
|
|
2
|
-
import
|
|
3
|
-
DefinitionNode,
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
NullValueNode,
|
|
7
|
-
ObjectValueNode,
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
2
|
+
import {
|
|
3
|
+
type DefinitionNode,
|
|
4
|
+
Kind,
|
|
5
|
+
type ListValueNode,
|
|
6
|
+
type NullValueNode,
|
|
7
|
+
type ObjectValueNode,
|
|
8
|
+
type OperationDefinitionNode,
|
|
9
|
+
type OperationTypeNode,
|
|
10
|
+
type TypeNode,
|
|
11
|
+
type ValueNode,
|
|
12
|
+
type VariableDefinitionNode,
|
|
13
|
+
type VariableNode,
|
|
13
14
|
} from 'graphql'
|
|
14
15
|
import { useMemo } from 'react'
|
|
15
16
|
import type { FieldValues } from 'react-hook-form'
|
|
@@ -64,10 +65,10 @@ type DeepStringify<V> = {
|
|
|
64
65
|
type FieldTypes = LiteralUnion<keyof Scalars, string> | FieldTypes[]
|
|
65
66
|
|
|
66
67
|
function variableType<T extends TypeNode>(type: T): FieldTypes {
|
|
67
|
-
if (type.kind ===
|
|
68
|
+
if (type.kind === Kind.LIST_TYPE) {
|
|
68
69
|
return [variableType(type.type)]
|
|
69
70
|
}
|
|
70
|
-
if (type.kind ===
|
|
71
|
+
if (type.kind === Kind.NON_NULL_TYPE) {
|
|
71
72
|
return variableType(type.type)
|
|
72
73
|
}
|
|
73
74
|
|
|
@@ -103,7 +104,7 @@ export function handlerFactory<Q, V extends FieldValues>(
|
|
|
103
104
|
definition.variableDefinitions.forEach((variable: VariableDefinitionNode) => {
|
|
104
105
|
const name = variable.variable.name.value as keyof V
|
|
105
106
|
|
|
106
|
-
requiredPartial = { ...requiredPartial, [name]: variable.type.kind ===
|
|
107
|
+
requiredPartial = { ...requiredPartial, [name]: variable.type.kind === Kind.NON_NULL_TYPE }
|
|
107
108
|
encodingPartial = { ...encodingPartial, [name]: variableType(variable.type) }
|
|
108
109
|
|
|
109
110
|
if (variable.defaultValue && isWithValueNode(variable.defaultValue)) {
|
package/src/utils/tryTuple.ts
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
/** @public */
|
|
1
2
|
export const tryAsync =
|
|
2
3
|
<R, Args extends unknown[]>(
|
|
3
4
|
fn: (...args: Args) => Promise<R> | R,
|
|
@@ -11,6 +12,7 @@ export const tryAsync =
|
|
|
11
12
|
}
|
|
12
13
|
}
|
|
13
14
|
|
|
15
|
+
/** @public */
|
|
14
16
|
export const trySync =
|
|
15
17
|
<R, Args extends unknown[]>(
|
|
16
18
|
fn: (...args: Args) => R,
|
package/src/utils/debounce.ts
DELETED
|
@@ -1,85 +0,0 @@
|
|
|
1
|
-
export interface Cancelable {
|
|
2
|
-
clear(): void
|
|
3
|
-
}
|
|
4
|
-
|
|
5
|
-
export type DebounceOptions = {
|
|
6
|
-
/**
|
|
7
|
-
*
|
|
8
|
-
* Wait for 200 ms after a request before submitting.
|
|
9
|
-
*
|
|
10
|
-
* in milliseconds
|
|
11
|
-
*/
|
|
12
|
-
wait?: number
|
|
13
|
-
/**
|
|
14
|
-
* If there are any pending calls, execute them after this time anyways.
|
|
15
|
-
*
|
|
16
|
-
* in milliseconds
|
|
17
|
-
*/
|
|
18
|
-
maxWait?: number
|
|
19
|
-
/**
|
|
20
|
-
* By default the initialWait is the same as the wait time, but you can set this to a lower value for the initial call.
|
|
21
|
-
*
|
|
22
|
-
* in milliseconds
|
|
23
|
-
*/
|
|
24
|
-
initialWait?: number
|
|
25
|
-
}
|
|
26
|
-
|
|
27
|
-
type DebounceState = {
|
|
28
|
-
timeout: ReturnType<typeof setTimeout> | null
|
|
29
|
-
firstCallTime: number | null
|
|
30
|
-
isFirstCall: boolean
|
|
31
|
-
}
|
|
32
|
-
|
|
33
|
-
export default function debounce<T extends (...args: unknown[]) => unknown>({
|
|
34
|
-
func,
|
|
35
|
-
wait = 166,
|
|
36
|
-
maxWait = 100000,
|
|
37
|
-
initialWait = wait,
|
|
38
|
-
}: DebounceOptions & { func: T }): T & Cancelable {
|
|
39
|
-
let state: DebounceState = { timeout: null, firstCallTime: null, isFirstCall: true }
|
|
40
|
-
|
|
41
|
-
// Guidance for developers - Logging warnings for invalid parameter combinations
|
|
42
|
-
if (process.env.NODE_ENV !== 'production') {
|
|
43
|
-
// Rewrite to array
|
|
44
|
-
const params = { wait, initialWait, maxWait }
|
|
45
|
-
const invalidParams = Object.entries(params)
|
|
46
|
-
.filter(([, value]) => value < 0)
|
|
47
|
-
.map(([key]) => key)
|
|
48
|
-
|
|
49
|
-
if (invalidParams.length > 0)
|
|
50
|
-
console.warn(`debounce: ${invalidParams.join(', ')} should not be negative.`)
|
|
51
|
-
|
|
52
|
-
if (maxWait < wait)
|
|
53
|
-
console.warn(`debounce: maxWait should not be less than wait. This does nothing`)
|
|
54
|
-
}
|
|
55
|
-
|
|
56
|
-
const clear = () => {
|
|
57
|
-
if (state.timeout !== null) clearTimeout(state.timeout)
|
|
58
|
-
state = { timeout: null, firstCallTime: null, isFirstCall: true }
|
|
59
|
-
}
|
|
60
|
-
|
|
61
|
-
function debounced<This>(this: This, ...args: Parameters<T>) {
|
|
62
|
-
const now = Date.now()
|
|
63
|
-
state.firstCallTime ??= now
|
|
64
|
-
|
|
65
|
-
const exec = () => {
|
|
66
|
-
state.isFirstCall = false
|
|
67
|
-
clear()
|
|
68
|
-
func.apply(this, args)
|
|
69
|
-
}
|
|
70
|
-
|
|
71
|
-
const delay = state.isFirstCall ? initialWait : wait
|
|
72
|
-
|
|
73
|
-
if (now - state.firstCallTime >= maxWait) {
|
|
74
|
-
exec()
|
|
75
|
-
return
|
|
76
|
-
}
|
|
77
|
-
|
|
78
|
-
if (state.timeout !== null) clearTimeout(state.timeout)
|
|
79
|
-
state.timeout = setTimeout(exec, delay)
|
|
80
|
-
}
|
|
81
|
-
|
|
82
|
-
debounced.clear = clear
|
|
83
|
-
|
|
84
|
-
return debounced as T & Cancelable
|
|
85
|
-
}
|
|
@@ -1,20 +0,0 @@
|
|
|
1
|
-
// eslint-disable-next-line import/no-extraneous-dependencies
|
|
2
|
-
import useEventCallback from '@mui/utils/useEventCallback'
|
|
3
|
-
import { useMemo } from 'react'
|
|
4
|
-
import debounce, { DebounceOptions } from './debounce'
|
|
5
|
-
|
|
6
|
-
export function useDebouncedCallback<T extends (...args: any[]) => unknown>(
|
|
7
|
-
callback: T,
|
|
8
|
-
{ initialWait, maxWait, wait }: DebounceOptions = {},
|
|
9
|
-
): T {
|
|
10
|
-
const func = useEventCallback(callback)
|
|
11
|
-
|
|
12
|
-
const debounced = useEventCallback(
|
|
13
|
-
useMemo(
|
|
14
|
-
() => debounce({ func, initialWait, maxWait, wait }),
|
|
15
|
-
[func, initialWait, maxWait, wait],
|
|
16
|
-
),
|
|
17
|
-
)
|
|
18
|
-
|
|
19
|
-
return debounced
|
|
20
|
-
}
|