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