mobx-react-hook-form 2.1.2 → 3.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/README.md CHANGED
@@ -39,11 +39,8 @@ class YourVM {
39
39
 
40
40
 
41
41
  const YourView = () => {
42
- const form = useMobxForm(yourVM.form)
43
-
44
-
45
42
  return (
46
- <form onSubmit={form.onSubmit} onReset={form.onReset}>
43
+ <form onSubmit={yourVM.form.submit} onReset={yourVM.form.reset}>
47
44
  <Controller control={form.control} name={'username'} render={...} />
48
45
  </form>
49
46
  )
@@ -1,34 +1,248 @@
1
- import { createFormControl, FieldValues, FormState, SubmitErrorHandler, SubmitHandler, UseFormProps } from 'react-hook-form';
2
- import { Maybe } from 'yummies/utils/types';
1
+ import { BaseSyntheticEvent } from 'react';
2
+ import { Control, createFormControl, DeepMap, DeepPartial, FieldErrors, FieldValues, FormState, UseFormClearErrors, UseFormRegister, UseFormReset, UseFormResetField, UseFormSetError, UseFormSetFocus, UseFormTrigger, UseFormUnregister } from 'react-hook-form';
3
3
  import { MobxFormParams } from './mobx-form.types.js';
4
- export declare class MobxForm<TFieldValues extends FieldValues = FieldValues, TContext = any, TTransformedValues = TFieldValues> {
4
+ type FormFullState<TFieldValues extends FieldValues> = FormState<TFieldValues> & {
5
+ values: TFieldValues;
6
+ };
7
+ export declare class MobxForm<TFieldValues extends FieldValues = FieldValues, TContext = any, TTransformedValues = TFieldValues> implements FormFullState<TFieldValues> {
5
8
  private config;
6
- protected abortController: AbortController;
9
+ values: TFieldValues;
10
+ isDirty: boolean;
11
+ isLoading: boolean;
12
+ isSubmitted: boolean;
13
+ isSubmitSuccessful: boolean;
14
+ isSubmitting: boolean;
15
+ isValidating: boolean;
16
+ isValid: boolean;
17
+ disabled: boolean;
18
+ submitCount: number;
19
+ defaultValues: Readonly<DeepPartial<TFieldValues>> | undefined;
20
+ dirtyFields: Partial<Readonly<DeepMap<DeepPartial<TFieldValues>, boolean>>>;
21
+ touchedFields: Partial<Readonly<DeepMap<DeepPartial<TFieldValues>, boolean>>>;
22
+ validatingFields: Partial<Readonly<DeepMap<DeepPartial<TFieldValues>, boolean>>>;
23
+ errors: FieldErrors<TFieldValues>;
24
+ isReady: boolean;
7
25
  /**
8
- * Real react-hook-form params
9
- * Needed to connect real react-hook-form to this mobx wrapper
26
+ * Set an error for the field. When set an error which is not associated to a field then manual `clearErrors` invoke is required.
27
+ *
28
+ * @remarks
29
+ * [API](https://react-hook-form.com/docs/useform/seterror) • [Demo](https://codesandbox.io/s/react-hook-form-v7-ts-seterror-nfxxu) • [Video](https://www.youtube.com/watch?v=raMqvE0YyIY)
30
+ *
31
+ * @param name - the path name to the form field value.
32
+ * @param error - an error object which contains type and optional message
33
+ * @param options - whether or not to focus on the field
34
+ *
35
+ * @example
36
+ * ```tsx
37
+ * // when the error is not associated with any fields, `clearError` will need to invoke to clear the error
38
+ * const onSubmit = () => setError("serverError", { type: "server", message: "Error occurred"})
39
+ *
40
+ * <button onClick={() => setError("name", { type: "min" })} />
41
+ *
42
+ * // focus on the input after setting the error
43
+ * <button onClick={() => setError("name", { type: "max" }, { shouldFocus: true })} />
44
+ * ```
10
45
  */
11
- params: UseFormProps<TFieldValues, TContext, TTransformedValues>;
12
- protected handleSubmit(...args: Parameters<SubmitHandler<TTransformedValues>>): void | Promise<void>;
13
- protected handleSubmitFailed(...args: Parameters<SubmitErrorHandler<TFieldValues>>): void | Promise<void>;
14
- protected handleReset(): void;
46
+ setError: UseFormSetError<TFieldValues>;
15
47
  /**
16
- * Original react-hook-form form
48
+ * Clear the entire form errors.
49
+ *
50
+ * @remarks
51
+ * [API](https://react-hook-form.com/docs/useform/clearerrors) • [Demo](https://codesandbox.io/s/react-hook-form-v7-ts-clearerrors-w3ymx)
52
+ *
53
+ * @param name - the path name to the form field value.
54
+ *
55
+ * @example
56
+ * Clear all errors
57
+ * ```tsx
58
+ * clearErrors(); // clear the entire form error
59
+ * clearErrors(["name", "name1"]) // clear an array of fields' error
60
+ * clearErrors("name2"); // clear a single field error
61
+ * ```
62
+ */
63
+ clearErrors: UseFormClearErrors<TFieldValues>;
64
+ /**
65
+ * Trigger field or form validation
66
+ *
67
+ * @remarks
68
+ * [API](https://react-hook-form.com/docs/useform/trigger) • [Demo](https://codesandbox.io/s/react-hook-form-v7-ts-triggervalidation-forked-xs7hl) • [Video](https://www.youtube.com/watch?v=-bcyJCDjksE)
69
+ *
70
+ * @param name - provide empty argument will trigger the entire form validation, an array of field names will validate an array of fields, and a single field name will only trigger that field's validation.
71
+ * @param options - should focus on the error field
72
+ *
73
+ * @returns validation result
74
+ *
75
+ * @example
76
+ * ```tsx
77
+ * useEffect(() => {
78
+ * trigger();
79
+ * }, [trigger])
80
+ *
81
+ * <button onClick={async () => {
82
+ * const result = await trigger(); // result will be a boolean value
83
+ * }}>
84
+ * trigger
85
+ * </button>
86
+ * ```
87
+ */
88
+ trigger: UseFormTrigger<TFieldValues>;
89
+ /**
90
+ * Reset a field state and reference.
91
+ *
92
+ * @remarks
93
+ * [API](https://react-hook-form.com/docs/useform/resetfield) • [Demo](https://codesandbox.io/s/priceless-firefly-d0kuv) • [Video](https://www.youtube.com/watch?v=IdLFcNaEFEo)
94
+ *
95
+ * @param name - the path name to the form field value.
96
+ * @param options - keep form state options
97
+ *
98
+ * @example
99
+ * ```tsx
100
+ * <input {...register("firstName", { required: true })} />
101
+ * <button type="button" onClick={() => resetField("firstName"))}>Reset</button>
102
+ * ```
103
+ */
104
+ resetField: UseFormResetField<TFieldValues>;
105
+ /**
106
+ * Unregister a field reference and remove its value.
107
+ *
108
+ * @remarks
109
+ * [API](https://react-hook-form.com/docs/useform/unregister) • [Demo](https://codesandbox.io/s/react-hook-form-unregister-4k2ey) • [Video](https://www.youtube.com/watch?v=TM99g_NW5Gk&feature=emb_imp_woyt)
110
+ *
111
+ * @param name - the path name to the form field value.
112
+ * @param options - keep form state options
113
+ *
114
+ * @example
115
+ * ```tsx
116
+ * register("name", { required: true })
117
+ *
118
+ * <button onClick={() => unregister("name")} />
119
+ * // there are various keep options to retain formState
120
+ * <button onClick={() => unregister("name", { keepErrors: true })} />
121
+ * ```
122
+ */
123
+ unregister: UseFormUnregister<TFieldValues>;
124
+ /**
125
+ * Form control
126
+ *
127
+ * @remarks
128
+ * [API](https://react-hook-form.com/docs/useform/control)
129
+ *
17
130
  */
18
- form: ReturnType<typeof createFormControl<TFieldValues, TContext, TTransformedValues>>;
131
+ control: Control<TFieldValues, TContext, TTransformedValues>;
19
132
  /**
20
- * form state received from form.formState
133
+ * Register field into hook form with or without the actual DOM ref. You can invoke register anywhere in the component including at `useEffect`.
134
+ *
135
+ * @remarks
136
+ * [API](https://react-hook-form.com/docs/useform/register) • [Demo](https://codesandbox.io/s/react-hook-form-register-ts-ip2j3) • [Video](https://www.youtube.com/watch?v=JFIpCoajYkA)
137
+ *
138
+ * @param name - the path name to the form field value, name is required and unique
139
+ * @param options - register options include validation, disabled, unregister, value as and dependent validation
140
+ *
141
+ * @returns onChange, onBlur, name, ref, and native contribute attribute if browser validation is enabled.
142
+ *
143
+ * @example
144
+ * ```tsx
145
+ * // Register HTML native input
146
+ * <input {...register("input")} />
147
+ * <select {...register("select")} />
148
+ *
149
+ * // Register options
150
+ * <textarea {...register("textarea", { required: "This is required.", maxLength: 20 })} />
151
+ * <input type="number" {...register("name2", { valueAsNumber: true })} />
152
+ * <input {...register("name3", { deps: ["name2"] })} />
153
+ *
154
+ * // Register custom field at useEffect
155
+ * useEffect(() => {
156
+ * register("name4");
157
+ * register("name5", { value: "hiddenValue" });
158
+ * }, [register])
159
+ *
160
+ * // Register without ref
161
+ * const { onChange, onBlur, name } = register("name6")
162
+ * <input onChange={onChange} onBlur={onBlur} name={name} />
163
+ * ```
21
164
  */
22
- state: Maybe<Partial<FormState<TFieldValues>> & {
23
- values: TFieldValues;
24
- }>;
165
+ register: UseFormRegister<TFieldValues>;
25
166
  /**
26
- * Raw data received from form.getValues()
167
+ * Set focus on a registered field. You can start to invoke this method after all fields are mounted to the DOM.
168
+ *
169
+ * @remarks
170
+ * [API](https://react-hook-form.com/docs/useform/setfocus) • [Demo](https://codesandbox.io/s/setfocus-rolus)
171
+ *
172
+ * @param name - the path name to the form field value.
173
+ * @param options - input focus behavior options
174
+ *
175
+ * @example
176
+ * ```tsx
177
+ * useEffect(() => {
178
+ * setFocus("name");
179
+ * }, [setFocus])
180
+ * // shouldSelect allows to select input's content on focus
181
+ * <button onClick={() => setFocus("name", { shouldSelect: true })}>Focus</button>
182
+ * ```
27
183
  */
28
- data: TFieldValues;
184
+ setFocus: UseFormSetFocus<TFieldValues>;
185
+ /**
186
+ * Reset at the entire form state.
187
+ *
188
+ * @remarks
189
+ * [API](https://react-hook-form.com/docs/useform/reset) • [Demo](https://codesandbox.io/s/react-hook-form-reset-v7-ts-pu901) • [Video](https://www.youtube.com/watch?v=qmCLBjyPwVk)
190
+ *
191
+ * @param values - the entire form values to be reset
192
+ * @param keepStateOptions - keep form state options
193
+ *
194
+ * @example
195
+ * ```tsx
196
+ * useEffect(() => {
197
+ * // reset the entire form after component mount or form defaultValues is ready
198
+ * reset({
199
+ * fieldA: "test"
200
+ * fieldB: "test"
201
+ * });
202
+ * }, [reset])
203
+ *
204
+ * // reset by combine with existing form values
205
+ * reset({
206
+ * ...getValues(),
207
+ * fieldB: "test"
208
+ *});
209
+ *
210
+ * // reset and keep form state
211
+ * reset({
212
+ * ...getValues(),
213
+ *}, {
214
+ * keepErrors: true,
215
+ * keepDirty: true
216
+ *});
217
+ * ```
218
+ */
219
+ resetForm: UseFormReset<TFieldValues>;
220
+ protected abortController: AbortController;
221
+ /**
222
+ * Original react-hook-form form
223
+ */
224
+ originalForm: ReturnType<typeof createFormControl<TFieldValues, TContext, TTransformedValues>>;
29
225
  constructor(config: MobxFormParams<TFieldValues, TContext, TTransformedValues>);
30
226
  destroy(): void;
31
- submit: (event?: any) => Promise<void>;
32
- reset: () => void;
227
+ /**
228
+ * Method to manually submit form.
229
+ * Used to attach this method to <form /> element
230
+ *
231
+ * @example
232
+ *
233
+ * <form onSubmit={form.submit} />
234
+ */
235
+ submit(e?: BaseSyntheticEvent): Promise<TTransformedValues>;
236
+ /**
237
+ * Method to manually reset all form.
238
+ * Used to attach this method to <form /> element
239
+ *
240
+ * @example
241
+ *
242
+ * <form onReset={form.reset} />
243
+ */
244
+ reset(e?: BaseSyntheticEvent): void;
245
+ private updateFormState;
33
246
  }
247
+ export {};
34
248
  //# sourceMappingURL=mobx-form.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"mobx-form.d.ts","sourceRoot":"","sources":["../../src/mobx-form/mobx-form.ts"],"names":[],"mappings":"AAGA,OAAO,EACL,iBAAiB,EACjB,WAAW,EACX,SAAS,EACT,kBAAkB,EAClB,aAAa,EACb,YAAY,EACb,MAAM,iBAAiB,CAAC;AACzB,OAAO,EAAE,KAAK,EAAE,MAAM,qBAAqB,CAAC;AAE5C,OAAO,EAAE,cAAc,EAAE,MAAM,sBAAsB,CAAC;AAEtD,qBAAa,QAAQ,CACnB,YAAY,SAAS,WAAW,GAAG,WAAW,EAC9C,QAAQ,GAAG,GAAG,EACd,kBAAkB,GAAG,YAAY;IAqD/B,OAAO,CAAC,MAAM;IAnDhB,SAAS,CAAC,eAAe,EAAE,eAAe,CAAC;IAE3C;;;OAGG;IACH,MAAM,EAAE,YAAY,CAAC,YAAY,EAAE,QAAQ,EAAE,kBAAkB,CAAC,CAAC;IAEjE,SAAS,CAAC,YAAY,CAEpB,GAAG,IAAI,EAAE,UAAU,CAAC,aAAa,CAAC,kBAAkB,CAAC,CAAC,GACrD,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC;IAKvB,SAAS,CAAC,kBAAkB,CAE1B,GAAG,IAAI,EAAE,UAAU,CAAC,kBAAkB,CAAC,YAAY,CAAC,CAAC,GACpD,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC;IAKvB,SAAS,CAAC,WAAW;IAKrB;;OAEG;IACH,IAAI,EAAE,UAAU,CACd,OAAO,iBAAiB,CAAC,YAAY,EAAE,QAAQ,EAAE,kBAAkB,CAAC,CACrE,CAAC;IAEF;;OAEG;IACH,KAAK,EAAE,KAAK,CACV,OAAO,CAAC,SAAS,CAAC,YAAY,CAAC,CAAC,GAAG;QACjC,MAAM,EAAE,YAAY,CAAC;KACtB,CACF,CAAC;IAEF;;OAEG;IACH,IAAI,EAAE,YAAY,CAAC;gBAGT,MAAM,EAAE,cAAc,CAAC,YAAY,EAAE,QAAQ,EAAE,kBAAkB,CAAC;IAgD5E,OAAO,IAAI,IAAI;IAIf,MAAM,GAAU,QAAQ,GAAG,mBAOzB;IAEF,KAAK,aAEH;CACH"}
1
+ {"version":3,"file":"mobx-form.d.ts","sourceRoot":"","sources":["../../src/mobx-form/mobx-form.ts"],"names":[],"mappings":"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"}
@@ -1,91 +1,350 @@
1
1
  /* eslint-disable @typescript-eslint/ban-ts-comment */
2
2
  import { LinkedAbortController } from 'linked-abort-controller';
3
- import { action, makeObservable, observable, runInAction } from 'mobx';
3
+ import { action, makeObservable, observable } from 'mobx';
4
4
  import { createFormControl, } from 'react-hook-form';
5
5
  export class MobxForm {
6
6
  config;
7
- abortController;
7
+ values;
8
+ isDirty = false;
9
+ isLoading = false;
10
+ isSubmitted = false;
11
+ isSubmitSuccessful = false;
12
+ isSubmitting = false;
13
+ isValidating = false;
14
+ isValid = false;
15
+ disabled = false;
16
+ submitCount = 0;
17
+ defaultValues;
18
+ dirtyFields = {};
19
+ touchedFields = {};
20
+ validatingFields = {};
21
+ errors = {};
22
+ isReady = false;
8
23
  /**
9
- * Real react-hook-form params
10
- * Needed to connect real react-hook-form to this mobx wrapper
24
+ * Set an error for the field. When set an error which is not associated to a field then manual `clearErrors` invoke is required.
25
+ *
26
+ * @remarks
27
+ * [API](https://react-hook-form.com/docs/useform/seterror) • [Demo](https://codesandbox.io/s/react-hook-form-v7-ts-seterror-nfxxu) • [Video](https://www.youtube.com/watch?v=raMqvE0YyIY)
28
+ *
29
+ * @param name - the path name to the form field value.
30
+ * @param error - an error object which contains type and optional message
31
+ * @param options - whether or not to focus on the field
32
+ *
33
+ * @example
34
+ * ```tsx
35
+ * // when the error is not associated with any fields, `clearError` will need to invoke to clear the error
36
+ * const onSubmit = () => setError("serverError", { type: "server", message: "Error occurred"})
37
+ *
38
+ * <button onClick={() => setError("name", { type: "min" })} />
39
+ *
40
+ * // focus on the input after setting the error
41
+ * <button onClick={() => setError("name", { type: "max" }, { shouldFocus: true })} />
42
+ * ```
11
43
  */
12
- params;
13
- handleSubmit(
14
- // eslint-disable-next-line @typescript-eslint/no-unused-vars
15
- ...args) {
16
- this.config.onSubmit?.(...args);
17
- // used to override
18
- }
19
- handleSubmitFailed(
20
- // eslint-disable-next-line @typescript-eslint/no-unused-vars
21
- ...args) {
22
- this.config.onSubmitFailed?.(...args);
23
- // used to override
24
- }
25
- handleReset() {
26
- this.config.onReset?.();
27
- // used to override
28
- }
44
+ setError;
29
45
  /**
30
- * Original react-hook-form form
46
+ * Clear the entire form errors.
47
+ *
48
+ * @remarks
49
+ * [API](https://react-hook-form.com/docs/useform/clearerrors) • [Demo](https://codesandbox.io/s/react-hook-form-v7-ts-clearerrors-w3ymx)
50
+ *
51
+ * @param name - the path name to the form field value.
52
+ *
53
+ * @example
54
+ * Clear all errors
55
+ * ```tsx
56
+ * clearErrors(); // clear the entire form error
57
+ * clearErrors(["name", "name1"]) // clear an array of fields' error
58
+ * clearErrors("name2"); // clear a single field error
59
+ * ```
60
+ */
61
+ clearErrors;
62
+ /**
63
+ * Trigger field or form validation
64
+ *
65
+ * @remarks
66
+ * [API](https://react-hook-form.com/docs/useform/trigger) • [Demo](https://codesandbox.io/s/react-hook-form-v7-ts-triggervalidation-forked-xs7hl) • [Video](https://www.youtube.com/watch?v=-bcyJCDjksE)
67
+ *
68
+ * @param name - provide empty argument will trigger the entire form validation, an array of field names will validate an array of fields, and a single field name will only trigger that field's validation.
69
+ * @param options - should focus on the error field
70
+ *
71
+ * @returns validation result
72
+ *
73
+ * @example
74
+ * ```tsx
75
+ * useEffect(() => {
76
+ * trigger();
77
+ * }, [trigger])
78
+ *
79
+ * <button onClick={async () => {
80
+ * const result = await trigger(); // result will be a boolean value
81
+ * }}>
82
+ * trigger
83
+ * </button>
84
+ * ```
85
+ */
86
+ trigger;
87
+ /**
88
+ * Reset a field state and reference.
89
+ *
90
+ * @remarks
91
+ * [API](https://react-hook-form.com/docs/useform/resetfield) • [Demo](https://codesandbox.io/s/priceless-firefly-d0kuv) • [Video](https://www.youtube.com/watch?v=IdLFcNaEFEo)
92
+ *
93
+ * @param name - the path name to the form field value.
94
+ * @param options - keep form state options
95
+ *
96
+ * @example
97
+ * ```tsx
98
+ * <input {...register("firstName", { required: true })} />
99
+ * <button type="button" onClick={() => resetField("firstName"))}>Reset</button>
100
+ * ```
101
+ */
102
+ resetField;
103
+ /**
104
+ * Unregister a field reference and remove its value.
105
+ *
106
+ * @remarks
107
+ * [API](https://react-hook-form.com/docs/useform/unregister) • [Demo](https://codesandbox.io/s/react-hook-form-unregister-4k2ey) • [Video](https://www.youtube.com/watch?v=TM99g_NW5Gk&feature=emb_imp_woyt)
108
+ *
109
+ * @param name - the path name to the form field value.
110
+ * @param options - keep form state options
111
+ *
112
+ * @example
113
+ * ```tsx
114
+ * register("name", { required: true })
115
+ *
116
+ * <button onClick={() => unregister("name")} />
117
+ * // there are various keep options to retain formState
118
+ * <button onClick={() => unregister("name", { keepErrors: true })} />
119
+ * ```
120
+ */
121
+ unregister;
122
+ /**
123
+ * Form control
124
+ *
125
+ * @remarks
126
+ * [API](https://react-hook-form.com/docs/useform/control)
127
+ *
128
+ */
129
+ control;
130
+ /**
131
+ * Register field into hook form with or without the actual DOM ref. You can invoke register anywhere in the component including at `useEffect`.
132
+ *
133
+ * @remarks
134
+ * [API](https://react-hook-form.com/docs/useform/register) • [Demo](https://codesandbox.io/s/react-hook-form-register-ts-ip2j3) • [Video](https://www.youtube.com/watch?v=JFIpCoajYkA)
135
+ *
136
+ * @param name - the path name to the form field value, name is required and unique
137
+ * @param options - register options include validation, disabled, unregister, value as and dependent validation
138
+ *
139
+ * @returns onChange, onBlur, name, ref, and native contribute attribute if browser validation is enabled.
140
+ *
141
+ * @example
142
+ * ```tsx
143
+ * // Register HTML native input
144
+ * <input {...register("input")} />
145
+ * <select {...register("select")} />
146
+ *
147
+ * // Register options
148
+ * <textarea {...register("textarea", { required: "This is required.", maxLength: 20 })} />
149
+ * <input type="number" {...register("name2", { valueAsNumber: true })} />
150
+ * <input {...register("name3", { deps: ["name2"] })} />
151
+ *
152
+ * // Register custom field at useEffect
153
+ * useEffect(() => {
154
+ * register("name4");
155
+ * register("name5", { value: "hiddenValue" });
156
+ * }, [register])
157
+ *
158
+ * // Register without ref
159
+ * const { onChange, onBlur, name } = register("name6")
160
+ * <input onChange={onChange} onBlur={onBlur} name={name} />
161
+ * ```
162
+ */
163
+ register;
164
+ /**
165
+ * Set focus on a registered field. You can start to invoke this method after all fields are mounted to the DOM.
166
+ *
167
+ * @remarks
168
+ * [API](https://react-hook-form.com/docs/useform/setfocus) • [Demo](https://codesandbox.io/s/setfocus-rolus)
169
+ *
170
+ * @param name - the path name to the form field value.
171
+ * @param options - input focus behavior options
172
+ *
173
+ * @example
174
+ * ```tsx
175
+ * useEffect(() => {
176
+ * setFocus("name");
177
+ * }, [setFocus])
178
+ * // shouldSelect allows to select input's content on focus
179
+ * <button onClick={() => setFocus("name", { shouldSelect: true })}>Focus</button>
180
+ * ```
31
181
  */
32
- form;
182
+ setFocus;
33
183
  /**
34
- * form state received from form.formState
184
+ * Reset at the entire form state.
185
+ *
186
+ * @remarks
187
+ * [API](https://react-hook-form.com/docs/useform/reset) • [Demo](https://codesandbox.io/s/react-hook-form-reset-v7-ts-pu901) • [Video](https://www.youtube.com/watch?v=qmCLBjyPwVk)
188
+ *
189
+ * @param values - the entire form values to be reset
190
+ * @param keepStateOptions - keep form state options
191
+ *
192
+ * @example
193
+ * ```tsx
194
+ * useEffect(() => {
195
+ * // reset the entire form after component mount or form defaultValues is ready
196
+ * reset({
197
+ * fieldA: "test"
198
+ * fieldB: "test"
199
+ * });
200
+ * }, [reset])
201
+ *
202
+ * // reset by combine with existing form values
203
+ * reset({
204
+ * ...getValues(),
205
+ * fieldB: "test"
206
+ *});
207
+ *
208
+ * // reset and keep form state
209
+ * reset({
210
+ * ...getValues(),
211
+ *}, {
212
+ * keepErrors: true,
213
+ * keepDirty: true
214
+ *});
215
+ * ```
35
216
  */
36
- state;
217
+ resetForm;
218
+ abortController;
37
219
  /**
38
- * Raw data received from form.getValues()
220
+ * Original react-hook-form form
39
221
  */
40
- data;
222
+ originalForm;
41
223
  constructor(config) {
42
224
  this.config = config;
43
- this.params = config;
44
225
  this.abortController = new LinkedAbortController(config.abortSignal);
45
- this.form = createFormControl(config);
46
- this.data = this.form.getValues();
47
- this.state = null;
48
- this.params = config;
49
- const subscription = this.form.subscribe({
226
+ this.originalForm = createFormControl(config);
227
+ this.setError = this.originalForm.setError;
228
+ this.clearErrors = this.originalForm.clearErrors;
229
+ this.trigger = this.originalForm.trigger;
230
+ this.resetField = this.originalForm.resetField;
231
+ this.unregister = this.originalForm.unregister;
232
+ this.control = this.originalForm.control;
233
+ this.register = this.originalForm.register;
234
+ this.setFocus = this.originalForm.setFocus;
235
+ this.resetForm = this.originalForm.reset;
236
+ Object.assign(this, {
237
+ values: this.originalForm.getValues(),
238
+ defaultValues: config.defaultValues || {},
239
+ });
240
+ const subscription = this.originalForm.subscribe({
241
+ formState: {
242
+ values: true,
243
+ errors: true,
244
+ isValid: true,
245
+ isDirty: true,
246
+ isValidating: true,
247
+ dirtyFields: true,
248
+ touchedFields: true,
249
+ validatingFields: true,
250
+ },
50
251
  callback: (rawFormState) => {
51
- runInAction(() => {
52
- if (this.state) {
53
- Object.assign(this.state, rawFormState);
54
- }
55
- else {
56
- this.state = rawFormState;
57
- }
58
- Object.assign(this.data, rawFormState.values);
59
- });
252
+ this.updateFormState(rawFormState);
60
253
  },
61
254
  });
255
+ observable.deep(this, 'values');
256
+ observable.ref(this, 'isDirty');
257
+ observable.ref(this, 'isLoading');
258
+ observable.ref(this, 'isSubmitted');
259
+ observable.ref(this, 'isSubmitSuccessful');
260
+ observable.ref(this, 'isSubmitting');
261
+ observable.ref(this, 'isValidating');
262
+ observable.ref(this, 'isValid');
263
+ observable.ref(this, 'disabled');
264
+ observable.ref(this, 'submitCount');
265
+ observable.ref(this, 'isReady');
266
+ observable.deep(this, 'defaultValues');
267
+ observable.deep(this, 'dirtyFields');
268
+ observable.deep(this, 'touchedFields');
269
+ observable.deep(this, 'validatingFields');
270
+ observable.deep(this, 'errors');
271
+ action(this, 'updateFormState');
272
+ observable.ref(this, 'originalForm');
273
+ action.bound(this, 'submit');
274
+ action.bound(this, 'reset');
275
+ makeObservable(this);
62
276
  this.abortController.signal.addEventListener('abort', () => {
63
277
  subscription();
64
278
  // @ts-ignore
65
- this.form = null;
279
+ this.originalForm = null;
66
280
  // @ts-ignore
67
281
  this.data = null;
68
- // @ts-ignore
69
- this.state = null;
70
282
  });
71
- observable.deep(this, 'state');
72
- observable.deep(this, 'data');
73
- observable.ref(this, 'params');
74
- observable.ref(this, 'form');
75
- action.bound(this, 'setParams');
76
- action.bound(this, 'updateParams');
77
- action.bound(this, 'syncForm');
78
- makeObservable(this);
79
283
  }
80
284
  destroy() {
81
285
  this.abortController.abort();
82
286
  }
83
- submit = async (event) => {
84
- if (this.config.onSubmit) {
85
- await this.form.handleSubmit(this.config.onSubmit, this.config.onSubmitFailed)(event);
287
+ /**
288
+ * Method to manually submit form.
289
+ * Used to attach this method to <form /> element
290
+ *
291
+ * @example
292
+ *
293
+ * <form onSubmit={form.submit} />
294
+ */
295
+ submit(e) {
296
+ return new Promise((resolve, reject) => {
297
+ this.originalForm.handleSubmit((data, event) => {
298
+ this.config.onSubmit?.(data, event);
299
+ resolve(data);
300
+ }, (errors, event) => {
301
+ this.config.onSubmitFailed?.(errors, event);
302
+ reject(errors);
303
+ })(e);
304
+ });
305
+ }
306
+ /**
307
+ * Method to manually reset all form.
308
+ * Used to attach this method to <form /> element
309
+ *
310
+ * @example
311
+ *
312
+ * <form onReset={form.reset} />
313
+ */
314
+ reset(e) {
315
+ this.resetForm();
316
+ this.config.onReset?.(e);
317
+ }
318
+ updateFormState({ values, errors, ...simpleProperties }) {
319
+ Object.entries(simpleProperties).forEach(([key, value]) => {
320
+ if (value != null) {
321
+ // @ts-ignore
322
+ this[key] = value;
323
+ }
324
+ });
325
+ if (errors) {
326
+ const currentErrorsSet = new Set(Object.keys(this.errors));
327
+ const newErrors = Object.keys(errors);
328
+ for (const errorField of newErrors) {
329
+ if (currentErrorsSet.has(errorField)) {
330
+ currentErrorsSet.delete(errorField);
331
+ // @ts-ignore
332
+ Object.assign(this.errors[errorField], errors[errorField]);
333
+ }
334
+ else {
335
+ // @ts-ignore
336
+ this.errors[errorField] = errors[errorField];
337
+ }
338
+ }
339
+ currentErrorsSet.forEach((errorField) => {
340
+ // @ts-ignore
341
+ delete this.errors[errorField];
342
+ });
86
343
  }
87
- };
88
- reset = () => {
89
- this.form.reset();
90
- };
344
+ else {
345
+ this.errors = {};
346
+ }
347
+ // @ts-ignore
348
+ this.values = values ?? {};
349
+ }
91
350
  }
@@ -20,9 +20,8 @@ export interface MobxFormParams<TFieldValues extends FieldValues = FieldValues,
20
20
  /**
21
21
  * Form reset handler
22
22
  */
23
- onReset?: VoidFunction;
23
+ onReset?: (event: any) => void;
24
24
  }
25
- export type ExtractFormFieldValues<T extends AnyMobxForm> = Exclude<T['params']['values'], undefined | null>;
26
- export type ExtractFormContext<T extends AnyMobxForm> = Exclude<T['params']['context'], undefined | null>;
25
+ export type ExtractFormFieldValues<T extends AnyMobxForm> = Exclude<T['values'], undefined | null>;
27
26
  export type ExtractFormFieldOutputValues<T extends AnyMobxForm> = T extends MobxForm<any, any, infer TFieldOutputValues> ? TFieldOutputValues : never;
28
27
  //# sourceMappingURL=mobx-form.types.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"mobx-form.types.d.ts","sourceRoot":"","sources":["../../src/mobx-form/mobx-form.types.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,WAAW,EACX,kBAAkB,EAClB,aAAa,EACb,YAAY,EACb,MAAM,iBAAiB,CAAC;AAEzB,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,gBAAgB,CAAC;AAE/C,MAAM,MAAM,WAAW,GAAG,QAAQ,CAAC,GAAG,EAAE,GAAG,EAAE,GAAG,CAAC,CAAC;AAElD;;GAEG;AACH,MAAM,WAAW,cAAc,CAC7B,YAAY,SAAS,WAAW,GAAG,WAAW,EAC9C,QAAQ,GAAG,GAAG,EACd,kBAAkB,GAAG,YAAY,CACjC,SAAQ,YAAY,CAAC,YAAY,EAAE,QAAQ,EAAE,kBAAkB,CAAC;IAChE;;OAEG;IACH,WAAW,CAAC,EAAE,WAAW,CAAC;IAC1B;;OAEG;IACH,QAAQ,CAAC,EAAE,aAAa,CAAC,kBAAkB,CAAC,CAAC;IAC7C;;OAEG;IACH,cAAc,CAAC,EAAE,kBAAkB,CAAC,YAAY,CAAC,CAAC;IAClD;;OAEG;IACH,OAAO,CAAC,EAAE,YAAY,CAAC;CACxB;AAED,MAAM,MAAM,sBAAsB,CAAC,CAAC,SAAS,WAAW,IAAI,OAAO,CACjE,CAAC,CAAC,QAAQ,CAAC,CAAC,QAAQ,CAAC,EACrB,SAAS,GAAG,IAAI,CACjB,CAAC;AAEF,MAAM,MAAM,kBAAkB,CAAC,CAAC,SAAS,WAAW,IAAI,OAAO,CAC7D,CAAC,CAAC,QAAQ,CAAC,CAAC,SAAS,CAAC,EACtB,SAAS,GAAG,IAAI,CACjB,CAAC;AAEF,MAAM,MAAM,4BAA4B,CAAC,CAAC,SAAS,WAAW,IAC5D,CAAC,SAAS,QAAQ,CAAC,GAAG,EAAE,GAAG,EAAE,MAAM,kBAAkB,CAAC,GAClD,kBAAkB,GAClB,KAAK,CAAC"}
1
+ {"version":3,"file":"mobx-form.types.d.ts","sourceRoot":"","sources":["../../src/mobx-form/mobx-form.types.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,WAAW,EACX,kBAAkB,EAClB,aAAa,EACb,YAAY,EACb,MAAM,iBAAiB,CAAC;AAEzB,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,gBAAgB,CAAC;AAE/C,MAAM,MAAM,WAAW,GAAG,QAAQ,CAAC,GAAG,EAAE,GAAG,EAAE,GAAG,CAAC,CAAC;AAElD;;GAEG;AACH,MAAM,WAAW,cAAc,CAC7B,YAAY,SAAS,WAAW,GAAG,WAAW,EAC9C,QAAQ,GAAG,GAAG,EACd,kBAAkB,GAAG,YAAY,CACjC,SAAQ,YAAY,CAAC,YAAY,EAAE,QAAQ,EAAE,kBAAkB,CAAC;IAChE;;OAEG;IACH,WAAW,CAAC,EAAE,WAAW,CAAC;IAC1B;;OAEG;IACH,QAAQ,CAAC,EAAE,aAAa,CAAC,kBAAkB,CAAC,CAAC;IAC7C;;OAEG;IACH,cAAc,CAAC,EAAE,kBAAkB,CAAC,YAAY,CAAC,CAAC;IAClD;;OAEG;IACH,OAAO,CAAC,EAAE,CAAC,KAAK,EAAE,GAAG,KAAK,IAAI,CAAC;CAChC;AAED,MAAM,MAAM,sBAAsB,CAAC,CAAC,SAAS,WAAW,IAAI,OAAO,CACjE,CAAC,CAAC,QAAQ,CAAC,EACX,SAAS,GAAG,IAAI,CACjB,CAAC;AAEF,MAAM,MAAM,4BAA4B,CAAC,CAAC,SAAS,WAAW,IAC5D,CAAC,SAAS,QAAQ,CAAC,GAAG,EAAE,GAAG,EAAE,MAAM,kBAAkB,CAAC,GAClD,kBAAkB,GAClB,KAAK,CAAC"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "mobx-react-hook-form",
3
- "version": "2.1.2",
3
+ "version": "3.0.0",
4
4
  "description": "",
5
5
  "type": "module",
6
6
  "keywords": [],
@@ -22,6 +22,7 @@
22
22
  },
23
23
  "dependencies": {
24
24
  "disposer-util": "^2.0.0",
25
+ "@types/react": "^18.3.1",
25
26
  "linked-abort-controller": "^1.1.0",
26
27
  "lodash-es": "^4.17.21",
27
28
  "yummies": "^3.0.39"
@@ -31,6 +32,7 @@
31
32
  "eslint": "^8.57.1",
32
33
  "js2me-eslint-config": "^1.0.7",
33
34
  "js2me-exports-post-build-script": "^3.0.1",
35
+ "nodemon": "^3.1.10",
34
36
  "rimraf": "^6.0.1",
35
37
  "typescript": "^5.8.3"
36
38
  },
@@ -62,6 +64,7 @@
62
64
  "check": "eslint . --fix",
63
65
  "prebuild": "npm run clean && npm run check",
64
66
  "build": "tsc && node ./post-build.mjs",
67
+ "build:watch": "npm run build && nodemon --delay 0.5 --watch src --ext ts,tsx --exec \"tsc && node ./post-build.mjs\"",
65
68
  "pub": "PUBLISH=true pnpm run build",
66
69
  "pub:next": "PUBLISH=true PUBLISH_TAG=next PUBLISH_FORCE=true pnpm run build",
67
70
  "pub:patch": "PUBLISH=true PUBLISH_VERSION=patch pnpm run build",