@conform-to/react 0.9.1 → 1.0.0-pre.1
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/context.d.ts +64 -0
- package/context.js +196 -0
- package/context.mjs +183 -0
- package/helpers.d.ts +206 -50
- package/helpers.js +206 -101
- package/helpers.mjs +199 -85
- package/hooks.d.ts +33 -199
- package/hooks.js +83 -715
- package/hooks.mjs +81 -710
- package/index.d.ts +6 -3
- package/index.js +22 -15
- package/index.mjs +6 -4
- package/integrations.d.ts +18 -0
- package/integrations.js +161 -0
- package/integrations.mjs +155 -0
- package/package.json +3 -3
- package/validitystate.d.ts +12 -0
- package/validitystate.js +38 -0
- package/validitystate.mjs +34 -0
package/hooks.js
CHANGED
|
@@ -2,739 +2,107 @@
|
|
|
2
2
|
|
|
3
3
|
Object.defineProperty(exports, '__esModule', { value: true });
|
|
4
4
|
|
|
5
|
-
var _rollupPluginBabelHelpers = require('./_virtual/_rollupPluginBabelHelpers.js');
|
|
6
5
|
var dom = require('@conform-to/dom');
|
|
7
6
|
var react = require('react');
|
|
7
|
+
var context = require('./context.js');
|
|
8
8
|
|
|
9
9
|
/**
|
|
10
|
-
*
|
|
10
|
+
* useLayoutEffect is client-only.
|
|
11
|
+
* This basically makes it a no-op on server
|
|
11
12
|
*/
|
|
12
|
-
|
|
13
|
-
function
|
|
14
|
-
var
|
|
15
|
-
|
|
16
|
-
setNoValidate(true);
|
|
17
|
-
}, []);
|
|
18
|
-
return noValidate;
|
|
19
|
-
}
|
|
20
|
-
function useFormRef(userProvidedRef) {
|
|
21
|
-
var formRef = react.useRef(null);
|
|
22
|
-
return userProvidedRef !== null && userProvidedRef !== void 0 ? userProvidedRef : formRef;
|
|
13
|
+
var useSafeLayoutEffect = typeof document === 'undefined' ? react.useEffect : react.useLayoutEffect;
|
|
14
|
+
function useFormId(preferredId) {
|
|
15
|
+
var id = react.useId();
|
|
16
|
+
return preferredId !== null && preferredId !== void 0 ? preferredId : id;
|
|
23
17
|
}
|
|
24
|
-
function
|
|
25
|
-
var
|
|
18
|
+
function useNoValidate() {
|
|
19
|
+
var defaultNoValidate = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : true;
|
|
20
|
+
var [noValidate, setNoValidate] = react.useState(defaultNoValidate);
|
|
26
21
|
useSafeLayoutEffect(() => {
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
var [submission, setSubmission] = react.useState(lastSubmission);
|
|
33
|
-
var report = react.useCallback((form, submission) => {
|
|
34
|
-
var event = new CustomEvent('conform', {
|
|
35
|
-
detail: submission.intent
|
|
36
|
-
});
|
|
37
|
-
form.dispatchEvent(event);
|
|
38
|
-
setSubmission(submission);
|
|
39
|
-
}, []);
|
|
40
|
-
react.useEffect(() => {
|
|
41
|
-
var form = ref.current;
|
|
42
|
-
if (!form || !lastSubmission) {
|
|
43
|
-
return;
|
|
44
|
-
}
|
|
45
|
-
if (!lastSubmission.payload) {
|
|
46
|
-
// If the default value is empty, we can safely reset the form.
|
|
47
|
-
// This ensure the behavior is consistent with and without JS.
|
|
48
|
-
form.reset();
|
|
49
|
-
|
|
50
|
-
// There is no need to report the submission anymore.
|
|
51
|
-
return;
|
|
52
|
-
}
|
|
53
|
-
report(form, lastSubmission);
|
|
54
|
-
}, [ref, lastSubmission, report]);
|
|
55
|
-
react.useEffect(() => {
|
|
56
|
-
var form = ref.current;
|
|
57
|
-
if (!form || !submission) {
|
|
58
|
-
return;
|
|
22
|
+
// This is necessary to fix an issue in strict mode with related to our proxy setup
|
|
23
|
+
// It avoids the component from being rerendered without re-rendering the child
|
|
24
|
+
// Which reset the proxy but failed to capture its usage within child component
|
|
25
|
+
if (!noValidate) {
|
|
26
|
+
setNoValidate(true);
|
|
59
27
|
}
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
return report;
|
|
63
|
-
}
|
|
64
|
-
function useFormError(ref, config) {
|
|
65
|
-
var [error, setError] = react.useState(() => {
|
|
66
|
-
if (!config.initialError) {
|
|
67
|
-
return {};
|
|
68
|
-
}
|
|
69
|
-
var result = {};
|
|
70
|
-
for (var [name, message] of Object.entries(config.initialError)) {
|
|
71
|
-
var [path, ...restPaths] = dom.getPaths(name);
|
|
72
|
-
if (typeof path !== 'undefined' && restPaths.length === 0) {
|
|
73
|
-
result[path] = message;
|
|
74
|
-
}
|
|
75
|
-
}
|
|
76
|
-
return result;
|
|
77
|
-
});
|
|
78
|
-
react.useEffect(() => {
|
|
79
|
-
var handleInvalid = event => {
|
|
80
|
-
var _config$name;
|
|
81
|
-
var form = dom.getFormElement(ref.current);
|
|
82
|
-
var element = event.target;
|
|
83
|
-
var prefix = (_config$name = config.name) !== null && _config$name !== void 0 ? _config$name : '';
|
|
84
|
-
if (!dom.isFieldElement(element) || element.form !== form || !element.name.startsWith(prefix) || !element.dataset.conformTouched) {
|
|
85
|
-
return;
|
|
86
|
-
}
|
|
87
|
-
var name = element.name.slice(prefix.length);
|
|
88
|
-
var [path, ...restPaths] = dom.getPaths(name);
|
|
89
|
-
if (typeof path === 'undefined' || restPaths.length > 0) {
|
|
90
|
-
return;
|
|
91
|
-
}
|
|
92
|
-
setError(prev => {
|
|
93
|
-
if (element.validationMessage === dom.getValidationMessage(prev[path])) {
|
|
94
|
-
return prev;
|
|
95
|
-
}
|
|
96
|
-
return _rollupPluginBabelHelpers.objectSpread2(_rollupPluginBabelHelpers.objectSpread2({}, prev), {}, {
|
|
97
|
-
[path]: dom.getErrors(element.validationMessage)
|
|
98
|
-
});
|
|
99
|
-
});
|
|
100
|
-
event.preventDefault();
|
|
101
|
-
};
|
|
102
|
-
var handleReset = event => {
|
|
103
|
-
var form = dom.getFormElement(ref.current);
|
|
104
|
-
if (form && event.target === form) {
|
|
105
|
-
setError({});
|
|
106
|
-
}
|
|
107
|
-
};
|
|
108
|
-
document.addEventListener('reset', handleReset);
|
|
109
|
-
document.addEventListener('invalid', handleInvalid, true);
|
|
110
|
-
return () => {
|
|
111
|
-
document.removeEventListener('reset', handleReset);
|
|
112
|
-
document.removeEventListener('invalid', handleInvalid, true);
|
|
113
|
-
};
|
|
114
|
-
}, [ref, config.name]);
|
|
115
|
-
return [error, setError];
|
|
28
|
+
}, [noValidate]);
|
|
29
|
+
return noValidate;
|
|
116
30
|
}
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
var _config$lastSubmissio3, _config$lastSubmissio4;
|
|
126
|
-
var config = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};
|
|
127
|
-
var configRef = useConfigRef(config);
|
|
128
|
-
var ref = useFormRef(config.ref);
|
|
129
|
-
var noValidate = useNoValidate(config.noValidate, config.fallbackNative);
|
|
130
|
-
var report = useFormReporter(ref, config.lastSubmission);
|
|
131
|
-
var [errors, setErrors] = react.useState(() => {
|
|
132
|
-
var _config$lastSubmissio, _config$lastSubmissio2;
|
|
133
|
-
return (_config$lastSubmissio = (_config$lastSubmissio2 = config.lastSubmission) === null || _config$lastSubmissio2 === void 0 ? void 0 : _config$lastSubmissio2.error['']) !== null && _config$lastSubmissio !== void 0 ? _config$lastSubmissio : [];
|
|
134
|
-
});
|
|
135
|
-
var initialError = react.useMemo(() => {
|
|
136
|
-
var _submission$error$sco;
|
|
137
|
-
var submission = config.lastSubmission;
|
|
138
|
-
if (!submission) {
|
|
139
|
-
return {};
|
|
140
|
-
}
|
|
141
|
-
var intent = dom.parseIntent(submission.intent);
|
|
142
|
-
var scope = getScope(intent);
|
|
143
|
-
if (typeof scope !== 'string') {
|
|
144
|
-
return submission.error;
|
|
145
|
-
}
|
|
146
|
-
return {
|
|
147
|
-
[scope]: (_submission$error$sco = submission.error[scope]) !== null && _submission$error$sco !== void 0 ? _submission$error$sco : []
|
|
148
|
-
};
|
|
149
|
-
}, [config.lastSubmission]);
|
|
150
|
-
// This payload from lastSubmission is only useful before hydration
|
|
151
|
-
// After hydration, any new payload on lastSubmission will be ignored
|
|
152
|
-
var [defaultValueFromLastSubmission, setDefaultValueFromLastSubmission] = react.useState( // @ts-expect-error defaultValue is not in Submission type
|
|
153
|
-
(_config$lastSubmissio3 = (_config$lastSubmissio4 = config.lastSubmission) === null || _config$lastSubmissio4 === void 0 ? void 0 : _config$lastSubmissio4.payload) !== null && _config$lastSubmissio3 !== void 0 ? _config$lastSubmissio3 : null);
|
|
154
|
-
var fieldset = useFieldset(ref, {
|
|
155
|
-
defaultValue: defaultValueFromLastSubmission !== null && defaultValueFromLastSubmission !== void 0 ? defaultValueFromLastSubmission : config.defaultValue,
|
|
156
|
-
initialError,
|
|
157
|
-
constraint: config.constraint,
|
|
158
|
-
form: config.id
|
|
159
|
-
});
|
|
160
|
-
react.useEffect(() => {
|
|
161
|
-
// custom validate handler
|
|
162
|
-
var createValidateHandler = type => event => {
|
|
163
|
-
var field = event.target;
|
|
164
|
-
var form = ref.current;
|
|
165
|
-
var {
|
|
166
|
-
shouldValidate = 'onSubmit',
|
|
167
|
-
shouldRevalidate = shouldValidate
|
|
168
|
-
} = configRef.current;
|
|
169
|
-
if (!form || !dom.isFocusableFormControl(field) || field.form !== form || !field.name) {
|
|
170
|
-
return;
|
|
171
|
-
}
|
|
172
|
-
if (field.dataset.conformTouched ? shouldRevalidate === type : shouldValidate === type) {
|
|
173
|
-
dom.requestIntent(form, dom.validate(field.name));
|
|
174
|
-
}
|
|
175
|
-
};
|
|
176
|
-
var handleInvalid = event => {
|
|
177
|
-
var form = ref.current;
|
|
178
|
-
var field = event.target;
|
|
179
|
-
if (!form || !dom.isFieldElement(field) || field.form !== form || field.name !== FORM_ERROR_ELEMENT_NAME) {
|
|
180
|
-
return;
|
|
181
|
-
}
|
|
182
|
-
event.preventDefault();
|
|
183
|
-
if (field.dataset.conformTouched) {
|
|
184
|
-
setErrors(dom.getErrors(field.validationMessage));
|
|
185
|
-
}
|
|
186
|
-
};
|
|
187
|
-
var handleReset = event => {
|
|
188
|
-
var form = ref.current;
|
|
189
|
-
if (!form || event.target !== form) {
|
|
190
|
-
return;
|
|
191
|
-
}
|
|
192
|
-
|
|
193
|
-
// Reset all field state
|
|
194
|
-
for (var _element of dom.getFormControls(form)) {
|
|
195
|
-
delete _element.dataset.conformTouched;
|
|
196
|
-
_element.setCustomValidity('');
|
|
197
|
-
}
|
|
198
|
-
setErrors([]);
|
|
199
|
-
setDefaultValueFromLastSubmission(null);
|
|
200
|
-
};
|
|
201
|
-
var handleInput = createValidateHandler('onInput');
|
|
202
|
-
var handleBlur = createValidateHandler('onBlur');
|
|
203
|
-
document.addEventListener('input', handleInput, true);
|
|
204
|
-
document.addEventListener('blur', handleBlur, true);
|
|
205
|
-
document.addEventListener('invalid', handleInvalid, true);
|
|
206
|
-
document.addEventListener('reset', handleReset);
|
|
207
|
-
return () => {
|
|
208
|
-
document.removeEventListener('input', handleInput, true);
|
|
209
|
-
document.removeEventListener('blur', handleBlur, true);
|
|
210
|
-
document.removeEventListener('invalid', handleInvalid, true);
|
|
211
|
-
document.removeEventListener('reset', handleReset);
|
|
212
|
-
};
|
|
213
|
-
}, [ref, configRef]);
|
|
214
|
-
var form = {
|
|
215
|
-
ref,
|
|
216
|
-
error: errors[0],
|
|
217
|
-
errors,
|
|
218
|
-
props: {
|
|
219
|
-
ref,
|
|
220
|
-
noValidate,
|
|
221
|
-
onSubmit(event) {
|
|
222
|
-
var form = event.currentTarget;
|
|
223
|
-
var nativeEvent = event.nativeEvent;
|
|
224
|
-
var submitter = nativeEvent.submitter;
|
|
225
|
-
if (event.defaultPrevented) {
|
|
226
|
-
return;
|
|
227
|
-
}
|
|
228
|
-
try {
|
|
229
|
-
var _config$onValidate, _config$onValidate2;
|
|
230
|
-
var formData = dom.getFormData(form, submitter);
|
|
231
|
-
var submission = (_config$onValidate = (_config$onValidate2 = config.onValidate) === null || _config$onValidate2 === void 0 ? void 0 : _config$onValidate2.call(config, {
|
|
232
|
-
form,
|
|
233
|
-
formData
|
|
234
|
-
})) !== null && _config$onValidate !== void 0 ? _config$onValidate : dom.parse(formData);
|
|
235
|
-
var {
|
|
236
|
-
errors: _errors,
|
|
237
|
-
shouldServerValidate
|
|
238
|
-
} = Object.entries(submission.error).reduce((result, _ref) => {
|
|
239
|
-
var [, error] = _ref;
|
|
240
|
-
for (var message of error) {
|
|
241
|
-
if (message === dom.VALIDATION_UNDEFINED) {
|
|
242
|
-
result.shouldServerValidate = true;
|
|
243
|
-
} else if (message !== dom.VALIDATION_SKIPPED) {
|
|
244
|
-
result.errors.push(message);
|
|
245
|
-
}
|
|
246
|
-
}
|
|
247
|
-
return result;
|
|
248
|
-
}, {
|
|
249
|
-
errors: [],
|
|
250
|
-
shouldServerValidate: false
|
|
251
|
-
});
|
|
252
|
-
if (
|
|
253
|
-
// has client validation
|
|
254
|
-
typeof config.onValidate !== 'undefined' &&
|
|
255
|
-
// not necessary to validate on the server
|
|
256
|
-
!shouldServerValidate && (
|
|
257
|
-
// client validation failed or non submit intent
|
|
258
|
-
!config.noValidate && !(submitter !== null && submitter !== void 0 && submitter.formNoValidate) && _errors.length > 0 || dom.parseIntent(submission.intent) !== null)) {
|
|
259
|
-
report(form, submission);
|
|
260
|
-
event.preventDefault();
|
|
261
|
-
} else {
|
|
262
|
-
var _config$onSubmit;
|
|
263
|
-
(_config$onSubmit = config.onSubmit) === null || _config$onSubmit === void 0 ? void 0 : _config$onSubmit.call(config, event, {
|
|
264
|
-
formData,
|
|
265
|
-
submission,
|
|
266
|
-
action: dom.getFormAction(nativeEvent),
|
|
267
|
-
encType: dom.getFormEncType(nativeEvent),
|
|
268
|
-
method: dom.getFormMethod(nativeEvent)
|
|
269
|
-
});
|
|
270
|
-
}
|
|
271
|
-
} catch (error) {
|
|
272
|
-
// eslint-disable-next-line no-console
|
|
273
|
-
console.warn('Client validation failed', error);
|
|
274
|
-
}
|
|
275
|
-
}
|
|
276
|
-
}
|
|
277
|
-
};
|
|
278
|
-
if (config.id) {
|
|
279
|
-
form.id = config.id;
|
|
280
|
-
form.errorId = "".concat(config.id, "-error");
|
|
281
|
-
form.props.id = form.id;
|
|
282
|
-
}
|
|
283
|
-
if (form.errorId && form.errors.length > 0) {
|
|
284
|
-
form.props['aria-invalid'] = 'true';
|
|
285
|
-
form.props['aria-describedby'] = form.errorId;
|
|
31
|
+
function useForm(options) {
|
|
32
|
+
var formId = useFormId(options.id);
|
|
33
|
+
var initializeContext = () => dom.createForm(formId, options);
|
|
34
|
+
var [form, setForm] = react.useState(initializeContext);
|
|
35
|
+
|
|
36
|
+
// If id changes, reinitialize the form immediately
|
|
37
|
+
if (formId !== form.id) {
|
|
38
|
+
setForm(initializeContext);
|
|
286
39
|
}
|
|
287
|
-
|
|
288
|
-
}
|
|
289
|
-
|
|
290
|
-
/**
|
|
291
|
-
* A set of field configuration
|
|
292
|
-
*/
|
|
293
|
-
|
|
294
|
-
/**
|
|
295
|
-
* Returns all the information about the fieldset.
|
|
296
|
-
*
|
|
297
|
-
* @see https://conform.guide/api/react#usefieldset
|
|
298
|
-
*/
|
|
299
|
-
|
|
300
|
-
function useFieldset(ref, config) {
|
|
301
|
-
var [error] = useFormError(ref, {
|
|
302
|
-
initialError: config.initialError,
|
|
303
|
-
name: config.name
|
|
304
|
-
});
|
|
305
|
-
|
|
306
|
-
/**
|
|
307
|
-
* This allows us constructing the field at runtime as we have no information
|
|
308
|
-
* about which fields would be available. The proxy will also help tracking
|
|
309
|
-
* the usage of each field for optimization in the future.
|
|
310
|
-
*/
|
|
311
|
-
return new Proxy({}, {
|
|
312
|
-
get(_target, key) {
|
|
313
|
-
var _fieldsetConfig$const, _fieldsetConfig$initi, _fieldsetConfig$defau;
|
|
314
|
-
if (typeof key !== 'string') {
|
|
315
|
-
return;
|
|
316
|
-
}
|
|
317
|
-
var fieldsetConfig = config;
|
|
318
|
-
var constraint = (_fieldsetConfig$const = fieldsetConfig.constraint) === null || _fieldsetConfig$const === void 0 ? void 0 : _fieldsetConfig$const[key];
|
|
319
|
-
var errors = error === null || error === void 0 ? void 0 : error[key];
|
|
320
|
-
var initialError = Object.entries((_fieldsetConfig$initi = fieldsetConfig.initialError) !== null && _fieldsetConfig$initi !== void 0 ? _fieldsetConfig$initi : {}).reduce((result, _ref2) => {
|
|
321
|
-
var [name, message] = _ref2;
|
|
322
|
-
var [field, ...paths] = dom.getPaths(name);
|
|
323
|
-
if (field === key) {
|
|
324
|
-
result[dom.getName(paths)] = message;
|
|
325
|
-
}
|
|
326
|
-
return result;
|
|
327
|
-
}, {});
|
|
328
|
-
var field = _rollupPluginBabelHelpers.objectSpread2(_rollupPluginBabelHelpers.objectSpread2({}, constraint), {}, {
|
|
329
|
-
name: fieldsetConfig.name ? "".concat(fieldsetConfig.name, ".").concat(key) : key,
|
|
330
|
-
// @ts-expect-error The FieldValue type might need a rework
|
|
331
|
-
defaultValue: (_fieldsetConfig$defau = fieldsetConfig.defaultValue) === null || _fieldsetConfig$defau === void 0 ? void 0 : _fieldsetConfig$defau[key],
|
|
332
|
-
initialError,
|
|
333
|
-
error: errors === null || errors === void 0 ? void 0 : errors[0],
|
|
334
|
-
errors
|
|
335
|
-
});
|
|
336
|
-
if (fieldsetConfig.form) {
|
|
337
|
-
field.form = fieldsetConfig.form;
|
|
338
|
-
field.id = "".concat(fieldsetConfig.form, "-").concat(field.name);
|
|
339
|
-
field.errorId = "".concat(field.id, "-error");
|
|
340
|
-
field.descriptionId = "".concat(field.id, "-description");
|
|
341
|
-
}
|
|
342
|
-
return field;
|
|
343
|
-
}
|
|
344
|
-
});
|
|
345
|
-
}
|
|
346
|
-
|
|
347
|
-
/**
|
|
348
|
-
* Returns a list of key and field config.
|
|
349
|
-
*
|
|
350
|
-
* @see https://conform.guide/api/react#usefieldlist
|
|
351
|
-
*/
|
|
352
|
-
function useFieldList(ref, config) {
|
|
353
|
-
var configRef = useConfigRef(config);
|
|
354
|
-
var [error, setError] = useFormError(ref, {
|
|
355
|
-
initialError: config.initialError,
|
|
356
|
-
name: config.name
|
|
357
|
-
});
|
|
358
|
-
var [entries, setEntries] = react.useState(() => {
|
|
359
|
-
var _config$defaultValue;
|
|
360
|
-
return Object.entries((_config$defaultValue = config.defaultValue) !== null && _config$defaultValue !== void 0 ? _config$defaultValue : []);
|
|
361
|
-
});
|
|
362
|
-
react.useEffect(() => {
|
|
363
|
-
var conformHandler = event => {
|
|
364
|
-
var form = dom.getFormElement(ref.current);
|
|
365
|
-
if (!form || event.target !== form) {
|
|
366
|
-
return;
|
|
367
|
-
}
|
|
368
|
-
var intent = dom.parseIntent(event.detail);
|
|
369
|
-
if ((intent === null || intent === void 0 ? void 0 : intent.type) !== 'list' || (intent === null || intent === void 0 ? void 0 : intent.payload.name) !== configRef.current.name) {
|
|
370
|
-
return;
|
|
371
|
-
}
|
|
372
|
-
setEntries(entries => {
|
|
373
|
-
var list = [...entries];
|
|
374
|
-
switch (intent.payload.operation) {
|
|
375
|
-
case 'append':
|
|
376
|
-
case 'prepend':
|
|
377
|
-
case 'insert':
|
|
378
|
-
case 'replace':
|
|
379
|
-
return dom.updateList(list, _rollupPluginBabelHelpers.objectSpread2(_rollupPluginBabelHelpers.objectSpread2({}, intent.payload), {}, {
|
|
380
|
-
defaultValue: [
|
|
381
|
-
// Generate a random key to avoid conflicts
|
|
382
|
-
getUniqueKey(), intent.payload.defaultValue]
|
|
383
|
-
}));
|
|
384
|
-
default:
|
|
385
|
-
return dom.updateList(list, intent.payload);
|
|
386
|
-
}
|
|
387
|
-
});
|
|
388
|
-
setError(error => {
|
|
389
|
-
var errorList = [];
|
|
390
|
-
for (var [key, messages] of Object.entries(error)) {
|
|
391
|
-
if (typeof key === 'number') {
|
|
392
|
-
errorList[key] = messages;
|
|
393
|
-
}
|
|
394
|
-
}
|
|
395
|
-
switch (intent.payload.operation) {
|
|
396
|
-
case 'append':
|
|
397
|
-
case 'prepend':
|
|
398
|
-
case 'insert':
|
|
399
|
-
case 'replace':
|
|
400
|
-
errorList = dom.updateList(errorList, _rollupPluginBabelHelpers.objectSpread2(_rollupPluginBabelHelpers.objectSpread2({}, intent.payload), {}, {
|
|
401
|
-
defaultValue: undefined
|
|
402
|
-
}));
|
|
403
|
-
break;
|
|
404
|
-
default:
|
|
405
|
-
errorList = dom.updateList(errorList, intent.payload);
|
|
406
|
-
break;
|
|
407
|
-
}
|
|
408
|
-
return Object.assign({}, errorList);
|
|
409
|
-
});
|
|
410
|
-
};
|
|
411
|
-
var resetHandler = event => {
|
|
412
|
-
var _configRef$current$de;
|
|
413
|
-
var form = dom.getFormElement(ref.current);
|
|
414
|
-
if (!form || event.target !== form) {
|
|
415
|
-
return;
|
|
416
|
-
}
|
|
417
|
-
setEntries(Object.entries((_configRef$current$de = configRef.current.defaultValue) !== null && _configRef$current$de !== void 0 ? _configRef$current$de : []));
|
|
418
|
-
};
|
|
419
|
-
|
|
420
|
-
// @ts-expect-error Custom event: conform
|
|
421
|
-
document.addEventListener('conform', conformHandler, true);
|
|
422
|
-
document.addEventListener('reset', resetHandler);
|
|
423
|
-
return () => {
|
|
424
|
-
// @ts-expect-error Custom event: conform
|
|
425
|
-
document.removeEventListener('conform', conformHandler, true);
|
|
426
|
-
document.removeEventListener('reset', resetHandler);
|
|
427
|
-
};
|
|
428
|
-
}, [ref, configRef, setError]);
|
|
429
|
-
return entries.map((_ref3, index) => {
|
|
430
|
-
var _config$initialError;
|
|
431
|
-
var [key, defaultValue] = _ref3;
|
|
432
|
-
var errors = error[index];
|
|
433
|
-
var initialError = Object.entries((_config$initialError = config.initialError) !== null && _config$initialError !== void 0 ? _config$initialError : {}).reduce((result, _ref4) => {
|
|
434
|
-
var [name, message] = _ref4;
|
|
435
|
-
var [field, ...paths] = dom.getPaths(name);
|
|
436
|
-
if (field === index) {
|
|
437
|
-
result[dom.getName(paths)] = message;
|
|
438
|
-
}
|
|
439
|
-
return result;
|
|
440
|
-
}, {});
|
|
441
|
-
var fieldConfig = {
|
|
442
|
-
name: "".concat(config.name, "[").concat(index, "]"),
|
|
443
|
-
defaultValue,
|
|
444
|
-
initialError,
|
|
445
|
-
error: errors === null || errors === void 0 ? void 0 : errors[0],
|
|
446
|
-
errors
|
|
447
|
-
};
|
|
448
|
-
if (config.form) {
|
|
449
|
-
fieldConfig.form = config.form;
|
|
450
|
-
fieldConfig.id = "".concat(config.form, "-").concat(config.name, "-").concat(index);
|
|
451
|
-
fieldConfig.errorId = "".concat(fieldConfig.id, "-error");
|
|
452
|
-
fieldConfig.descriptionId = "".concat(fieldConfig.id, "-description");
|
|
453
|
-
}
|
|
454
|
-
return _rollupPluginBabelHelpers.objectSpread2({
|
|
455
|
-
key
|
|
456
|
-
}, fieldConfig);
|
|
457
|
-
});
|
|
458
|
-
}
|
|
459
|
-
|
|
460
|
-
/**
|
|
461
|
-
* useLayoutEffect is client-only.
|
|
462
|
-
* This basically makes it a no-op on server
|
|
463
|
-
*/
|
|
464
|
-
var useSafeLayoutEffect = typeof document === 'undefined' ? react.useEffect : react.useLayoutEffect;
|
|
465
|
-
/**
|
|
466
|
-
* Returns a ref object and a set of helpers that dispatch corresponding dom event.
|
|
467
|
-
*
|
|
468
|
-
* @see https://conform.guide/api/react#useinputevent
|
|
469
|
-
*/
|
|
470
|
-
function useInputEvent(options) {
|
|
471
|
-
var optionsRef = useConfigRef(options);
|
|
472
|
-
var eventDispatched = react.useRef({
|
|
473
|
-
onInput: false,
|
|
474
|
-
onFocus: false,
|
|
475
|
-
onBlur: false
|
|
476
|
-
});
|
|
40
|
+
var optionsRef = react.useRef(options);
|
|
477
41
|
useSafeLayoutEffect(() => {
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
var element = typeof ((_optionsRef$current = optionsRef.current) === null || _optionsRef$current === void 0 ? void 0 : _optionsRef$current.ref) === 'function' ? (_optionsRef$current2 = optionsRef.current) === null || _optionsRef$current2 === void 0 ? void 0 : _optionsRef$current2.ref() : (_optionsRef$current3 = optionsRef.current) === null || _optionsRef$current3 === void 0 ? void 0 : _optionsRef$current3.ref.current;
|
|
482
|
-
if (dom.isFieldElement(element) && (listener === 'onReset' ? event.target === element.form : event.target === element)) {
|
|
483
|
-
var _optionsRef$current4, _optionsRef$current4$;
|
|
484
|
-
if (listener !== 'onReset') {
|
|
485
|
-
eventDispatched.current[listener] = true;
|
|
486
|
-
}
|
|
487
|
-
(_optionsRef$current4 = optionsRef.current) === null || _optionsRef$current4 === void 0 || (_optionsRef$current4$ = _optionsRef$current4[listener]) === null || _optionsRef$current4$ === void 0 ? void 0 : _optionsRef$current4$.call(_optionsRef$current4, event);
|
|
488
|
-
}
|
|
489
|
-
};
|
|
490
|
-
};
|
|
491
|
-
var inputHandler = createEventListener('onInput');
|
|
492
|
-
var focusHandler = createEventListener('onFocus');
|
|
493
|
-
var blurHandler = createEventListener('onBlur');
|
|
494
|
-
var resetHandler = createEventListener('onReset');
|
|
495
|
-
|
|
496
|
-
// focus/blur event does not bubble
|
|
497
|
-
document.addEventListener('input', inputHandler, true);
|
|
498
|
-
document.addEventListener('focus', focusHandler, true);
|
|
499
|
-
document.addEventListener('blur', blurHandler, true);
|
|
500
|
-
document.addEventListener('reset', resetHandler);
|
|
42
|
+
document.addEventListener('input', form.input);
|
|
43
|
+
document.addEventListener('focusout', form.blur);
|
|
44
|
+
document.addEventListener('reset', form.reset);
|
|
501
45
|
return () => {
|
|
502
|
-
document.removeEventListener('input',
|
|
503
|
-
document.removeEventListener('
|
|
504
|
-
document.removeEventListener('
|
|
505
|
-
document.removeEventListener('reset', resetHandler);
|
|
506
|
-
};
|
|
507
|
-
}, []);
|
|
508
|
-
var control = react.useMemo(() => {
|
|
509
|
-
var dispatch = (listener, fn) => {
|
|
510
|
-
if (!eventDispatched.current[listener]) {
|
|
511
|
-
var _optionsRef$current5, _optionsRef$current6, _optionsRef$current7;
|
|
512
|
-
var _element2 = typeof ((_optionsRef$current5 = optionsRef.current) === null || _optionsRef$current5 === void 0 ? void 0 : _optionsRef$current5.ref) === 'function' ? (_optionsRef$current6 = optionsRef.current) === null || _optionsRef$current6 === void 0 ? void 0 : _optionsRef$current6.ref() : (_optionsRef$current7 = optionsRef.current) === null || _optionsRef$current7 === void 0 ? void 0 : _optionsRef$current7.ref.current;
|
|
513
|
-
if (!dom.isFieldElement(_element2)) {
|
|
514
|
-
// eslint-disable-next-line no-console
|
|
515
|
-
console.warn('Failed to dispatch event; is the input mounted?');
|
|
516
|
-
return;
|
|
517
|
-
}
|
|
518
|
-
|
|
519
|
-
// To avoid recursion
|
|
520
|
-
eventDispatched.current[listener] = true;
|
|
521
|
-
fn(_element2);
|
|
522
|
-
}
|
|
523
|
-
eventDispatched.current[listener] = false;
|
|
524
|
-
};
|
|
525
|
-
return {
|
|
526
|
-
change(eventOrValue) {
|
|
527
|
-
dispatch('onInput', element => {
|
|
528
|
-
if (element instanceof HTMLInputElement && (element.type === 'checkbox' || element.type === 'radio')) {
|
|
529
|
-
if (typeof eventOrValue !== 'boolean') {
|
|
530
|
-
throw new Error('You should pass a boolean when changing a checkbox or radio input');
|
|
531
|
-
}
|
|
532
|
-
element.checked = eventOrValue;
|
|
533
|
-
} else {
|
|
534
|
-
if (typeof eventOrValue === 'boolean') {
|
|
535
|
-
throw new Error('You can pass a boolean only when changing a checkbox or radio input');
|
|
536
|
-
}
|
|
537
|
-
var _value = typeof eventOrValue === 'string' ? eventOrValue : eventOrValue.target.value;
|
|
538
|
-
|
|
539
|
-
// No change event will triggered on React if `element.value` is updated
|
|
540
|
-
// before dispatching the event
|
|
541
|
-
if (element.value !== _value) {
|
|
542
|
-
/**
|
|
543
|
-
* Triggering react custom change event
|
|
544
|
-
* Solution based on dom-testing-library
|
|
545
|
-
* @see https://github.com/facebook/react/issues/10135#issuecomment-401496776
|
|
546
|
-
* @see https://github.com/testing-library/dom-testing-library/blob/main/src/events.js#L104-L123
|
|
547
|
-
*/
|
|
548
|
-
var {
|
|
549
|
-
set: valueSetter
|
|
550
|
-
} = Object.getOwnPropertyDescriptor(element, 'value') || {};
|
|
551
|
-
var prototype = Object.getPrototypeOf(element);
|
|
552
|
-
var {
|
|
553
|
-
set: prototypeValueSetter
|
|
554
|
-
} = Object.getOwnPropertyDescriptor(prototype, 'value') || {};
|
|
555
|
-
if (prototypeValueSetter && valueSetter !== prototypeValueSetter) {
|
|
556
|
-
prototypeValueSetter.call(element, _value);
|
|
557
|
-
} else {
|
|
558
|
-
if (valueSetter) {
|
|
559
|
-
valueSetter.call(element, _value);
|
|
560
|
-
} else {
|
|
561
|
-
throw new Error('The given element does not have a value setter');
|
|
562
|
-
}
|
|
563
|
-
}
|
|
564
|
-
}
|
|
565
|
-
}
|
|
566
|
-
|
|
567
|
-
// Dispatch input event with the updated input value
|
|
568
|
-
element.dispatchEvent(new InputEvent('input', {
|
|
569
|
-
bubbles: true
|
|
570
|
-
}));
|
|
571
|
-
// Dispatch change event (necessary for select to update the selected option)
|
|
572
|
-
element.dispatchEvent(new Event('change', {
|
|
573
|
-
bubbles: true
|
|
574
|
-
}));
|
|
575
|
-
});
|
|
576
|
-
},
|
|
577
|
-
focus() {
|
|
578
|
-
dispatch('onFocus', element => {
|
|
579
|
-
element.dispatchEvent(new FocusEvent('focusin', {
|
|
580
|
-
bubbles: true
|
|
581
|
-
}));
|
|
582
|
-
element.dispatchEvent(new FocusEvent('focus'));
|
|
583
|
-
});
|
|
584
|
-
},
|
|
585
|
-
blur() {
|
|
586
|
-
dispatch('onBlur', element => {
|
|
587
|
-
element.dispatchEvent(new FocusEvent('focusout', {
|
|
588
|
-
bubbles: true
|
|
589
|
-
}));
|
|
590
|
-
element.dispatchEvent(new FocusEvent('blur'));
|
|
591
|
-
});
|
|
592
|
-
}
|
|
46
|
+
document.removeEventListener('input', form.input);
|
|
47
|
+
document.removeEventListener('focusout', form.blur);
|
|
48
|
+
document.removeEventListener('reset', form.reset);
|
|
593
49
|
};
|
|
594
|
-
}, [
|
|
595
|
-
|
|
596
|
-
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
/**
|
|
600
|
-
* Validate the form with the Constraint Validation API
|
|
601
|
-
* @see https://conform.guide/api/react#validateconstraint
|
|
602
|
-
*/
|
|
603
|
-
function validateConstraint(options) {
|
|
604
|
-
var _options$formData, _options$formatMessag;
|
|
605
|
-
var formData = (_options$formData = options === null || options === void 0 ? void 0 : options.formData) !== null && _options$formData !== void 0 ? _options$formData : new FormData(options.form);
|
|
606
|
-
var getDefaultErrors = (validity, result) => {
|
|
607
|
-
var errors = [];
|
|
608
|
-
if (validity.valueMissing) errors.push('required');
|
|
609
|
-
if (validity.typeMismatch || validity.badInput) errors.push('type');
|
|
610
|
-
if (validity.tooShort) errors.push('minLength');
|
|
611
|
-
if (validity.rangeUnderflow) errors.push('min');
|
|
612
|
-
if (validity.stepMismatch) errors.push('step');
|
|
613
|
-
if (validity.tooLong) errors.push('maxLength');
|
|
614
|
-
if (validity.rangeOverflow) errors.push('max');
|
|
615
|
-
if (validity.patternMismatch) errors.push('pattern');
|
|
616
|
-
for (var [constraintName, valid] of Object.entries(result)) {
|
|
617
|
-
if (!valid) {
|
|
618
|
-
errors.push(constraintName);
|
|
619
|
-
}
|
|
50
|
+
}, [form]);
|
|
51
|
+
useSafeLayoutEffect(() => {
|
|
52
|
+
if (options.lastResult === optionsRef.current.lastResult) {
|
|
53
|
+
// If there is no change, do nothing
|
|
54
|
+
return;
|
|
620
55
|
}
|
|
621
|
-
|
|
622
|
-
|
|
623
|
-
|
|
624
|
-
|
|
625
|
-
|
|
626
|
-
} = _ref5;
|
|
627
|
-
return defaultErrors;
|
|
628
|
-
};
|
|
629
|
-
return dom.parse(formData, {
|
|
630
|
-
resolve() {
|
|
631
|
-
var error = {};
|
|
632
|
-
var constraintPattern = /^constraint[A-Z][^A-Z]*$/;
|
|
633
|
-
var _loop = function _loop(_element3) {
|
|
634
|
-
if (dom.isFieldElement(_element3)) {
|
|
635
|
-
var name = _element3.name !== FORM_ERROR_ELEMENT_NAME ? _element3.name : '';
|
|
636
|
-
var constraint = Object.entries(_element3.dataset).reduce((result, _ref6) => {
|
|
637
|
-
var [name, attributeValue = ''] = _ref6;
|
|
638
|
-
if (constraintPattern.test(name)) {
|
|
639
|
-
var _options$constraint;
|
|
640
|
-
var constraintName = name.slice(10).toLowerCase();
|
|
641
|
-
var _validate = (_options$constraint = options.constraint) === null || _options$constraint === void 0 ? void 0 : _options$constraint[constraintName];
|
|
642
|
-
if (typeof _validate === 'function') {
|
|
643
|
-
result[constraintName] = _validate(_element3.value, {
|
|
644
|
-
formData,
|
|
645
|
-
attributeValue
|
|
646
|
-
});
|
|
647
|
-
} else {
|
|
648
|
-
// eslint-disable-next-line no-console
|
|
649
|
-
console.warn("Found an \"".concat(constraintName, "\" constraint with undefined definition; Please specify it on the validateConstraint API."));
|
|
650
|
-
}
|
|
651
|
-
}
|
|
652
|
-
return result;
|
|
653
|
-
}, {});
|
|
654
|
-
var errors = formatMessages({
|
|
655
|
-
name,
|
|
656
|
-
validity: _element3.validity,
|
|
657
|
-
constraint,
|
|
658
|
-
defaultErrors: getDefaultErrors(_element3.validity, constraint)
|
|
659
|
-
});
|
|
660
|
-
if (errors.length > 0) {
|
|
661
|
-
error[name] = errors;
|
|
662
|
-
}
|
|
663
|
-
}
|
|
664
|
-
};
|
|
665
|
-
for (var _element3 of options.form.elements) {
|
|
666
|
-
_loop(_element3);
|
|
667
|
-
}
|
|
668
|
-
return {
|
|
669
|
-
error
|
|
670
|
-
};
|
|
56
|
+
if (options.lastResult) {
|
|
57
|
+
form.report(options.lastResult);
|
|
58
|
+
} else {
|
|
59
|
+
var _document$forms$named;
|
|
60
|
+
(_document$forms$named = document.forms.namedItem(form.id)) === null || _document$forms$named === void 0 ? void 0 : _document$forms$named.reset();
|
|
671
61
|
}
|
|
62
|
+
}, [form, options.lastResult]);
|
|
63
|
+
useSafeLayoutEffect(() => {
|
|
64
|
+
optionsRef.current = options;
|
|
65
|
+
form.update(options);
|
|
672
66
|
});
|
|
67
|
+
var subjectRef = context.useSubjectRef();
|
|
68
|
+
var state = context.useFormState(form, subjectRef);
|
|
69
|
+
var noValidate = useNoValidate(options.defaultNoValidate);
|
|
70
|
+
var meta = context.getFormMetadata(formId, state, subjectRef, form, noValidate);
|
|
71
|
+
return {
|
|
72
|
+
meta,
|
|
73
|
+
fields: meta.getFieldset()
|
|
74
|
+
};
|
|
673
75
|
}
|
|
674
|
-
function
|
|
675
|
-
var
|
|
676
|
-
|
|
677
|
-
|
|
678
|
-
|
|
679
|
-
return
|
|
76
|
+
function useFormMetadata(options) {
|
|
77
|
+
var subjectRef = context.useSubjectRef();
|
|
78
|
+
var form = context.useRegistry(options.formId);
|
|
79
|
+
var state = context.useFormState(form, subjectRef);
|
|
80
|
+
var noValidate = useNoValidate(options.defaultNoValidate);
|
|
81
|
+
return context.getFormMetadata(options.formId, state, subjectRef, form, noValidate);
|
|
680
82
|
}
|
|
681
|
-
function
|
|
682
|
-
|
|
683
|
-
|
|
684
|
-
|
|
685
|
-
|
|
686
|
-
|
|
687
|
-
|
|
688
|
-
|
|
689
|
-
//
|
|
690
|
-
|
|
691
|
-
|
|
692
|
-
|
|
693
|
-
|
|
694
|
-
|
|
695
|
-
|
|
696
|
-
|
|
697
|
-
|
|
698
|
-
|
|
699
|
-
}
|
|
700
|
-
}
|
|
701
|
-
var intent = dom.parseIntent(submission.intent);
|
|
702
|
-
var scope = getScope(intent);
|
|
703
|
-
for (var _element4 of dom.getFormControls(form)) {
|
|
704
|
-
var _submission$error$_el;
|
|
705
|
-
var _elementName = _element4.name !== FORM_ERROR_ELEMENT_NAME ? _element4.name : '';
|
|
706
|
-
var messages = (_submission$error$_el = submission.error[_elementName]) !== null && _submission$error$_el !== void 0 ? _submission$error$_el : [];
|
|
707
|
-
if (scope === null || scope === _elementName) {
|
|
708
|
-
_element4.dataset.conformTouched = 'true';
|
|
709
|
-
}
|
|
710
|
-
if (!messages.includes(dom.VALIDATION_SKIPPED) && !messages.includes(dom.VALIDATION_UNDEFINED)) {
|
|
711
|
-
var invalidEvent = new Event('invalid', {
|
|
712
|
-
cancelable: true
|
|
713
|
-
});
|
|
714
|
-
_element4.setCustomValidity(dom.getValidationMessage(messages));
|
|
715
|
-
_element4.dispatchEvent(invalidEvent);
|
|
716
|
-
}
|
|
717
|
-
}
|
|
718
|
-
if (!intent) {
|
|
719
|
-
dom.focusFirstInvalidControl(form);
|
|
720
|
-
}
|
|
721
|
-
}
|
|
722
|
-
function getScope(intent) {
|
|
723
|
-
switch (intent === null || intent === void 0 ? void 0 : intent.type) {
|
|
724
|
-
case 'validate':
|
|
725
|
-
return intent.payload;
|
|
726
|
-
case 'list':
|
|
727
|
-
return intent.payload.name;
|
|
728
|
-
}
|
|
729
|
-
return null;
|
|
83
|
+
function useField(options) {
|
|
84
|
+
var subjectRef = context.useSubjectRef();
|
|
85
|
+
var context$1 = context.useRegistry(options.formId);
|
|
86
|
+
var state = context.useFormState(context$1, subjectRef);
|
|
87
|
+
var meta = context.getFieldMetadata(options.formId, state, subjectRef, options.name);
|
|
88
|
+
var form = context.getFormMetadata(options.formId, state, subjectRef, context$1, false);
|
|
89
|
+
return {
|
|
90
|
+
meta,
|
|
91
|
+
// @ts-expect-error The types is used as a hint only
|
|
92
|
+
get fields() {
|
|
93
|
+
return meta.getFieldset();
|
|
94
|
+
},
|
|
95
|
+
// @ts-expect-error The types is used as a hint only
|
|
96
|
+
get list() {
|
|
97
|
+
return meta.getFieldList();
|
|
98
|
+
},
|
|
99
|
+
form
|
|
100
|
+
};
|
|
730
101
|
}
|
|
731
102
|
|
|
732
|
-
exports.
|
|
733
|
-
exports.getScope = getScope;
|
|
734
|
-
exports.getUniqueKey = getUniqueKey;
|
|
735
|
-
exports.reportSubmission = reportSubmission;
|
|
736
|
-
exports.useFieldList = useFieldList;
|
|
737
|
-
exports.useFieldset = useFieldset;
|
|
103
|
+
exports.useField = useField;
|
|
738
104
|
exports.useForm = useForm;
|
|
739
|
-
exports.
|
|
740
|
-
exports.
|
|
105
|
+
exports.useFormId = useFormId;
|
|
106
|
+
exports.useFormMetadata = useFormMetadata;
|
|
107
|
+
exports.useNoValidate = useNoValidate;
|
|
108
|
+
exports.useSafeLayoutEffect = useSafeLayoutEffect;
|