@conform-to/react 0.2.0 → 0.3.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/README.md +319 -228
- package/helpers.d.ts +4 -4
- package/helpers.js +28 -30
- package/hooks.d.ts +114 -35
- package/hooks.js +394 -318
- package/index.d.ts +1 -1
- package/index.js +4 -4
- package/module/helpers.js +28 -30
- package/module/hooks.js +396 -320
- package/module/index.js +1 -1
- package/package.json +2 -2
package/hooks.js
CHANGED
|
@@ -5,416 +5,492 @@ Object.defineProperty(exports, '__esModule', { value: true });
|
|
|
5
5
|
var _rollupPluginBabelHelpers = require('./_virtual/_rollupPluginBabelHelpers.js');
|
|
6
6
|
var dom = require('@conform-to/dom');
|
|
7
7
|
var react = require('react');
|
|
8
|
-
|
|
8
|
+
var helpers = require('./helpers.js');
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* Returns properties required to hook into form events.
|
|
12
|
+
* Applied custom validation and define when error should be reported.
|
|
13
|
+
*
|
|
14
|
+
* @see https://github.com/edmundhung/conform/tree/v0.3.0-pre.0/packages/conform-react/README.md#useform
|
|
15
|
+
*/
|
|
9
16
|
function useForm() {
|
|
17
|
+
var config = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};
|
|
10
18
|
var {
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
noValidate = false,
|
|
14
|
-
fallbackNative = false,
|
|
15
|
-
initialReport = 'onSubmit'
|
|
16
|
-
} = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};
|
|
19
|
+
validate
|
|
20
|
+
} = config;
|
|
17
21
|
var ref = react.useRef(null);
|
|
18
|
-
var [
|
|
19
|
-
|
|
20
|
-
var handleSubmit = event => {
|
|
21
|
-
if (!noValidate) {
|
|
22
|
-
dom.setFieldState(event.currentTarget, {
|
|
23
|
-
touched: true
|
|
24
|
-
});
|
|
25
|
-
|
|
26
|
-
if (!dom.shouldSkipValidate(event.nativeEvent) && !event.currentTarget.reportValidity()) {
|
|
27
|
-
return event.preventDefault();
|
|
28
|
-
}
|
|
29
|
-
}
|
|
30
|
-
|
|
31
|
-
onSubmit === null || onSubmit === void 0 ? void 0 : onSubmit(event);
|
|
32
|
-
};
|
|
33
|
-
|
|
34
|
-
var handleReset = event => {
|
|
35
|
-
dom.setFieldState(event.currentTarget, {
|
|
36
|
-
touched: false
|
|
37
|
-
});
|
|
38
|
-
onReset === null || onReset === void 0 ? void 0 : onReset(event);
|
|
39
|
-
};
|
|
40
|
-
|
|
22
|
+
var [noValidate, setNoValidate] = react.useState(config.noValidate || !config.fallbackNative);
|
|
41
23
|
react.useEffect(() => {
|
|
42
|
-
|
|
24
|
+
setNoValidate(true);
|
|
43
25
|
}, []);
|
|
44
26
|
react.useEffect(() => {
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
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
|
|
48
31
|
|
|
49
|
-
var handleChange = event => {
|
|
50
|
-
var _event$target;
|
|
51
32
|
|
|
52
|
-
|
|
33
|
+
var handleInput = event => {
|
|
34
|
+
var field = event.target;
|
|
35
|
+
var form = ref.current;
|
|
36
|
+
|
|
37
|
+
if (!form || !dom.isFieldElement(field) || field.form !== form) {
|
|
53
38
|
return;
|
|
54
39
|
}
|
|
55
40
|
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
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
|
+
|
|
61
48
|
|
|
62
|
-
|
|
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
|
+
}
|
|
55
|
+
}
|
|
63
56
|
};
|
|
64
57
|
|
|
65
58
|
var handleBlur = event => {
|
|
66
|
-
var
|
|
59
|
+
var field = event.target;
|
|
60
|
+
var form = ref.current;
|
|
67
61
|
|
|
68
|
-
if (!
|
|
62
|
+
if (!form || !dom.isFieldElement(field) || field.form !== form || config.noValidate || config.initialReport !== 'onBlur') {
|
|
69
63
|
return;
|
|
70
64
|
}
|
|
71
65
|
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
66
|
+
field.dataset.conformTouched = 'true';
|
|
67
|
+
field.reportValidity();
|
|
68
|
+
};
|
|
69
|
+
|
|
70
|
+
var handleReset = event => {
|
|
71
|
+
var form = ref.current;
|
|
72
|
+
|
|
73
|
+
if (!form || event.target !== form) {
|
|
74
|
+
return;
|
|
75
|
+
} // Reset all field state
|
|
76
|
+
|
|
77
|
+
|
|
78
|
+
for (var field of form.elements) {
|
|
79
|
+
if (dom.isFieldElement(field)) {
|
|
80
|
+
delete field.dataset.conformTouched;
|
|
81
|
+
}
|
|
76
82
|
}
|
|
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
|
+
|
|
77
88
|
|
|
78
|
-
|
|
89
|
+
setTimeout(() => {
|
|
90
|
+
validate === null || validate === void 0 ? void 0 : validate(form);
|
|
91
|
+
}, 0);
|
|
79
92
|
};
|
|
93
|
+
/**
|
|
94
|
+
* The input event handler will be triggered in capturing phase in order to
|
|
95
|
+
* allow follow-up action in the bubble phase based on the latest validity
|
|
96
|
+
* E.g. `useFieldset` reset the error of valid field after checking the
|
|
97
|
+
* validity in the bubble phase.
|
|
98
|
+
*/
|
|
99
|
+
|
|
80
100
|
|
|
81
|
-
document.addEventListener('input',
|
|
82
|
-
document.addEventListener('
|
|
101
|
+
document.addEventListener('input', handleInput, true);
|
|
102
|
+
document.addEventListener('blur', handleBlur, true);
|
|
103
|
+
document.addEventListener('reset', handleReset);
|
|
83
104
|
return () => {
|
|
84
|
-
document.removeEventListener('input',
|
|
85
|
-
document.removeEventListener('
|
|
105
|
+
document.removeEventListener('input', handleInput, true);
|
|
106
|
+
document.removeEventListener('blur', handleBlur, true);
|
|
107
|
+
document.removeEventListener('reset', handleReset);
|
|
86
108
|
};
|
|
87
|
-
}, [
|
|
109
|
+
}, [validate, config.initialReport, config.noValidate]);
|
|
88
110
|
return {
|
|
89
111
|
ref,
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
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';
|
|
109
132
|
}
|
|
133
|
+
} // Check the validity of the form
|
|
110
134
|
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
135
|
+
|
|
136
|
+
if (!event.currentTarget.reportValidity()) {
|
|
137
|
+
event.preventDefault();
|
|
114
138
|
}
|
|
139
|
+
}
|
|
115
140
|
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
var {
|
|
119
|
-
keys,
|
|
120
|
-
error
|
|
121
|
-
} = action.payload;
|
|
122
|
-
var nextState = state;
|
|
123
|
-
|
|
124
|
-
for (var _key of Object.keys(keys)) {
|
|
125
|
-
var prevError = state[_key];
|
|
126
|
-
var nextError = error === null || error === void 0 ? void 0 : error[_key];
|
|
127
|
-
|
|
128
|
-
if (typeof nextError === 'string' && prevError !== nextError) {
|
|
129
|
-
return _rollupPluginBabelHelpers.objectSpread2(_rollupPluginBabelHelpers.objectSpread2({}, nextState), {}, {
|
|
130
|
-
[_key]: nextError
|
|
131
|
-
});
|
|
132
|
-
}
|
|
133
|
-
}
|
|
141
|
+
if (!event.defaultPrevented) {
|
|
142
|
+
var _config$onSubmit;
|
|
134
143
|
|
|
135
|
-
|
|
136
|
-
|
|
144
|
+
(_config$onSubmit = config.onSubmit) === null || _config$onSubmit === void 0 ? void 0 : _config$onSubmit.call(config, event);
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
};
|
|
149
|
+
}
|
|
150
|
+
/**
|
|
151
|
+
* All the information of the field, including state and config.
|
|
152
|
+
*/
|
|
137
153
|
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
fieldset
|
|
142
|
-
} = action.payload;
|
|
143
|
-
var updates = [];
|
|
154
|
+
function useFieldset(ref, config) {
|
|
155
|
+
var [error, setError] = react.useState(() => {
|
|
156
|
+
var result = {};
|
|
144
157
|
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
continue;
|
|
148
|
-
}
|
|
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;
|
|
149
160
|
|
|
150
|
-
|
|
161
|
+
if (_error !== null && _error !== void 0 && _error.message) {
|
|
162
|
+
result[key] = _error.message;
|
|
163
|
+
}
|
|
164
|
+
}
|
|
151
165
|
|
|
152
|
-
|
|
153
|
-
|
|
166
|
+
return result;
|
|
167
|
+
});
|
|
168
|
+
react.useEffect(() => {
|
|
169
|
+
/**
|
|
170
|
+
* Reset the error state of each field if its validity is changed.
|
|
171
|
+
*
|
|
172
|
+
* This is a workaround as no official way is provided to notify
|
|
173
|
+
* when the validity of the field is changed from `invalid` to `valid`.
|
|
174
|
+
*/
|
|
175
|
+
var resetError = form => {
|
|
176
|
+
setError(prev => {
|
|
177
|
+
var next = prev;
|
|
178
|
+
|
|
179
|
+
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);
|
|
182
|
+
|
|
183
|
+
if (key) {
|
|
184
|
+
var _next$key, _next;
|
|
185
|
+
|
|
186
|
+
var prevMessage = (_next$key = (_next = next) === null || _next === void 0 ? void 0 : _next[key]) !== null && _next$key !== void 0 ? _next$key : '';
|
|
187
|
+
var nextMessage = field.validationMessage;
|
|
188
|
+
/**
|
|
189
|
+
* Techincally, checking prevMessage not being empty while nextMessage being empty
|
|
190
|
+
* is sufficient for our usecase. It checks if the message is changed instead to allow
|
|
191
|
+
* the hook to be useful independently.
|
|
192
|
+
*/
|
|
193
|
+
|
|
194
|
+
if (prevMessage !== '' && prevMessage !== nextMessage) {
|
|
195
|
+
next = _rollupPluginBabelHelpers.objectSpread2(_rollupPluginBabelHelpers.objectSpread2({}, next), {}, {
|
|
196
|
+
[key]: nextMessage
|
|
197
|
+
});
|
|
198
|
+
}
|
|
154
199
|
}
|
|
155
200
|
}
|
|
201
|
+
}
|
|
156
202
|
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
203
|
+
return next;
|
|
204
|
+
});
|
|
205
|
+
};
|
|
160
206
|
|
|
161
|
-
|
|
162
|
-
|
|
207
|
+
var handleInput = event => {
|
|
208
|
+
var form = dom.getFormElement(ref.current);
|
|
209
|
+
var field = event.target;
|
|
163
210
|
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
}
|
|
168
|
-
}
|
|
169
|
-
}, {}, () => Object.fromEntries(Object.keys(schema.fields).reduce((result, name) => {
|
|
170
|
-
var _config$error;
|
|
211
|
+
if (!form || !dom.isFieldElement(field) || field.form !== form) {
|
|
212
|
+
return;
|
|
213
|
+
}
|
|
171
214
|
|
|
172
|
-
|
|
215
|
+
resetError(form);
|
|
216
|
+
};
|
|
173
217
|
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
218
|
+
var invalidHandler = event => {
|
|
219
|
+
var form = dom.getFormElement(ref.current);
|
|
220
|
+
var field = event.target;
|
|
177
221
|
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
var _schema$validate;
|
|
222
|
+
if (!form || !dom.isFieldElement(field) || field.form !== form) {
|
|
223
|
+
return;
|
|
224
|
+
}
|
|
182
225
|
|
|
183
|
-
|
|
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
|
|
184
227
|
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
}
|
|
228
|
+
if (key) {
|
|
229
|
+
setError(prev => {
|
|
230
|
+
var _prev$key;
|
|
189
231
|
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
232
|
+
var prevMessage = (_prev$key = prev === null || prev === void 0 ? void 0 : prev[key]) !== null && _prev$key !== void 0 ? _prev$key : '';
|
|
233
|
+
|
|
234
|
+
if (prevMessage === field.validationMessage) {
|
|
235
|
+
return prev;
|
|
236
|
+
}
|
|
193
237
|
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
238
|
+
return _rollupPluginBabelHelpers.objectSpread2(_rollupPluginBabelHelpers.objectSpread2({}, prev), {}, {
|
|
239
|
+
[key]: field.validationMessage
|
|
240
|
+
});
|
|
241
|
+
});
|
|
242
|
+
event.preventDefault();
|
|
199
243
|
}
|
|
200
|
-
}
|
|
244
|
+
};
|
|
245
|
+
|
|
246
|
+
var submitHandler = event => {
|
|
247
|
+
var form = dom.getFormElement(ref.current);
|
|
201
248
|
|
|
202
|
-
|
|
203
|
-
if (e.target !== fieldset.form) {
|
|
249
|
+
if (!form || event.target !== form) {
|
|
204
250
|
return;
|
|
205
|
-
}
|
|
251
|
+
} // This helps resetting error that fullfilled by the submitter
|
|
206
252
|
|
|
207
|
-
dispatch({
|
|
208
|
-
type: 'reset'
|
|
209
|
-
});
|
|
210
|
-
setTimeout(() => {
|
|
211
|
-
var _schema$validate2;
|
|
212
253
|
|
|
213
|
-
|
|
214
|
-
(_schema$validate2 = schema.validate) === null || _schema$validate2 === void 0 ? void 0 : _schema$validate2.call(schema, fieldset);
|
|
215
|
-
}, 0);
|
|
254
|
+
resetError(form);
|
|
216
255
|
};
|
|
217
256
|
|
|
257
|
+
var resetHandler = event => {
|
|
258
|
+
var form = dom.getFormElement(ref.current);
|
|
259
|
+
|
|
260
|
+
if (!form || event.target !== form) {
|
|
261
|
+
return;
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
setError({});
|
|
265
|
+
};
|
|
266
|
+
|
|
267
|
+
document.addEventListener('input', handleInput); // The invalid event does not bubble and so listening on the capturing pharse is needed
|
|
268
|
+
|
|
269
|
+
document.addEventListener('invalid', invalidHandler, true);
|
|
270
|
+
document.addEventListener('submit', submitHandler);
|
|
218
271
|
document.addEventListener('reset', resetHandler);
|
|
219
272
|
return () => {
|
|
273
|
+
document.removeEventListener('input', handleInput);
|
|
274
|
+
document.removeEventListener('invalid', invalidHandler, true);
|
|
275
|
+
document.removeEventListener('submit', submitHandler);
|
|
220
276
|
document.removeEventListener('reset', resetHandler);
|
|
221
277
|
};
|
|
222
|
-
},
|
|
223
|
-
[schema.validate]);
|
|
278
|
+
}, [ref, config === null || config === void 0 ? void 0 : config.name]);
|
|
224
279
|
react.useEffect(() => {
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
payload: {
|
|
228
|
-
keys: Object.keys(schema.fields),
|
|
229
|
-
error: config.error
|
|
230
|
-
}
|
|
231
|
-
});
|
|
232
|
-
}, [config.error, schema.fields]);
|
|
233
|
-
return [{
|
|
234
|
-
ref,
|
|
235
|
-
name: config.name,
|
|
236
|
-
form: config.form,
|
|
237
|
-
|
|
238
|
-
onInput(e) {
|
|
239
|
-
var _schema$validate3;
|
|
240
|
-
|
|
241
|
-
var fieldset = e.currentTarget;
|
|
242
|
-
(_schema$validate3 = schema.validate) === null || _schema$validate3 === void 0 ? void 0 : _schema$validate3.call(schema, fieldset);
|
|
243
|
-
dispatch({
|
|
244
|
-
type: 'cleanup',
|
|
245
|
-
payload: {
|
|
246
|
-
fieldset
|
|
247
|
-
}
|
|
248
|
-
});
|
|
249
|
-
},
|
|
280
|
+
setError(prev => {
|
|
281
|
+
var next = prev;
|
|
250
282
|
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
var key = Object.keys(schema.fields).find(key => (element === null || element === void 0 ? void 0 : element.name) === dom.getName([e.currentTarget.name, key]));
|
|
254
|
-
|
|
255
|
-
if (!element || !key) {
|
|
256
|
-
return;
|
|
257
|
-
} // Disable browser report
|
|
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;
|
|
258
285
|
|
|
286
|
+
if (next[key] !== (_error2 === null || _error2 === void 0 ? void 0 : _error2.message)) {
|
|
287
|
+
var _error2$message;
|
|
259
288
|
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
payload: {
|
|
264
|
-
key,
|
|
265
|
-
message: element.validationMessage
|
|
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
|
+
});
|
|
266
292
|
}
|
|
267
|
-
}
|
|
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]);
|
|
298
|
+
/**
|
|
299
|
+
* This allows us constructing the field at runtime as we have no information
|
|
300
|
+
* about which fields would be available. The proxy will also help tracking
|
|
301
|
+
* the usage of each field for optimization in the future.
|
|
302
|
+
*/
|
|
303
|
+
|
|
304
|
+
return new Proxy({}, {
|
|
305
|
+
get(_target, key) {
|
|
306
|
+
var _constraint, _config$defaultValue, _config$initialError3, _config$initialError4, _error$key;
|
|
307
|
+
|
|
308
|
+
if (typeof key !== 'string') {
|
|
309
|
+
return;
|
|
310
|
+
}
|
|
311
|
+
|
|
312
|
+
var constraint = config === null || config === void 0 ? void 0 : (_constraint = config.constraint) === null || _constraint === void 0 ? void 0 : _constraint[key];
|
|
313
|
+
var field = {
|
|
314
|
+
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
|
|
319
|
+
}, constraint),
|
|
320
|
+
error: (_error$key = error === null || error === void 0 ? void 0 : error[key]) !== null && _error$key !== void 0 ? _error$key : ''
|
|
321
|
+
};
|
|
322
|
+
return field;
|
|
268
323
|
}
|
|
269
324
|
|
|
270
|
-
}
|
|
271
|
-
error: Object.assign({}, config.error, errorMessage)
|
|
272
|
-
}))];
|
|
325
|
+
});
|
|
273
326
|
}
|
|
274
|
-
|
|
327
|
+
|
|
328
|
+
/**
|
|
329
|
+
* Returns a list of key and config, with a group of helpers
|
|
330
|
+
* configuring buttons for list manipulation
|
|
331
|
+
*
|
|
332
|
+
* @see https://github.com/edmundhung/conform/tree/v0.3.0-pre.0/packages/conform-react/README.md#usefieldlist
|
|
333
|
+
*/
|
|
334
|
+
function useFieldList(ref, config) {
|
|
275
335
|
var [entries, setEntries] = react.useState(() => {
|
|
276
|
-
var
|
|
336
|
+
var _config$defaultValue2;
|
|
277
337
|
|
|
278
|
-
return Object.entries((
|
|
338
|
+
return Object.entries((_config$defaultValue2 = config.defaultValue) !== null && _config$defaultValue2 !== void 0 ? _config$defaultValue2 : [undefined]);
|
|
279
339
|
});
|
|
280
340
|
var list = entries.map((_ref, index) => {
|
|
281
|
-
var
|
|
341
|
+
var _config$defaultValue3, _config$initialError5, _config$initialError6;
|
|
282
342
|
|
|
283
343
|
var [key, defaultValue] = _ref;
|
|
284
344
|
return {
|
|
285
|
-
key
|
|
286
|
-
|
|
287
|
-
name: "".concat(
|
|
288
|
-
defaultValue: defaultValue !== null && defaultValue !== void 0 ? defaultValue : (
|
|
289
|
-
|
|
290
|
-
multiple: false
|
|
345
|
+
key,
|
|
346
|
+
config: _rollupPluginBabelHelpers.objectSpread2(_rollupPluginBabelHelpers.objectSpread2({}, config), {}, {
|
|
347
|
+
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
|
|
291
350
|
})
|
|
292
351
|
};
|
|
293
352
|
});
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
353
|
+
/***
|
|
354
|
+
* This use proxy to capture all information about the command and
|
|
355
|
+
* have it encoded in the value.
|
|
356
|
+
*/
|
|
357
|
+
|
|
358
|
+
var control = new Proxy({}, {
|
|
359
|
+
get(_target, type) {
|
|
360
|
+
return function () {
|
|
361
|
+
var payload = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};
|
|
362
|
+
return {
|
|
363
|
+
name: dom.listCommandKey,
|
|
364
|
+
value: dom.serializeListCommand(config.name, {
|
|
365
|
+
type,
|
|
366
|
+
payload
|
|
367
|
+
}),
|
|
368
|
+
form: config.form,
|
|
369
|
+
formNoValidate: true
|
|
370
|
+
};
|
|
371
|
+
};
|
|
372
|
+
}
|
|
305
373
|
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
return _rollupPluginBabelHelpers.objectSpread2(_rollupPluginBabelHelpers.objectSpread2({}, dom.getControlButtonProps(props.name, 'append', {
|
|
311
|
-
defaultValue
|
|
312
|
-
})), {}, {
|
|
313
|
-
onClick(e) {
|
|
314
|
-
setEntries(entries => dom.applyControlCommand([...entries], 'append', {
|
|
315
|
-
defaultValue: ["".concat(Date.now()), defaultValue]
|
|
316
|
-
}));
|
|
317
|
-
e.preventDefault();
|
|
318
|
-
}
|
|
374
|
+
});
|
|
375
|
+
react.useEffect(() => {
|
|
376
|
+
setEntries(prevEntries => {
|
|
377
|
+
var _config$defaultValue4;
|
|
319
378
|
|
|
320
|
-
|
|
321
|
-
},
|
|
322
|
-
|
|
323
|
-
replace(index, defaultValue) {
|
|
324
|
-
return _rollupPluginBabelHelpers.objectSpread2(_rollupPluginBabelHelpers.objectSpread2({}, dom.getControlButtonProps(props.name, 'replace', {
|
|
325
|
-
index,
|
|
326
|
-
defaultValue
|
|
327
|
-
})), {}, {
|
|
328
|
-
onClick(e) {
|
|
329
|
-
setEntries(entries => dom.applyControlCommand([...entries], 'replace', {
|
|
330
|
-
defaultValue: ["".concat(Date.now()), defaultValue],
|
|
331
|
-
index
|
|
332
|
-
}));
|
|
333
|
-
e.preventDefault();
|
|
334
|
-
}
|
|
379
|
+
var nextEntries = Object.entries((_config$defaultValue4 = config.defaultValue) !== null && _config$defaultValue4 !== void 0 ? _config$defaultValue4 : [undefined]);
|
|
335
380
|
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
remove(index) {
|
|
340
|
-
return _rollupPluginBabelHelpers.objectSpread2(_rollupPluginBabelHelpers.objectSpread2({}, dom.getControlButtonProps(props.name, 'remove', {
|
|
341
|
-
index
|
|
342
|
-
})), {}, {
|
|
343
|
-
onClick(e) {
|
|
344
|
-
setEntries(entries => dom.applyControlCommand([...entries], 'remove', {
|
|
345
|
-
index
|
|
346
|
-
}));
|
|
347
|
-
e.preventDefault();
|
|
348
|
-
}
|
|
381
|
+
if (prevEntries.length !== nextEntries.length) {
|
|
382
|
+
return nextEntries;
|
|
383
|
+
}
|
|
349
384
|
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
reorder(fromIndex, toIndex) {
|
|
354
|
-
return _rollupPluginBabelHelpers.objectSpread2(_rollupPluginBabelHelpers.objectSpread2({}, dom.getControlButtonProps(props.name, 'reorder', {
|
|
355
|
-
from: fromIndex,
|
|
356
|
-
to: toIndex
|
|
357
|
-
})), {}, {
|
|
358
|
-
onClick(e) {
|
|
359
|
-
if (fromIndex !== toIndex) {
|
|
360
|
-
setEntries(entries => dom.applyControlCommand([...entries], 'reorder', {
|
|
361
|
-
from: fromIndex,
|
|
362
|
-
to: toIndex
|
|
363
|
-
}));
|
|
364
|
-
}
|
|
385
|
+
for (var i = 0; i < prevEntries.length; i++) {
|
|
386
|
+
var [prevKey, prevValue] = prevEntries[i];
|
|
387
|
+
var [nextKey, nextValue] = nextEntries[i];
|
|
365
388
|
|
|
366
|
-
|
|
389
|
+
if (prevKey !== nextKey || prevValue !== nextValue) {
|
|
390
|
+
return nextEntries;
|
|
367
391
|
}
|
|
392
|
+
} // No need to rerender in this case
|
|
368
393
|
|
|
369
|
-
});
|
|
370
|
-
}
|
|
371
394
|
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
395
|
+
return prevEntries;
|
|
396
|
+
});
|
|
397
|
+
|
|
398
|
+
var submitHandler = event => {
|
|
399
|
+
var form = dom.getFormElement(ref.current);
|
|
400
|
+
|
|
401
|
+
if (!form || event.target !== form || !(event.submitter instanceof HTMLButtonElement) || event.submitter.name !== dom.listCommandKey) {
|
|
402
|
+
return;
|
|
403
|
+
}
|
|
404
|
+
|
|
405
|
+
var [name, command] = dom.parseListCommand(event.submitter.value);
|
|
406
|
+
|
|
407
|
+
if (name !== config.name) {
|
|
408
|
+
// Ensure the scope of the listener are limited to specific field name
|
|
409
|
+
return;
|
|
410
|
+
}
|
|
411
|
+
|
|
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));
|
|
421
|
+
event.preventDefault();
|
|
422
|
+
};
|
|
423
|
+
|
|
424
|
+
var resetHandler = event => {
|
|
425
|
+
var _config$defaultValue5;
|
|
426
|
+
|
|
427
|
+
var form = dom.getFormElement(ref.current);
|
|
428
|
+
|
|
429
|
+
if (!form || event.target !== form) {
|
|
430
|
+
return;
|
|
431
|
+
}
|
|
375
432
|
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
433
|
+
setEntries(Object.entries((_config$defaultValue5 = config.defaultValue) !== null && _config$defaultValue5 !== void 0 ? _config$defaultValue5 : []));
|
|
434
|
+
};
|
|
435
|
+
|
|
436
|
+
document.addEventListener('submit', submitHandler, true);
|
|
437
|
+
document.addEventListener('reset', resetHandler);
|
|
438
|
+
return () => {
|
|
439
|
+
document.removeEventListener('submit', submitHandler, true);
|
|
440
|
+
document.removeEventListener('reset', resetHandler);
|
|
441
|
+
};
|
|
442
|
+
}, [ref, config.name, config.defaultValue]);
|
|
443
|
+
return [list, control];
|
|
379
444
|
}
|
|
445
|
+
|
|
446
|
+
/**
|
|
447
|
+
* Returns the properties required to configure a shadow input for validation.
|
|
448
|
+
* This is particular useful when integrating dropdown and datepicker whichs
|
|
449
|
+
* introduces custom input mode.
|
|
450
|
+
*
|
|
451
|
+
* @see https://github.com/edmundhung/conform/tree/v0.3.0-pre.0/packages/conform-react/README.md#usecontrolledinput
|
|
452
|
+
*/
|
|
380
453
|
function useControlledInput(field) {
|
|
381
|
-
var
|
|
454
|
+
var _field$defaultValue;
|
|
382
455
|
|
|
383
456
|
var ref = react.useRef(null);
|
|
384
|
-
var
|
|
385
|
-
ref,
|
|
386
|
-
name: field.name,
|
|
387
|
-
form: field.form,
|
|
388
|
-
defaultValue: field.defaultValue,
|
|
389
|
-
required: field.required,
|
|
390
|
-
minLength: field.minLength,
|
|
391
|
-
maxLength: field.maxLength,
|
|
392
|
-
min: field.min,
|
|
393
|
-
max: field.max,
|
|
394
|
-
step: field.step,
|
|
395
|
-
pattern: field.pattern,
|
|
396
|
-
hidden: true,
|
|
397
|
-
'aria-hidden': true
|
|
398
|
-
}), [field.name, field.form, field.defaultValue, field.required, field.minLength, field.maxLength, field.min, field.max, field.step, field.pattern]);
|
|
399
|
-
return [input, {
|
|
400
|
-
value: (_ref$current$value = (_ref$current = ref.current) === null || _ref$current === void 0 ? void 0 : _ref$current.value) !== null && _ref$current$value !== void 0 ? _ref$current$value : "".concat((_field$defaultValue = field.defaultValue) !== null && _field$defaultValue !== void 0 ? _field$defaultValue : ''),
|
|
401
|
-
onChange: value => {
|
|
402
|
-
if (!ref.current) {
|
|
403
|
-
return;
|
|
404
|
-
}
|
|
457
|
+
var [value, setValue] = react.useState("".concat((_field$defaultValue = field.defaultValue) !== null && _field$defaultValue !== void 0 ? _field$defaultValue : ''));
|
|
405
458
|
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
}));
|
|
410
|
-
},
|
|
411
|
-
onBlur: () => {
|
|
412
|
-
var _ref$current2;
|
|
413
|
-
|
|
414
|
-
(_ref$current2 = ref.current) === null || _ref$current2 === void 0 ? void 0 : _ref$current2.dispatchEvent(new FocusEvent('focusout', {
|
|
415
|
-
bubbles: true
|
|
416
|
-
}));
|
|
459
|
+
var handleChange = eventOrValue => {
|
|
460
|
+
if (!ref.current) {
|
|
461
|
+
return;
|
|
417
462
|
}
|
|
463
|
+
|
|
464
|
+
var newValue = typeof eventOrValue === 'string' ? eventOrValue : eventOrValue.target.value;
|
|
465
|
+
ref.current.value = newValue;
|
|
466
|
+
ref.current.dispatchEvent(new InputEvent('input', {
|
|
467
|
+
bubbles: true
|
|
468
|
+
}));
|
|
469
|
+
setValue(newValue);
|
|
470
|
+
};
|
|
471
|
+
|
|
472
|
+
var handleBlur = () => {
|
|
473
|
+
var _ref$current;
|
|
474
|
+
|
|
475
|
+
(_ref$current = ref.current) === null || _ref$current === void 0 ? void 0 : _ref$current.dispatchEvent(new FocusEvent('blur', {
|
|
476
|
+
bubbles: true
|
|
477
|
+
}));
|
|
478
|
+
};
|
|
479
|
+
|
|
480
|
+
var handleInvalid = event => {
|
|
481
|
+
event.preventDefault();
|
|
482
|
+
};
|
|
483
|
+
|
|
484
|
+
return [_rollupPluginBabelHelpers.objectSpread2({
|
|
485
|
+
ref,
|
|
486
|
+
hidden: true
|
|
487
|
+
}, helpers.input(field, {
|
|
488
|
+
type: 'text'
|
|
489
|
+
})), {
|
|
490
|
+
value,
|
|
491
|
+
onChange: handleChange,
|
|
492
|
+
onBlur: handleBlur,
|
|
493
|
+
onInvalid: handleInvalid
|
|
418
494
|
}];
|
|
419
495
|
}
|
|
420
496
|
|