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