@conform-to/react 0.4.1 → 0.5.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 +223 -295
- package/helpers.d.ts +19 -6
- package/helpers.js +21 -14
- package/hooks.d.ts +22 -28
- package/hooks.js +109 -127
- package/index.d.ts +1 -1
- package/index.js +16 -0
- package/module/helpers.js +21 -14
- package/module/hooks.js +110 -128
- package/module/index.js +1 -1
- package/package.json +2 -2
package/index.js
CHANGED
|
@@ -16,14 +16,30 @@ Object.defineProperty(exports, 'hasError', {
|
|
|
16
16
|
enumerable: true,
|
|
17
17
|
get: function () { return dom.hasError; }
|
|
18
18
|
});
|
|
19
|
+
Object.defineProperty(exports, 'list', {
|
|
20
|
+
enumerable: true,
|
|
21
|
+
get: function () { return dom.list; }
|
|
22
|
+
});
|
|
19
23
|
Object.defineProperty(exports, 'parse', {
|
|
20
24
|
enumerable: true,
|
|
21
25
|
get: function () { return dom.parse; }
|
|
22
26
|
});
|
|
27
|
+
Object.defineProperty(exports, 'requestCommand', {
|
|
28
|
+
enumerable: true,
|
|
29
|
+
get: function () { return dom.requestCommand; }
|
|
30
|
+
});
|
|
31
|
+
Object.defineProperty(exports, 'requestSubmit', {
|
|
32
|
+
enumerable: true,
|
|
33
|
+
get: function () { return dom.requestSubmit; }
|
|
34
|
+
});
|
|
23
35
|
Object.defineProperty(exports, 'shouldValidate', {
|
|
24
36
|
enumerable: true,
|
|
25
37
|
get: function () { return dom.shouldValidate; }
|
|
26
38
|
});
|
|
39
|
+
Object.defineProperty(exports, 'validate', {
|
|
40
|
+
enumerable: true,
|
|
41
|
+
get: function () { return dom.validate; }
|
|
42
|
+
});
|
|
27
43
|
exports.useControlledInput = hooks.useControlledInput;
|
|
28
44
|
exports.useFieldList = hooks.useFieldList;
|
|
29
45
|
exports.useFieldset = hooks.useFieldset;
|
package/module/helpers.js
CHANGED
|
@@ -1,11 +1,9 @@
|
|
|
1
1
|
function input(config) {
|
|
2
|
-
var
|
|
3
|
-
|
|
4
|
-
value
|
|
5
|
-
} = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};
|
|
6
|
-
var isCheckboxOrRadio = type === 'checkbox' || type === 'radio';
|
|
2
|
+
var _config$initialError;
|
|
3
|
+
var options = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};
|
|
7
4
|
var attributes = {
|
|
8
|
-
|
|
5
|
+
id: config.id,
|
|
6
|
+
type: options.type,
|
|
9
7
|
name: config.name,
|
|
10
8
|
form: config.form,
|
|
11
9
|
required: config.required,
|
|
@@ -15,27 +13,33 @@ function input(config) {
|
|
|
15
13
|
max: config.max,
|
|
16
14
|
step: config.step,
|
|
17
15
|
pattern: config.pattern,
|
|
18
|
-
multiple: config.multiple
|
|
16
|
+
multiple: config.multiple,
|
|
17
|
+
'aria-invalid': Boolean((_config$initialError = config.initialError) === null || _config$initialError === void 0 ? void 0 : _config$initialError.length),
|
|
18
|
+
'aria-describedby': config.errorId
|
|
19
19
|
};
|
|
20
20
|
if (config.initialError && config.initialError.length > 0) {
|
|
21
21
|
attributes.autoFocus = true;
|
|
22
22
|
}
|
|
23
|
-
if (
|
|
24
|
-
|
|
23
|
+
if (options.type === 'checkbox' || options.type === 'radio') {
|
|
24
|
+
var _options$value;
|
|
25
|
+
attributes.value = (_options$value = options.value) !== null && _options$value !== void 0 ? _options$value : 'on';
|
|
25
26
|
attributes.defaultChecked = config.defaultValue === attributes.value;
|
|
26
|
-
} else {
|
|
27
|
+
} else if (options.type !== 'file') {
|
|
27
28
|
attributes.defaultValue = config.defaultValue;
|
|
28
29
|
}
|
|
29
30
|
return attributes;
|
|
30
31
|
}
|
|
31
32
|
function select(config) {
|
|
32
|
-
var _config$defaultValue;
|
|
33
|
+
var _config$defaultValue, _config$initialError2;
|
|
33
34
|
var attributes = {
|
|
35
|
+
id: config.id,
|
|
34
36
|
name: config.name,
|
|
35
37
|
form: config.form,
|
|
36
38
|
defaultValue: config.multiple ? Array.isArray(config.defaultValue) ? config.defaultValue : [] : "".concat((_config$defaultValue = config.defaultValue) !== null && _config$defaultValue !== void 0 ? _config$defaultValue : ''),
|
|
37
39
|
required: config.required,
|
|
38
|
-
multiple: config.multiple
|
|
40
|
+
multiple: config.multiple,
|
|
41
|
+
'aria-invalid': Boolean((_config$initialError2 = config.initialError) === null || _config$initialError2 === void 0 ? void 0 : _config$initialError2.length),
|
|
42
|
+
'aria-describedby': config.errorId
|
|
39
43
|
};
|
|
40
44
|
if (config.initialError && config.initialError.length > 0) {
|
|
41
45
|
attributes.autoFocus = true;
|
|
@@ -43,15 +47,18 @@ function select(config) {
|
|
|
43
47
|
return attributes;
|
|
44
48
|
}
|
|
45
49
|
function textarea(config) {
|
|
46
|
-
var _config$defaultValue2;
|
|
50
|
+
var _config$defaultValue2, _config$initialError3;
|
|
47
51
|
var attributes = {
|
|
52
|
+
id: config.id,
|
|
48
53
|
name: config.name,
|
|
49
54
|
form: config.form,
|
|
50
55
|
defaultValue: "".concat((_config$defaultValue2 = config.defaultValue) !== null && _config$defaultValue2 !== void 0 ? _config$defaultValue2 : ''),
|
|
51
56
|
required: config.required,
|
|
52
57
|
minLength: config.minLength,
|
|
53
58
|
maxLength: config.maxLength,
|
|
54
|
-
autoFocus: Boolean(config.initialError)
|
|
59
|
+
autoFocus: Boolean(config.initialError),
|
|
60
|
+
'aria-invalid': Boolean((_config$initialError3 = config.initialError) === null || _config$initialError3 === void 0 ? void 0 : _config$initialError3.length),
|
|
61
|
+
'aria-describedby': config.errorId
|
|
55
62
|
};
|
|
56
63
|
if (config.initialError && config.initialError.length > 0) {
|
|
57
64
|
attributes.autoFocus = true;
|
package/module/hooks.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { objectSpread2 as _objectSpread2 } from './_virtual/_rollupPluginBabelHelpers.js';
|
|
2
|
-
import {
|
|
2
|
+
import { shouldValidate, reportSubmission, getFormData, parse, isFieldElement, hasError, getPaths, getName, requestCommand, validate, 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://
|
|
10
|
+
* @see https://conform.guide/api/react#useform
|
|
11
11
|
*/
|
|
12
12
|
function useForm() {
|
|
13
13
|
var config = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};
|
|
@@ -21,17 +21,26 @@ function useForm() {
|
|
|
21
21
|
})) !== null && _config$state$error$f !== void 0 ? _config$state$error$f : [];
|
|
22
22
|
return message !== null && message !== void 0 ? message : '';
|
|
23
23
|
});
|
|
24
|
-
var [
|
|
25
|
-
var
|
|
26
|
-
|
|
24
|
+
var [uncontrolledState, setUncontrolledState] = useState(() => {
|
|
25
|
+
var submission = config.state;
|
|
26
|
+
if (!submission) {
|
|
27
|
+
return {
|
|
28
|
+
defaultValue: config.defaultValue
|
|
29
|
+
};
|
|
30
|
+
}
|
|
27
31
|
return {
|
|
28
|
-
defaultValue:
|
|
29
|
-
initialError: error.filter(_ref2 => {
|
|
32
|
+
defaultValue: submission.value,
|
|
33
|
+
initialError: submission.error.filter(_ref2 => {
|
|
30
34
|
var [name] = _ref2;
|
|
31
|
-
return name !== '' &&
|
|
35
|
+
return name !== '' && shouldValidate(submission, name);
|
|
32
36
|
})
|
|
33
37
|
};
|
|
34
38
|
});
|
|
39
|
+
var fieldsetConfig = _objectSpread2(_objectSpread2({}, uncontrolledState), {}, {
|
|
40
|
+
constraint: config.constraint,
|
|
41
|
+
form: config.id
|
|
42
|
+
});
|
|
43
|
+
var fieldset = useFieldset(ref, fieldsetConfig);
|
|
35
44
|
var [noValidate, setNoValidate] = useState(config.noValidate || !config.fallbackNative);
|
|
36
45
|
useEffect(() => {
|
|
37
46
|
configRef.current = config;
|
|
@@ -44,11 +53,7 @@ function useForm() {
|
|
|
44
53
|
if (!form || !config.state) {
|
|
45
54
|
return;
|
|
46
55
|
}
|
|
47
|
-
|
|
48
|
-
if (!form.reportValidity()) {
|
|
49
|
-
focusFirstInvalidField(form);
|
|
50
|
-
}
|
|
51
|
-
requestSubmit(form);
|
|
56
|
+
reportSubmission(form, config.state);
|
|
52
57
|
}, [config.state]);
|
|
53
58
|
useEffect(() => {
|
|
54
59
|
// Revalidate the form when input value is changed
|
|
@@ -59,11 +64,8 @@ function useForm() {
|
|
|
59
64
|
if (!form || !isFieldElement(field) || field.form !== form) {
|
|
60
65
|
return;
|
|
61
66
|
}
|
|
62
|
-
if (formConfig.initialReport === 'onChange') {
|
|
63
|
-
field.
|
|
64
|
-
}
|
|
65
|
-
if (field.dataset.conformTouched) {
|
|
66
|
-
requestValidate(form, field.name);
|
|
67
|
+
if (field.dataset.conformTouched || formConfig.initialReport === 'onChange') {
|
|
68
|
+
requestCommand(form, validate(field.name));
|
|
67
69
|
}
|
|
68
70
|
};
|
|
69
71
|
var handleBlur = event => {
|
|
@@ -74,14 +76,13 @@ function useForm() {
|
|
|
74
76
|
return;
|
|
75
77
|
}
|
|
76
78
|
if (formConfig.initialReport === 'onBlur' && !field.dataset.conformTouched) {
|
|
77
|
-
field.
|
|
78
|
-
requestValidate(form, field.name);
|
|
79
|
+
requestCommand(form, validate(field.name));
|
|
79
80
|
}
|
|
80
81
|
};
|
|
81
82
|
var handleInvalid = event => {
|
|
82
83
|
var form = getFormElement(ref.current);
|
|
83
84
|
var field = event.target;
|
|
84
|
-
if (!form || !isFieldElement(field) || field.form !== form || field.name !== '') {
|
|
85
|
+
if (!form || !isFieldElement(field) || field.form !== form || field.name !== '__form__') {
|
|
85
86
|
return;
|
|
86
87
|
}
|
|
87
88
|
event.preventDefault();
|
|
@@ -100,11 +101,12 @@ function useForm() {
|
|
|
100
101
|
for (var field of form.elements) {
|
|
101
102
|
if (isFieldElement(field)) {
|
|
102
103
|
delete field.dataset.conformTouched;
|
|
104
|
+
field.setAttribute('aria-invalid', 'false');
|
|
103
105
|
field.setCustomValidity('');
|
|
104
106
|
}
|
|
105
107
|
}
|
|
106
108
|
setError('');
|
|
107
|
-
|
|
109
|
+
setUncontrolledState({
|
|
108
110
|
defaultValue: formConfig.defaultValue,
|
|
109
111
|
initialError: []
|
|
110
112
|
});
|
|
@@ -127,22 +129,18 @@ function useForm() {
|
|
|
127
129
|
document.removeEventListener('reset', handleReset);
|
|
128
130
|
};
|
|
129
131
|
}, []);
|
|
130
|
-
|
|
132
|
+
var form = {
|
|
133
|
+
id: config.id,
|
|
131
134
|
ref,
|
|
132
135
|
error,
|
|
133
136
|
props: {
|
|
134
137
|
ref,
|
|
138
|
+
id: config.id,
|
|
135
139
|
noValidate,
|
|
136
140
|
onSubmit(event) {
|
|
137
141
|
var form = event.currentTarget;
|
|
138
142
|
var nativeEvent = event.nativeEvent;
|
|
139
143
|
var submitter = nativeEvent.submitter;
|
|
140
|
-
for (var element of form.elements) {
|
|
141
|
-
if (isFieldElement(element) && element.name === '' && element.willValidate) {
|
|
142
|
-
setError(element.validationMessage);
|
|
143
|
-
break;
|
|
144
|
-
}
|
|
145
|
-
}
|
|
146
144
|
|
|
147
145
|
/**
|
|
148
146
|
* It checks defaultPrevented to confirm if the submission is intentional
|
|
@@ -150,8 +148,7 @@ function useForm() {
|
|
|
150
148
|
* event is captured and revalidate the form with new fields without triggering
|
|
151
149
|
* a form submission at the same time.
|
|
152
150
|
*/
|
|
153
|
-
if (
|
|
154
|
-
event.preventDefault();
|
|
151
|
+
if (event.defaultPrevented) {
|
|
155
152
|
return;
|
|
156
153
|
}
|
|
157
154
|
try {
|
|
@@ -172,29 +169,15 @@ function useForm() {
|
|
|
172
169
|
*
|
|
173
170
|
* This is mainly used to showcase the constraint validation API.
|
|
174
171
|
*/
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
});
|
|
180
|
-
for (var _element of form.elements) {
|
|
181
|
-
if (isFieldElement(_element) && _element.willValidate) {
|
|
182
|
-
submission.error.push([_element.name, _element.validationMessage]);
|
|
172
|
+
for (var element of form.elements) {
|
|
173
|
+
if (isFieldElement(element) && element.willValidate) {
|
|
174
|
+
element.setCustomValidity('');
|
|
175
|
+
submission.error.push([element.name, element.validationMessage]);
|
|
183
176
|
}
|
|
184
177
|
}
|
|
185
178
|
}
|
|
186
179
|
}
|
|
187
|
-
|
|
188
|
-
// Touch all fields only if the submitter is not a command button
|
|
189
|
-
if (submission.type === 'submit') {
|
|
190
|
-
for (var field of form.elements) {
|
|
191
|
-
if (isFieldElement(field)) {
|
|
192
|
-
// Mark the field as touched
|
|
193
|
-
field.dataset.conformTouched = 'true';
|
|
194
|
-
}
|
|
195
|
-
}
|
|
196
|
-
}
|
|
197
|
-
if (!config.noValidate && !submitter.formNoValidate && hasError(submission.error) || submission.type === 'validate' && config.mode !== 'server-validation') {
|
|
180
|
+
if (!config.noValidate && !(submitter !== null && submitter !== void 0 && submitter.formNoValidate) && hasError(submission.error) || submission.type === 'validate' && config.mode !== 'server-validation') {
|
|
198
181
|
event.preventDefault();
|
|
199
182
|
} else {
|
|
200
183
|
var _config$onSubmit;
|
|
@@ -204,10 +187,7 @@ function useForm() {
|
|
|
204
187
|
});
|
|
205
188
|
}
|
|
206
189
|
if (event.defaultPrevented) {
|
|
207
|
-
|
|
208
|
-
if (!form.reportValidity()) {
|
|
209
|
-
focusFirstInvalidField(form);
|
|
210
|
-
}
|
|
190
|
+
reportSubmission(form, submission);
|
|
211
191
|
}
|
|
212
192
|
} catch (e) {
|
|
213
193
|
console.warn(e);
|
|
@@ -216,6 +196,7 @@ function useForm() {
|
|
|
216
196
|
},
|
|
217
197
|
config: fieldsetConfig
|
|
218
198
|
};
|
|
199
|
+
return [form, fieldset];
|
|
219
200
|
}
|
|
220
201
|
|
|
221
202
|
/**
|
|
@@ -276,6 +257,10 @@ function useFieldset(ref, config) {
|
|
|
276
257
|
// Update the error only if the field belongs to the fieldset
|
|
277
258
|
if (typeof key === 'string' && paths.length === 0) {
|
|
278
259
|
if (field.dataset.conformTouched) {
|
|
260
|
+
// Update the aria attribute only if it is set
|
|
261
|
+
if (field.getAttribute('aria-invalid')) {
|
|
262
|
+
field.setAttribute('aria-invalid', field.validationMessage !== '' ? 'true' : 'false');
|
|
263
|
+
}
|
|
279
264
|
setError(prev => {
|
|
280
265
|
var _prev$key;
|
|
281
266
|
var prevMessage = (_prev$key = prev === null || prev === void 0 ? void 0 : prev[key]) !== null && _prev$key !== void 0 ? _prev$key : '';
|
|
@@ -290,38 +275,6 @@ function useFieldset(ref, config) {
|
|
|
290
275
|
event.preventDefault();
|
|
291
276
|
}
|
|
292
277
|
};
|
|
293
|
-
var submitHandler = event => {
|
|
294
|
-
var form = getFormElement(ref.current);
|
|
295
|
-
if (!form || event.target !== form) {
|
|
296
|
-
return;
|
|
297
|
-
}
|
|
298
|
-
|
|
299
|
-
/**
|
|
300
|
-
* Reset the error state of each field if its validity is changed.
|
|
301
|
-
*
|
|
302
|
-
* This is a workaround as no official way is provided to notify
|
|
303
|
-
* when the validity of the field is changed from `invalid` to `valid`.
|
|
304
|
-
*/
|
|
305
|
-
setError(prev => {
|
|
306
|
-
var _configRef$current$na2;
|
|
307
|
-
var next = prev;
|
|
308
|
-
var fieldsetName = (_configRef$current$na2 = configRef.current.name) !== null && _configRef$current$na2 !== void 0 ? _configRef$current$na2 : '';
|
|
309
|
-
for (var field of form.elements) {
|
|
310
|
-
if (isFieldElement(field) && field.name.startsWith(fieldsetName)) {
|
|
311
|
-
var _next$key, _next;
|
|
312
|
-
var key = fieldsetName ? field.name.slice(fieldsetName.length + 1) : field.name;
|
|
313
|
-
var prevMessage = (_next$key = (_next = next) === null || _next === void 0 ? void 0 : _next[key]) !== null && _next$key !== void 0 ? _next$key : '';
|
|
314
|
-
var nextMessage = field.validationMessage;
|
|
315
|
-
if (prevMessage !== '' && nextMessage === '') {
|
|
316
|
-
next = _objectSpread2(_objectSpread2({}, next), {}, {
|
|
317
|
-
[key]: ''
|
|
318
|
-
});
|
|
319
|
-
}
|
|
320
|
-
}
|
|
321
|
-
}
|
|
322
|
-
return next;
|
|
323
|
-
});
|
|
324
|
-
};
|
|
325
278
|
var resetHandler = event => {
|
|
326
279
|
var _fieldsetConfig$defau;
|
|
327
280
|
var form = getFormElement(ref.current);
|
|
@@ -339,11 +292,9 @@ function useFieldset(ref, config) {
|
|
|
339
292
|
|
|
340
293
|
// The invalid event does not bubble and so listening on the capturing pharse is needed
|
|
341
294
|
document.addEventListener('invalid', invalidHandler, true);
|
|
342
|
-
document.addEventListener('submit', submitHandler);
|
|
343
295
|
document.addEventListener('reset', resetHandler);
|
|
344
296
|
return () => {
|
|
345
297
|
document.removeEventListener('invalid', invalidHandler, true);
|
|
346
|
-
document.removeEventListener('submit', submitHandler);
|
|
347
298
|
document.removeEventListener('reset', resetHandler);
|
|
348
299
|
};
|
|
349
300
|
}, [ref]);
|
|
@@ -364,21 +315,26 @@ function useFieldset(ref, config) {
|
|
|
364
315
|
var field = {
|
|
365
316
|
config: _objectSpread2({
|
|
366
317
|
name: fieldsetConfig.name ? "".concat(fieldsetConfig.name, ".").concat(key) : key,
|
|
367
|
-
form: fieldsetConfig.form,
|
|
368
318
|
defaultValue: uncontrolledState.defaultValue[key],
|
|
369
319
|
initialError: uncontrolledState.initialError[key]
|
|
370
320
|
}, constraint),
|
|
371
321
|
error: (_error$key = error === null || error === void 0 ? void 0 : error[key]) !== null && _error$key !== void 0 ? _error$key : ''
|
|
372
322
|
};
|
|
323
|
+
if (fieldsetConfig.form) {
|
|
324
|
+
field.config.form = fieldsetConfig.form;
|
|
325
|
+
field.config.id = "".concat(fieldsetConfig.form, "-").concat(field.config.name);
|
|
326
|
+
field.config.errorId = "".concat(field.config.id, "-error");
|
|
327
|
+
}
|
|
373
328
|
return field;
|
|
374
329
|
}
|
|
375
330
|
});
|
|
376
331
|
}
|
|
332
|
+
|
|
377
333
|
/**
|
|
378
334
|
* Returns a list of key and config, with a group of helpers
|
|
379
335
|
* configuring buttons for list manipulation
|
|
380
336
|
*
|
|
381
|
-
* @see https://
|
|
337
|
+
* @see https://conform.guide/api/react#usefieldlist
|
|
382
338
|
*/
|
|
383
339
|
function useFieldList(ref, config) {
|
|
384
340
|
var configRef = useRef(config);
|
|
@@ -404,48 +360,40 @@ function useFieldList(ref, config) {
|
|
|
404
360
|
initialError
|
|
405
361
|
};
|
|
406
362
|
});
|
|
363
|
+
var [error, setError] = useState(() => uncontrolledState.initialError.map(error => error === null || error === void 0 ? void 0 : error[0][1]));
|
|
407
364
|
var [entries, setEntries] = useState(() => {
|
|
408
365
|
var _config$defaultValue3;
|
|
409
366
|
return Object.entries((_config$defaultValue3 = config.defaultValue) !== null && _config$defaultValue3 !== void 0 ? _config$defaultValue3 : [undefined]);
|
|
410
367
|
});
|
|
411
|
-
var list = entries.map((_ref3, index) => {
|
|
412
|
-
var [key, defaultValue] = _ref3;
|
|
413
|
-
return {
|
|
414
|
-
key,
|
|
415
|
-
config: {
|
|
416
|
-
name: "".concat(config.name, "[").concat(index, "]"),
|
|
417
|
-
form: config.form,
|
|
418
|
-
defaultValue: defaultValue !== null && defaultValue !== void 0 ? defaultValue : uncontrolledState.defaultValue[index],
|
|
419
|
-
initialError: uncontrolledState.initialError[index]
|
|
420
|
-
}
|
|
421
|
-
};
|
|
422
|
-
});
|
|
423
|
-
|
|
424
|
-
/***
|
|
425
|
-
* This use proxy to capture all information about the command and
|
|
426
|
-
* have it encoded in the value.
|
|
427
|
-
*/
|
|
428
|
-
var command = new Proxy({}, {
|
|
429
|
-
get(_target, type) {
|
|
430
|
-
return function () {
|
|
431
|
-
var payload = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};
|
|
432
|
-
return {
|
|
433
|
-
name: 'conform/list',
|
|
434
|
-
value: JSON.stringify({
|
|
435
|
-
type,
|
|
436
|
-
scope: config.name,
|
|
437
|
-
payload
|
|
438
|
-
}),
|
|
439
|
-
form: config.form,
|
|
440
|
-
formNoValidate: true
|
|
441
|
-
};
|
|
442
|
-
};
|
|
443
|
-
}
|
|
444
|
-
});
|
|
445
368
|
useEffect(() => {
|
|
446
369
|
configRef.current = config;
|
|
447
370
|
});
|
|
448
371
|
useEffect(() => {
|
|
372
|
+
var invalidHandler = event => {
|
|
373
|
+
var _configRef$current$na2;
|
|
374
|
+
var form = getFormElement(ref.current);
|
|
375
|
+
var field = event.target;
|
|
376
|
+
var prefix = (_configRef$current$na2 = configRef.current.name) !== null && _configRef$current$na2 !== void 0 ? _configRef$current$na2 : '';
|
|
377
|
+
if (!form || !isFieldElement(field) || field.form !== form || !field.name.startsWith(prefix)) {
|
|
378
|
+
return;
|
|
379
|
+
}
|
|
380
|
+
var [index, ...paths] = getPaths(prefix.length > 0 ? field.name.slice(prefix.length) : field.name);
|
|
381
|
+
|
|
382
|
+
// Update the error only if the field belongs to the fieldset
|
|
383
|
+
if (typeof index === 'number' && paths.length === 0) {
|
|
384
|
+
if (field.dataset.conformTouched) {
|
|
385
|
+
setError(prev => {
|
|
386
|
+
var _prev$index;
|
|
387
|
+
var prevMessage = (_prev$index = prev === null || prev === void 0 ? void 0 : prev[index]) !== null && _prev$index !== void 0 ? _prev$index : '';
|
|
388
|
+
if (prevMessage === field.validationMessage) {
|
|
389
|
+
return prev;
|
|
390
|
+
}
|
|
391
|
+
return [...prev.slice(0, index), field.validationMessage, ...prev.slice(index + 1)];
|
|
392
|
+
});
|
|
393
|
+
}
|
|
394
|
+
event.preventDefault();
|
|
395
|
+
}
|
|
396
|
+
};
|
|
449
397
|
var submitHandler = event => {
|
|
450
398
|
var form = getFormElement(ref.current);
|
|
451
399
|
if (!form || event.target !== form || !(event.submitter instanceof HTMLButtonElement) || event.submitter.name !== 'conform/list') {
|
|
@@ -472,6 +420,22 @@ function useFieldList(ref, config) {
|
|
|
472
420
|
}
|
|
473
421
|
}
|
|
474
422
|
});
|
|
423
|
+
setError(error => {
|
|
424
|
+
switch (command.type) {
|
|
425
|
+
case 'append':
|
|
426
|
+
case 'prepend':
|
|
427
|
+
case 'replace':
|
|
428
|
+
return updateList([...error], _objectSpread2(_objectSpread2({}, command), {}, {
|
|
429
|
+
payload: _objectSpread2(_objectSpread2({}, command.payload), {}, {
|
|
430
|
+
defaultValue: undefined
|
|
431
|
+
})
|
|
432
|
+
}));
|
|
433
|
+
default:
|
|
434
|
+
{
|
|
435
|
+
return updateList([...error], command);
|
|
436
|
+
}
|
|
437
|
+
}
|
|
438
|
+
});
|
|
475
439
|
event.preventDefault();
|
|
476
440
|
};
|
|
477
441
|
var resetHandler = event => {
|
|
@@ -486,24 +450,42 @@ function useFieldList(ref, config) {
|
|
|
486
450
|
initialError: []
|
|
487
451
|
});
|
|
488
452
|
setEntries(Object.entries((_fieldConfig$defaultV2 = fieldConfig.defaultValue) !== null && _fieldConfig$defaultV2 !== void 0 ? _fieldConfig$defaultV2 : [undefined]));
|
|
453
|
+
setError([]);
|
|
489
454
|
};
|
|
490
455
|
document.addEventListener('submit', submitHandler, true);
|
|
456
|
+
document.addEventListener('invalid', invalidHandler, true);
|
|
491
457
|
document.addEventListener('reset', resetHandler);
|
|
492
458
|
return () => {
|
|
493
459
|
document.removeEventListener('submit', submitHandler, true);
|
|
460
|
+
document.removeEventListener('invalid', invalidHandler, true);
|
|
494
461
|
document.removeEventListener('reset', resetHandler);
|
|
495
462
|
};
|
|
496
463
|
}, [ref]);
|
|
497
|
-
return
|
|
498
|
-
|
|
499
|
-
|
|
464
|
+
return entries.map((_ref3, index) => {
|
|
465
|
+
var [key, defaultValue] = _ref3;
|
|
466
|
+
var fieldConfig = {
|
|
467
|
+
name: "".concat(config.name, "[").concat(index, "]"),
|
|
468
|
+
defaultValue: defaultValue !== null && defaultValue !== void 0 ? defaultValue : uncontrolledState.defaultValue[index],
|
|
469
|
+
initialError: uncontrolledState.initialError[index]
|
|
470
|
+
};
|
|
471
|
+
if (config.form) {
|
|
472
|
+
fieldConfig.form = config.form;
|
|
473
|
+
fieldConfig.id = "".concat(config.form, "-").concat(config.name);
|
|
474
|
+
fieldConfig.errorId = "".concat(fieldConfig.id, "-error");
|
|
475
|
+
}
|
|
476
|
+
return {
|
|
477
|
+
key,
|
|
478
|
+
error: error[index],
|
|
479
|
+
config: fieldConfig
|
|
480
|
+
};
|
|
481
|
+
});
|
|
500
482
|
}
|
|
501
483
|
/**
|
|
502
484
|
* Returns the properties required to configure a shadow input for validation.
|
|
503
485
|
* This is particular useful when integrating dropdown and datepicker whichs
|
|
504
486
|
* introduces custom input mode.
|
|
505
487
|
*
|
|
506
|
-
* @see https://
|
|
488
|
+
* @see https://conform.guide/api/react#usecontrolledinput
|
|
507
489
|
*/
|
|
508
490
|
function useControlledInput(config) {
|
|
509
491
|
var _config$defaultValue4;
|
package/module/index.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
export { getFormElements, hasError, parse, shouldValidate } from '@conform-to/dom';
|
|
1
|
+
export { getFormElements, hasError, list, parse, requestCommand, requestSubmit, shouldValidate, validate } 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.
|
|
5
|
+
"version": "0.5.0",
|
|
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.
|
|
22
|
+
"@conform-to/dom": "0.5.0"
|
|
23
23
|
},
|
|
24
24
|
"peerDependencies": {
|
|
25
25
|
"react": ">=16.8"
|