@conform-to/react 0.3.0 → 0.4.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,5 +1,5 @@
1
1
  import { objectSpread2 as _objectSpread2 } from './_virtual/_rollupPluginBabelHelpers.js';
2
- import { isFieldElement, listCommandKey, serializeListCommand, getFormElement, getKey, parseListCommand, updateList } from '@conform-to/dom';
2
+ import { getSubmissionType, reportValidity, focusFirstInvalidField, requestSubmit, isFieldElement, getFormData, parse, getPaths, getName, requestValidate, getFormElement, parseListCommand, updateList } from '@conform-to/dom';
3
3
  import { useRef, useState, useEffect } from 'react';
4
4
  import { input } from './helpers.js';
5
5
 
@@ -7,64 +7,107 @@ import { input } from './helpers.js';
7
7
  * Returns properties required to hook into form events.
8
8
  * Applied custom validation and define when error should be reported.
9
9
  *
10
- * @see https://github.com/edmundhung/conform/tree/v0.3.0/packages/conform-react/README.md#useform
10
+ * @see https://github.com/edmundhung/conform/tree/v0.4.0-pre.0/packages/conform-react/README.md#useform
11
11
  */
12
12
  function useForm() {
13
13
  var config = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};
14
- var {
15
- validate
16
- } = config;
14
+ var configRef = useRef(config);
17
15
  var ref = useRef(null);
16
+ var [error, setError] = useState(() => {
17
+ var _config$state$error$f, _config$state, _config$state$error;
18
+
19
+ 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 => {
20
+ var [key] = _ref;
21
+ return key === '';
22
+ })) !== null && _config$state$error$f !== void 0 ? _config$state$error$f : [];
23
+ return message !== null && message !== void 0 ? message : '';
24
+ });
25
+ var [fieldsetConfig, setFieldsetConfig] = useState(() => {
26
+ var _config$state$error2, _config$state2, _config$state3, _config$state$value, _config$state4;
27
+
28
+ var error = (_config$state$error2 = (_config$state2 = config.state) === null || _config$state2 === void 0 ? void 0 : _config$state2.error) !== null && _config$state$error2 !== void 0 ? _config$state$error2 : [];
29
+ var scope = (_config$state3 = config.state) === null || _config$state3 === void 0 ? void 0 : _config$state3.scope;
30
+ return {
31
+ defaultValue: (_config$state$value = (_config$state4 = config.state) === null || _config$state4 === void 0 ? void 0 : _config$state4.value) !== null && _config$state$value !== void 0 ? _config$state$value : config.defaultValue,
32
+ initialError: error.filter(_ref2 => {
33
+ var [name] = _ref2;
34
+ return name !== '' && getSubmissionType(name) === null && (!scope || scope.includes(name));
35
+ })
36
+ };
37
+ });
18
38
  var [noValidate, setNoValidate] = useState(config.noValidate || !config.fallbackNative);
39
+ useEffect(() => {
40
+ configRef.current = config;
41
+ });
19
42
  useEffect(() => {
20
43
  setNoValidate(true);
21
44
  }, []);
22
45
  useEffect(() => {
23
- // Initialize form validation messages
24
- if (ref.current) {
25
- validate === null || validate === void 0 ? void 0 : validate(ref.current);
26
- } // Revalidate the form when input value is changed
46
+ var form = ref.current;
27
47
 
48
+ if (!form || !config.state) {
49
+ return;
50
+ }
51
+
52
+ if (!reportValidity(form, config.state)) {
53
+ focusFirstInvalidField(form, config.state.scope);
54
+ }
28
55
 
56
+ requestSubmit(form);
57
+ }, [config.state]);
58
+ useEffect(() => {
59
+ // Revalidate the form when input value is changed
29
60
  var handleInput = event => {
30
61
  var field = event.target;
31
62
  var form = ref.current;
63
+ var formConfig = configRef.current;
32
64
 
33
65
  if (!form || !isFieldElement(field) || field.form !== form) {
34
66
  return;
35
67
  }
36
68
 
37
- validate === null || validate === void 0 ? void 0 : validate(form);
38
-
39
- if (!config.noValidate) {
40
- if (config.initialReport === 'onChange') {
41
- field.dataset.conformTouched = 'true';
42
- } // Field validity might be changed due to cross reference
43
-
69
+ if (formConfig.initialReport === 'onChange') {
70
+ field.dataset.conformTouched = 'true';
71
+ }
44
72
 
45
- for (var _field of form.elements) {
46
- if (isFieldElement(_field) && _field.dataset.conformTouched) {
47
- // Report latest error for all touched fields
48
- _field.checkValidity();
49
- }
50
- }
73
+ if (field.dataset.conformTouched) {
74
+ requestValidate(form, field.name);
51
75
  }
52
76
  };
53
77
 
54
78
  var handleBlur = event => {
55
79
  var field = event.target;
56
80
  var form = ref.current;
81
+ var formConfig = configRef.current;
57
82
 
58
- if (!form || !isFieldElement(field) || field.form !== form || config.noValidate || config.initialReport !== 'onBlur') {
83
+ if (!form || !isFieldElement(field) || field.form !== form) {
59
84
  return;
60
85
  }
61
86
 
62
- field.dataset.conformTouched = 'true';
63
- field.reportValidity();
87
+ if (formConfig.initialReport === 'onBlur' && !field.dataset.conformTouched) {
88
+ field.dataset.conformTouched = 'true';
89
+ requestValidate(form, field.name);
90
+ }
91
+ };
92
+
93
+ var handleInvalid = event => {
94
+ var form = getFormElement(ref.current);
95
+ var field = event.target;
96
+
97
+ if (!form || !isFieldElement(field) || field.form !== form || field.name !== '') {
98
+ return;
99
+ }
100
+
101
+ event.preventDefault();
102
+
103
+ if (field.dataset.conformTouched) {
104
+ setError(field.validationMessage);
105
+ }
64
106
  };
65
107
 
66
108
  var handleReset = event => {
67
109
  var form = ref.current;
110
+ var formConfig = configRef.current;
68
111
 
69
112
  if (!form || event.target !== form) {
70
113
  return;
@@ -74,17 +117,15 @@ function useForm() {
74
117
  for (var field of form.elements) {
75
118
  if (isFieldElement(field)) {
76
119
  delete field.dataset.conformTouched;
120
+ field.setCustomValidity('');
77
121
  }
78
122
  }
79
- /**
80
- * The reset event is triggered before form reset happens.
81
- * This make sure the form to be revalidated with initial values.
82
- */
83
123
 
84
-
85
- setTimeout(() => {
86
- validate === null || validate === void 0 ? void 0 : validate(form);
87
- }, 0);
124
+ setError('');
125
+ setFieldsetConfig({
126
+ defaultValue: formConfig.defaultValue,
127
+ initialError: []
128
+ });
88
129
  };
89
130
  /**
90
131
  * The input event handler will be triggered in capturing phase in order to
@@ -96,51 +137,83 @@ function useForm() {
96
137
 
97
138
  document.addEventListener('input', handleInput, true);
98
139
  document.addEventListener('blur', handleBlur, true);
140
+ document.addEventListener('invalid', handleInvalid, true);
99
141
  document.addEventListener('reset', handleReset);
100
142
  return () => {
101
143
  document.removeEventListener('input', handleInput, true);
102
144
  document.removeEventListener('blur', handleBlur, true);
145
+ document.removeEventListener('invalid', handleInvalid, true);
103
146
  document.removeEventListener('reset', handleReset);
104
147
  };
105
- }, [validate, config.initialReport, config.noValidate]);
148
+ }, []);
106
149
  return {
107
150
  ref,
108
- noValidate,
109
-
110
- onSubmit(event) {
111
- var form = event.currentTarget;
112
- var nativeEvent = event.nativeEvent;
113
- var submitter = nativeEvent.submitter instanceof HTMLButtonElement || nativeEvent.submitter instanceof HTMLInputElement ? nativeEvent.submitter : null; // Validating the form with the submitter value
114
-
115
- validate === null || validate === void 0 ? void 0 : validate(form, submitter);
116
- /**
117
- * It checks defaultPrevented to confirm if the submission is intentional
118
- * This is utilized by `useFieldList` to modify the list state when the submit
119
- * event is captured and revalidate the form with new fields without triggering
120
- * a form submission at the same time.
121
- */
122
-
123
- if (!config.noValidate && !(submitter !== null && submitter !== void 0 && submitter.formNoValidate) && !event.defaultPrevented) {
124
- // Mark all fields as touched
125
- for (var field of form.elements) {
126
- if (isFieldElement(field)) {
127
- field.dataset.conformTouched = 'true';
151
+ error,
152
+ props: {
153
+ ref,
154
+ noValidate,
155
+
156
+ onSubmit(event) {
157
+ var form = event.currentTarget;
158
+ var nativeEvent = event.nativeEvent;
159
+ var submitter = nativeEvent.submitter;
160
+
161
+ for (var element of form.elements) {
162
+ if (isFieldElement(element) && element.name === '') {
163
+ setError(element.validationMessage);
164
+ break;
128
165
  }
129
- } // Check the validity of the form
166
+ }
167
+ /**
168
+ * It checks defaultPrevented to confirm if the submission is intentional
169
+ * This is utilized by `useFieldList` to modify the list state when the submit
170
+ * event is captured and revalidate the form with new fields without triggering
171
+ * a form submission at the same time.
172
+ */
130
173
 
131
174
 
132
- if (!event.currentTarget.reportValidity()) {
175
+ if (!submitter || event.defaultPrevented) {
133
176
  event.preventDefault();
177
+ return;
178
+ }
179
+
180
+ var formData = getFormData(form, submitter);
181
+ var submission = parse(formData);
182
+ var context = {
183
+ form,
184
+ formData,
185
+ submission
186
+ }; // Touch all fields only if the submitter is not a command button
187
+
188
+ if (!submission.type) {
189
+ for (var field of form.elements) {
190
+ if (isFieldElement(field)) {
191
+ // Mark the field as touched
192
+ field.dataset.conformTouched = 'true';
193
+ }
194
+ }
195
+ }
196
+
197
+ if (typeof config.onValidate === 'function' && !config.noValidate && !submitter.formNoValidate) {
198
+ try {
199
+ if (!config.onValidate(context)) {
200
+ focusFirstInvalidField(form);
201
+ event.preventDefault();
202
+ }
203
+ } catch (e) {
204
+ console.warn(e);
205
+ }
134
206
  }
135
- }
136
207
 
137
- if (!event.defaultPrevented) {
138
- var _config$onSubmit;
208
+ if (!event.defaultPrevented) {
209
+ var _config$onSubmit;
139
210
 
140
- (_config$onSubmit = config.onSubmit) === null || _config$onSubmit === void 0 ? void 0 : _config$onSubmit.call(config, event);
211
+ (_config$onSubmit = config.onSubmit) === null || _config$onSubmit === void 0 ? void 0 : _config$onSubmit.call(config, event, context);
212
+ }
141
213
  }
142
- }
143
214
 
215
+ },
216
+ config: fieldsetConfig
144
217
  };
145
218
  }
146
219
  /**
@@ -148,19 +221,55 @@ function useForm() {
148
221
  */
149
222
 
150
223
  function useFieldset(ref, config) {
224
+ var configRef = useRef(config);
225
+ var [uncontrolledState, setUncontrolledState] = useState( // @ts-expect-error
226
+ () => {
227
+ var _config$defaultValue;
228
+
229
+ var initialError = {};
230
+
231
+ for (var [name, message] of (_config$initialError = config === null || config === void 0 ? void 0 : config.initialError) !== null && _config$initialError !== void 0 ? _config$initialError : []) {
232
+ var _config$initialError;
233
+
234
+ var [key, ...paths] = getPaths(name);
235
+
236
+ if (typeof key === 'string') {
237
+ var _initialError$key;
238
+
239
+ var scopedName = getName(paths);
240
+ var entries = (_initialError$key = initialError[key]) !== null && _initialError$key !== void 0 ? _initialError$key : [];
241
+
242
+ if (scopedName === '' && entries.length > 0 && entries[0][0] !== '') {
243
+ initialError[key] = [[scopedName, message], ...entries];
244
+ } else {
245
+ initialError[key] = [...entries, [scopedName, message]];
246
+ }
247
+ }
248
+ }
249
+
250
+ return {
251
+ defaultValue: (_config$defaultValue = config === null || config === void 0 ? void 0 : config.defaultValue) !== null && _config$defaultValue !== void 0 ? _config$defaultValue : {},
252
+ initialError
253
+ };
254
+ });
151
255
  var [error, setError] = useState(() => {
152
256
  var result = {};
153
257
 
154
- for (var [key, _error] of Object.entries((_config$initialError = config === null || config === void 0 ? void 0 : config.initialError) !== null && _config$initialError !== void 0 ? _config$initialError : {})) {
155
- var _config$initialError;
258
+ for (var [key, entries] of Object.entries(uncontrolledState.initialError)) {
259
+ var _entries$;
156
260
 
157
- if (_error !== null && _error !== void 0 && _error.message) {
158
- result[key] = _error.message;
261
+ var [name, message] = (_entries$ = entries === null || entries === void 0 ? void 0 : entries[0]) !== null && _entries$ !== void 0 ? _entries$ : [];
262
+
263
+ if (name === '') {
264
+ result[key] = message !== null && message !== void 0 ? message : '';
159
265
  }
160
266
  }
161
267
 
162
268
  return result;
163
269
  });
270
+ useEffect(() => {
271
+ configRef.current = config;
272
+ });
164
273
  useEffect(() => {
165
274
  /**
166
275
  * Reset the error state of each field if its validity is changed.
@@ -170,13 +279,16 @@ function useFieldset(ref, config) {
170
279
  */
171
280
  var resetError = form => {
172
281
  setError(prev => {
282
+ var _configRef$current$na, _configRef$current;
283
+
173
284
  var next = prev;
285
+ var fieldsetName = (_configRef$current$na = (_configRef$current = configRef.current) === null || _configRef$current === void 0 ? void 0 : _configRef$current.name) !== null && _configRef$current$na !== void 0 ? _configRef$current$na : '';
174
286
 
175
287
  for (var field of form.elements) {
176
- if (isFieldElement(field)) {
177
- var key = getKey(field.name, config === null || config === void 0 ? void 0 : config.name);
288
+ if (isFieldElement(field) && field.name.startsWith(fieldsetName)) {
289
+ var [key, ...paths] = getPaths(fieldsetName.length > 0 ? field.name.slice(fieldsetName.length + 1) : field.name);
178
290
 
179
- if (key) {
291
+ if (typeof key === 'string' && paths.length === 0) {
180
292
  var _next$key, _next;
181
293
 
182
294
  var prevMessage = (_next$key = (_next = next) === null || _next === void 0 ? void 0 : _next[key]) !== null && _next$key !== void 0 ? _next$key : '';
@@ -212,29 +324,35 @@ function useFieldset(ref, config) {
212
324
  };
213
325
 
214
326
  var invalidHandler = event => {
327
+ var _configRef$current$na2, _configRef$current2;
328
+
215
329
  var form = getFormElement(ref.current);
216
330
  var field = event.target;
331
+ var fieldsetName = (_configRef$current$na2 = (_configRef$current2 = configRef.current) === null || _configRef$current2 === void 0 ? void 0 : _configRef$current2.name) !== null && _configRef$current$na2 !== void 0 ? _configRef$current$na2 : '';
217
332
 
218
- if (!form || !isFieldElement(field) || field.form !== form) {
333
+ if (!form || !isFieldElement(field) || field.form !== form || !field.name.startsWith(fieldsetName)) {
219
334
  return;
220
335
  }
221
336
 
222
- var key = getKey(field.name, config === null || config === void 0 ? void 0 : config.name); // Update the error only if the field belongs to the fieldset
337
+ var [key, ...paths] = getPaths(fieldsetName.length > 0 ? field.name.slice(fieldsetName.length + 1) : field.name); // Update the error only if the field belongs to the fieldset
223
338
 
224
- if (key) {
225
- setError(prev => {
226
- var _prev$key;
339
+ if (typeof key === 'string' && paths.length === 0) {
340
+ if (field.dataset.conformTouched) {
341
+ setError(prev => {
342
+ var _prev$key;
227
343
 
228
- var prevMessage = (_prev$key = prev === null || prev === void 0 ? void 0 : prev[key]) !== null && _prev$key !== void 0 ? _prev$key : '';
344
+ var prevMessage = (_prev$key = prev === null || prev === void 0 ? void 0 : prev[key]) !== null && _prev$key !== void 0 ? _prev$key : '';
229
345
 
230
- if (prevMessage === field.validationMessage) {
231
- return prev;
232
- }
346
+ if (prevMessage === field.validationMessage) {
347
+ return prev;
348
+ }
233
349
 
234
- return _objectSpread2(_objectSpread2({}, prev), {}, {
235
- [key]: field.validationMessage
350
+ return _objectSpread2(_objectSpread2({}, prev), {}, {
351
+ [key]: field.validationMessage
352
+ });
236
353
  });
237
- });
354
+ }
355
+
238
356
  event.preventDefault();
239
357
  }
240
358
  };
@@ -251,12 +369,20 @@ function useFieldset(ref, config) {
251
369
  };
252
370
 
253
371
  var resetHandler = event => {
372
+ var _fieldsetConfig$defau;
373
+
254
374
  var form = getFormElement(ref.current);
255
375
 
256
376
  if (!form || event.target !== form) {
257
377
  return;
258
378
  }
259
379
 
380
+ var fieldsetConfig = configRef.current;
381
+ setUncontrolledState({
382
+ // @ts-expect-error
383
+ defaultValue: (_fieldsetConfig$defau = fieldsetConfig === null || fieldsetConfig === void 0 ? void 0 : fieldsetConfig.defaultValue) !== null && _fieldsetConfig$defau !== void 0 ? _fieldsetConfig$defau : {},
384
+ initialError: {}
385
+ });
260
386
  setError({});
261
387
  };
262
388
 
@@ -271,26 +397,7 @@ function useFieldset(ref, config) {
271
397
  document.removeEventListener('submit', submitHandler);
272
398
  document.removeEventListener('reset', resetHandler);
273
399
  };
274
- }, [ref, config === null || config === void 0 ? void 0 : config.name]);
275
- useEffect(() => {
276
- setError(prev => {
277
- var next = prev;
278
-
279
- for (var [key, _error2] of Object.entries((_config$initialError2 = config === null || config === void 0 ? void 0 : config.initialError) !== null && _config$initialError2 !== void 0 ? _config$initialError2 : {})) {
280
- var _config$initialError2;
281
-
282
- if (next[key] !== (_error2 === null || _error2 === void 0 ? void 0 : _error2.message)) {
283
- var _error2$message;
284
-
285
- next = _objectSpread2(_objectSpread2({}, next), {}, {
286
- [key]: (_error2$message = _error2 === null || _error2 === void 0 ? void 0 : _error2.message) !== null && _error2$message !== void 0 ? _error2$message : ''
287
- });
288
- }
289
- }
290
-
291
- return next;
292
- });
293
- }, [config === null || config === void 0 ? void 0 : config.name, config === null || config === void 0 ? void 0 : config.initialError]);
400
+ }, [ref]);
294
401
  /**
295
402
  * This allows us constructing the field at runtime as we have no information
296
403
  * about which fields would be available. The proxy will also help tracking
@@ -299,19 +406,20 @@ function useFieldset(ref, config) {
299
406
 
300
407
  return new Proxy({}, {
301
408
  get(_target, key) {
302
- var _constraint, _config$defaultValue, _config$initialError3, _config$initialError4, _error$key;
409
+ var _fieldsetConfig$const, _error$key;
303
410
 
304
411
  if (typeof key !== 'string') {
305
412
  return;
306
413
  }
307
414
 
308
- var constraint = config === null || config === void 0 ? void 0 : (_constraint = config.constraint) === null || _constraint === void 0 ? void 0 : _constraint[key];
415
+ var fieldsetConfig = config !== null && config !== void 0 ? config : {};
416
+ var constraint = (_fieldsetConfig$const = fieldsetConfig.constraint) === null || _fieldsetConfig$const === void 0 ? void 0 : _fieldsetConfig$const[key];
309
417
  var field = {
310
418
  config: _objectSpread2({
311
- name: config !== null && config !== void 0 && config.name ? "".concat(config.name, ".").concat(key) : key,
312
- form: config === null || config === void 0 ? void 0 : config.form,
313
- defaultValue: config === null || config === void 0 ? void 0 : (_config$defaultValue = config.defaultValue) === null || _config$defaultValue === void 0 ? void 0 : _config$defaultValue[key],
314
- initialError: config === null || config === void 0 ? void 0 : (_config$initialError3 = config.initialError) === null || _config$initialError3 === void 0 ? void 0 : (_config$initialError4 = _config$initialError3[key]) === null || _config$initialError4 === void 0 ? void 0 : _config$initialError4.details
419
+ name: fieldsetConfig.name ? "".concat(fieldsetConfig.name, ".").concat(key) : key,
420
+ form: fieldsetConfig.form,
421
+ defaultValue: uncontrolledState.defaultValue[key],
422
+ initialError: uncontrolledState.initialError[key]
315
423
  }, constraint),
316
424
  error: (_error$key = error === null || error === void 0 ? void 0 : error[key]) !== null && _error$key !== void 0 ? _error$key : ''
317
425
  };
@@ -325,25 +433,55 @@ function useFieldset(ref, config) {
325
433
  * Returns a list of key and config, with a group of helpers
326
434
  * configuring buttons for list manipulation
327
435
  *
328
- * @see https://github.com/edmundhung/conform/tree/v0.3.0/packages/conform-react/README.md#usefieldlist
436
+ * @see https://github.com/edmundhung/conform/tree/v0.4.0-pre.0/packages/conform-react/README.md#usefieldlist
329
437
  */
330
438
  function useFieldList(ref, config) {
331
- var [entries, setEntries] = useState(() => {
439
+ var configRef = useRef(config);
440
+ var [uncontrolledState, setUncontrolledState] = useState(() => {
332
441
  var _config$defaultValue2;
333
442
 
334
- return Object.entries((_config$defaultValue2 = config.defaultValue) !== null && _config$defaultValue2 !== void 0 ? _config$defaultValue2 : [undefined]);
443
+ var initialError = [];
444
+
445
+ for (var [name, message] of (_config$initialError2 = config === null || config === void 0 ? void 0 : config.initialError) !== null && _config$initialError2 !== void 0 ? _config$initialError2 : []) {
446
+ var _config$initialError2;
447
+
448
+ var [index, ...paths] = getPaths(name);
449
+
450
+ if (typeof index === 'number') {
451
+ var _initialError$index;
452
+
453
+ var scopedName = getName(paths);
454
+
455
+ var _entries = (_initialError$index = initialError[index]) !== null && _initialError$index !== void 0 ? _initialError$index : [];
456
+
457
+ if (scopedName === '' && _entries.length > 0 && _entries[0][0] !== '') {
458
+ initialError[index] = [[scopedName, message], ..._entries];
459
+ } else {
460
+ initialError[index] = [..._entries, [scopedName, message]];
461
+ }
462
+ }
463
+ }
464
+
465
+ return {
466
+ defaultValue: (_config$defaultValue2 = config.defaultValue) !== null && _config$defaultValue2 !== void 0 ? _config$defaultValue2 : [],
467
+ initialError
468
+ };
335
469
  });
336
- var list = entries.map((_ref, index) => {
337
- var _config$defaultValue3, _config$initialError5, _config$initialError6;
470
+ var [entries, setEntries] = useState(() => {
471
+ var _config$defaultValue3;
338
472
 
339
- var [key, defaultValue] = _ref;
473
+ return Object.entries((_config$defaultValue3 = config.defaultValue) !== null && _config$defaultValue3 !== void 0 ? _config$defaultValue3 : [undefined]);
474
+ });
475
+ var list = entries.map((_ref3, index) => {
476
+ var [key, defaultValue] = _ref3;
340
477
  return {
341
478
  key,
342
- config: _objectSpread2(_objectSpread2({}, config), {}, {
479
+ config: {
343
480
  name: "".concat(config.name, "[").concat(index, "]"),
344
- defaultValue: defaultValue !== null && defaultValue !== void 0 ? defaultValue : (_config$defaultValue3 = config.defaultValue) === null || _config$defaultValue3 === void 0 ? void 0 : _config$defaultValue3[index],
345
- initialError: (_config$initialError5 = config.initialError) === null || _config$initialError5 === void 0 ? void 0 : (_config$initialError6 = _config$initialError5[index]) === null || _config$initialError6 === void 0 ? void 0 : _config$initialError6.details
346
- })
481
+ form: config.form,
482
+ defaultValue: defaultValue !== null && defaultValue !== void 0 ? defaultValue : uncontrolledState.defaultValue[index],
483
+ initialError: uncontrolledState.initialError[index]
484
+ }
347
485
  };
348
486
  });
349
487
  /***
@@ -356,9 +494,10 @@ function useFieldList(ref, config) {
356
494
  return function () {
357
495
  var payload = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};
358
496
  return {
359
- name: listCommandKey,
360
- value: serializeListCommand(config.name, {
497
+ name: 'conform/list',
498
+ value: JSON.stringify({
361
499
  type,
500
+ scope: config.name,
362
501
  payload
363
502
  }),
364
503
  form: config.form,
@@ -369,56 +508,45 @@ function useFieldList(ref, config) {
369
508
 
370
509
  });
371
510
  useEffect(() => {
372
- setEntries(prevEntries => {
373
- var _config$defaultValue4;
374
-
375
- var nextEntries = Object.entries((_config$defaultValue4 = config.defaultValue) !== null && _config$defaultValue4 !== void 0 ? _config$defaultValue4 : [undefined]);
376
-
377
- if (prevEntries.length !== nextEntries.length) {
378
- return nextEntries;
379
- }
380
-
381
- for (var i = 0; i < prevEntries.length; i++) {
382
- var [prevKey, prevValue] = prevEntries[i];
383
- var [nextKey, nextValue] = nextEntries[i];
384
-
385
- if (prevKey !== nextKey || prevValue !== nextValue) {
386
- return nextEntries;
387
- }
388
- } // No need to rerender in this case
389
-
390
-
391
- return prevEntries;
392
- });
393
-
511
+ configRef.current = config;
512
+ });
513
+ useEffect(() => {
394
514
  var submitHandler = event => {
395
515
  var form = getFormElement(ref.current);
396
516
 
397
- if (!form || event.target !== form || !(event.submitter instanceof HTMLButtonElement) || event.submitter.name !== listCommandKey) {
517
+ if (!form || event.target !== form || !(event.submitter instanceof HTMLButtonElement) || event.submitter.name !== 'conform/list') {
398
518
  return;
399
519
  }
400
520
 
401
- var [name, command] = parseListCommand(event.submitter.value);
521
+ var command = parseListCommand(event.submitter.value);
402
522
 
403
- if (name !== config.name) {
523
+ if (command.scope !== configRef.current.name) {
404
524
  // Ensure the scope of the listener are limited to specific field name
405
525
  return;
406
526
  }
407
527
 
408
- switch (command.type) {
409
- case 'append':
410
- case 'prepend':
411
- case 'replace':
412
- command.payload.defaultValue = ["".concat(Date.now()), command.payload.defaultValue];
413
- break;
414
- }
415
-
416
- setEntries(entries => updateList([...(entries !== null && entries !== void 0 ? entries : [])], command));
528
+ setEntries(entries => {
529
+ switch (command.type) {
530
+ case 'append':
531
+ case 'prepend':
532
+ case 'replace':
533
+ return updateList([...(entries !== null && entries !== void 0 ? entries : [])], _objectSpread2(_objectSpread2({}, command), {}, {
534
+ payload: _objectSpread2(_objectSpread2({}, command.payload), {}, {
535
+ defaultValue: ["".concat(Date.now()), command.payload.defaultValue]
536
+ })
537
+ }));
538
+
539
+ default:
540
+ {
541
+ return updateList([...(entries !== null && entries !== void 0 ? entries : [])], command);
542
+ }
543
+ }
544
+ });
417
545
  event.preventDefault();
418
546
  };
419
547
 
420
548
  var resetHandler = event => {
421
- var _config$defaultValue5;
549
+ var _fieldConfig$defaultV, _fieldConfig$defaultV2;
422
550
 
423
551
  var form = getFormElement(ref.current);
424
552
 
@@ -426,7 +554,12 @@ function useFieldList(ref, config) {
426
554
  return;
427
555
  }
428
556
 
429
- setEntries(Object.entries((_config$defaultValue5 = config.defaultValue) !== null && _config$defaultValue5 !== void 0 ? _config$defaultValue5 : []));
557
+ var fieldConfig = configRef.current;
558
+ setUncontrolledState({
559
+ defaultValue: (_fieldConfig$defaultV = fieldConfig.defaultValue) !== null && _fieldConfig$defaultV !== void 0 ? _fieldConfig$defaultV : [],
560
+ initialError: []
561
+ });
562
+ setEntries(Object.entries((_fieldConfig$defaultV2 = fieldConfig.defaultValue) !== null && _fieldConfig$defaultV2 !== void 0 ? _fieldConfig$defaultV2 : [undefined]));
430
563
  };
431
564
 
432
565
  document.addEventListener('submit', submitHandler, true);
@@ -435,7 +568,7 @@ function useFieldList(ref, config) {
435
568
  document.removeEventListener('submit', submitHandler, true);
436
569
  document.removeEventListener('reset', resetHandler);
437
570
  };
438
- }, [ref, config.name, config.defaultValue]);
571
+ }, [ref]);
439
572
  return [list, control];
440
573
  }
441
574
 
@@ -444,13 +577,19 @@ function useFieldList(ref, config) {
444
577
  * This is particular useful when integrating dropdown and datepicker whichs
445
578
  * introduces custom input mode.
446
579
  *
447
- * @see https://github.com/edmundhung/conform/tree/v0.3.0/packages/conform-react/README.md#usecontrolledinput
580
+ * @see https://github.com/edmundhung/conform/tree/v0.4.0-pre.0/packages/conform-react/README.md#usecontrolledinput
448
581
  */
449
- function useControlledInput(field) {
450
- var _field$defaultValue;
582
+ function useControlledInput(config) {
583
+ var _config$defaultValue4;
451
584
 
452
585
  var ref = useRef(null);
453
- var [value, setValue] = useState("".concat((_field$defaultValue = field.defaultValue) !== null && _field$defaultValue !== void 0 ? _field$defaultValue : ''));
586
+ var inputRef = useRef(null);
587
+ var configRef = useRef(config);
588
+ var [uncontrolledState, setUncontrolledState] = useState({
589
+ defaultValue: config.defaultValue,
590
+ initialError: config.initialError
591
+ });
592
+ var [value, setValue] = useState("".concat((_config$defaultValue4 = config.defaultValue) !== null && _config$defaultValue4 !== void 0 ? _config$defaultValue4 : ''));
454
593
 
455
594
  var handleChange = eventOrValue => {
456
595
  if (!ref.current) {
@@ -477,12 +616,55 @@ function useControlledInput(field) {
477
616
  event.preventDefault();
478
617
  };
479
618
 
619
+ useEffect(() => {
620
+ configRef.current = config;
621
+ });
622
+ useEffect(() => {
623
+ var resetHandler = event => {
624
+ var _configRef$current$de;
625
+
626
+ var form = getFormElement(ref.current);
627
+
628
+ if (!form || event.target !== form) {
629
+ return;
630
+ }
631
+
632
+ setUncontrolledState({
633
+ defaultValue: configRef.current.defaultValue,
634
+ initialError: configRef.current.initialError
635
+ });
636
+ setValue("".concat((_configRef$current$de = configRef.current.defaultValue) !== null && _configRef$current$de !== void 0 ? _configRef$current$de : ''));
637
+ };
638
+
639
+ document.addEventListener('reset', resetHandler);
640
+ return () => {
641
+ document.removeEventListener('reset', resetHandler);
642
+ };
643
+ }, []);
480
644
  return [_objectSpread2({
481
645
  ref,
482
- hidden: true
483
- }, input(field, {
646
+ style: {
647
+ position: 'absolute',
648
+ width: '1px',
649
+ height: '1px',
650
+ padding: 0,
651
+ margin: '-1px',
652
+ overflow: 'hidden',
653
+ clip: 'rect(0,0,0,0)',
654
+ whiteSpace: 'nowrap',
655
+ borderWidth: 0
656
+ },
657
+
658
+ onFocus() {
659
+ var _inputRef$current;
660
+
661
+ (_inputRef$current = inputRef.current) === null || _inputRef$current === void 0 ? void 0 : _inputRef$current.focus();
662
+ }
663
+
664
+ }, input(_objectSpread2(_objectSpread2({}, config), uncontrolledState), {
484
665
  type: 'text'
485
666
  })), {
667
+ ref: inputRef,
486
668
  value,
487
669
  onChange: handleChange,
488
670
  onBlur: handleBlur,