@conform-to/react 0.3.1 → 0.4.0-pre.1

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