@conform-to/react 0.4.0-pre.0 → 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 +186 -77
- package/hooks.d.ts +11 -7
- package/hooks.js +93 -93
- package/index.d.ts +1 -1
- package/index.js +6 -2
- package/module/hooks.js +94 -94
- package/module/index.js +1 -1
- package/package.json +2 -2
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
|
|
25
|
-
2. It
|
|
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
|
|
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
|
-
|
|
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
|
-
|
|
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 `
|
|
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
|
|
104
|
+
const form = useForm();
|
|
88
105
|
|
|
89
106
|
return (
|
|
90
107
|
<form
|
|
91
|
-
ref={
|
|
92
|
-
onSubmit={
|
|
93
|
-
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
|
|
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
|
|
130
|
+
const form = useForm();
|
|
114
131
|
|
|
115
132
|
return (
|
|
116
|
-
<Form method="post" action="/login" {...
|
|
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 `
|
|
143
|
+
<summary>Is the `onValidate` function required?</summary>
|
|
127
144
|
|
|
128
|
-
The `
|
|
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
|
|
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
|
|
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
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { type FieldConfig, type FieldElement, type FieldValue, type FieldsetConstraint, type
|
|
1
|
+
import { type FieldConfig, type FieldElement, type FieldValue, type FieldsetConstraint, type ListCommand, type Primitive, type Submission } from '@conform-to/dom';
|
|
2
2
|
import { type InputHTMLAttributes, type FormEvent, type RefObject } from 'react';
|
|
3
3
|
interface FormContext<Schema extends Record<string, any>> {
|
|
4
4
|
form: HTMLFormElement;
|
|
@@ -6,6 +6,10 @@ interface FormContext<Schema extends Record<string, any>> {
|
|
|
6
6
|
submission: Submission<Schema>;
|
|
7
7
|
}
|
|
8
8
|
export interface FormConfig<Schema extends Record<string, any>> {
|
|
9
|
+
/**
|
|
10
|
+
* Validation mode. Default to `client-only`.
|
|
11
|
+
*/
|
|
12
|
+
mode?: 'client-only' | 'server-validation';
|
|
9
13
|
/**
|
|
10
14
|
* Define when the error should be reported initially.
|
|
11
15
|
* Support "onSubmit", "onChange", "onBlur".
|
|
@@ -20,7 +24,7 @@ export interface FormConfig<Schema extends Record<string, any>> {
|
|
|
20
24
|
/**
|
|
21
25
|
* An object describing the state from the last submission
|
|
22
26
|
*/
|
|
23
|
-
state?:
|
|
27
|
+
state?: Submission<Schema>;
|
|
24
28
|
/**
|
|
25
29
|
* Enable native validation before hydation.
|
|
26
30
|
*
|
|
@@ -36,7 +40,7 @@ export interface FormConfig<Schema extends Record<string, any>> {
|
|
|
36
40
|
/**
|
|
37
41
|
* A function to be called when the form should be (re)validated.
|
|
38
42
|
*/
|
|
39
|
-
onValidate?: (context: FormContext<Schema>) =>
|
|
43
|
+
onValidate?: (context: FormContext<Schema>) => Array<[string, string]>;
|
|
40
44
|
/**
|
|
41
45
|
* The submit event handler of the form. It will be called
|
|
42
46
|
* only when the form is considered valid.
|
|
@@ -61,7 +65,7 @@ interface Form<Schema extends Record<string, any>> {
|
|
|
61
65
|
* Returns properties required to hook into form events.
|
|
62
66
|
* Applied custom validation and define when error should be reported.
|
|
63
67
|
*
|
|
64
|
-
* @see https://github.com/edmundhung/conform/tree/v0.4.0-pre.
|
|
68
|
+
* @see https://github.com/edmundhung/conform/tree/v0.4.0-pre.2/packages/conform-react/README.md#useform
|
|
65
69
|
*/
|
|
66
70
|
export declare function useForm<Schema extends Record<string, any>>(config?: FormConfig<Schema>): Form<Schema>;
|
|
67
71
|
/**
|
|
@@ -102,7 +106,7 @@ export interface FieldsetConfig<Schema extends Record<string, any>> {
|
|
|
102
106
|
/**
|
|
103
107
|
* Returns all the information about the fieldset.
|
|
104
108
|
*
|
|
105
|
-
* @see https://github.com/edmundhung/conform/tree/v0.4.0-pre.
|
|
109
|
+
* @see https://github.com/edmundhung/conform/tree/v0.4.0-pre.2/packages/conform-react/README.md#usefieldset
|
|
106
110
|
*/
|
|
107
111
|
export declare function useFieldset<Schema extends Record<string, any>>(ref: RefObject<HTMLFormElement | HTMLFieldSetElement>, config?: FieldsetConfig<Schema>): Fieldset<Schema>;
|
|
108
112
|
export declare function useFieldset<Schema extends Record<string, any>>(ref: RefObject<HTMLFormElement | HTMLFieldSetElement>, config?: FieldConfig<Schema>): Fieldset<Schema>;
|
|
@@ -129,7 +133,7 @@ interface ListControl<Schema> {
|
|
|
129
133
|
* Returns a list of key and config, with a group of helpers
|
|
130
134
|
* configuring buttons for list manipulation
|
|
131
135
|
*
|
|
132
|
-
* @see https://github.com/edmundhung/conform/tree/v0.4.0-pre.
|
|
136
|
+
* @see https://github.com/edmundhung/conform/tree/v0.4.0-pre.2/packages/conform-react/README.md#usefieldlist
|
|
133
137
|
*/
|
|
134
138
|
export declare function useFieldList<Payload = any>(ref: RefObject<HTMLFormElement | HTMLFieldSetElement>, config: FieldConfig<Array<Payload>>): [
|
|
135
139
|
Array<{
|
|
@@ -159,7 +163,7 @@ interface InputControl<Element extends {
|
|
|
159
163
|
* This is particular useful when integrating dropdown and datepicker whichs
|
|
160
164
|
* introduces custom input mode.
|
|
161
165
|
*
|
|
162
|
-
* @see https://github.com/edmundhung/conform/tree/v0.4.0-pre.
|
|
166
|
+
* @see https://github.com/edmundhung/conform/tree/v0.4.0-pre.2/packages/conform-react/README.md#usecontrolledinput
|
|
163
167
|
*/
|
|
164
168
|
export declare function useControlledInput<Element extends {
|
|
165
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.
|
|
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] : {};
|
|
@@ -27,15 +27,14 @@ function useForm() {
|
|
|
27
27
|
return message !== null && message !== void 0 ? message : '';
|
|
28
28
|
});
|
|
29
29
|
var [fieldsetConfig, setFieldsetConfig] = react.useState(() => {
|
|
30
|
-
var _config$state$error2, _config$state2, _config$
|
|
30
|
+
var _config$state$error2, _config$state2, _config$state$value, _config$state3;
|
|
31
31
|
|
|
32
32
|
var error = (_config$state$error2 = (_config$state2 = config.state) === null || _config$state2 === void 0 ? void 0 : _config$state2.error) !== null && _config$state$error2 !== void 0 ? _config$state$error2 : [];
|
|
33
|
-
var scope = (_config$state3 = config.state) === null || _config$state3 === void 0 ? void 0 : _config$state3.scope;
|
|
34
33
|
return {
|
|
35
|
-
defaultValue: (_config$state$value = (_config$
|
|
34
|
+
defaultValue: (_config$state$value = (_config$state3 = config.state) === null || _config$state3 === void 0 ? void 0 : _config$state3.value) !== null && _config$state$value !== void 0 ? _config$state$value : config.defaultValue,
|
|
36
35
|
initialError: error.filter(_ref2 => {
|
|
37
36
|
var [name] = _ref2;
|
|
38
|
-
return name !== '' && dom.getSubmissionType(name) === null
|
|
37
|
+
return name !== '' && dom.getSubmissionType(name) === null;
|
|
39
38
|
})
|
|
40
39
|
};
|
|
41
40
|
});
|
|
@@ -53,8 +52,10 @@ function useForm() {
|
|
|
53
52
|
return;
|
|
54
53
|
}
|
|
55
54
|
|
|
56
|
-
|
|
57
|
-
|
|
55
|
+
dom.setFormError(form, config.state);
|
|
56
|
+
|
|
57
|
+
if (!form.reportValidity()) {
|
|
58
|
+
dom.focusFirstInvalidField(form);
|
|
58
59
|
}
|
|
59
60
|
|
|
60
61
|
dom.requestSubmit(form);
|
|
@@ -181,38 +182,62 @@ function useForm() {
|
|
|
181
182
|
return;
|
|
182
183
|
}
|
|
183
184
|
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
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
|
+
}
|
|
197
200
|
}
|
|
198
201
|
}
|
|
199
|
-
}
|
|
200
202
|
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
203
|
+
var _error;
|
|
204
|
+
|
|
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
|
+
});
|
|
206
215
|
}
|
|
207
|
-
|
|
208
|
-
|
|
216
|
+
|
|
217
|
+
_error = dom.getFormError(form);
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
if (_error.length > 0) {
|
|
221
|
+
submission.error.push(..._error);
|
|
209
222
|
}
|
|
210
|
-
}
|
|
211
223
|
|
|
212
|
-
|
|
213
|
-
|
|
224
|
+
if (!config.noValidate && !submitter.formNoValidate && dom.hasError(submission.error) || submission.context === 'validate' && config.mode !== 'server-validation') {
|
|
225
|
+
event.preventDefault();
|
|
226
|
+
} else {
|
|
227
|
+
var _config$onSubmit;
|
|
214
228
|
|
|
215
|
-
|
|
229
|
+
(_config$onSubmit = config.onSubmit) === null || _config$onSubmit === void 0 ? void 0 : _config$onSubmit.call(config, event, _context);
|
|
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);
|
|
216
241
|
}
|
|
217
242
|
}
|
|
218
243
|
|
|
@@ -275,64 +300,12 @@ function useFieldset(ref, config) {
|
|
|
275
300
|
configRef.current = config;
|
|
276
301
|
});
|
|
277
302
|
react.useEffect(() => {
|
|
278
|
-
/**
|
|
279
|
-
* Reset the error state of each field if its validity is changed.
|
|
280
|
-
*
|
|
281
|
-
* This is a workaround as no official way is provided to notify
|
|
282
|
-
* when the validity of the field is changed from `invalid` to `valid`.
|
|
283
|
-
*/
|
|
284
|
-
var resetError = form => {
|
|
285
|
-
setError(prev => {
|
|
286
|
-
var _configRef$current$na, _configRef$current;
|
|
287
|
-
|
|
288
|
-
var next = prev;
|
|
289
|
-
var fieldsetName = (_configRef$current$na = (_configRef$current = configRef.current) === null || _configRef$current === void 0 ? void 0 : _configRef$current.name) !== null && _configRef$current$na !== void 0 ? _configRef$current$na : '';
|
|
290
|
-
|
|
291
|
-
for (var field of form.elements) {
|
|
292
|
-
if (dom.isFieldElement(field) && field.name.startsWith(fieldsetName)) {
|
|
293
|
-
var [key, ...paths] = dom.getPaths(fieldsetName.length > 0 ? field.name.slice(fieldsetName.length + 1) : field.name);
|
|
294
|
-
|
|
295
|
-
if (typeof key === 'string' && paths.length === 0) {
|
|
296
|
-
var _next$key, _next;
|
|
297
|
-
|
|
298
|
-
var prevMessage = (_next$key = (_next = next) === null || _next === void 0 ? void 0 : _next[key]) !== null && _next$key !== void 0 ? _next$key : '';
|
|
299
|
-
var nextMessage = field.validationMessage;
|
|
300
|
-
/**
|
|
301
|
-
* Techincally, checking prevMessage not being empty while nextMessage being empty
|
|
302
|
-
* is sufficient for our usecase. It checks if the message is changed instead to allow
|
|
303
|
-
* the hook to be useful independently.
|
|
304
|
-
*/
|
|
305
|
-
|
|
306
|
-
if (prevMessage !== '' && prevMessage !== nextMessage) {
|
|
307
|
-
next = _rollupPluginBabelHelpers.objectSpread2(_rollupPluginBabelHelpers.objectSpread2({}, next), {}, {
|
|
308
|
-
[key]: nextMessage
|
|
309
|
-
});
|
|
310
|
-
}
|
|
311
|
-
}
|
|
312
|
-
}
|
|
313
|
-
}
|
|
314
|
-
|
|
315
|
-
return next;
|
|
316
|
-
});
|
|
317
|
-
};
|
|
318
|
-
|
|
319
|
-
var handleInput = event => {
|
|
320
|
-
var form = dom.getFormElement(ref.current);
|
|
321
|
-
var field = event.target;
|
|
322
|
-
|
|
323
|
-
if (!form || !dom.isFieldElement(field) || field.form !== form) {
|
|
324
|
-
return;
|
|
325
|
-
}
|
|
326
|
-
|
|
327
|
-
resetError(form);
|
|
328
|
-
};
|
|
329
|
-
|
|
330
303
|
var invalidHandler = event => {
|
|
331
|
-
var _configRef$current$
|
|
304
|
+
var _configRef$current$na, _configRef$current;
|
|
332
305
|
|
|
333
306
|
var form = dom.getFormElement(ref.current);
|
|
334
307
|
var field = event.target;
|
|
335
|
-
var fieldsetName = (_configRef$current$
|
|
308
|
+
var fieldsetName = (_configRef$current$na = (_configRef$current = configRef.current) === null || _configRef$current === void 0 ? void 0 : _configRef$current.name) !== null && _configRef$current$na !== void 0 ? _configRef$current$na : '';
|
|
336
309
|
|
|
337
310
|
if (!form || !dom.isFieldElement(field) || field.form !== form || !field.name.startsWith(fieldsetName)) {
|
|
338
311
|
return;
|
|
@@ -366,10 +339,39 @@ function useFieldset(ref, config) {
|
|
|
366
339
|
|
|
367
340
|
if (!form || event.target !== form) {
|
|
368
341
|
return;
|
|
369
|
-
}
|
|
342
|
+
}
|
|
343
|
+
/**
|
|
344
|
+
* Reset the error state of each field if its validity is changed.
|
|
345
|
+
*
|
|
346
|
+
* This is a workaround as no official way is provided to notify
|
|
347
|
+
* when the validity of the field is changed from `invalid` to `valid`.
|
|
348
|
+
*/
|
|
349
|
+
|
|
370
350
|
|
|
351
|
+
setError(prev => {
|
|
352
|
+
var _configRef$current$na2, _configRef$current2;
|
|
353
|
+
|
|
354
|
+
var next = prev;
|
|
355
|
+
var fieldsetName = (_configRef$current$na2 = (_configRef$current2 = configRef.current) === null || _configRef$current2 === void 0 ? void 0 : _configRef$current2.name) !== null && _configRef$current$na2 !== void 0 ? _configRef$current$na2 : '';
|
|
356
|
+
|
|
357
|
+
for (var field of form.elements) {
|
|
358
|
+
if (dom.isFieldElement(field) && field.name.startsWith(fieldsetName)) {
|
|
359
|
+
var _next$key, _next;
|
|
360
|
+
|
|
361
|
+
var key = fieldsetName ? field.name.slice(fieldsetName.length + 1) : field.name;
|
|
362
|
+
var prevMessage = (_next$key = (_next = next) === null || _next === void 0 ? void 0 : _next[key]) !== null && _next$key !== void 0 ? _next$key : '';
|
|
363
|
+
var nextMessage = field.validationMessage;
|
|
371
364
|
|
|
372
|
-
|
|
365
|
+
if (prevMessage !== '' && nextMessage === '') {
|
|
366
|
+
next = _rollupPluginBabelHelpers.objectSpread2(_rollupPluginBabelHelpers.objectSpread2({}, next), {}, {
|
|
367
|
+
[key]: ''
|
|
368
|
+
});
|
|
369
|
+
}
|
|
370
|
+
}
|
|
371
|
+
}
|
|
372
|
+
|
|
373
|
+
return next;
|
|
374
|
+
});
|
|
373
375
|
};
|
|
374
376
|
|
|
375
377
|
var resetHandler = event => {
|
|
@@ -388,15 +390,13 @@ function useFieldset(ref, config) {
|
|
|
388
390
|
initialError: {}
|
|
389
391
|
});
|
|
390
392
|
setError({});
|
|
391
|
-
};
|
|
393
|
+
}; // The invalid event does not bubble and so listening on the capturing pharse is needed
|
|
392
394
|
|
|
393
|
-
document.addEventListener('input', handleInput); // The invalid event does not bubble and so listening on the capturing pharse is needed
|
|
394
395
|
|
|
395
396
|
document.addEventListener('invalid', invalidHandler, true);
|
|
396
397
|
document.addEventListener('submit', submitHandler);
|
|
397
398
|
document.addEventListener('reset', resetHandler);
|
|
398
399
|
return () => {
|
|
399
|
-
document.removeEventListener('input', handleInput);
|
|
400
400
|
document.removeEventListener('invalid', invalidHandler, true);
|
|
401
401
|
document.removeEventListener('submit', submitHandler);
|
|
402
402
|
document.removeEventListener('reset', resetHandler);
|
|
@@ -437,7 +437,7 @@ function useFieldset(ref, config) {
|
|
|
437
437
|
* Returns a list of key and config, with a group of helpers
|
|
438
438
|
* configuring buttons for list manipulation
|
|
439
439
|
*
|
|
440
|
-
* @see https://github.com/edmundhung/conform/tree/v0.4.0-pre.
|
|
440
|
+
* @see https://github.com/edmundhung/conform/tree/v0.4.0-pre.2/packages/conform-react/README.md#usefieldlist
|
|
441
441
|
*/
|
|
442
442
|
function useFieldList(ref, config) {
|
|
443
443
|
var configRef = react.useRef(config);
|
|
@@ -581,7 +581,7 @@ function useFieldList(ref, config) {
|
|
|
581
581
|
* This is particular useful when integrating dropdown and datepicker whichs
|
|
582
582
|
* introduces custom input mode.
|
|
583
583
|
*
|
|
584
|
-
* @see https://github.com/edmundhung/conform/tree/v0.4.0-pre.
|
|
584
|
+
* @see https://github.com/edmundhung/conform/tree/v0.4.0-pre.2/packages/conform-react/README.md#usecontrolledinput
|
|
585
585
|
*/
|
|
586
586
|
function useControlledInput(config) {
|
|
587
587
|
var _config$defaultValue4;
|
package/index.d.ts
CHANGED
|
@@ -1,3 +1,3 @@
|
|
|
1
|
-
export { type FieldsetConstraint, type
|
|
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,9 +24,9 @@ Object.defineProperty(exports, 'parse', {
|
|
|
20
24
|
enumerable: true,
|
|
21
25
|
get: function () { return dom.parse; }
|
|
22
26
|
});
|
|
23
|
-
Object.defineProperty(exports, '
|
|
27
|
+
Object.defineProperty(exports, 'shouldValidate', {
|
|
24
28
|
enumerable: true,
|
|
25
|
-
get: function () { return dom.
|
|
29
|
+
get: function () { return dom.shouldValidate; }
|
|
26
30
|
});
|
|
27
31
|
exports.useControlledInput = hooks.useControlledInput;
|
|
28
32
|
exports.useFieldList = hooks.useFieldList;
|
package/module/hooks.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { objectSpread2 as _objectSpread2 } from './_virtual/_rollupPluginBabelHelpers.js';
|
|
2
|
-
import { getSubmissionType,
|
|
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.
|
|
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] : {};
|
|
@@ -23,15 +23,14 @@ function useForm() {
|
|
|
23
23
|
return message !== null && message !== void 0 ? message : '';
|
|
24
24
|
});
|
|
25
25
|
var [fieldsetConfig, setFieldsetConfig] = useState(() => {
|
|
26
|
-
var _config$state$error2, _config$state2, _config$
|
|
26
|
+
var _config$state$error2, _config$state2, _config$state$value, _config$state3;
|
|
27
27
|
|
|
28
28
|
var error = (_config$state$error2 = (_config$state2 = config.state) === null || _config$state2 === void 0 ? void 0 : _config$state2.error) !== null && _config$state$error2 !== void 0 ? _config$state$error2 : [];
|
|
29
|
-
var scope = (_config$state3 = config.state) === null || _config$state3 === void 0 ? void 0 : _config$state3.scope;
|
|
30
29
|
return {
|
|
31
|
-
defaultValue: (_config$state$value = (_config$
|
|
30
|
+
defaultValue: (_config$state$value = (_config$state3 = config.state) === null || _config$state3 === void 0 ? void 0 : _config$state3.value) !== null && _config$state$value !== void 0 ? _config$state$value : config.defaultValue,
|
|
32
31
|
initialError: error.filter(_ref2 => {
|
|
33
32
|
var [name] = _ref2;
|
|
34
|
-
return name !== '' && getSubmissionType(name) === null
|
|
33
|
+
return name !== '' && getSubmissionType(name) === null;
|
|
35
34
|
})
|
|
36
35
|
};
|
|
37
36
|
});
|
|
@@ -49,8 +48,10 @@ function useForm() {
|
|
|
49
48
|
return;
|
|
50
49
|
}
|
|
51
50
|
|
|
52
|
-
|
|
53
|
-
|
|
51
|
+
setFormError(form, config.state);
|
|
52
|
+
|
|
53
|
+
if (!form.reportValidity()) {
|
|
54
|
+
focusFirstInvalidField(form);
|
|
54
55
|
}
|
|
55
56
|
|
|
56
57
|
requestSubmit(form);
|
|
@@ -177,38 +178,62 @@ function useForm() {
|
|
|
177
178
|
return;
|
|
178
179
|
}
|
|
179
180
|
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
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
|
+
}
|
|
193
196
|
}
|
|
194
197
|
}
|
|
195
|
-
}
|
|
196
198
|
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
199
|
+
var _error;
|
|
200
|
+
|
|
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
|
+
});
|
|
202
211
|
}
|
|
203
|
-
|
|
204
|
-
|
|
212
|
+
|
|
213
|
+
_error = getFormError(form);
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
if (_error.length > 0) {
|
|
217
|
+
submission.error.push(..._error);
|
|
205
218
|
}
|
|
206
|
-
}
|
|
207
219
|
|
|
208
|
-
|
|
209
|
-
|
|
220
|
+
if (!config.noValidate && !submitter.formNoValidate && hasError(submission.error) || submission.context === 'validate' && config.mode !== 'server-validation') {
|
|
221
|
+
event.preventDefault();
|
|
222
|
+
} else {
|
|
223
|
+
var _config$onSubmit;
|
|
210
224
|
|
|
211
|
-
|
|
225
|
+
(_config$onSubmit = config.onSubmit) === null || _config$onSubmit === void 0 ? void 0 : _config$onSubmit.call(config, event, _context);
|
|
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);
|
|
212
237
|
}
|
|
213
238
|
}
|
|
214
239
|
|
|
@@ -271,64 +296,12 @@ function useFieldset(ref, config) {
|
|
|
271
296
|
configRef.current = config;
|
|
272
297
|
});
|
|
273
298
|
useEffect(() => {
|
|
274
|
-
/**
|
|
275
|
-
* Reset the error state of each field if its validity is changed.
|
|
276
|
-
*
|
|
277
|
-
* This is a workaround as no official way is provided to notify
|
|
278
|
-
* when the validity of the field is changed from `invalid` to `valid`.
|
|
279
|
-
*/
|
|
280
|
-
var resetError = form => {
|
|
281
|
-
setError(prev => {
|
|
282
|
-
var _configRef$current$na, _configRef$current;
|
|
283
|
-
|
|
284
|
-
var next = prev;
|
|
285
|
-
var fieldsetName = (_configRef$current$na = (_configRef$current = configRef.current) === null || _configRef$current === void 0 ? void 0 : _configRef$current.name) !== null && _configRef$current$na !== void 0 ? _configRef$current$na : '';
|
|
286
|
-
|
|
287
|
-
for (var field of form.elements) {
|
|
288
|
-
if (isFieldElement(field) && field.name.startsWith(fieldsetName)) {
|
|
289
|
-
var [key, ...paths] = getPaths(fieldsetName.length > 0 ? field.name.slice(fieldsetName.length + 1) : field.name);
|
|
290
|
-
|
|
291
|
-
if (typeof key === 'string' && paths.length === 0) {
|
|
292
|
-
var _next$key, _next;
|
|
293
|
-
|
|
294
|
-
var prevMessage = (_next$key = (_next = next) === null || _next === void 0 ? void 0 : _next[key]) !== null && _next$key !== void 0 ? _next$key : '';
|
|
295
|
-
var nextMessage = field.validationMessage;
|
|
296
|
-
/**
|
|
297
|
-
* Techincally, checking prevMessage not being empty while nextMessage being empty
|
|
298
|
-
* is sufficient for our usecase. It checks if the message is changed instead to allow
|
|
299
|
-
* the hook to be useful independently.
|
|
300
|
-
*/
|
|
301
|
-
|
|
302
|
-
if (prevMessage !== '' && prevMessage !== nextMessage) {
|
|
303
|
-
next = _objectSpread2(_objectSpread2({}, next), {}, {
|
|
304
|
-
[key]: nextMessage
|
|
305
|
-
});
|
|
306
|
-
}
|
|
307
|
-
}
|
|
308
|
-
}
|
|
309
|
-
}
|
|
310
|
-
|
|
311
|
-
return next;
|
|
312
|
-
});
|
|
313
|
-
};
|
|
314
|
-
|
|
315
|
-
var handleInput = event => {
|
|
316
|
-
var form = getFormElement(ref.current);
|
|
317
|
-
var field = event.target;
|
|
318
|
-
|
|
319
|
-
if (!form || !isFieldElement(field) || field.form !== form) {
|
|
320
|
-
return;
|
|
321
|
-
}
|
|
322
|
-
|
|
323
|
-
resetError(form);
|
|
324
|
-
};
|
|
325
|
-
|
|
326
299
|
var invalidHandler = event => {
|
|
327
|
-
var _configRef$current$
|
|
300
|
+
var _configRef$current$na, _configRef$current;
|
|
328
301
|
|
|
329
302
|
var form = getFormElement(ref.current);
|
|
330
303
|
var field = event.target;
|
|
331
|
-
var fieldsetName = (_configRef$current$
|
|
304
|
+
var fieldsetName = (_configRef$current$na = (_configRef$current = configRef.current) === null || _configRef$current === void 0 ? void 0 : _configRef$current.name) !== null && _configRef$current$na !== void 0 ? _configRef$current$na : '';
|
|
332
305
|
|
|
333
306
|
if (!form || !isFieldElement(field) || field.form !== form || !field.name.startsWith(fieldsetName)) {
|
|
334
307
|
return;
|
|
@@ -362,10 +335,39 @@ function useFieldset(ref, config) {
|
|
|
362
335
|
|
|
363
336
|
if (!form || event.target !== form) {
|
|
364
337
|
return;
|
|
365
|
-
}
|
|
338
|
+
}
|
|
339
|
+
/**
|
|
340
|
+
* Reset the error state of each field if its validity is changed.
|
|
341
|
+
*
|
|
342
|
+
* This is a workaround as no official way is provided to notify
|
|
343
|
+
* when the validity of the field is changed from `invalid` to `valid`.
|
|
344
|
+
*/
|
|
345
|
+
|
|
366
346
|
|
|
347
|
+
setError(prev => {
|
|
348
|
+
var _configRef$current$na2, _configRef$current2;
|
|
349
|
+
|
|
350
|
+
var next = prev;
|
|
351
|
+
var fieldsetName = (_configRef$current$na2 = (_configRef$current2 = configRef.current) === null || _configRef$current2 === void 0 ? void 0 : _configRef$current2.name) !== null && _configRef$current$na2 !== void 0 ? _configRef$current$na2 : '';
|
|
352
|
+
|
|
353
|
+
for (var field of form.elements) {
|
|
354
|
+
if (isFieldElement(field) && field.name.startsWith(fieldsetName)) {
|
|
355
|
+
var _next$key, _next;
|
|
356
|
+
|
|
357
|
+
var key = fieldsetName ? field.name.slice(fieldsetName.length + 1) : field.name;
|
|
358
|
+
var prevMessage = (_next$key = (_next = next) === null || _next === void 0 ? void 0 : _next[key]) !== null && _next$key !== void 0 ? _next$key : '';
|
|
359
|
+
var nextMessage = field.validationMessage;
|
|
367
360
|
|
|
368
|
-
|
|
361
|
+
if (prevMessage !== '' && nextMessage === '') {
|
|
362
|
+
next = _objectSpread2(_objectSpread2({}, next), {}, {
|
|
363
|
+
[key]: ''
|
|
364
|
+
});
|
|
365
|
+
}
|
|
366
|
+
}
|
|
367
|
+
}
|
|
368
|
+
|
|
369
|
+
return next;
|
|
370
|
+
});
|
|
369
371
|
};
|
|
370
372
|
|
|
371
373
|
var resetHandler = event => {
|
|
@@ -384,15 +386,13 @@ function useFieldset(ref, config) {
|
|
|
384
386
|
initialError: {}
|
|
385
387
|
});
|
|
386
388
|
setError({});
|
|
387
|
-
};
|
|
389
|
+
}; // The invalid event does not bubble and so listening on the capturing pharse is needed
|
|
388
390
|
|
|
389
|
-
document.addEventListener('input', handleInput); // The invalid event does not bubble and so listening on the capturing pharse is needed
|
|
390
391
|
|
|
391
392
|
document.addEventListener('invalid', invalidHandler, true);
|
|
392
393
|
document.addEventListener('submit', submitHandler);
|
|
393
394
|
document.addEventListener('reset', resetHandler);
|
|
394
395
|
return () => {
|
|
395
|
-
document.removeEventListener('input', handleInput);
|
|
396
396
|
document.removeEventListener('invalid', invalidHandler, true);
|
|
397
397
|
document.removeEventListener('submit', submitHandler);
|
|
398
398
|
document.removeEventListener('reset', resetHandler);
|
|
@@ -433,7 +433,7 @@ function useFieldset(ref, config) {
|
|
|
433
433
|
* Returns a list of key and config, with a group of helpers
|
|
434
434
|
* configuring buttons for list manipulation
|
|
435
435
|
*
|
|
436
|
-
* @see https://github.com/edmundhung/conform/tree/v0.4.0-pre.
|
|
436
|
+
* @see https://github.com/edmundhung/conform/tree/v0.4.0-pre.2/packages/conform-react/README.md#usefieldlist
|
|
437
437
|
*/
|
|
438
438
|
function useFieldList(ref, config) {
|
|
439
439
|
var configRef = useRef(config);
|
|
@@ -577,7 +577,7 @@ function useFieldList(ref, config) {
|
|
|
577
577
|
* This is particular useful when integrating dropdown and datepicker whichs
|
|
578
578
|
* introduces custom input mode.
|
|
579
579
|
*
|
|
580
|
-
* @see https://github.com/edmundhung/conform/tree/v0.4.0-pre.
|
|
580
|
+
* @see https://github.com/edmundhung/conform/tree/v0.4.0-pre.2/packages/conform-react/README.md#usecontrolledinput
|
|
581
581
|
*/
|
|
582
582
|
function useControlledInput(config) {
|
|
583
583
|
var _config$defaultValue4;
|
package/module/index.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
export { hasError, isFieldElement, parse,
|
|
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.
|
|
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.
|
|
22
|
+
"@conform-to/dom": "0.4.0-pre.2"
|
|
23
23
|
},
|
|
24
24
|
"peerDependencies": {
|
|
25
25
|
"react": ">=16.8"
|