@conform-to/react 0.5.1 → 0.6.0-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/module/hooks.js CHANGED
@@ -1,7 +1,6 @@
1
1
  import { objectSpread2 as _objectSpread2 } from './_virtual/_rollupPluginBabelHelpers.js';
2
- import { shouldValidate, reportSubmission, getFormData, parse, isFieldElement, hasError, getPaths, getName, requestCommand, validate, getFormElement, parseListCommand, updateList } from '@conform-to/dom';
2
+ import { getValidationMessage, shouldValidate, parseListCommand, reportSubmission, getFormData, parse, VALIDATION_UNDEFINED, getFormAttributes, getPaths, getName, getErrors, isFieldElement, requestIntent, validate, getFormElement, updateList } from '@conform-to/dom';
3
3
  import { useRef, useState, useEffect, useMemo, useLayoutEffect } from 'react';
4
- import { input } from './helpers.js';
5
4
 
6
5
  /**
7
6
  * Returns properties required to hook into form events.
@@ -10,16 +9,17 @@ import { input } from './helpers.js';
10
9
  * @see https://conform.guide/api/react#useform
11
10
  */
12
11
  function useForm() {
12
+ var _config$state;
13
13
  var config = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};
14
14
  var configRef = useRef(config);
15
15
  var ref = useRef(null);
16
+ var [lastSubmission, setLastSubmission] = useState((_config$state = config.state) !== null && _config$state !== void 0 ? _config$state : null);
16
17
  var [error, setError] = useState(() => {
17
- var _config$state$error$f, _config$state, _config$state$error;
18
- var [, message] = (_config$state$error$f = (_config$state = config.state) === null || _config$state === void 0 ? void 0 : (_config$state$error = _config$state.error) === null || _config$state$error === void 0 ? void 0 : _config$state$error.find(_ref => {
19
- var [key] = _ref;
20
- return key === '';
21
- })) !== null && _config$state$error$f !== void 0 ? _config$state$error$f : [];
22
- return message !== null && message !== void 0 ? message : '';
18
+ if (!config.state) {
19
+ return '';
20
+ }
21
+ var message = config.state.error[''];
22
+ return getValidationMessage(message);
23
23
  });
24
24
  var [uncontrolledState, setUncontrolledState] = useState(() => {
25
25
  var submission = config.state;
@@ -29,11 +29,14 @@ function useForm() {
29
29
  };
30
30
  }
31
31
  return {
32
- defaultValue: submission.value,
33
- initialError: submission.error.filter(_ref2 => {
34
- var [name] = _ref2;
35
- return name !== '' && shouldValidate(submission, name);
36
- })
32
+ defaultValue: submission.payload,
33
+ initialError: Object.entries(submission.error).reduce((result, _ref) => {
34
+ var [name, message] = _ref;
35
+ if (name !== '' && shouldValidate(submission.intent, name)) {
36
+ result[name] = message;
37
+ }
38
+ return result;
39
+ }, {})
37
40
  };
38
41
  });
39
42
  var fieldsetConfig = _objectSpread2(_objectSpread2({}, uncontrolledState), {}, {
@@ -50,11 +53,25 @@ function useForm() {
50
53
  }, []);
51
54
  useEffect(() => {
52
55
  var form = ref.current;
53
- if (!form || !config.state) {
56
+ var submission = config.state;
57
+ if (!form || !submission) {
54
58
  return;
55
59
  }
56
- reportSubmission(form, config.state);
60
+ var listCommand = parseListCommand(submission.intent);
61
+ if (listCommand) {
62
+ form.dispatchEvent(new CustomEvent('conform/list', {
63
+ detail: submission.intent
64
+ }));
65
+ }
66
+ setLastSubmission(submission);
57
67
  }, [config.state]);
68
+ useEffect(() => {
69
+ var form = ref.current;
70
+ if (!form || !lastSubmission) {
71
+ return;
72
+ }
73
+ reportSubmission(ref.current, lastSubmission);
74
+ }, [lastSubmission]);
58
75
  useEffect(() => {
59
76
  // Revalidate the form when input value is changed
60
77
  var handleInput = event => {
@@ -65,7 +82,7 @@ function useForm() {
65
82
  return;
66
83
  }
67
84
  if (field.dataset.conformTouched || formConfig.initialReport === 'onChange') {
68
- requestCommand(form, validate(field.name));
85
+ requestIntent(form, validate(field.name));
69
86
  }
70
87
  };
71
88
  var handleBlur = event => {
@@ -76,7 +93,7 @@ function useForm() {
76
93
  return;
77
94
  }
78
95
  if (formConfig.initialReport === 'onBlur' && !field.dataset.conformTouched) {
79
- requestCommand(form, validate(field.name));
96
+ requestIntent(form, validate(field.name));
80
97
  }
81
98
  };
82
99
  var handleInvalid = event => {
@@ -107,8 +124,7 @@ function useForm() {
107
124
  }
108
125
  setError('');
109
126
  setUncontrolledState({
110
- defaultValue: formConfig.defaultValue,
111
- initialError: []
127
+ defaultValue: formConfig.defaultValue
112
128
  });
113
129
  };
114
130
 
@@ -141,53 +157,38 @@ function useForm() {
141
157
  var form = event.currentTarget;
142
158
  var nativeEvent = event.nativeEvent;
143
159
  var submitter = nativeEvent.submitter;
144
-
145
- /**
146
- * It checks defaultPrevented to confirm if the submission is intentional
147
- * This is utilized by `useFieldList` to modify the list state when the submit
148
- * event is captured and revalidate the form with new fields without triggering
149
- * a form submission at the same time.
150
- */
151
160
  if (event.defaultPrevented) {
152
161
  return;
153
162
  }
154
163
  try {
155
- var submission;
164
+ var _config$onValidate;
156
165
  var formData = getFormData(form, submitter);
157
- if (typeof config.onValidate === 'function') {
158
- submission = config.onValidate({
159
- form,
160
- formData
161
- });
162
- } else {
163
- submission = parse(formData);
164
- if (config.mode !== 'server-validation') {
165
- /**
166
- * As there is no custom logic defined,
167
- * removing the custom validity state will allow us
168
- * finding the latest validation message.
169
- *
170
- * This is mainly used to showcase the constraint validation API.
171
- */
172
- for (var element of form.elements) {
173
- if (isFieldElement(element) && element.willValidate) {
174
- element.setCustomValidity('');
175
- submission.error.push([element.name, element.validationMessage]);
176
- }
177
- }
166
+ var getSubmission = (_config$onValidate = config.onValidate) !== null && _config$onValidate !== void 0 ? _config$onValidate : context => parse(context.formData);
167
+ var submission = getSubmission({
168
+ form,
169
+ formData
170
+ });
171
+ if (!config.noValidate && !(submitter !== null && submitter !== void 0 && submitter.formNoValidate) && Object.entries(submission.error).some(_ref2 => {
172
+ var [, message] = _ref2;
173
+ return message !== '' && ![].concat(message).includes(VALIDATION_UNDEFINED);
174
+ }) || typeof config.onValidate !== 'undefined' && (submission.intent.startsWith('validate') || submission.intent.startsWith('list')) && Object.entries(submission.error).every(_ref3 => {
175
+ var [, message] = _ref3;
176
+ return ![].concat(message).includes(VALIDATION_UNDEFINED);
177
+ })) {
178
+ var listCommand = parseListCommand(submission.intent);
179
+ if (listCommand) {
180
+ form.dispatchEvent(new CustomEvent('conform/list', {
181
+ detail: submission.intent
182
+ }));
178
183
  }
179
- }
180
- if (!config.noValidate && !(submitter !== null && submitter !== void 0 && submitter.formNoValidate) && hasError(submission.error) || submission.type === 'validate' && config.mode !== 'server-validation') {
184
+ setLastSubmission(submission);
181
185
  event.preventDefault();
182
186
  } else {
183
187
  var _config$onSubmit;
184
- (_config$onSubmit = config.onSubmit) === null || _config$onSubmit === void 0 ? void 0 : _config$onSubmit.call(config, event, {
188
+ (_config$onSubmit = config.onSubmit) === null || _config$onSubmit === void 0 ? void 0 : _config$onSubmit.call(config, event, _objectSpread2({
185
189
  formData,
186
190
  submission
187
- });
188
- }
189
- if (event.defaultPrevented) {
190
- reportSubmission(form, submission);
191
+ }, getFormAttributes(form, submitter)));
191
192
  }
192
193
  } catch (e) {
193
194
  console.warn(e);
@@ -210,18 +211,14 @@ function useFieldset(ref, config) {
210
211
  () => {
211
212
  var _config$defaultValue;
212
213
  var initialError = {};
213
- for (var [name, message] of (_config$initialError = config === null || config === void 0 ? void 0 : config.initialError) !== null && _config$initialError !== void 0 ? _config$initialError : []) {
214
+ 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 : {})) {
214
215
  var _config$initialError;
215
216
  var [key, ...paths] = getPaths(name);
216
217
  if (typeof key === 'string') {
217
- var _initialError$key;
218
218
  var scopedName = getName(paths);
219
- var entries = (_initialError$key = initialError[key]) !== null && _initialError$key !== void 0 ? _initialError$key : [];
220
- if (scopedName === '' && entries.length > 0 && entries[0][0] !== '') {
221
- initialError[key] = [[scopedName, message], ...entries];
222
- } else {
223
- initialError[key] = [...entries, [scopedName, message]];
224
- }
219
+ initialError[key] = _objectSpread2(_objectSpread2({}, initialError[key]), {}, {
220
+ [scopedName]: message
221
+ });
225
222
  }
226
223
  }
227
224
  return {
@@ -231,12 +228,8 @@ function useFieldset(ref, config) {
231
228
  });
232
229
  var [error, setError] = useState(() => {
233
230
  var result = {};
234
- for (var [key, entries] of Object.entries(uncontrolledState.initialError)) {
235
- var _entries$;
236
- var [name, message] = (_entries$ = entries === null || entries === void 0 ? void 0 : entries[0]) !== null && _entries$ !== void 0 ? _entries$ : [];
237
- if (name === '') {
238
- result[key] = message !== null && message !== void 0 ? message : '';
239
- }
231
+ for (var [key, _error] of Object.entries(uncontrolledState.initialError)) {
232
+ result[key] = getErrors(getValidationMessage(_error === null || _error === void 0 ? void 0 : _error['']));
240
233
  }
241
234
  return result;
242
235
  });
@@ -262,13 +255,12 @@ function useFieldset(ref, config) {
262
255
  field.setAttribute('aria-invalid', field.validationMessage !== '' ? 'true' : 'false');
263
256
  }
264
257
  setError(prev => {
265
- var _prev$key;
266
- var prevMessage = (_prev$key = prev === null || prev === void 0 ? void 0 : prev[key]) !== null && _prev$key !== void 0 ? _prev$key : '';
258
+ var prevMessage = getValidationMessage(prev === null || prev === void 0 ? void 0 : prev[key]);
267
259
  if (prevMessage === field.validationMessage) {
268
260
  return prev;
269
261
  }
270
262
  return _objectSpread2(_objectSpread2({}, prev), {}, {
271
- [key]: field.validationMessage
263
+ [key]: getErrors(field.validationMessage)
272
264
  });
273
265
  });
274
266
  }
@@ -306,19 +298,21 @@ function useFieldset(ref, config) {
306
298
  */
307
299
  return new Proxy({}, {
308
300
  get(_target, key) {
309
- var _fieldsetConfig$const, _error$key;
301
+ var _fieldsetConfig$const;
310
302
  if (typeof key !== 'string') {
311
303
  return;
312
304
  }
313
305
  var fieldsetConfig = config !== null && config !== void 0 ? config : {};
314
306
  var constraint = (_fieldsetConfig$const = fieldsetConfig.constraint) === null || _fieldsetConfig$const === void 0 ? void 0 : _fieldsetConfig$const[key];
307
+ var errors = error === null || error === void 0 ? void 0 : error[key];
315
308
  var field = {
316
309
  config: _objectSpread2({
317
310
  name: fieldsetConfig.name ? "".concat(fieldsetConfig.name, ".").concat(key) : key,
318
311
  defaultValue: uncontrolledState.defaultValue[key],
319
312
  initialError: uncontrolledState.initialError[key]
320
313
  }, constraint),
321
- error: (_error$key = error === null || error === void 0 ? void 0 : error[key]) !== null && _error$key !== void 0 ? _error$key : ''
314
+ error: errors === null || errors === void 0 ? void 0 : errors[0],
315
+ errors
322
316
  };
323
317
  if (fieldsetConfig.form) {
324
318
  field.config.form = fieldsetConfig.form;
@@ -341,18 +335,14 @@ function useFieldList(ref, config) {
341
335
  var [uncontrolledState, setUncontrolledState] = useState(() => {
342
336
  var _config$defaultValue2;
343
337
  var initialError = [];
344
- for (var [name, message] of (_config$initialError2 = config === null || config === void 0 ? void 0 : config.initialError) !== null && _config$initialError2 !== void 0 ? _config$initialError2 : []) {
338
+ for (var [name, message] of Object.entries((_config$initialError2 = config === null || config === void 0 ? void 0 : config.initialError) !== null && _config$initialError2 !== void 0 ? _config$initialError2 : {})) {
345
339
  var _config$initialError2;
346
340
  var [index, ...paths] = getPaths(name);
347
341
  if (typeof index === 'number') {
348
- var _initialError$index;
349
342
  var scopedName = getName(paths);
350
- var _entries = (_initialError$index = initialError[index]) !== null && _initialError$index !== void 0 ? _initialError$index : [];
351
- if (scopedName === '' && _entries.length > 0 && _entries[0][0] !== '') {
352
- initialError[index] = [[scopedName, message], ..._entries];
353
- } else {
354
- initialError[index] = [..._entries, [scopedName, message]];
355
- }
343
+ initialError[index] = _objectSpread2(_objectSpread2({}, initialError[index]), {}, {
344
+ [scopedName]: message
345
+ });
356
346
  }
357
347
  }
358
348
  return {
@@ -360,7 +350,7 @@ function useFieldList(ref, config) {
360
350
  initialError
361
351
  };
362
352
  });
363
- var [error, setError] = useState(() => uncontrolledState.initialError.map(error => error === null || error === void 0 ? void 0 : error[0][1]));
353
+ var [error, setError] = useState(() => uncontrolledState.initialError.map(error => getErrors(getValidationMessage(error === null || error === void 0 ? void 0 : error['']))));
364
354
  var [entries, setEntries] = useState(() => {
365
355
  var _config$defaultValue3;
366
356
  return Object.entries((_config$defaultValue3 = config.defaultValue) !== null && _config$defaultValue3 !== void 0 ? _config$defaultValue3 : [undefined]);
@@ -383,24 +373,23 @@ function useFieldList(ref, config) {
383
373
  if (typeof index === 'number' && paths.length === 0) {
384
374
  if (field.dataset.conformTouched) {
385
375
  setError(prev => {
386
- var _prev$index;
387
- var prevMessage = (_prev$index = prev === null || prev === void 0 ? void 0 : prev[index]) !== null && _prev$index !== void 0 ? _prev$index : '';
376
+ var prevMessage = getValidationMessage(prev === null || prev === void 0 ? void 0 : prev[index]);
388
377
  if (prevMessage === field.validationMessage) {
389
378
  return prev;
390
379
  }
391
- return [...prev.slice(0, index), field.validationMessage, ...prev.slice(index + 1)];
380
+ return [...prev.slice(0, index), getErrors(field.validationMessage), ...prev.slice(index + 1)];
392
381
  });
393
382
  }
394
383
  event.preventDefault();
395
384
  }
396
385
  };
397
- var submitHandler = event => {
386
+ var listHandler = event => {
398
387
  var form = getFormElement(ref.current);
399
- if (!form || event.target !== form || !(event.submitter instanceof HTMLButtonElement) || event.submitter.name !== 'conform/list') {
388
+ if (!form || event.target !== form) {
400
389
  return;
401
390
  }
402
- var command = parseListCommand(event.submitter.value);
403
- if (command.scope !== configRef.current.name) {
391
+ var command = parseListCommand(event.detail);
392
+ if ((command === null || command === void 0 ? void 0 : command.scope) !== configRef.current.name) {
404
393
  // Ensure the scope of the listener are limited to specific field name
405
394
  return;
406
395
  }
@@ -411,7 +400,9 @@ function useFieldList(ref, config) {
411
400
  case 'replace':
412
401
  return updateList([...(entries !== null && entries !== void 0 ? entries : [])], _objectSpread2(_objectSpread2({}, command), {}, {
413
402
  payload: _objectSpread2(_objectSpread2({}, command.payload), {}, {
414
- defaultValue: ["".concat(Date.now()), command.payload.defaultValue]
403
+ defaultValue: ["".concat(Date.now()),
404
+ // @ts-expect-error unknown type as it is sent through network
405
+ command.payload.defaultValue]
415
406
  })
416
407
  }));
417
408
  default:
@@ -436,7 +427,6 @@ function useFieldList(ref, config) {
436
427
  }
437
428
  }
438
429
  });
439
- event.preventDefault();
440
430
  };
441
431
  var resetHandler = event => {
442
432
  var _fieldConfig$defaultV, _fieldConfig$defaultV2;
@@ -452,17 +442,21 @@ function useFieldList(ref, config) {
452
442
  setEntries(Object.entries((_fieldConfig$defaultV2 = fieldConfig.defaultValue) !== null && _fieldConfig$defaultV2 !== void 0 ? _fieldConfig$defaultV2 : [undefined]));
453
443
  setError([]);
454
444
  };
455
- document.addEventListener('submit', submitHandler, true);
445
+
446
+ // @ts-expect-error Custom event: conform/list
447
+ document.addEventListener('conform/list', listHandler, true);
456
448
  document.addEventListener('invalid', invalidHandler, true);
457
449
  document.addEventListener('reset', resetHandler);
458
450
  return () => {
459
- document.removeEventListener('submit', submitHandler, true);
451
+ // @ts-expect-error Custom event: conform/list
452
+ document.removeEventListener('conform/list', listHandler, true);
460
453
  document.removeEventListener('invalid', invalidHandler, true);
461
454
  document.removeEventListener('reset', resetHandler);
462
455
  };
463
456
  }, [ref]);
464
- return entries.map((_ref3, index) => {
465
- var [key, defaultValue] = _ref3;
457
+ return entries.map((_ref4, index) => {
458
+ var [key, defaultValue] = _ref4;
459
+ var errors = error[index];
466
460
  var fieldConfig = {
467
461
  name: "".concat(config.name, "[").concat(index, "]"),
468
462
  defaultValue: defaultValue !== null && defaultValue !== void 0 ? defaultValue : uncontrolledState.defaultValue[index],
@@ -475,86 +469,12 @@ function useFieldList(ref, config) {
475
469
  }
476
470
  return {
477
471
  key,
478
- error: error[index],
472
+ error: errors === null || errors === void 0 ? void 0 : errors[0],
473
+ errors,
479
474
  config: fieldConfig
480
475
  };
481
476
  });
482
477
  }
483
- /**
484
- * Returns the properties required to configure a shadow input for validation.
485
- * This is particular useful when integrating dropdown and datepicker whichs
486
- * introduces custom input mode.
487
- *
488
- * @deprecated Please use the `useInputEvent` hook instead
489
- * @see https://conform.guide/api/react#usecontrolledinput
490
- */
491
- function useControlledInput(config) {
492
- var _config$defaultValue4;
493
- var ref = useRef(null);
494
- var inputRef = useRef(null);
495
- var configRef = useRef(config);
496
- var [uncontrolledState, setUncontrolledState] = useState({
497
- defaultValue: config.defaultValue,
498
- initialError: config.initialError
499
- });
500
- var [value, setValue] = useState("".concat((_config$defaultValue4 = config.defaultValue) !== null && _config$defaultValue4 !== void 0 ? _config$defaultValue4 : ''));
501
- var handleChange = eventOrValue => {
502
- if (!ref.current) {
503
- return;
504
- }
505
- var newValue = typeof eventOrValue === 'string' ? eventOrValue : eventOrValue.target.value;
506
- ref.current.value = newValue;
507
- ref.current.dispatchEvent(new InputEvent('input', {
508
- bubbles: true
509
- }));
510
- setValue(newValue);
511
- };
512
- var handleBlur = () => {
513
- var _ref$current;
514
- (_ref$current = ref.current) === null || _ref$current === void 0 ? void 0 : _ref$current.dispatchEvent(new FocusEvent('blur', {
515
- bubbles: true
516
- }));
517
- };
518
- var handleInvalid = event => {
519
- event.preventDefault();
520
- };
521
- useEffect(() => {
522
- configRef.current = config;
523
- });
524
- useEffect(() => {
525
- var resetHandler = event => {
526
- var _configRef$current$de;
527
- var form = getFormElement(ref.current);
528
- if (!form || event.target !== form) {
529
- return;
530
- }
531
- setUncontrolledState({
532
- defaultValue: configRef.current.defaultValue,
533
- initialError: configRef.current.initialError
534
- });
535
- setValue("".concat((_configRef$current$de = configRef.current.defaultValue) !== null && _configRef$current$de !== void 0 ? _configRef$current$de : ''));
536
- };
537
- document.addEventListener('reset', resetHandler);
538
- return () => {
539
- document.removeEventListener('reset', resetHandler);
540
- };
541
- }, []);
542
- return [_objectSpread2({
543
- ref,
544
- onFocus() {
545
- var _inputRef$current;
546
- (_inputRef$current = inputRef.current) === null || _inputRef$current === void 0 ? void 0 : _inputRef$current.focus();
547
- }
548
- }, input(_objectSpread2(_objectSpread2({}, config), uncontrolledState), {
549
- hidden: true
550
- })), {
551
- ref: inputRef,
552
- value,
553
- onChange: handleChange,
554
- onBlur: handleBlur,
555
- onInvalid: handleInvalid
556
- }];
557
- }
558
478
 
559
479
  /**
560
480
  * Triggering react custom change event
@@ -731,4 +651,4 @@ function useInputEvent(options) {
731
651
  return [ref, control];
732
652
  }
733
653
 
734
- export { useControlledInput, useFieldList, useFieldset, useForm, useInputEvent };
654
+ export { useFieldList, useFieldset, useForm, useInputEvent };
package/module/index.js CHANGED
@@ -1,4 +1,4 @@
1
- export { getFormElements, hasError, list, parse, requestCommand, requestSubmit, shouldValidate, validate } from '@conform-to/dom';
2
- export { useControlledInput, useFieldList, useFieldset, useForm, useInputEvent } from './hooks.js';
1
+ export { getFormElements, list, parse, requestIntent, requestSubmit, validate, validateConstraint } from '@conform-to/dom';
2
+ export { useFieldList, useFieldset, useForm, useInputEvent } from './hooks.js';
3
3
  import * as helpers from './helpers.js';
4
4
  export { helpers as conform };
package/package.json CHANGED
@@ -2,7 +2,7 @@
2
2
  "name": "@conform-to/react",
3
3
  "description": "Conform view adapter for react",
4
4
  "license": "MIT",
5
- "version": "0.5.1",
5
+ "version": "0.6.0-pre.0",
6
6
  "main": "index.js",
7
7
  "module": "module/index.js",
8
8
  "repository": {
@@ -19,7 +19,7 @@
19
19
  "url": "https://github.com/edmundhung/conform/issues"
20
20
  },
21
21
  "dependencies": {
22
- "@conform-to/dom": "0.5.1"
22
+ "@conform-to/dom": "0.6.0-pre.0"
23
23
  },
24
24
  "peerDependencies": {
25
25
  "react": ">=16.8"