@conform-to/react 0.6.1 → 0.6.3-pre.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/base.d.ts +0 -0
- package/helpers.d.ts +5 -4
- package/helpers.js +3 -8
- package/hooks.d.ts +69 -18
- package/hooks.js +342 -218
- package/index.d.ts +2 -2
- package/index.js +1 -4
- package/module/helpers.js +2 -1
- package/module/hooks.js +338 -220
- package/module/index.js +2 -2
- package/package.json +2 -2
package/hooks.js
CHANGED
|
@@ -6,6 +6,122 @@ var _rollupPluginBabelHelpers = require('./_virtual/_rollupPluginBabelHelpers.js
|
|
|
6
6
|
var dom = require('@conform-to/dom');
|
|
7
7
|
var react = require('react');
|
|
8
8
|
|
|
9
|
+
/**
|
|
10
|
+
* Normalize error to an array of string.
|
|
11
|
+
*/
|
|
12
|
+
function normalizeError(error) {
|
|
13
|
+
if (!error) {
|
|
14
|
+
// This treat both empty string and undefined as no error.
|
|
15
|
+
return [];
|
|
16
|
+
}
|
|
17
|
+
return [].concat(error);
|
|
18
|
+
}
|
|
19
|
+
function useNoValidate(defaultNoValidate, validateBeforeHydrate) {
|
|
20
|
+
var [noValidate, setNoValidate] = react.useState(defaultNoValidate || !validateBeforeHydrate);
|
|
21
|
+
react.useEffect(() => {
|
|
22
|
+
setNoValidate(true);
|
|
23
|
+
}, []);
|
|
24
|
+
return noValidate;
|
|
25
|
+
}
|
|
26
|
+
function useFormRef(userProvidedRef) {
|
|
27
|
+
var formRef = react.useRef(null);
|
|
28
|
+
return userProvidedRef !== null && userProvidedRef !== void 0 ? userProvidedRef : formRef;
|
|
29
|
+
}
|
|
30
|
+
function useConfigRef(config) {
|
|
31
|
+
var ref = react.useRef(config);
|
|
32
|
+
useSafeLayoutEffect(() => {
|
|
33
|
+
ref.current = config;
|
|
34
|
+
});
|
|
35
|
+
return ref;
|
|
36
|
+
}
|
|
37
|
+
function useFormReporter(ref, lastSubmission) {
|
|
38
|
+
var [submission, setSubmission] = react.useState(lastSubmission);
|
|
39
|
+
var report = react.useCallback((form, submission) => {
|
|
40
|
+
var event = new CustomEvent('conform', {
|
|
41
|
+
detail: submission.intent
|
|
42
|
+
});
|
|
43
|
+
form.dispatchEvent(event);
|
|
44
|
+
setSubmission(submission);
|
|
45
|
+
}, []);
|
|
46
|
+
react.useEffect(() => {
|
|
47
|
+
var form = ref.current;
|
|
48
|
+
if (!form || !lastSubmission) {
|
|
49
|
+
return;
|
|
50
|
+
}
|
|
51
|
+
report(form, lastSubmission);
|
|
52
|
+
}, [ref, lastSubmission, report]);
|
|
53
|
+
react.useEffect(() => {
|
|
54
|
+
var form = ref.current;
|
|
55
|
+
if (!form || !submission) {
|
|
56
|
+
return;
|
|
57
|
+
}
|
|
58
|
+
reportSubmission(form, submission);
|
|
59
|
+
}, [ref, submission]);
|
|
60
|
+
return report;
|
|
61
|
+
}
|
|
62
|
+
function useFormError(ref, config) {
|
|
63
|
+
var [error, setError] = react.useState(() => {
|
|
64
|
+
if (!config.initialError) {
|
|
65
|
+
return {};
|
|
66
|
+
}
|
|
67
|
+
var result = {};
|
|
68
|
+
for (var [name, message] of Object.entries(config.initialError)) {
|
|
69
|
+
var paths = dom.getPaths(name);
|
|
70
|
+
if (paths.length === 1) {
|
|
71
|
+
result[paths[0]] = normalizeError(message);
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
return result;
|
|
75
|
+
});
|
|
76
|
+
react.useEffect(() => {
|
|
77
|
+
var handleInvalid = event => {
|
|
78
|
+
var form = dom.getFormElement(ref.current);
|
|
79
|
+
var element = event.target;
|
|
80
|
+
if (!dom.isFieldElement(element) || element.form !== form || !element.dataset.conformTouched) {
|
|
81
|
+
return;
|
|
82
|
+
}
|
|
83
|
+
var key = element.name;
|
|
84
|
+
if (config.name) {
|
|
85
|
+
var scopePaths = dom.getPaths(config.name);
|
|
86
|
+
var fieldPaths = dom.getPaths(element.name);
|
|
87
|
+
for (var i = 0; i <= scopePaths.length; i++) {
|
|
88
|
+
var path = fieldPaths[i];
|
|
89
|
+
if (i < scopePaths.length) {
|
|
90
|
+
// Skip if the field is not in the scope
|
|
91
|
+
if (path !== scopePaths[i]) {
|
|
92
|
+
return;
|
|
93
|
+
}
|
|
94
|
+
} else {
|
|
95
|
+
key = path;
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
setError(prev => {
|
|
100
|
+
if (element.validationMessage === dom.getValidationMessage(prev[key])) {
|
|
101
|
+
return prev;
|
|
102
|
+
}
|
|
103
|
+
return _rollupPluginBabelHelpers.objectSpread2(_rollupPluginBabelHelpers.objectSpread2({}, prev), {}, {
|
|
104
|
+
[key]: dom.getErrors(element.validationMessage)
|
|
105
|
+
});
|
|
106
|
+
});
|
|
107
|
+
event.preventDefault();
|
|
108
|
+
};
|
|
109
|
+
var handleReset = event => {
|
|
110
|
+
var form = dom.getFormElement(ref.current);
|
|
111
|
+
if (form && event.target === form) {
|
|
112
|
+
setError({});
|
|
113
|
+
}
|
|
114
|
+
};
|
|
115
|
+
document.addEventListener('reset', handleReset);
|
|
116
|
+
document.addEventListener('invalid', handleInvalid, true);
|
|
117
|
+
return () => {
|
|
118
|
+
document.removeEventListener('reset', handleReset);
|
|
119
|
+
document.removeEventListener('invalid', handleInvalid, true);
|
|
120
|
+
};
|
|
121
|
+
}, [ref, config.name]);
|
|
122
|
+
return [error, setError];
|
|
123
|
+
}
|
|
124
|
+
|
|
9
125
|
/**
|
|
10
126
|
* Returns properties required to hook into form events.
|
|
11
127
|
* Applied custom validation and define when error should be reported.
|
|
@@ -13,16 +129,15 @@ var react = require('react');
|
|
|
13
129
|
* @see https://conform.guide/api/react#useform
|
|
14
130
|
*/
|
|
15
131
|
function useForm() {
|
|
16
|
-
var
|
|
132
|
+
var _ref, _config$lastSubmissio2;
|
|
17
133
|
var config = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};
|
|
18
|
-
var configRef =
|
|
19
|
-
var
|
|
20
|
-
var
|
|
134
|
+
var configRef = useConfigRef(config);
|
|
135
|
+
var ref = useFormRef(config.ref);
|
|
136
|
+
var noValidate = useNoValidate(config.noValidate, config.fallbackNative);
|
|
137
|
+
var report = useFormReporter(ref, config.lastSubmission);
|
|
21
138
|
var [errors, setErrors] = react.useState(() => {
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
}
|
|
25
|
-
return [].concat(config.lastSubmission.error['']);
|
|
139
|
+
var _config$lastSubmissio;
|
|
140
|
+
return normalizeError((_config$lastSubmissio = config.lastSubmission) === null || _config$lastSubmissio === void 0 ? void 0 : _config$lastSubmissio.error['']);
|
|
26
141
|
});
|
|
27
142
|
var initialError = react.useMemo(() => {
|
|
28
143
|
var submission = config.lastSubmission;
|
|
@@ -30,87 +145,37 @@ function useForm() {
|
|
|
30
145
|
return {};
|
|
31
146
|
}
|
|
32
147
|
var scope = dom.getScope(submission.intent);
|
|
33
|
-
return
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
result[name] = message;
|
|
37
|
-
}
|
|
38
|
-
return result;
|
|
39
|
-
}, {});
|
|
148
|
+
return scope === null ? submission.error : {
|
|
149
|
+
[scope]: submission.error[scope]
|
|
150
|
+
};
|
|
40
151
|
}, [config.lastSubmission]);
|
|
41
|
-
var ref = (_config$ref = config.ref) !== null && _config$ref !== void 0 ? _config$ref : formRef;
|
|
42
152
|
var fieldset = useFieldset(ref, {
|
|
43
|
-
defaultValue: (
|
|
153
|
+
defaultValue: (_ref = (_config$lastSubmissio2 = config.lastSubmission) === null || _config$lastSubmissio2 === void 0 ? void 0 : _config$lastSubmissio2.payload) !== null && _ref !== void 0 ? _ref : config.defaultValue,
|
|
44
154
|
initialError,
|
|
45
155
|
constraint: config.constraint,
|
|
46
156
|
form: config.id
|
|
47
157
|
});
|
|
48
|
-
var [noValidate, setNoValidate] = react.useState(config.noValidate || !config.fallbackNative);
|
|
49
|
-
useSafeLayoutEffect(() => {
|
|
50
|
-
configRef.current = config;
|
|
51
|
-
});
|
|
52
|
-
react.useEffect(() => {
|
|
53
|
-
setNoValidate(true);
|
|
54
|
-
}, []);
|
|
55
|
-
react.useEffect(() => {
|
|
56
|
-
var form = ref.current;
|
|
57
|
-
var submission = config.lastSubmission;
|
|
58
|
-
if (!form || !submission) {
|
|
59
|
-
return;
|
|
60
|
-
}
|
|
61
|
-
var listCommand = dom.parseListCommand(submission.intent);
|
|
62
|
-
if (listCommand) {
|
|
63
|
-
form.dispatchEvent(new CustomEvent('conform/list', {
|
|
64
|
-
detail: submission.intent
|
|
65
|
-
}));
|
|
66
|
-
}
|
|
67
|
-
setLastSubmission(submission);
|
|
68
|
-
}, [ref, config.lastSubmission]);
|
|
69
158
|
react.useEffect(() => {
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
return;
|
|
73
|
-
}
|
|
74
|
-
dom.reportSubmission(form, lastSubmission);
|
|
75
|
-
}, [ref, lastSubmission]);
|
|
76
|
-
react.useEffect(() => {
|
|
77
|
-
// Revalidate the form when input value is changed
|
|
78
|
-
var handleInput = event => {
|
|
79
|
-
var field = event.target;
|
|
80
|
-
var form = ref.current;
|
|
81
|
-
var formConfig = configRef.current;
|
|
82
|
-
var {
|
|
83
|
-
initialReport = 'onSubmit',
|
|
84
|
-
shouldValidate = initialReport === 'onChange' ? 'onInput' : initialReport,
|
|
85
|
-
shouldRevalidate = 'onInput'
|
|
86
|
-
} = formConfig;
|
|
87
|
-
if (!form || !dom.isFieldElement(field) || field.form !== form) {
|
|
88
|
-
return;
|
|
89
|
-
}
|
|
90
|
-
if (field.dataset.conformTouched ? shouldRevalidate === 'onInput' : shouldValidate === 'onInput') {
|
|
91
|
-
dom.requestIntent(form, dom.validate(field.name));
|
|
92
|
-
}
|
|
93
|
-
};
|
|
94
|
-
var handleBlur = event => {
|
|
159
|
+
// custom validate handler
|
|
160
|
+
var createValidateHandler = name => event => {
|
|
95
161
|
var field = event.target;
|
|
96
162
|
var form = ref.current;
|
|
97
|
-
var formConfig = configRef.current;
|
|
98
163
|
var {
|
|
99
164
|
initialReport = 'onSubmit',
|
|
100
165
|
shouldValidate = initialReport === 'onChange' ? 'onInput' : initialReport,
|
|
101
166
|
shouldRevalidate = 'onInput'
|
|
102
|
-
} =
|
|
103
|
-
if (!form || !dom.
|
|
167
|
+
} = configRef.current;
|
|
168
|
+
if (!form || !dom.isFocusableFormControl(field) || field.form !== form || !field.name) {
|
|
104
169
|
return;
|
|
105
170
|
}
|
|
106
|
-
if (field.dataset.conformTouched ? shouldRevalidate ===
|
|
171
|
+
if (field.dataset.conformTouched ? shouldRevalidate === name : shouldValidate === name) {
|
|
107
172
|
dom.requestIntent(form, dom.validate(field.name));
|
|
108
173
|
}
|
|
109
174
|
};
|
|
110
175
|
var handleInvalid = event => {
|
|
111
176
|
var form = ref.current;
|
|
112
177
|
var field = event.target;
|
|
113
|
-
if (!form || !dom.isFieldElement(field) || field.form !== form || field.name !==
|
|
178
|
+
if (!form || !dom.isFieldElement(field) || field.form !== form || field.name !== FORM_ERROR_ELEMENT_NAME) {
|
|
114
179
|
return;
|
|
115
180
|
}
|
|
116
181
|
event.preventDefault();
|
|
@@ -125,14 +190,14 @@ function useForm() {
|
|
|
125
190
|
}
|
|
126
191
|
|
|
127
192
|
// Reset all field state
|
|
128
|
-
for (var
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
field.setCustomValidity('');
|
|
132
|
-
}
|
|
193
|
+
for (var element of dom.getFormControls(form)) {
|
|
194
|
+
delete element.dataset.conformTouched;
|
|
195
|
+
element.setCustomValidity('');
|
|
133
196
|
}
|
|
134
197
|
setErrors([]);
|
|
135
198
|
};
|
|
199
|
+
var handleInput = createValidateHandler('onInput');
|
|
200
|
+
var handleBlur = createValidateHandler('onBlur');
|
|
136
201
|
document.addEventListener('input', handleInput, true);
|
|
137
202
|
document.addEventListener('blur', handleBlur, true);
|
|
138
203
|
document.addEventListener('invalid', handleInvalid, true);
|
|
@@ -143,7 +208,7 @@ function useForm() {
|
|
|
143
208
|
document.removeEventListener('invalid', handleInvalid, true);
|
|
144
209
|
document.removeEventListener('reset', handleReset);
|
|
145
210
|
};
|
|
146
|
-
}, [ref]);
|
|
211
|
+
}, [ref, configRef]);
|
|
147
212
|
var form = {
|
|
148
213
|
ref,
|
|
149
214
|
error: errors[0],
|
|
@@ -159,34 +224,32 @@ function useForm() {
|
|
|
159
224
|
return;
|
|
160
225
|
}
|
|
161
226
|
try {
|
|
162
|
-
var _config$onValidate;
|
|
227
|
+
var _config$onValidate, _config$onValidate2;
|
|
163
228
|
var formData = dom.getFormData(form, submitter);
|
|
164
|
-
var
|
|
165
|
-
var submission = getSubmission({
|
|
229
|
+
var submission = (_config$onValidate = (_config$onValidate2 = config.onValidate) === null || _config$onValidate2 === void 0 ? void 0 : _config$onValidate2.call(config, {
|
|
166
230
|
form,
|
|
167
231
|
formData
|
|
168
|
-
});
|
|
169
|
-
|
|
170
|
-
var [, message] =
|
|
171
|
-
return
|
|
172
|
-
}
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
detail: submission.intent
|
|
180
|
-
}));
|
|
181
|
-
}
|
|
182
|
-
setLastSubmission(submission);
|
|
232
|
+
})) !== null && _config$onValidate !== void 0 ? _config$onValidate : dom.parse(formData);
|
|
233
|
+
var messages = Object.entries(submission.error).reduce((messages, _ref2) => {
|
|
234
|
+
var [, message] = _ref2;
|
|
235
|
+
return messages.concat(normalizeError(message));
|
|
236
|
+
}, []);
|
|
237
|
+
var shouldValidate = !config.noValidate && !(submitter !== null && submitter !== void 0 && submitter.formNoValidate);
|
|
238
|
+
var shouldFallbackToServer = messages.includes(VALIDATION_UNDEFINED);
|
|
239
|
+
var hasClientValidation = typeof config.onValidate !== 'undefined';
|
|
240
|
+
var isValid = messages.length === 0;
|
|
241
|
+
if (hasClientValidation && (dom.isSubmitting(submission.intent) ? shouldValidate && !isValid : !shouldFallbackToServer)) {
|
|
242
|
+
report(form, submission);
|
|
183
243
|
event.preventDefault();
|
|
184
244
|
} else {
|
|
185
245
|
var _config$onSubmit;
|
|
186
|
-
(_config$onSubmit = config.onSubmit) === null || _config$onSubmit === void 0 ? void 0 : _config$onSubmit.call(config, event,
|
|
246
|
+
(_config$onSubmit = config.onSubmit) === null || _config$onSubmit === void 0 ? void 0 : _config$onSubmit.call(config, event, {
|
|
187
247
|
formData,
|
|
188
|
-
submission
|
|
189
|
-
|
|
248
|
+
submission,
|
|
249
|
+
action: dom.getFormAction(nativeEvent),
|
|
250
|
+
encType: dom.getFormEncType(nativeEvent),
|
|
251
|
+
method: dom.getFormMethod(nativeEvent)
|
|
252
|
+
});
|
|
190
253
|
}
|
|
191
254
|
} catch (e) {
|
|
192
255
|
console.warn(e);
|
|
@@ -211,67 +274,10 @@ function useForm() {
|
|
|
211
274
|
*/
|
|
212
275
|
|
|
213
276
|
function useFieldset(ref, config) {
|
|
214
|
-
var
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
if (!initialError) {
|
|
218
|
-
return {};
|
|
219
|
-
}
|
|
220
|
-
var result = {};
|
|
221
|
-
for (var [name, message] of Object.entries(initialError)) {
|
|
222
|
-
var [key, ...paths] = dom.getPaths(name);
|
|
223
|
-
if (typeof key === 'string' && paths.length === 0) {
|
|
224
|
-
result[key] = [].concat(message !== null && message !== void 0 ? message : []);
|
|
225
|
-
}
|
|
226
|
-
}
|
|
227
|
-
return result;
|
|
228
|
-
});
|
|
229
|
-
useSafeLayoutEffect(() => {
|
|
230
|
-
configRef.current = config;
|
|
277
|
+
var [error] = useFormError(ref, {
|
|
278
|
+
initialError: config.initialError,
|
|
279
|
+
name: config.name
|
|
231
280
|
});
|
|
232
|
-
react.useEffect(() => {
|
|
233
|
-
var invalidHandler = event => {
|
|
234
|
-
var _configRef$current$na;
|
|
235
|
-
var form = dom.getFormElement(ref.current);
|
|
236
|
-
var field = event.target;
|
|
237
|
-
var fieldsetName = (_configRef$current$na = configRef.current.name) !== null && _configRef$current$na !== void 0 ? _configRef$current$na : '';
|
|
238
|
-
if (!form || !dom.isFieldElement(field) || field.form !== form || !field.name.startsWith(fieldsetName)) {
|
|
239
|
-
return;
|
|
240
|
-
}
|
|
241
|
-
var [key, ...paths] = dom.getPaths(fieldsetName.length > 0 ? field.name.slice(fieldsetName.length + 1) : field.name);
|
|
242
|
-
|
|
243
|
-
// Update the error only if the field belongs to the fieldset
|
|
244
|
-
if (typeof key === 'string' && paths.length === 0) {
|
|
245
|
-
if (field.dataset.conformTouched) {
|
|
246
|
-
setError(prev => {
|
|
247
|
-
var prevMessage = dom.getValidationMessage(prev === null || prev === void 0 ? void 0 : prev[key]);
|
|
248
|
-
if (prevMessage === field.validationMessage) {
|
|
249
|
-
return prev;
|
|
250
|
-
}
|
|
251
|
-
return _rollupPluginBabelHelpers.objectSpread2(_rollupPluginBabelHelpers.objectSpread2({}, prev), {}, {
|
|
252
|
-
[key]: dom.getErrors(field.validationMessage)
|
|
253
|
-
});
|
|
254
|
-
});
|
|
255
|
-
}
|
|
256
|
-
event.preventDefault();
|
|
257
|
-
}
|
|
258
|
-
};
|
|
259
|
-
var resetHandler = event => {
|
|
260
|
-
var form = dom.getFormElement(ref.current);
|
|
261
|
-
if (!form || event.target !== form) {
|
|
262
|
-
return;
|
|
263
|
-
}
|
|
264
|
-
setError({});
|
|
265
|
-
};
|
|
266
|
-
|
|
267
|
-
// The invalid event does not bubble and so listening on the capturing pharse is needed
|
|
268
|
-
document.addEventListener('invalid', invalidHandler, true);
|
|
269
|
-
document.addEventListener('reset', resetHandler);
|
|
270
|
-
return () => {
|
|
271
|
-
document.removeEventListener('invalid', invalidHandler, true);
|
|
272
|
-
document.removeEventListener('reset', resetHandler);
|
|
273
|
-
};
|
|
274
|
-
}, [ref]);
|
|
275
281
|
|
|
276
282
|
/**
|
|
277
283
|
* This allows us constructing the field at runtime as we have no information
|
|
@@ -287,8 +293,8 @@ function useFieldset(ref, config) {
|
|
|
287
293
|
var fieldsetConfig = config;
|
|
288
294
|
var constraint = (_fieldsetConfig$const = fieldsetConfig.constraint) === null || _fieldsetConfig$const === void 0 ? void 0 : _fieldsetConfig$const[key];
|
|
289
295
|
var errors = error === null || error === void 0 ? void 0 : error[key];
|
|
290
|
-
var initialError = Object.entries((_fieldsetConfig$initi = fieldsetConfig.initialError) !== null && _fieldsetConfig$initi !== void 0 ? _fieldsetConfig$initi : {}).reduce((result,
|
|
291
|
-
var [name, message] =
|
|
296
|
+
var initialError = Object.entries((_fieldsetConfig$initi = fieldsetConfig.initialError) !== null && _fieldsetConfig$initi !== void 0 ? _fieldsetConfig$initi : {}).reduce((result, _ref3) => {
|
|
297
|
+
var [name, message] = _ref3;
|
|
292
298
|
var [field, ...paths] = dom.getPaths(name);
|
|
293
299
|
if (field === key) {
|
|
294
300
|
result[dom.getName(paths)] = message;
|
|
@@ -297,6 +303,7 @@ function useFieldset(ref, config) {
|
|
|
297
303
|
}, {});
|
|
298
304
|
var field = _rollupPluginBabelHelpers.objectSpread2(_rollupPluginBabelHelpers.objectSpread2({}, constraint), {}, {
|
|
299
305
|
name: fieldsetConfig.name ? "".concat(fieldsetConfig.name, ".").concat(key) : key,
|
|
306
|
+
// @ts-expect-error The FieldValue type might need a rework
|
|
300
307
|
defaultValue: (_fieldsetConfig$defau = fieldsetConfig.defaultValue) === null || _fieldsetConfig$defau === void 0 ? void 0 : _fieldsetConfig$defau[key],
|
|
301
308
|
initialError,
|
|
302
309
|
error: errors === null || errors === void 0 ? void 0 : errors[0],
|
|
@@ -314,57 +321,22 @@ function useFieldset(ref, config) {
|
|
|
314
321
|
}
|
|
315
322
|
|
|
316
323
|
/**
|
|
317
|
-
* Returns a list of key and config
|
|
318
|
-
* configuring buttons for list manipulation
|
|
324
|
+
* Returns a list of key and field config.
|
|
319
325
|
*
|
|
320
326
|
* @see https://conform.guide/api/react#usefieldlist
|
|
321
327
|
*/
|
|
322
328
|
function useFieldList(ref, config) {
|
|
323
|
-
var configRef =
|
|
324
|
-
var [error, setError] =
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
var _config$initialError;
|
|
328
|
-
var [index, ...paths] = dom.getPaths(name);
|
|
329
|
-
if (typeof index === 'number' && paths.length === 0) {
|
|
330
|
-
initialError[index] = [].concat(message !== null && message !== void 0 ? message : []);
|
|
331
|
-
}
|
|
332
|
-
}
|
|
333
|
-
return initialError;
|
|
329
|
+
var configRef = useConfigRef(config);
|
|
330
|
+
var [error, setError] = useFormError(ref, {
|
|
331
|
+
initialError: config.initialError,
|
|
332
|
+
name: config.name
|
|
334
333
|
});
|
|
335
334
|
var [entries, setEntries] = react.useState(() => {
|
|
336
335
|
var _config$defaultValue;
|
|
337
336
|
return Object.entries((_config$defaultValue = config.defaultValue) !== null && _config$defaultValue !== void 0 ? _config$defaultValue : [undefined]);
|
|
338
337
|
});
|
|
339
|
-
useSafeLayoutEffect(() => {
|
|
340
|
-
configRef.current = config;
|
|
341
|
-
});
|
|
342
338
|
react.useEffect(() => {
|
|
343
|
-
var
|
|
344
|
-
var _configRef$current$na2;
|
|
345
|
-
var form = dom.getFormElement(ref.current);
|
|
346
|
-
var field = event.target;
|
|
347
|
-
var prefix = (_configRef$current$na2 = configRef.current.name) !== null && _configRef$current$na2 !== void 0 ? _configRef$current$na2 : '';
|
|
348
|
-
if (!form || !dom.isFieldElement(field) || field.form !== form || !field.name.startsWith(prefix)) {
|
|
349
|
-
return;
|
|
350
|
-
}
|
|
351
|
-
var [index, ...paths] = dom.getPaths(prefix.length > 0 ? field.name.slice(prefix.length) : field.name);
|
|
352
|
-
|
|
353
|
-
// Update the error only if the field belongs to the fieldset
|
|
354
|
-
if (typeof index === 'number' && paths.length === 0) {
|
|
355
|
-
if (field.dataset.conformTouched) {
|
|
356
|
-
setError(prev => {
|
|
357
|
-
var prevMessage = dom.getValidationMessage(prev === null || prev === void 0 ? void 0 : prev[index]);
|
|
358
|
-
if (prevMessage === field.validationMessage) {
|
|
359
|
-
return prev;
|
|
360
|
-
}
|
|
361
|
-
return [...prev.slice(0, index), dom.getErrors(field.validationMessage), ...prev.slice(index + 1)];
|
|
362
|
-
});
|
|
363
|
-
}
|
|
364
|
-
event.preventDefault();
|
|
365
|
-
}
|
|
366
|
-
};
|
|
367
|
-
var listHandler = event => {
|
|
339
|
+
var conformHandler = event => {
|
|
368
340
|
var form = dom.getFormElement(ref.current);
|
|
369
341
|
if (!form || event.target !== form) {
|
|
370
342
|
return;
|
|
@@ -393,20 +365,29 @@ function useFieldList(ref, config) {
|
|
|
393
365
|
}
|
|
394
366
|
});
|
|
395
367
|
setError(error => {
|
|
368
|
+
var errorList = [];
|
|
369
|
+
for (var [key, messages] of Object.entries(error)) {
|
|
370
|
+
if (typeof key === 'number') {
|
|
371
|
+
errorList[key] = messages;
|
|
372
|
+
}
|
|
373
|
+
}
|
|
396
374
|
switch (command.type) {
|
|
397
375
|
case 'append':
|
|
398
376
|
case 'prepend':
|
|
399
377
|
case 'replace':
|
|
400
|
-
|
|
378
|
+
errorList = dom.updateList(errorList, _rollupPluginBabelHelpers.objectSpread2(_rollupPluginBabelHelpers.objectSpread2({}, command), {}, {
|
|
401
379
|
payload: _rollupPluginBabelHelpers.objectSpread2(_rollupPluginBabelHelpers.objectSpread2({}, command.payload), {}, {
|
|
402
380
|
defaultValue: undefined
|
|
403
381
|
})
|
|
404
382
|
}));
|
|
383
|
+
break;
|
|
405
384
|
default:
|
|
406
385
|
{
|
|
407
|
-
|
|
386
|
+
errorList = dom.updateList(errorList, command);
|
|
387
|
+
break;
|
|
408
388
|
}
|
|
409
389
|
}
|
|
390
|
+
return Object.assign({}, errorList);
|
|
410
391
|
});
|
|
411
392
|
};
|
|
412
393
|
var resetHandler = event => {
|
|
@@ -416,26 +397,23 @@ function useFieldList(ref, config) {
|
|
|
416
397
|
return;
|
|
417
398
|
}
|
|
418
399
|
setEntries(Object.entries((_configRef$current$de = configRef.current.defaultValue) !== null && _configRef$current$de !== void 0 ? _configRef$current$de : [undefined]));
|
|
419
|
-
setError([]);
|
|
420
400
|
};
|
|
421
401
|
|
|
422
|
-
// @ts-expect-error Custom event: conform
|
|
423
|
-
document.addEventListener('conform
|
|
424
|
-
document.addEventListener('invalid', invalidHandler, true);
|
|
402
|
+
// @ts-expect-error Custom event: conform
|
|
403
|
+
document.addEventListener('conform', conformHandler, true);
|
|
425
404
|
document.addEventListener('reset', resetHandler);
|
|
426
405
|
return () => {
|
|
427
|
-
// @ts-expect-error Custom event: conform
|
|
428
|
-
document.removeEventListener('conform
|
|
429
|
-
document.removeEventListener('invalid', invalidHandler, true);
|
|
406
|
+
// @ts-expect-error Custom event: conform
|
|
407
|
+
document.removeEventListener('conform', conformHandler, true);
|
|
430
408
|
document.removeEventListener('reset', resetHandler);
|
|
431
409
|
};
|
|
432
|
-
}, [ref]);
|
|
433
|
-
return entries.map((
|
|
434
|
-
var _config$
|
|
435
|
-
var [key, defaultValue] =
|
|
410
|
+
}, [ref, configRef, setError]);
|
|
411
|
+
return entries.map((_ref4, index) => {
|
|
412
|
+
var _config$initialError, _config$defaultValue2;
|
|
413
|
+
var [key, defaultValue] = _ref4;
|
|
436
414
|
var errors = error[index];
|
|
437
|
-
var initialError = Object.entries((_config$
|
|
438
|
-
var [name, message] =
|
|
415
|
+
var initialError = Object.entries((_config$initialError = config.initialError) !== null && _config$initialError !== void 0 ? _config$initialError : {}).reduce((result, _ref5) => {
|
|
416
|
+
var [name, message] = _ref5;
|
|
439
417
|
var [field, ...paths] = dom.getPaths(name);
|
|
440
418
|
if (field === index) {
|
|
441
419
|
result[dom.getName(paths)] = message;
|
|
@@ -497,13 +475,10 @@ function setNativeValue(element, value) {
|
|
|
497
475
|
var useSafeLayoutEffect = typeof document === 'undefined' ? react.useEffect : react.useLayoutEffect;
|
|
498
476
|
function useInputEvent(options) {
|
|
499
477
|
var ref = react.useRef(null);
|
|
500
|
-
var optionsRef =
|
|
478
|
+
var optionsRef = useConfigRef(options);
|
|
501
479
|
var changeDispatched = react.useRef(false);
|
|
502
480
|
var focusDispatched = react.useRef(false);
|
|
503
481
|
var blurDispatched = react.useRef(false);
|
|
504
|
-
useSafeLayoutEffect(() => {
|
|
505
|
-
optionsRef.current = options;
|
|
506
|
-
});
|
|
507
482
|
useSafeLayoutEffect(() => {
|
|
508
483
|
var getInputElement = () => {
|
|
509
484
|
var _optionsRef$current$g, _optionsRef$current, _optionsRef$current$g2;
|
|
@@ -632,11 +607,160 @@ function useInputEvent(options) {
|
|
|
632
607
|
blurDispatched.current = false;
|
|
633
608
|
}
|
|
634
609
|
};
|
|
635
|
-
}, []);
|
|
610
|
+
}, [optionsRef]);
|
|
636
611
|
return [ref, control];
|
|
637
612
|
}
|
|
613
|
+
var VALIDATION_UNDEFINED = '__undefined__';
|
|
614
|
+
var VALIDATION_SKIPPED = '__skipped__';
|
|
615
|
+
var FORM_ERROR_ELEMENT_NAME = '__form__';
|
|
616
|
+
|
|
617
|
+
/**
|
|
618
|
+
* Validate the form with the Constraint Validation API
|
|
619
|
+
* @see https://conform.guide/api/react#validateconstraint
|
|
620
|
+
*/
|
|
621
|
+
function validateConstraint(options) {
|
|
622
|
+
var _options$formData, _options$formatMessag;
|
|
623
|
+
var formData = (_options$formData = options === null || options === void 0 ? void 0 : options.formData) !== null && _options$formData !== void 0 ? _options$formData : new FormData(options.form);
|
|
624
|
+
var getDefaultErrors = (validity, result) => {
|
|
625
|
+
var errors = [];
|
|
626
|
+
if (validity.valueMissing) errors.push('required');
|
|
627
|
+
if (validity.typeMismatch || validity.badInput) errors.push('type');
|
|
628
|
+
if (validity.tooShort) errors.push('minLength');
|
|
629
|
+
if (validity.rangeUnderflow) errors.push('min');
|
|
630
|
+
if (validity.stepMismatch) errors.push('step');
|
|
631
|
+
if (validity.tooLong) errors.push('maxLength');
|
|
632
|
+
if (validity.rangeOverflow) errors.push('max');
|
|
633
|
+
if (validity.patternMismatch) errors.push('pattern');
|
|
634
|
+
for (var [constraintName, valid] of Object.entries(result)) {
|
|
635
|
+
if (!valid) {
|
|
636
|
+
errors.push(constraintName);
|
|
637
|
+
}
|
|
638
|
+
}
|
|
639
|
+
return errors;
|
|
640
|
+
};
|
|
641
|
+
var formatMessages = (_options$formatMessag = options === null || options === void 0 ? void 0 : options.formatMessages) !== null && _options$formatMessag !== void 0 ? _options$formatMessag : _ref6 => {
|
|
642
|
+
var {
|
|
643
|
+
defaultErrors
|
|
644
|
+
} = _ref6;
|
|
645
|
+
return defaultErrors;
|
|
646
|
+
};
|
|
647
|
+
return dom.parse(formData, {
|
|
648
|
+
resolve(payload, intent) {
|
|
649
|
+
var error = {};
|
|
650
|
+
var constraintPattern = /^constraint[A-Z][^A-Z]*$/;
|
|
651
|
+
var _loop = function _loop(element) {
|
|
652
|
+
if (dom.isFieldElement(element)) {
|
|
653
|
+
var _options$acceptMultip, _options$acceptMultip2;
|
|
654
|
+
var name = element.name !== FORM_ERROR_ELEMENT_NAME ? element.name : '';
|
|
655
|
+
var constraint = Object.entries(element.dataset).reduce((result, _ref7) => {
|
|
656
|
+
var [name, attributeValue = ''] = _ref7;
|
|
657
|
+
if (constraintPattern.test(name)) {
|
|
658
|
+
var _options$constraint;
|
|
659
|
+
var constraintName = name.slice(10).toLowerCase();
|
|
660
|
+
var _validate = (_options$constraint = options.constraint) === null || _options$constraint === void 0 ? void 0 : _options$constraint[constraintName];
|
|
661
|
+
if (typeof _validate === 'function') {
|
|
662
|
+
result[constraintName] = _validate(element.value, {
|
|
663
|
+
formData,
|
|
664
|
+
attributeValue
|
|
665
|
+
});
|
|
666
|
+
} else {
|
|
667
|
+
console.warn("Found an \"".concat(constraintName, "\" constraint with undefined definition; Please specify it on the validateConstraint API."));
|
|
668
|
+
}
|
|
669
|
+
}
|
|
670
|
+
return result;
|
|
671
|
+
}, {});
|
|
672
|
+
var errors = formatMessages({
|
|
673
|
+
name,
|
|
674
|
+
validity: element.validity,
|
|
675
|
+
constraint,
|
|
676
|
+
defaultErrors: getDefaultErrors(element.validity, constraint)
|
|
677
|
+
});
|
|
678
|
+
var shouldAcceptMultipleErrors = (_options$acceptMultip = options === null || options === void 0 ? void 0 : (_options$acceptMultip2 = options.acceptMultipleErrors) === null || _options$acceptMultip2 === void 0 ? void 0 : _options$acceptMultip2.call(options, {
|
|
679
|
+
name,
|
|
680
|
+
payload,
|
|
681
|
+
intent
|
|
682
|
+
})) !== null && _options$acceptMultip !== void 0 ? _options$acceptMultip : false;
|
|
683
|
+
if (errors.length > 0) {
|
|
684
|
+
error[name] = shouldAcceptMultipleErrors ? errors : errors[0];
|
|
685
|
+
}
|
|
686
|
+
}
|
|
687
|
+
};
|
|
688
|
+
for (var element of options.form.elements) {
|
|
689
|
+
_loop(element);
|
|
690
|
+
}
|
|
691
|
+
return {
|
|
692
|
+
error
|
|
693
|
+
};
|
|
694
|
+
}
|
|
695
|
+
});
|
|
696
|
+
}
|
|
697
|
+
function reportSubmission(form, submission) {
|
|
698
|
+
for (var [name, message] of Object.entries(submission.error)) {
|
|
699
|
+
// There is no need to create a placeholder button if all we want is to reset the error
|
|
700
|
+
if (message === '') {
|
|
701
|
+
continue;
|
|
702
|
+
}
|
|
703
|
+
|
|
704
|
+
// We can't use empty string as button name
|
|
705
|
+
// As `form.element.namedItem('')` will always returns null
|
|
706
|
+
var elementName = name ? name : FORM_ERROR_ELEMENT_NAME;
|
|
707
|
+
var item = form.elements.namedItem(elementName);
|
|
708
|
+
if (item instanceof RadioNodeList) {
|
|
709
|
+
for (var field of item) {
|
|
710
|
+
if (field.type !== 'radio') {
|
|
711
|
+
console.warn('Repeated field name is not supported.');
|
|
712
|
+
continue;
|
|
713
|
+
}
|
|
714
|
+
}
|
|
715
|
+
}
|
|
716
|
+
if (item === null) {
|
|
717
|
+
// Create placeholder button to keep the error without contributing to the form data
|
|
718
|
+
var button = document.createElement('button');
|
|
719
|
+
button.name = elementName;
|
|
720
|
+
button.hidden = true;
|
|
721
|
+
button.dataset.conformTouched = 'true';
|
|
722
|
+
form.appendChild(button);
|
|
723
|
+
}
|
|
724
|
+
}
|
|
725
|
+
var scope = dom.getScope(submission.intent);
|
|
726
|
+
for (var element of dom.getFormControls(form)) {
|
|
727
|
+
var _elementName = element.name !== FORM_ERROR_ELEMENT_NAME ? element.name : '';
|
|
728
|
+
var messages = normalizeError(submission.error[_elementName]);
|
|
729
|
+
if (scope === null || scope === _elementName) {
|
|
730
|
+
element.dataset.conformTouched = 'true';
|
|
731
|
+
}
|
|
732
|
+
if (!messages.includes(VALIDATION_SKIPPED) && !messages.includes(VALIDATION_UNDEFINED)) {
|
|
733
|
+
var invalidEvent = new Event('invalid', {
|
|
734
|
+
cancelable: true
|
|
735
|
+
});
|
|
736
|
+
element.setCustomValidity(dom.getValidationMessage(messages));
|
|
737
|
+
element.dispatchEvent(invalidEvent);
|
|
738
|
+
}
|
|
739
|
+
}
|
|
740
|
+
if (dom.isSubmitting(submission.intent) || isFocusedOnIntentButton(form, submission.intent)) {
|
|
741
|
+
if (scope) {
|
|
742
|
+
dom.focusFormControl(form, scope);
|
|
743
|
+
} else {
|
|
744
|
+
dom.focusFirstInvalidControl(form);
|
|
745
|
+
}
|
|
746
|
+
}
|
|
747
|
+
}
|
|
748
|
+
|
|
749
|
+
/**
|
|
750
|
+
* Check if the current focus is on a intent button.
|
|
751
|
+
*/
|
|
752
|
+
function isFocusedOnIntentButton(form, intent) {
|
|
753
|
+
var element = document.activeElement;
|
|
754
|
+
return dom.isFieldElement(element) && element.type === 'submit' && element.form === form && element.name === dom.INTENT && element.value === intent;
|
|
755
|
+
}
|
|
638
756
|
|
|
757
|
+
exports.FORM_ERROR_ELEMENT_NAME = FORM_ERROR_ELEMENT_NAME;
|
|
758
|
+
exports.VALIDATION_SKIPPED = VALIDATION_SKIPPED;
|
|
759
|
+
exports.VALIDATION_UNDEFINED = VALIDATION_UNDEFINED;
|
|
760
|
+
exports.isFocusedOnIntentButton = isFocusedOnIntentButton;
|
|
761
|
+
exports.reportSubmission = reportSubmission;
|
|
639
762
|
exports.useFieldList = useFieldList;
|
|
640
763
|
exports.useFieldset = useFieldset;
|
|
641
764
|
exports.useForm = useForm;
|
|
642
765
|
exports.useInputEvent = useInputEvent;
|
|
766
|
+
exports.validateConstraint = validateConstraint;
|