@conform-to/react 0.6.1 → 0.6.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/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 _config$lastSubmissio, _config$ref, _ref2, _config$lastSubmissio2;
132
+ var _ref, _config$lastSubmissio2;
17
133
  var config = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};
18
- var configRef = react.useRef(config);
19
- var formRef = react.useRef(null);
20
- var [lastSubmission, setLastSubmission] = react.useState((_config$lastSubmissio = config.lastSubmission) !== null && _config$lastSubmissio !== void 0 ? _config$lastSubmissio : null);
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
- if (!config.lastSubmission) {
23
- return [];
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 Object.entries(submission.error).reduce((result, _ref) => {
34
- var [name, message] = _ref;
35
- if (name !== '' && (scope === null || scope === name)) {
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: (_ref2 = (_config$lastSubmissio2 = config.lastSubmission) === null || _config$lastSubmissio2 === void 0 ? void 0 : _config$lastSubmissio2.payload) !== null && _ref2 !== void 0 ? _ref2 : config.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
- var form = ref.current;
71
- if (!form || !lastSubmission) {
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
- } = formConfig;
103
- if (!form || !dom.isFieldElement(field) || field.form !== form) {
167
+ } = configRef.current;
168
+ if (!form || !dom.isFocusableFormControl(field) || field.form !== form) {
104
169
  return;
105
170
  }
106
- if (field.dataset.conformTouched ? shouldRevalidate === 'onBlur' : shouldValidate === 'onBlur') {
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 !== dom.FORM_ERROR_ELEMENT_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 field of form.elements) {
129
- if (dom.isFieldElement(field)) {
130
- delete field.dataset.conformTouched;
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 getSubmission = (_config$onValidate = config.onValidate) !== null && _config$onValidate !== void 0 ? _config$onValidate : context => dom.parse(context.formData);
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
- if (!config.noValidate && !(submitter !== null && submitter !== void 0 && submitter.formNoValidate) && Object.entries(submission.error).some(_ref3 => {
170
- var [, message] = _ref3;
171
- return message !== '' && ![].concat(message).includes(dom.VALIDATION_UNDEFINED);
172
- }) || typeof config.onValidate !== 'undefined' && (submission.intent.startsWith('validate') || submission.intent.startsWith('list')) && Object.entries(submission.error).every(_ref4 => {
173
- var [, message] = _ref4;
174
- return ![].concat(message).includes(dom.VALIDATION_UNDEFINED);
175
- })) {
176
- var listCommand = dom.parseListCommand(submission.intent);
177
- if (listCommand) {
178
- form.dispatchEvent(new CustomEvent('conform/list', {
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, _rollupPluginBabelHelpers.objectSpread2({
246
+ (_config$onSubmit = config.onSubmit) === null || _config$onSubmit === void 0 ? void 0 : _config$onSubmit.call(config, event, {
187
247
  formData,
188
- submission
189
- }, dom.getFormAttributes(form, submitter)));
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 configRef = react.useRef(config);
215
- var [error, setError] = react.useState(() => {
216
- var initialError = config === null || config === void 0 ? void 0 : config.initialError;
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, _ref5) => {
291
- var [name, message] = _ref5;
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;
@@ -314,57 +320,22 @@ function useFieldset(ref, config) {
314
320
  }
315
321
 
316
322
  /**
317
- * Returns a list of key and config, with a group of helpers
318
- * configuring buttons for list manipulation
323
+ * Returns a list of key and field config.
319
324
  *
320
325
  * @see https://conform.guide/api/react#usefieldlist
321
326
  */
322
327
  function useFieldList(ref, config) {
323
- var configRef = react.useRef(config);
324
- var [error, setError] = react.useState(() => {
325
- var initialError = [];
326
- for (var [name, message] of Object.entries((_config$initialError = config === null || config === void 0 ? void 0 : config.initialError) !== null && _config$initialError !== void 0 ? _config$initialError : {})) {
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;
328
+ var configRef = useConfigRef(config);
329
+ var [error, setError] = useFormError(ref, {
330
+ initialError: config.initialError,
331
+ name: config.name
334
332
  });
335
333
  var [entries, setEntries] = react.useState(() => {
336
334
  var _config$defaultValue;
337
335
  return Object.entries((_config$defaultValue = config.defaultValue) !== null && _config$defaultValue !== void 0 ? _config$defaultValue : [undefined]);
338
336
  });
339
- useSafeLayoutEffect(() => {
340
- configRef.current = config;
341
- });
342
337
  react.useEffect(() => {
343
- var invalidHandler = event => {
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 => {
338
+ var conformHandler = event => {
368
339
  var form = dom.getFormElement(ref.current);
369
340
  if (!form || event.target !== form) {
370
341
  return;
@@ -393,20 +364,29 @@ function useFieldList(ref, config) {
393
364
  }
394
365
  });
395
366
  setError(error => {
367
+ var errorList = [];
368
+ for (var [key, messages] of Object.entries(error)) {
369
+ if (typeof key === 'number') {
370
+ errorList[key] = messages;
371
+ }
372
+ }
396
373
  switch (command.type) {
397
374
  case 'append':
398
375
  case 'prepend':
399
376
  case 'replace':
400
- return dom.updateList([...error], _rollupPluginBabelHelpers.objectSpread2(_rollupPluginBabelHelpers.objectSpread2({}, command), {}, {
377
+ errorList = dom.updateList(errorList, _rollupPluginBabelHelpers.objectSpread2(_rollupPluginBabelHelpers.objectSpread2({}, command), {}, {
401
378
  payload: _rollupPluginBabelHelpers.objectSpread2(_rollupPluginBabelHelpers.objectSpread2({}, command.payload), {}, {
402
379
  defaultValue: undefined
403
380
  })
404
381
  }));
382
+ break;
405
383
  default:
406
384
  {
407
- return dom.updateList([...error], command);
385
+ errorList = dom.updateList(errorList, command);
386
+ break;
408
387
  }
409
388
  }
389
+ return Object.assign({}, errorList);
410
390
  });
411
391
  };
412
392
  var resetHandler = event => {
@@ -416,26 +396,23 @@ function useFieldList(ref, config) {
416
396
  return;
417
397
  }
418
398
  setEntries(Object.entries((_configRef$current$de = configRef.current.defaultValue) !== null && _configRef$current$de !== void 0 ? _configRef$current$de : [undefined]));
419
- setError([]);
420
399
  };
421
400
 
422
- // @ts-expect-error Custom event: conform/list
423
- document.addEventListener('conform/list', listHandler, true);
424
- document.addEventListener('invalid', invalidHandler, true);
401
+ // @ts-expect-error Custom event: conform
402
+ document.addEventListener('conform', conformHandler, true);
425
403
  document.addEventListener('reset', resetHandler);
426
404
  return () => {
427
- // @ts-expect-error Custom event: conform/list
428
- document.removeEventListener('conform/list', listHandler, true);
429
- document.removeEventListener('invalid', invalidHandler, true);
405
+ // @ts-expect-error Custom event: conform
406
+ document.removeEventListener('conform', conformHandler, true);
430
407
  document.removeEventListener('reset', resetHandler);
431
408
  };
432
- }, [ref]);
433
- return entries.map((_ref6, index) => {
434
- var _config$initialError2, _config$defaultValue2;
435
- var [key, defaultValue] = _ref6;
409
+ }, [ref, configRef, setError]);
410
+ return entries.map((_ref4, index) => {
411
+ var _config$initialError, _config$defaultValue2;
412
+ var [key, defaultValue] = _ref4;
436
413
  var errors = error[index];
437
- var initialError = Object.entries((_config$initialError2 = config.initialError) !== null && _config$initialError2 !== void 0 ? _config$initialError2 : {}).reduce((result, _ref7) => {
438
- var [name, message] = _ref7;
414
+ var initialError = Object.entries((_config$initialError = config.initialError) !== null && _config$initialError !== void 0 ? _config$initialError : {}).reduce((result, _ref5) => {
415
+ var [name, message] = _ref5;
439
416
  var [field, ...paths] = dom.getPaths(name);
440
417
  if (field === index) {
441
418
  result[dom.getName(paths)] = message;
@@ -497,13 +474,10 @@ function setNativeValue(element, value) {
497
474
  var useSafeLayoutEffect = typeof document === 'undefined' ? react.useEffect : react.useLayoutEffect;
498
475
  function useInputEvent(options) {
499
476
  var ref = react.useRef(null);
500
- var optionsRef = react.useRef(options);
477
+ var optionsRef = useConfigRef(options);
501
478
  var changeDispatched = react.useRef(false);
502
479
  var focusDispatched = react.useRef(false);
503
480
  var blurDispatched = react.useRef(false);
504
- useSafeLayoutEffect(() => {
505
- optionsRef.current = options;
506
- });
507
481
  useSafeLayoutEffect(() => {
508
482
  var getInputElement = () => {
509
483
  var _optionsRef$current$g, _optionsRef$current, _optionsRef$current$g2;
@@ -632,11 +606,160 @@ function useInputEvent(options) {
632
606
  blurDispatched.current = false;
633
607
  }
634
608
  };
635
- }, []);
609
+ }, [optionsRef]);
636
610
  return [ref, control];
637
611
  }
612
+ var VALIDATION_UNDEFINED = '__undefined__';
613
+ var VALIDATION_SKIPPED = '__skipped__';
614
+ var FORM_ERROR_ELEMENT_NAME = '__form__';
615
+
616
+ /**
617
+ * Validate the form with the Constraint Validation API
618
+ * @see https://conform.guide/api/react#validateconstraint
619
+ */
620
+ function validateConstraint(options) {
621
+ var _options$formData, _options$formatMessag;
622
+ var formData = (_options$formData = options === null || options === void 0 ? void 0 : options.formData) !== null && _options$formData !== void 0 ? _options$formData : new FormData(options.form);
623
+ var getDefaultErrors = (validity, result) => {
624
+ var errors = [];
625
+ if (validity.valueMissing) errors.push('required');
626
+ if (validity.typeMismatch || validity.badInput) errors.push('type');
627
+ if (validity.tooShort) errors.push('minLength');
628
+ if (validity.rangeUnderflow) errors.push('min');
629
+ if (validity.stepMismatch) errors.push('step');
630
+ if (validity.tooLong) errors.push('maxLength');
631
+ if (validity.rangeOverflow) errors.push('max');
632
+ if (validity.patternMismatch) errors.push('pattern');
633
+ for (var [constraintName, valid] of Object.entries(result)) {
634
+ if (!valid) {
635
+ errors.push(constraintName);
636
+ }
637
+ }
638
+ return errors;
639
+ };
640
+ var formatMessages = (_options$formatMessag = options === null || options === void 0 ? void 0 : options.formatMessages) !== null && _options$formatMessag !== void 0 ? _options$formatMessag : _ref6 => {
641
+ var {
642
+ defaultErrors
643
+ } = _ref6;
644
+ return defaultErrors;
645
+ };
646
+ return dom.parse(formData, {
647
+ resolve(payload, intent) {
648
+ var error = {};
649
+ var constraintPattern = /^constraint[A-Z][^A-Z]*$/;
650
+ var _loop = function _loop(element) {
651
+ if (dom.isFieldElement(element)) {
652
+ var _options$acceptMultip, _options$acceptMultip2;
653
+ var name = element.name !== FORM_ERROR_ELEMENT_NAME ? element.name : '';
654
+ var constraint = Object.entries(element.dataset).reduce((result, _ref7) => {
655
+ var [name, attributeValue = ''] = _ref7;
656
+ if (constraintPattern.test(name)) {
657
+ var _options$constraint;
658
+ var constraintName = name.slice(10).toLowerCase();
659
+ var _validate = (_options$constraint = options.constraint) === null || _options$constraint === void 0 ? void 0 : _options$constraint[constraintName];
660
+ if (typeof _validate === 'function') {
661
+ result[constraintName] = _validate(element.value, {
662
+ formData,
663
+ attributeValue
664
+ });
665
+ } else {
666
+ console.warn("Found an \"".concat(constraintName, "\" constraint with undefined definition; Please specify it on the validateConstraint API."));
667
+ }
668
+ }
669
+ return result;
670
+ }, {});
671
+ var errors = formatMessages({
672
+ name,
673
+ validity: element.validity,
674
+ constraint,
675
+ defaultErrors: getDefaultErrors(element.validity, constraint)
676
+ });
677
+ 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, {
678
+ name,
679
+ payload,
680
+ intent
681
+ })) !== null && _options$acceptMultip !== void 0 ? _options$acceptMultip : false;
682
+ if (errors.length > 0) {
683
+ error[name] = shouldAcceptMultipleErrors ? errors : errors[0];
684
+ }
685
+ }
686
+ };
687
+ for (var element of options.form.elements) {
688
+ _loop(element);
689
+ }
690
+ return {
691
+ error
692
+ };
693
+ }
694
+ });
695
+ }
696
+ function reportSubmission(form, submission) {
697
+ for (var [name, message] of Object.entries(submission.error)) {
698
+ // There is no need to create a placeholder button if all we want is to reset the error
699
+ if (message === '') {
700
+ continue;
701
+ }
702
+
703
+ // We can't use empty string as button name
704
+ // As `form.element.namedItem('')` will always returns null
705
+ var elementName = name ? name : FORM_ERROR_ELEMENT_NAME;
706
+ var item = form.elements.namedItem(elementName);
707
+ if (item instanceof RadioNodeList) {
708
+ for (var field of item) {
709
+ if (field.type !== 'radio') {
710
+ console.warn('Repeated field name is not supported.');
711
+ continue;
712
+ }
713
+ }
714
+ }
715
+ if (item === null) {
716
+ // Create placeholder button to keep the error without contributing to the form data
717
+ var button = document.createElement('button');
718
+ button.name = elementName;
719
+ button.hidden = true;
720
+ button.dataset.conformTouched = 'true';
721
+ form.appendChild(button);
722
+ }
723
+ }
724
+ var scope = dom.getScope(submission.intent);
725
+ for (var element of dom.getFormControls(form)) {
726
+ var _elementName = element.name !== FORM_ERROR_ELEMENT_NAME ? element.name : '';
727
+ var messages = normalizeError(submission.error[_elementName]);
728
+ if (scope === null || scope === _elementName) {
729
+ element.dataset.conformTouched = 'true';
730
+ }
731
+ if (!messages.includes(VALIDATION_SKIPPED) && !messages.includes(VALIDATION_UNDEFINED)) {
732
+ var invalidEvent = new Event('invalid', {
733
+ cancelable: true
734
+ });
735
+ element.setCustomValidity(dom.getValidationMessage(messages));
736
+ element.dispatchEvent(invalidEvent);
737
+ }
738
+ }
739
+ if (dom.isSubmitting(submission.intent) || isFocusedOnIntentButton(form, submission.intent)) {
740
+ if (scope) {
741
+ dom.focusFormControl(form, scope);
742
+ } else {
743
+ dom.focusFirstInvalidControl(form);
744
+ }
745
+ }
746
+ }
747
+
748
+ /**
749
+ * Check if the current focus is on a intent button.
750
+ */
751
+ function isFocusedOnIntentButton(form, intent) {
752
+ var element = document.activeElement;
753
+ return dom.isFieldElement(element) && element.type === 'submit' && element.form === form && element.name === dom.INTENT && element.value === intent;
754
+ }
638
755
 
756
+ exports.FORM_ERROR_ELEMENT_NAME = FORM_ERROR_ELEMENT_NAME;
757
+ exports.VALIDATION_SKIPPED = VALIDATION_SKIPPED;
758
+ exports.VALIDATION_UNDEFINED = VALIDATION_UNDEFINED;
759
+ exports.isFocusedOnIntentButton = isFocusedOnIntentButton;
760
+ exports.reportSubmission = reportSubmission;
639
761
  exports.useFieldList = useFieldList;
640
762
  exports.useFieldset = useFieldset;
641
763
  exports.useForm = useForm;
642
764
  exports.useInputEvent = useInputEvent;
765
+ exports.validateConstraint = validateConstraint;