@conform-to/react 0.4.0-pre.1 → 0.4.0-pre.2

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
@@ -10,8 +10,12 @@
10
10
  - [useFieldset](#usefieldset)
11
11
  - [useFieldList](#usefieldlist)
12
12
  - [useControlledInput](#usecontrolledinput)
13
- - [createValidate](#createvalidate)
14
13
  - [conform](#conform)
14
+ - [hasError](#haserror)
15
+ - [isFieldElement](#isfieldelement)
16
+ - [parse](#parse)
17
+ - [setFormError](#setformerror)
18
+ - [shouldValidate](#shouldvalidate)
15
19
 
16
20
  <!-- /aside -->
17
21
 
@@ -21,15 +25,23 @@ By default, the browser calls the [reportValidity()](https://developer.mozilla.o
21
25
 
22
26
  This hook enhances the form validation behaviour in 3 parts:
23
27
 
24
- 1. It lets you hook up custom validation logic into different form events. For example, revalidation will be triggered whenever something changed.
25
- 2. It provides options for you to decide the best timing to start reporting errors. This could be as earliest as the user start typing, or also as late as the user try submitting the form.
28
+ 1. It enhances form validation with custom rules by subscribing to different DOM events and reporting the errors only when it is configured to do so.
29
+ 2. It unifies client and server validation in one place.
26
30
  3. It exposes the state of each field in the form of [data attributes](https://developer.mozilla.org/en-US/docs/Web/HTML/Global_attributes/data-*), such as `data-conform-touched`, allowing flexible styling across your form without the need to manipulate the class names.
27
31
 
28
32
  ```tsx
29
33
  import { useForm } from '@conform-to/react';
30
34
 
31
35
  function LoginForm() {
32
- const formProps = useForm({
36
+ const form = useForm({
37
+ /**
38
+ * Validation mode.
39
+ * Support "client-only" or "server-validation".
40
+ *
41
+ * Default to `client-only`.
42
+ */
43
+ mode: 'client-only',
44
+
33
45
  /**
34
46
  * Define when the error should be reported initially.
35
47
  * Support "onSubmit", "onChange", "onBlur".
@@ -38,6 +50,16 @@ function LoginForm() {
38
50
  */
39
51
  initialReport: 'onBlur',
40
52
 
53
+ /**
54
+ * An object representing the initial value of the form.
55
+ */
56
+ defaultValue: undefined;
57
+
58
+ /**
59
+ * An object describing the state from the last submission
60
+ */
61
+ state: undefined;
62
+
41
63
  /**
42
64
  * Enable native validation before hydation.
43
65
  *
@@ -54,43 +76,38 @@ function LoginForm() {
54
76
 
55
77
  /**
56
78
  * A function to be called when the form should be (re)validated.
79
+ * Only sync validation is supported
57
80
  */
58
- validate(form, submitter) {
81
+ onValidate({ form, formData, submission }) {
59
82
  // ...
60
83
  },
61
84
 
62
85
  /**
63
86
  * The submit event handler of the form.
64
87
  */
65
- onSubmit(event) {
88
+ onSubmit(event, { form, formData, submission }) {
66
89
  // ...
67
90
  },
68
91
  });
69
92
 
70
- return (
71
- <form {...formProps}>
72
- <input type="email" name="email" required />
73
- <input type="password" name="password" required />
74
- <button type="submit">Login</button>
75
- </form>
76
- );
93
+ // ...
77
94
  }
78
95
  ```
79
96
 
80
97
  <details>
81
- <summary>What is `formProps`?</summary>
98
+ <summary>What is `form.props`?</summary>
82
99
 
83
100
  It is a group of properties properties required to hook into form events. They can also be set explicitly as shown below:
84
101
 
85
102
  ```tsx
86
103
  function RandomForm() {
87
- const formProps = useForm();
104
+ const form = useForm();
88
105
 
89
106
  return (
90
107
  <form
91
- ref={formProps.ref}
92
- onSubmit={formProps.onSubmit}
93
- noValidate={formProps.noValidate}
108
+ ref={form.props.ref}
109
+ onSubmit={form.props.onSubmit}
110
+ noValidate={form.props.noValidate}
94
111
  >
95
112
  {/* ... */}
96
113
  </form>
@@ -103,17 +120,17 @@ function RandomForm() {
103
120
  <details>
104
121
  <summary>Does it work with custom form component like Remix Form?</summary>
105
122
 
106
- Yes! It will fallback to native form submission if the submit event handler is omitted or the event is not default prevented.
123
+ Yes! It will fallback to native form submission as long as the submit event is not default prevented.
107
124
 
108
125
  ```tsx
109
126
  import { useFrom } from '@conform-to/react';
110
127
  import { Form } from '@remix-run/react';
111
128
 
112
129
  function LoginForm() {
113
- const formProps = useForm();
130
+ const form = useForm();
114
131
 
115
132
  return (
116
- <Form method="post" action="/login" {...formProps}>
133
+ <Form method="post" action="/login" {...form.props}>
117
134
  {/* ... */}
118
135
  </Form>
119
136
  );
@@ -123,9 +140,9 @@ function LoginForm() {
123
140
  </details>
124
141
 
125
142
  <details>
126
- <summary>Is the `validate` function required?</summary>
143
+ <summary>Is the `onValidate` function required?</summary>
127
144
 
128
- The `validate` function is not required if the validation logic can be fully covered by the [native constraints](https://developer.mozilla.org/en-US/docs/Web/Guide/HTML/Constraint_validation#validation-related_attributes), e.g. **required** / **min** / **pattern** etc.
145
+ The `onValidate` function is not required if the validation logic can be fully covered by the [native constraints](https://developer.mozilla.org/en-US/docs/Web/Guide/HTML/Constraint_validation#validation-related_attributes), e.g. **required** / **min** / **pattern** etc.
129
146
 
130
147
  ```tsx
131
148
  import { useForm, useFieldset } from '@conform-to/react';
@@ -156,10 +173,10 @@ function LoginForm() {
156
173
 
157
174
  ### useFieldset
158
175
 
159
- This hook can be used to monitor the state of each field and help fields configuration. It lets you:
176
+ This hook can be used to monitor the state of each field and help configuration. It lets you:
160
177
 
161
178
  1. Capturing errors at the form/fieldset level, removing the need to setup invalid handler on each field.
162
- 2. Defining config in one central place. e.g. name, default value and constraint, then distributing it to each field using the [conform](#conform) helpers.
179
+ 2. Defining config in one central place. e.g. name, default value and constraint, then distributing it to each field with the [conform](#conform) helpers.
163
180
 
164
181
  ```tsx
165
182
  import { useForm, useFieldset } from '@conform-to/react';
@@ -493,58 +510,7 @@ function MuiForm() {
493
510
  <MenuItem value="c">Category C</MenuItem>
494
511
  </TextField>
495
512
  </fieldset>
496
- )
497
- }
498
- ```
499
-
500
- ---
501
-
502
- ### createValidate
503
-
504
- This help you configure a validate function to check the validity of each fields and setup custom messages using the Constraint Validation APIs.
505
-
506
- ```tsx
507
- import { useForm, createValidate } from '@conform-to/react';
508
-
509
- export default function SignupForm() {
510
- const formProps = useForm({
511
- validate: createValidate((field, formData) => {
512
- switch (field.name) {
513
- case 'email':
514
- if (field.validity.valueMissing) {
515
- field.setCustomValidity('Email is required');
516
- } else if (field.validity.typeMismatch) {
517
- field.setCustomValidity('Please enter a valid email');
518
- } else {
519
- field.setCustomValidity('');
520
- }
521
- break;
522
- case 'password':
523
- if (field.validity.valueMissing) {
524
- field.setCustomValidity('Password is required');
525
- } else if (field.validity.tooShort) {
526
- field.setCustomValidity(
527
- 'The password should be at least 10 characters long',
528
- );
529
- } else {
530
- field.setCustomValidity('');
531
- }
532
- break;
533
- case 'confirm-password': {
534
- if (field.validity.valueMissing) {
535
- field.setCustomValidity('Confirm Password is required');
536
- } else if (field.value !== formData.get('password')) {
537
- field.setCustomValidity('The password does not match');
538
- } else {
539
- field.setCustomValidity('');
540
- }
541
- break;
542
- }
543
- }
544
- }),
545
- });
546
-
547
- return <form {...formProps}>{/* ... */}</form>;
513
+ );
548
514
  }
549
515
  ```
550
516
 
@@ -615,3 +581,146 @@ function RandomForm() {
615
581
  );
616
582
  }
617
583
  ```
584
+
585
+ ### hasError
586
+
587
+ This helper checks if there is any message defined in error array with the provided name.
588
+
589
+ ```ts
590
+ import { hasError } from '@conform-to/react';
591
+
592
+ /**
593
+ * Assume the error looks like this:
594
+ */
595
+ const error = [['email', 'Email is required']];
596
+
597
+ // This will log `true`
598
+ console.log(hasError(error, 'email'));
599
+
600
+ // This will log `false`
601
+ console.log(hasError(error, 'password'));
602
+ ```
603
+
604
+ ---
605
+
606
+ ### isFieldElement
607
+
608
+ This checks if the provided element is an `input` / `select` / `textarea` or `button` HTML element with type guard. Useful when you need to access the validityState of the fields and modify the validation message manually.
609
+
610
+ ```tsx
611
+ import { isFieldElement } from '@conform-to/react';
612
+
613
+ export default function SignupForm() {
614
+ const form = useForm({
615
+ onValidate({ form }) {
616
+ for (const element of form.elements) {
617
+ if (isFieldElement(element)) {
618
+ switch (field.name) {
619
+ case 'email':
620
+ if (field.validity.valueMissing) {
621
+ field.setCustomValidity('Email is required');
622
+ } else if (field.validity.typeMismatch) {
623
+ field.setCustomValidity('Please enter a valid email');
624
+ } else {
625
+ field.setCustomValidity('');
626
+ }
627
+ break;
628
+ case 'password':
629
+ if (field.validity.valueMissing) {
630
+ field.setCustomValidity('Password is required');
631
+ } else if (field.validity.tooShort) {
632
+ field.setCustomValidity(
633
+ 'The password should be at least 10 characters long',
634
+ );
635
+ } else {
636
+ field.setCustomValidity('');
637
+ }
638
+ break;
639
+ case 'confirm-password': {
640
+ if (field.validity.valueMissing) {
641
+ field.setCustomValidity('Confirm Password is required');
642
+ } else if (field.value !== formData.get('password')) {
643
+ field.setCustomValidity('The password does not match');
644
+ } else {
645
+ field.setCustomValidity('');
646
+ }
647
+ break;
648
+ }
649
+ }
650
+ }
651
+ }
652
+ },
653
+ });
654
+
655
+ // ...
656
+ }
657
+ ```
658
+
659
+ ---
660
+
661
+ ### parse
662
+
663
+ It parses the formData based on the [naming convention](/docs/submission).
664
+
665
+ ```tsx
666
+ import { parse } from '@conform-to/react';
667
+
668
+ const formData = new FormData(formElement);
669
+ const submission = parse(formData);
670
+
671
+ console.log(submission);
672
+ ```
673
+
674
+ ---
675
+
676
+ ### setFormError
677
+
678
+ This helpers updates the form error based on the submission result by looping through all elements in the form and update the error with the [setCustomValidity](https://developer.mozilla.org/en-US/docs/Web/API/HTMLInputElement/setCustomValidity) API.
679
+
680
+ ```tsx
681
+ import { setFormError } from '@conform-to/react';
682
+
683
+ function ExampleForm() {
684
+ const form = useForm({
685
+ onValidate({ form, submission }) {
686
+ const error = validate(submission);
687
+
688
+ setFormError(form, {
689
+ ...submission,
690
+ error,
691
+ });
692
+ },
693
+ });
694
+
695
+ // ...
696
+ }
697
+ ```
698
+
699
+ ---
700
+
701
+ ### shouldValidate
702
+
703
+ This helper checks if the scope of validation includes a specific field by checking the submission:
704
+
705
+ ```tsx
706
+ import { shouldValidate } from '@conform-to/react';
707
+
708
+ /**
709
+ * The submission type and metadata give us hint on what should be valdiated.
710
+ * If the type is 'validate', only the field with name matching the metadata must be validated.
711
+ *
712
+ * However, if the type is `undefined`, both will log true (Default submission)
713
+ */
714
+ const submission = {
715
+ type: 'validate',
716
+ metadata: 'email',
717
+ value: {},
718
+ error: [],
719
+ };
720
+
721
+ // This will log 'true'
722
+ console.log(shouldValidate(submission, 'email'));
723
+
724
+ // This will log 'false'
725
+ console.log(shouldValidate(submission, 'password'));
726
+ ```
package/hooks.d.ts CHANGED
@@ -40,7 +40,7 @@ export interface FormConfig<Schema extends Record<string, any>> {
40
40
  /**
41
41
  * A function to be called when the form should be (re)validated.
42
42
  */
43
- onValidate?: (context: FormContext<Schema>) => void;
43
+ onValidate?: (context: FormContext<Schema>) => Array<[string, string]>;
44
44
  /**
45
45
  * The submit event handler of the form. It will be called
46
46
  * only when the form is considered valid.
@@ -65,7 +65,7 @@ interface Form<Schema extends Record<string, any>> {
65
65
  * Returns properties required to hook into form events.
66
66
  * Applied custom validation and define when error should be reported.
67
67
  *
68
- * @see https://github.com/edmundhung/conform/tree/v0.4.0-pre.1/packages/conform-react/README.md#useform
68
+ * @see https://github.com/edmundhung/conform/tree/v0.4.0-pre.2/packages/conform-react/README.md#useform
69
69
  */
70
70
  export declare function useForm<Schema extends Record<string, any>>(config?: FormConfig<Schema>): Form<Schema>;
71
71
  /**
@@ -106,7 +106,7 @@ export interface FieldsetConfig<Schema extends Record<string, any>> {
106
106
  /**
107
107
  * Returns all the information about the fieldset.
108
108
  *
109
- * @see https://github.com/edmundhung/conform/tree/v0.4.0-pre.1/packages/conform-react/README.md#usefieldset
109
+ * @see https://github.com/edmundhung/conform/tree/v0.4.0-pre.2/packages/conform-react/README.md#usefieldset
110
110
  */
111
111
  export declare function useFieldset<Schema extends Record<string, any>>(ref: RefObject<HTMLFormElement | HTMLFieldSetElement>, config?: FieldsetConfig<Schema>): Fieldset<Schema>;
112
112
  export declare function useFieldset<Schema extends Record<string, any>>(ref: RefObject<HTMLFormElement | HTMLFieldSetElement>, config?: FieldConfig<Schema>): Fieldset<Schema>;
@@ -133,7 +133,7 @@ interface ListControl<Schema> {
133
133
  * Returns a list of key and config, with a group of helpers
134
134
  * configuring buttons for list manipulation
135
135
  *
136
- * @see https://github.com/edmundhung/conform/tree/v0.4.0-pre.1/packages/conform-react/README.md#usefieldlist
136
+ * @see https://github.com/edmundhung/conform/tree/v0.4.0-pre.2/packages/conform-react/README.md#usefieldlist
137
137
  */
138
138
  export declare function useFieldList<Payload = any>(ref: RefObject<HTMLFormElement | HTMLFieldSetElement>, config: FieldConfig<Array<Payload>>): [
139
139
  Array<{
@@ -163,7 +163,7 @@ interface InputControl<Element extends {
163
163
  * This is particular useful when integrating dropdown and datepicker whichs
164
164
  * introduces custom input mode.
165
165
  *
166
- * @see https://github.com/edmundhung/conform/tree/v0.4.0-pre.1/packages/conform-react/README.md#usecontrolledinput
166
+ * @see https://github.com/edmundhung/conform/tree/v0.4.0-pre.2/packages/conform-react/README.md#usecontrolledinput
167
167
  */
168
168
  export declare function useControlledInput<Element extends {
169
169
  focus: () => void;
package/hooks.js CHANGED
@@ -11,7 +11,7 @@ var helpers = require('./helpers.js');
11
11
  * Returns properties required to hook into form events.
12
12
  * Applied custom validation and define when error should be reported.
13
13
  *
14
- * @see https://github.com/edmundhung/conform/tree/v0.4.0-pre.1/packages/conform-react/README.md#useform
14
+ * @see https://github.com/edmundhung/conform/tree/v0.4.0-pre.2/packages/conform-react/README.md#useform
15
15
  */
16
16
  function useForm() {
17
17
  var config = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};
@@ -182,48 +182,62 @@ function useForm() {
182
182
  return;
183
183
  }
184
184
 
185
- var formData = dom.getFormData(form, submitter);
186
- var submission = dom.parse(formData);
187
- var context = {
188
- form,
189
- formData,
190
- submission
191
- }; // Touch all fields only if the submitter is not a command button
192
-
193
- if (!submission.type) {
194
- for (var field of form.elements) {
195
- if (dom.isFieldElement(field)) {
196
- // Mark the field as touched
197
- field.dataset.conformTouched = 'true';
185
+ try {
186
+ var formData = dom.getFormData(form, submitter);
187
+ var submission = dom.parse(formData);
188
+ var _context = {
189
+ form,
190
+ formData,
191
+ submission
192
+ }; // Touch all fields only if the submitter is not a command button
193
+
194
+ if (submission.context === 'submit') {
195
+ for (var field of form.elements) {
196
+ if (dom.isFieldElement(field)) {
197
+ // Mark the field as touched
198
+ field.dataset.conformTouched = 'true';
199
+ }
198
200
  }
199
201
  }
200
- }
201
202
 
202
- try {
203
- if (!config.noValidate && !submitter.formNoValidate) {
204
- var _config$onValidate;
203
+ var _error;
205
204
 
206
- (_config$onValidate = config.onValidate) === null || _config$onValidate === void 0 ? void 0 : _config$onValidate.call(config, context);
207
-
208
- if (!form.reportValidity()) {
209
- dom.focusFirstInvalidField(form);
210
- event.preventDefault();
205
+ if (typeof config.onValidate === 'function') {
206
+ _error = config.onValidate(_context);
207
+ } else {
208
+ if (config.mode !== 'server-validation') {
209
+ // Clear previous result
210
+ dom.setFormError(form, {
211
+ context: 'submit',
212
+ value: {},
213
+ error: []
214
+ });
211
215
  }
216
+
217
+ _error = dom.getFormError(form);
212
218
  }
213
- } catch (e) {
214
- if (e !== form) {
215
- console.warn(e);
219
+
220
+ if (_error.length > 0) {
221
+ submission.error.push(..._error);
216
222
  }
217
- }
218
223
 
219
- if (!event.defaultPrevented) {
220
- if (config.mode !== 'server-validation' && submission.type === 'validate') {
224
+ if (!config.noValidate && !submitter.formNoValidate && dom.hasError(submission.error) || submission.context === 'validate' && config.mode !== 'server-validation') {
221
225
  event.preventDefault();
222
226
  } else {
223
227
  var _config$onSubmit;
224
228
 
225
- (_config$onSubmit = config.onSubmit) === null || _config$onSubmit === void 0 ? void 0 : _config$onSubmit.call(config, event, context);
229
+ (_config$onSubmit = config.onSubmit) === null || _config$onSubmit === void 0 ? void 0 : _config$onSubmit.call(config, event, _context);
226
230
  }
231
+
232
+ if (event.defaultPrevented) {
233
+ dom.setFormError(form, submission);
234
+
235
+ if (!form.reportValidity()) {
236
+ dom.focusFirstInvalidField(form);
237
+ }
238
+ }
239
+ } catch (e) {
240
+ console.warn(e);
227
241
  }
228
242
  }
229
243
 
@@ -423,7 +437,7 @@ function useFieldset(ref, config) {
423
437
  * Returns a list of key and config, with a group of helpers
424
438
  * configuring buttons for list manipulation
425
439
  *
426
- * @see https://github.com/edmundhung/conform/tree/v0.4.0-pre.1/packages/conform-react/README.md#usefieldlist
440
+ * @see https://github.com/edmundhung/conform/tree/v0.4.0-pre.2/packages/conform-react/README.md#usefieldlist
427
441
  */
428
442
  function useFieldList(ref, config) {
429
443
  var configRef = react.useRef(config);
@@ -567,7 +581,7 @@ function useFieldList(ref, config) {
567
581
  * This is particular useful when integrating dropdown and datepicker whichs
568
582
  * introduces custom input mode.
569
583
  *
570
- * @see https://github.com/edmundhung/conform/tree/v0.4.0-pre.1/packages/conform-react/README.md#usecontrolledinput
584
+ * @see https://github.com/edmundhung/conform/tree/v0.4.0-pre.2/packages/conform-react/README.md#usecontrolledinput
571
585
  */
572
586
  function useControlledInput(config) {
573
587
  var _config$defaultValue4;
package/index.d.ts CHANGED
@@ -1,3 +1,3 @@
1
- export { type FieldsetConstraint, type Submission, hasError, isFieldElement, parse, setFormError, shouldValidate, } from '@conform-to/dom';
1
+ export { type FieldsetConstraint, type Submission, getFormError, hasError, isFieldElement, parse, shouldValidate, } from '@conform-to/dom';
2
2
  export * from './hooks';
3
3
  export * as conform from './helpers';
package/index.js CHANGED
@@ -8,6 +8,10 @@ var helpers = require('./helpers.js');
8
8
 
9
9
 
10
10
 
11
+ Object.defineProperty(exports, 'getFormError', {
12
+ enumerable: true,
13
+ get: function () { return dom.getFormError; }
14
+ });
11
15
  Object.defineProperty(exports, 'hasError', {
12
16
  enumerable: true,
13
17
  get: function () { return dom.hasError; }
@@ -20,10 +24,6 @@ Object.defineProperty(exports, 'parse', {
20
24
  enumerable: true,
21
25
  get: function () { return dom.parse; }
22
26
  });
23
- Object.defineProperty(exports, 'setFormError', {
24
- enumerable: true,
25
- get: function () { return dom.setFormError; }
26
- });
27
27
  Object.defineProperty(exports, 'shouldValidate', {
28
28
  enumerable: true,
29
29
  get: function () { return dom.shouldValidate; }
package/module/hooks.js CHANGED
@@ -1,5 +1,5 @@
1
1
  import { objectSpread2 as _objectSpread2 } from './_virtual/_rollupPluginBabelHelpers.js';
2
- import { getSubmissionType, setFormError, focusFirstInvalidField, requestSubmit, isFieldElement, getFormData, parse, getPaths, getName, requestValidate, getFormElement, parseListCommand, updateList } from '@conform-to/dom';
2
+ import { getSubmissionType, setFormError, focusFirstInvalidField, requestSubmit, isFieldElement, getFormData, parse, getFormError, hasError, getPaths, getName, requestValidate, getFormElement, parseListCommand, updateList } from '@conform-to/dom';
3
3
  import { useRef, useState, useEffect } from 'react';
4
4
  import { input } from './helpers.js';
5
5
 
@@ -7,7 +7,7 @@ import { input } from './helpers.js';
7
7
  * Returns properties required to hook into form events.
8
8
  * Applied custom validation and define when error should be reported.
9
9
  *
10
- * @see https://github.com/edmundhung/conform/tree/v0.4.0-pre.1/packages/conform-react/README.md#useform
10
+ * @see https://github.com/edmundhung/conform/tree/v0.4.0-pre.2/packages/conform-react/README.md#useform
11
11
  */
12
12
  function useForm() {
13
13
  var config = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};
@@ -178,48 +178,62 @@ function useForm() {
178
178
  return;
179
179
  }
180
180
 
181
- var formData = getFormData(form, submitter);
182
- var submission = parse(formData);
183
- var context = {
184
- form,
185
- formData,
186
- submission
187
- }; // Touch all fields only if the submitter is not a command button
188
-
189
- if (!submission.type) {
190
- for (var field of form.elements) {
191
- if (isFieldElement(field)) {
192
- // Mark the field as touched
193
- field.dataset.conformTouched = 'true';
181
+ try {
182
+ var formData = getFormData(form, submitter);
183
+ var submission = parse(formData);
184
+ var _context = {
185
+ form,
186
+ formData,
187
+ submission
188
+ }; // Touch all fields only if the submitter is not a command button
189
+
190
+ if (submission.context === 'submit') {
191
+ for (var field of form.elements) {
192
+ if (isFieldElement(field)) {
193
+ // Mark the field as touched
194
+ field.dataset.conformTouched = 'true';
195
+ }
194
196
  }
195
197
  }
196
- }
197
198
 
198
- try {
199
- if (!config.noValidate && !submitter.formNoValidate) {
200
- var _config$onValidate;
199
+ var _error;
201
200
 
202
- (_config$onValidate = config.onValidate) === null || _config$onValidate === void 0 ? void 0 : _config$onValidate.call(config, context);
203
-
204
- if (!form.reportValidity()) {
205
- focusFirstInvalidField(form);
206
- event.preventDefault();
201
+ if (typeof config.onValidate === 'function') {
202
+ _error = config.onValidate(_context);
203
+ } else {
204
+ if (config.mode !== 'server-validation') {
205
+ // Clear previous result
206
+ setFormError(form, {
207
+ context: 'submit',
208
+ value: {},
209
+ error: []
210
+ });
207
211
  }
212
+
213
+ _error = getFormError(form);
208
214
  }
209
- } catch (e) {
210
- if (e !== form) {
211
- console.warn(e);
215
+
216
+ if (_error.length > 0) {
217
+ submission.error.push(..._error);
212
218
  }
213
- }
214
219
 
215
- if (!event.defaultPrevented) {
216
- if (config.mode !== 'server-validation' && submission.type === 'validate') {
220
+ if (!config.noValidate && !submitter.formNoValidate && hasError(submission.error) || submission.context === 'validate' && config.mode !== 'server-validation') {
217
221
  event.preventDefault();
218
222
  } else {
219
223
  var _config$onSubmit;
220
224
 
221
- (_config$onSubmit = config.onSubmit) === null || _config$onSubmit === void 0 ? void 0 : _config$onSubmit.call(config, event, context);
225
+ (_config$onSubmit = config.onSubmit) === null || _config$onSubmit === void 0 ? void 0 : _config$onSubmit.call(config, event, _context);
222
226
  }
227
+
228
+ if (event.defaultPrevented) {
229
+ setFormError(form, submission);
230
+
231
+ if (!form.reportValidity()) {
232
+ focusFirstInvalidField(form);
233
+ }
234
+ }
235
+ } catch (e) {
236
+ console.warn(e);
223
237
  }
224
238
  }
225
239
 
@@ -419,7 +433,7 @@ function useFieldset(ref, config) {
419
433
  * Returns a list of key and config, with a group of helpers
420
434
  * configuring buttons for list manipulation
421
435
  *
422
- * @see https://github.com/edmundhung/conform/tree/v0.4.0-pre.1/packages/conform-react/README.md#usefieldlist
436
+ * @see https://github.com/edmundhung/conform/tree/v0.4.0-pre.2/packages/conform-react/README.md#usefieldlist
423
437
  */
424
438
  function useFieldList(ref, config) {
425
439
  var configRef = useRef(config);
@@ -563,7 +577,7 @@ function useFieldList(ref, config) {
563
577
  * This is particular useful when integrating dropdown and datepicker whichs
564
578
  * introduces custom input mode.
565
579
  *
566
- * @see https://github.com/edmundhung/conform/tree/v0.4.0-pre.1/packages/conform-react/README.md#usecontrolledinput
580
+ * @see https://github.com/edmundhung/conform/tree/v0.4.0-pre.2/packages/conform-react/README.md#usecontrolledinput
567
581
  */
568
582
  function useControlledInput(config) {
569
583
  var _config$defaultValue4;
package/module/index.js CHANGED
@@ -1,4 +1,4 @@
1
- export { hasError, isFieldElement, parse, setFormError, shouldValidate } from '@conform-to/dom';
1
+ export { getFormError, hasError, isFieldElement, parse, shouldValidate } from '@conform-to/dom';
2
2
  export { useControlledInput, useFieldList, useFieldset, useForm } from './hooks.js';
3
3
  import * as helpers from './helpers.js';
4
4
  export { helpers as conform };
package/package.json CHANGED
@@ -2,7 +2,7 @@
2
2
  "name": "@conform-to/react",
3
3
  "description": "Conform view adapter for react",
4
4
  "license": "MIT",
5
- "version": "0.4.0-pre.1",
5
+ "version": "0.4.0-pre.2",
6
6
  "main": "index.js",
7
7
  "module": "module/index.js",
8
8
  "repository": {
@@ -19,7 +19,7 @@
19
19
  "url": "https://github.com/edmundhung/conform/issues"
20
20
  },
21
21
  "dependencies": {
22
- "@conform-to/dom": "0.4.0-pre.1"
22
+ "@conform-to/dom": "0.4.0-pre.2"
23
23
  },
24
24
  "peerDependencies": {
25
25
  "react": ">=16.8"