@conform-to/dom 0.6.1 → 0.6.2
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/dom.d.ts +55 -0
- package/dom.js +169 -0
- package/formdata.d.ts +39 -0
- package/formdata.js +127 -0
- package/index.d.ts +5 -203
- package/index.js +32 -430
- package/intent.d.ts +84 -0
- package/intent.js +132 -0
- package/module/dom.js +155 -0
- package/module/formdata.js +117 -0
- package/module/index.js +4 -408
- package/module/intent.js +120 -0
- package/module/parse.js +33 -0
- package/package.json +1 -1
- package/parse.d.ts +28 -0
- package/parse.js +37 -0
- package/types.d.ts +17 -0
package/module/dom.js
ADDED
|
@@ -0,0 +1,155 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* A type guard to check if the provided reference is a form control element, including
|
|
3
|
+
* `input`, `select`, `textarea` or `button`
|
|
4
|
+
*/
|
|
5
|
+
function isFormControl(element) {
|
|
6
|
+
return element instanceof Element && (element.tagName === 'INPUT' || element.tagName === 'SELECT' || element.tagName === 'TEXTAREA' || element.tagName === 'BUTTON');
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* A type guard to check if the provided reference is a focusable form control element.
|
|
11
|
+
*/
|
|
12
|
+
function isFocusableFormControl(element) {
|
|
13
|
+
return isFormControl(element) && element.willValidate && element.type !== 'submit';
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* Resolves the form action based on the submit event
|
|
18
|
+
*/
|
|
19
|
+
function getFormAction(event) {
|
|
20
|
+
var _ref, _submitter$getAttribu;
|
|
21
|
+
var form = event.target;
|
|
22
|
+
var submitter = event.submitter;
|
|
23
|
+
return (_ref = (_submitter$getAttribu = submitter === null || submitter === void 0 ? void 0 : submitter.getAttribute('formaction')) !== null && _submitter$getAttribu !== void 0 ? _submitter$getAttribu : form.getAttribute('action')) !== null && _ref !== void 0 ? _ref : "".concat(location.pathname).concat(location.search);
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
/**
|
|
27
|
+
* Resolves the form encoding type based on the submit event
|
|
28
|
+
*/
|
|
29
|
+
function getFormEncType(event) {
|
|
30
|
+
var _submitter$getAttribu2;
|
|
31
|
+
var form = event.target;
|
|
32
|
+
var submitter = event.submitter;
|
|
33
|
+
var encType = (_submitter$getAttribu2 = submitter === null || submitter === void 0 ? void 0 : submitter.getAttribute('formenctype')) !== null && _submitter$getAttribu2 !== void 0 ? _submitter$getAttribu2 : form.enctype;
|
|
34
|
+
if (['application/x-www-form-urlencoded', 'multipart/form-data'].includes(encType)) {
|
|
35
|
+
return encType;
|
|
36
|
+
}
|
|
37
|
+
return 'application/x-www-form-urlencoded';
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
/**
|
|
41
|
+
* Resolves the form method based on the submit event
|
|
42
|
+
*/
|
|
43
|
+
function getFormMethod(event) {
|
|
44
|
+
var _submitter$getAttribu3;
|
|
45
|
+
var form = event.target;
|
|
46
|
+
var submitter = event.submitter;
|
|
47
|
+
var method = (_submitter$getAttribu3 = submitter === null || submitter === void 0 ? void 0 : submitter.getAttribute('formmethod')) !== null && _submitter$getAttribu3 !== void 0 ? _submitter$getAttribu3 : form.getAttribute('method');
|
|
48
|
+
if (['get', 'post', 'put', 'patch', 'delete'].includes(method)) {
|
|
49
|
+
return method;
|
|
50
|
+
}
|
|
51
|
+
return 'get';
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
/**
|
|
55
|
+
* Resolve the form element
|
|
56
|
+
*/
|
|
57
|
+
function getFormElement(element) {
|
|
58
|
+
var form = element instanceof HTMLFormElement ? element : element === null || element === void 0 ? void 0 : element.form;
|
|
59
|
+
if (!form) {
|
|
60
|
+
return null;
|
|
61
|
+
}
|
|
62
|
+
return form;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
/**
|
|
66
|
+
* Returns a list of form control elements in the form
|
|
67
|
+
*/
|
|
68
|
+
function getFormControls(form) {
|
|
69
|
+
var formControls = [];
|
|
70
|
+
for (var element of form.elements) {
|
|
71
|
+
if (isFormControl(element)) {
|
|
72
|
+
formControls.push(element);
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
return formControls;
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
/**
|
|
79
|
+
* A function to create a submitter button element
|
|
80
|
+
*/
|
|
81
|
+
function createSubmitter(config) {
|
|
82
|
+
var button = document.createElement('button');
|
|
83
|
+
button.name = config.name;
|
|
84
|
+
button.value = config.value;
|
|
85
|
+
if (config.hidden) {
|
|
86
|
+
button.hidden = true;
|
|
87
|
+
}
|
|
88
|
+
if (config.formAction) {
|
|
89
|
+
button.formAction = config.formAction;
|
|
90
|
+
}
|
|
91
|
+
if (config.formEnctype) {
|
|
92
|
+
button.formEnctype = config.formEnctype;
|
|
93
|
+
}
|
|
94
|
+
if (config.formMethod) {
|
|
95
|
+
button.formMethod = config.formMethod;
|
|
96
|
+
}
|
|
97
|
+
if (config.formNoValidate) {
|
|
98
|
+
button.formNoValidate = true;
|
|
99
|
+
}
|
|
100
|
+
return button;
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
/**
|
|
104
|
+
* Trigger form submission with a submitter.
|
|
105
|
+
*/
|
|
106
|
+
function requestSubmit(form, submitter) {
|
|
107
|
+
var shouldRemoveSubmitter = false;
|
|
108
|
+
if (submitter && !submitter.isConnected) {
|
|
109
|
+
shouldRemoveSubmitter = true;
|
|
110
|
+
form.appendChild(submitter);
|
|
111
|
+
}
|
|
112
|
+
if (typeof form.requestSubmit === 'function') {
|
|
113
|
+
form.requestSubmit(submitter);
|
|
114
|
+
} else {
|
|
115
|
+
var event = new SubmitEvent('submit', {
|
|
116
|
+
bubbles: true,
|
|
117
|
+
cancelable: true,
|
|
118
|
+
submitter
|
|
119
|
+
});
|
|
120
|
+
form.dispatchEvent(event);
|
|
121
|
+
}
|
|
122
|
+
if (submitter && shouldRemoveSubmitter) {
|
|
123
|
+
form.removeChild(submitter);
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
/**
|
|
128
|
+
* Focus on the first invalid form control in the form
|
|
129
|
+
*/
|
|
130
|
+
function focusFirstInvalidControl(form) {
|
|
131
|
+
for (var element of form.elements) {
|
|
132
|
+
if (isFocusableFormControl(element) && !element.validity.valid) {
|
|
133
|
+
element.focus();
|
|
134
|
+
break;
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
/**
|
|
140
|
+
* Focus on the first form control with the provided name
|
|
141
|
+
*/
|
|
142
|
+
function focusFormControl(form, name) {
|
|
143
|
+
var element = form.elements.namedItem(name);
|
|
144
|
+
if (!element) {
|
|
145
|
+
return;
|
|
146
|
+
}
|
|
147
|
+
if (element instanceof RadioNodeList) {
|
|
148
|
+
element = element.item(0);
|
|
149
|
+
}
|
|
150
|
+
if (isFocusableFormControl(element)) {
|
|
151
|
+
element.focus();
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
export { createSubmitter, focusFirstInvalidControl, focusFormControl, getFormAction, getFormControls, getFormElement, getFormEncType, getFormMethod, isFocusableFormControl, isFormControl, requestSubmit };
|
|
@@ -0,0 +1,117 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* A ponyfill-like helper to get the form data with the submitter value.
|
|
3
|
+
* It does not respect the tree order nor handles the image input.
|
|
4
|
+
*
|
|
5
|
+
* @see https://developer.mozilla.org/en-US/docs/Web/API/FormData/FormData#parameters
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
function getFormData(form, submitter) {
|
|
9
|
+
var payload = new FormData(form);
|
|
10
|
+
if (submitter && submitter.type === 'submit' && submitter.name !== '') {
|
|
11
|
+
payload.append(submitter.name, submitter.value);
|
|
12
|
+
}
|
|
13
|
+
return payload;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* Returns the paths from a name based on the JS syntax convention
|
|
18
|
+
* @example
|
|
19
|
+
* ```js
|
|
20
|
+
* const paths = getPaths('todos[0].content'); // ['todos', 0, 'content']
|
|
21
|
+
* ```
|
|
22
|
+
*/
|
|
23
|
+
function getPaths(name) {
|
|
24
|
+
var pattern = /(\w*)\[(\d+)\]/;
|
|
25
|
+
if (!name) {
|
|
26
|
+
return [];
|
|
27
|
+
}
|
|
28
|
+
return name.split('.').flatMap(key => {
|
|
29
|
+
var matches = pattern.exec(key);
|
|
30
|
+
if (!matches) {
|
|
31
|
+
return key;
|
|
32
|
+
}
|
|
33
|
+
if (matches[1] === '') {
|
|
34
|
+
return Number(matches[2]);
|
|
35
|
+
}
|
|
36
|
+
return [matches[1], Number(matches[2])];
|
|
37
|
+
});
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
/**
|
|
41
|
+
* Returns a formatted name from the paths based on the JS syntax convention
|
|
42
|
+
* @example
|
|
43
|
+
* ```js
|
|
44
|
+
* const name = formatPaths(['todos', 0, 'content']); // "todos[0].content"
|
|
45
|
+
* ```
|
|
46
|
+
*/
|
|
47
|
+
function formatPaths(paths) {
|
|
48
|
+
return paths.reduce((name, path) => {
|
|
49
|
+
if (typeof path === 'number') {
|
|
50
|
+
return "".concat(name, "[").concat(path, "]");
|
|
51
|
+
}
|
|
52
|
+
if (name === '' || path === '') {
|
|
53
|
+
return [name, path].join('');
|
|
54
|
+
}
|
|
55
|
+
return [name, path].join('.');
|
|
56
|
+
}, '');
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
/**
|
|
60
|
+
* Assign a value to a target object by following the paths on the name
|
|
61
|
+
*/
|
|
62
|
+
function setValue(target, name, valueFn) {
|
|
63
|
+
var paths = getPaths(name);
|
|
64
|
+
var length = paths.length;
|
|
65
|
+
var lastIndex = length - 1;
|
|
66
|
+
var index = -1;
|
|
67
|
+
var pointer = target;
|
|
68
|
+
while (pointer != null && ++index < length) {
|
|
69
|
+
var _pointer$key;
|
|
70
|
+
var key = paths[index];
|
|
71
|
+
var next = paths[index + 1];
|
|
72
|
+
var newValue = index != lastIndex ? (_pointer$key = pointer[key]) !== null && _pointer$key !== void 0 ? _pointer$key : typeof next === 'number' ? [] : {} : valueFn(pointer[key]);
|
|
73
|
+
pointer[key] = newValue;
|
|
74
|
+
pointer = pointer[key];
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
/**
|
|
79
|
+
* Resolves the payload into a plain object based on the JS syntax convention
|
|
80
|
+
*/
|
|
81
|
+
function resolve(payload) {
|
|
82
|
+
var data = {};
|
|
83
|
+
var _loop = function _loop(value) {
|
|
84
|
+
setValue(data, name, prev => {
|
|
85
|
+
if (!prev) {
|
|
86
|
+
return value;
|
|
87
|
+
} else if (Array.isArray(prev)) {
|
|
88
|
+
return prev.concat(value);
|
|
89
|
+
} else {
|
|
90
|
+
return [prev, value];
|
|
91
|
+
}
|
|
92
|
+
});
|
|
93
|
+
};
|
|
94
|
+
for (var [name, value] of payload.entries()) {
|
|
95
|
+
_loop(value);
|
|
96
|
+
}
|
|
97
|
+
return data;
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
/**
|
|
101
|
+
* Format the error messages into a validation message
|
|
102
|
+
*/
|
|
103
|
+
function getValidationMessage(errors) {
|
|
104
|
+
return [].concat(errors !== null && errors !== void 0 ? errors : []).join(String.fromCharCode(31));
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
/**
|
|
108
|
+
* Retrieve the error messages from the validation message
|
|
109
|
+
*/
|
|
110
|
+
function getErrors(validationMessage) {
|
|
111
|
+
if (!validationMessage) {
|
|
112
|
+
return [];
|
|
113
|
+
}
|
|
114
|
+
return validationMessage.split(String.fromCharCode(31));
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
export { formatPaths, getErrors, getFormData, getPaths, getValidationMessage, resolve, setValue };
|
package/module/index.js
CHANGED
|
@@ -1,408 +1,4 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
*/
|
|
6
|
-
function isFieldElement(element) {
|
|
7
|
-
return element instanceof Element && (element.tagName === 'INPUT' || element.tagName === 'SELECT' || element.tagName === 'TEXTAREA' || element.tagName === 'BUTTON');
|
|
8
|
-
}
|
|
9
|
-
|
|
10
|
-
/**
|
|
11
|
-
* Find the corresponding paths based on the formatted name
|
|
12
|
-
* @param name formatted name
|
|
13
|
-
* @returns paths
|
|
14
|
-
*/
|
|
15
|
-
function getPaths(name) {
|
|
16
|
-
var pattern = /(\w*)\[(\d+)\]/;
|
|
17
|
-
if (!name) {
|
|
18
|
-
return [];
|
|
19
|
-
}
|
|
20
|
-
return name.split('.').flatMap(key => {
|
|
21
|
-
var matches = pattern.exec(key);
|
|
22
|
-
if (!matches) {
|
|
23
|
-
return key;
|
|
24
|
-
}
|
|
25
|
-
if (matches[1] === '') {
|
|
26
|
-
return Number(matches[2]);
|
|
27
|
-
}
|
|
28
|
-
return [matches[1], Number(matches[2])];
|
|
29
|
-
});
|
|
30
|
-
}
|
|
31
|
-
function getFormData(form, submitter) {
|
|
32
|
-
var payload = new FormData(form);
|
|
33
|
-
if (submitter !== null && submitter !== void 0 && submitter.name) {
|
|
34
|
-
payload.append(submitter.name, submitter.value);
|
|
35
|
-
}
|
|
36
|
-
return payload;
|
|
37
|
-
}
|
|
38
|
-
function getFormAttributes(form, submitter) {
|
|
39
|
-
var _ref, _submitter$getAttribu, _ref2, _submitter$getAttribu2, _submitter$getAttribu3;
|
|
40
|
-
var enforce = (value, list) => list.includes(value) ? value : list[0];
|
|
41
|
-
var action = (_ref = (_submitter$getAttribu = submitter === null || submitter === void 0 ? void 0 : submitter.getAttribute('formaction')) !== null && _submitter$getAttribu !== void 0 ? _submitter$getAttribu : form.getAttribute('action')) !== null && _ref !== void 0 ? _ref : "".concat(location.pathname).concat(location.search);
|
|
42
|
-
var method = (_ref2 = (_submitter$getAttribu2 = submitter === null || submitter === void 0 ? void 0 : submitter.getAttribute('formmethod')) !== null && _submitter$getAttribu2 !== void 0 ? _submitter$getAttribu2 : form.getAttribute('method')) !== null && _ref2 !== void 0 ? _ref2 : 'get';
|
|
43
|
-
var encType = (_submitter$getAttribu3 = submitter === null || submitter === void 0 ? void 0 : submitter.getAttribute('formenctype')) !== null && _submitter$getAttribu3 !== void 0 ? _submitter$getAttribu3 : form.enctype;
|
|
44
|
-
return {
|
|
45
|
-
action,
|
|
46
|
-
encType: enforce(encType, ['application/x-www-form-urlencoded', 'multipart/form-data']),
|
|
47
|
-
method: enforce(method, ['get', 'post', 'put', 'patch', 'delete'])
|
|
48
|
-
};
|
|
49
|
-
}
|
|
50
|
-
function getName(paths) {
|
|
51
|
-
return paths.reduce((name, path) => {
|
|
52
|
-
if (typeof path === 'number') {
|
|
53
|
-
return "".concat(name, "[").concat(path, "]");
|
|
54
|
-
}
|
|
55
|
-
if (name === '' || path === '') {
|
|
56
|
-
return [name, path].join('');
|
|
57
|
-
}
|
|
58
|
-
return [name, path].join('.');
|
|
59
|
-
}, '');
|
|
60
|
-
}
|
|
61
|
-
function getScope(intent) {
|
|
62
|
-
var _parseListCommand$sco, _parseListCommand;
|
|
63
|
-
var [type, ...rest] = intent.split('/');
|
|
64
|
-
switch (type) {
|
|
65
|
-
case 'validate':
|
|
66
|
-
return rest.length > 0 ? rest.join('/') : null;
|
|
67
|
-
case 'list':
|
|
68
|
-
return (_parseListCommand$sco = (_parseListCommand = parseListCommand(intent)) === null || _parseListCommand === void 0 ? void 0 : _parseListCommand.scope) !== null && _parseListCommand$sco !== void 0 ? _parseListCommand$sco : null;
|
|
69
|
-
default:
|
|
70
|
-
return null;
|
|
71
|
-
}
|
|
72
|
-
}
|
|
73
|
-
function isFocusedOnIntentButton(form, intent) {
|
|
74
|
-
var element = document.activeElement;
|
|
75
|
-
return isFieldElement(element) && element.tagName === 'BUTTON' && element.form === form && element.name === INTENT && element.value === intent;
|
|
76
|
-
}
|
|
77
|
-
function getValidationMessage(errors) {
|
|
78
|
-
return [].concat(errors !== null && errors !== void 0 ? errors : []).join(String.fromCharCode(31));
|
|
79
|
-
}
|
|
80
|
-
function getErrors(message) {
|
|
81
|
-
if (!message) {
|
|
82
|
-
return [];
|
|
83
|
-
}
|
|
84
|
-
return message.split(String.fromCharCode(31));
|
|
85
|
-
}
|
|
86
|
-
var FORM_ERROR_ELEMENT_NAME = '__form__';
|
|
87
|
-
var INTENT = '__intent__';
|
|
88
|
-
var VALIDATION_UNDEFINED = '__undefined__';
|
|
89
|
-
var VALIDATION_SKIPPED = '__skipped__';
|
|
90
|
-
function reportSubmission(form, submission) {
|
|
91
|
-
for (var [_name, message] of Object.entries(submission.error)) {
|
|
92
|
-
// There is no need to create a placeholder button if all we want is to reset the error
|
|
93
|
-
if (message === '') {
|
|
94
|
-
continue;
|
|
95
|
-
}
|
|
96
|
-
|
|
97
|
-
// We can't use empty string as button name
|
|
98
|
-
// As `form.element.namedItem('')` will always returns null
|
|
99
|
-
var elementName = _name ? _name : FORM_ERROR_ELEMENT_NAME;
|
|
100
|
-
var item = form.elements.namedItem(elementName);
|
|
101
|
-
if (item instanceof RadioNodeList) {
|
|
102
|
-
for (var field of item) {
|
|
103
|
-
if (field.type !== 'radio') {
|
|
104
|
-
console.warn('Repeated field name is not supported.');
|
|
105
|
-
continue;
|
|
106
|
-
}
|
|
107
|
-
}
|
|
108
|
-
}
|
|
109
|
-
if (item === null) {
|
|
110
|
-
// Create placeholder button to keep the error without contributing to the form data
|
|
111
|
-
var button = document.createElement('button');
|
|
112
|
-
button.name = elementName;
|
|
113
|
-
button.hidden = true;
|
|
114
|
-
button.dataset.conformTouched = 'true';
|
|
115
|
-
form.appendChild(button);
|
|
116
|
-
}
|
|
117
|
-
}
|
|
118
|
-
var focusedFirstInvalidField = false;
|
|
119
|
-
var scope = getScope(submission.intent);
|
|
120
|
-
var isSubmitting = submission.intent.slice(0, submission.intent.indexOf('/')) !== 'validate' && parseListCommand(submission.intent) === null;
|
|
121
|
-
for (var element of form.elements) {
|
|
122
|
-
if (isFieldElement(element) && element.willValidate) {
|
|
123
|
-
var _submission$error$_el;
|
|
124
|
-
var _elementName = element.name !== FORM_ERROR_ELEMENT_NAME ? element.name : '';
|
|
125
|
-
var messages = [].concat((_submission$error$_el = submission.error[_elementName]) !== null && _submission$error$_el !== void 0 ? _submission$error$_el : []);
|
|
126
|
-
var shouldValidate = scope === null || scope === _elementName;
|
|
127
|
-
if (shouldValidate) {
|
|
128
|
-
element.dataset.conformTouched = 'true';
|
|
129
|
-
}
|
|
130
|
-
if (!messages.includes(VALIDATION_SKIPPED) && !messages.includes(VALIDATION_UNDEFINED)) {
|
|
131
|
-
var invalidEvent = new Event('invalid', {
|
|
132
|
-
cancelable: true
|
|
133
|
-
});
|
|
134
|
-
element.setCustomValidity(getValidationMessage(messages));
|
|
135
|
-
element.dispatchEvent(invalidEvent);
|
|
136
|
-
}
|
|
137
|
-
if (!focusedFirstInvalidField && (isSubmitting || isFocusedOnIntentButton(form, submission.intent)) && shouldValidate && element.tagName !== 'BUTTON' && !element.validity.valid) {
|
|
138
|
-
element.focus();
|
|
139
|
-
focusedFirstInvalidField = true;
|
|
140
|
-
}
|
|
141
|
-
}
|
|
142
|
-
}
|
|
143
|
-
}
|
|
144
|
-
function setValue(target, paths, valueFn) {
|
|
145
|
-
var length = paths.length;
|
|
146
|
-
var lastIndex = length - 1;
|
|
147
|
-
var index = -1;
|
|
148
|
-
var pointer = target;
|
|
149
|
-
while (pointer != null && ++index < length) {
|
|
150
|
-
var _pointer$key;
|
|
151
|
-
var key = paths[index];
|
|
152
|
-
var next = paths[index + 1];
|
|
153
|
-
var newValue = index != lastIndex ? (_pointer$key = pointer[key]) !== null && _pointer$key !== void 0 ? _pointer$key : typeof next === 'number' ? [] : {} : valueFn(pointer[key]);
|
|
154
|
-
pointer[key] = newValue;
|
|
155
|
-
pointer = pointer[key];
|
|
156
|
-
}
|
|
157
|
-
}
|
|
158
|
-
|
|
159
|
-
/**
|
|
160
|
-
* Creates an intent button on demand and trigger a form submit by clicking it.
|
|
161
|
-
*/
|
|
162
|
-
function requestIntent(form, buttonProps) {
|
|
163
|
-
if (!form) {
|
|
164
|
-
console.warn('No form element is provided');
|
|
165
|
-
return;
|
|
166
|
-
}
|
|
167
|
-
var button = document.createElement('button');
|
|
168
|
-
button.name = INTENT;
|
|
169
|
-
button.value = buttonProps.value;
|
|
170
|
-
button.hidden = true;
|
|
171
|
-
if (buttonProps.formNoValidate) {
|
|
172
|
-
button.formNoValidate = true;
|
|
173
|
-
}
|
|
174
|
-
form.appendChild(button);
|
|
175
|
-
button.click();
|
|
176
|
-
form.removeChild(button);
|
|
177
|
-
}
|
|
178
|
-
|
|
179
|
-
/**
|
|
180
|
-
* Returns the properties required to configure an intent button for validation
|
|
181
|
-
*
|
|
182
|
-
* @see https://conform.guide/api/react#validate
|
|
183
|
-
*/
|
|
184
|
-
function validate(field) {
|
|
185
|
-
return {
|
|
186
|
-
name: INTENT,
|
|
187
|
-
value: field ? "validate/".concat(field) : 'validate',
|
|
188
|
-
formNoValidate: true
|
|
189
|
-
};
|
|
190
|
-
}
|
|
191
|
-
function getFormElement(element) {
|
|
192
|
-
var form = element instanceof HTMLFormElement ? element : element === null || element === void 0 ? void 0 : element.form;
|
|
193
|
-
if (!form) {
|
|
194
|
-
return null;
|
|
195
|
-
}
|
|
196
|
-
return form;
|
|
197
|
-
}
|
|
198
|
-
function parse(payload, options) {
|
|
199
|
-
var submission = {
|
|
200
|
-
intent: 'submit',
|
|
201
|
-
payload: {},
|
|
202
|
-
error: {}
|
|
203
|
-
};
|
|
204
|
-
var _loop = function _loop(_value) {
|
|
205
|
-
if (_name2 === INTENT) {
|
|
206
|
-
if (typeof _value !== 'string' || submission.intent !== 'submit') {
|
|
207
|
-
throw new Error('The intent could only be set on a button');
|
|
208
|
-
}
|
|
209
|
-
submission.intent = _value;
|
|
210
|
-
} else {
|
|
211
|
-
var _paths = getPaths(_name2);
|
|
212
|
-
setValue(submission.payload, _paths, prev => {
|
|
213
|
-
if (!prev) {
|
|
214
|
-
return _value;
|
|
215
|
-
} else if (Array.isArray(prev)) {
|
|
216
|
-
return prev.concat(_value);
|
|
217
|
-
} else {
|
|
218
|
-
return [prev, _value];
|
|
219
|
-
}
|
|
220
|
-
});
|
|
221
|
-
}
|
|
222
|
-
};
|
|
223
|
-
for (var [_name2, _value] of payload.entries()) {
|
|
224
|
-
_loop(_value);
|
|
225
|
-
}
|
|
226
|
-
var command = parseListCommand(submission.intent);
|
|
227
|
-
if (command) {
|
|
228
|
-
var paths = getPaths(command.scope);
|
|
229
|
-
setValue(submission.payload, paths, list => {
|
|
230
|
-
if (typeof list !== 'undefined' && !Array.isArray(list)) {
|
|
231
|
-
throw new Error('The list command can only be applied to a list');
|
|
232
|
-
}
|
|
233
|
-
return updateList(list !== null && list !== void 0 ? list : [], command);
|
|
234
|
-
});
|
|
235
|
-
}
|
|
236
|
-
if (typeof (options === null || options === void 0 ? void 0 : options.resolve) === 'undefined') {
|
|
237
|
-
return submission;
|
|
238
|
-
}
|
|
239
|
-
var result = options.resolve(submission.payload, submission.intent);
|
|
240
|
-
var mergeResolveResult = resolved => {
|
|
241
|
-
var result = _objectSpread2(_objectSpread2(_objectSpread2({}, submission), resolved), {}, {
|
|
242
|
-
toJSON() {
|
|
243
|
-
return {
|
|
244
|
-
intent: this.intent,
|
|
245
|
-
payload: this.payload,
|
|
246
|
-
error: this.error
|
|
247
|
-
};
|
|
248
|
-
}
|
|
249
|
-
});
|
|
250
|
-
return result;
|
|
251
|
-
};
|
|
252
|
-
if (result instanceof Promise) {
|
|
253
|
-
return result.then(mergeResolveResult);
|
|
254
|
-
}
|
|
255
|
-
return mergeResolveResult(result);
|
|
256
|
-
}
|
|
257
|
-
function parseListCommand(intent) {
|
|
258
|
-
try {
|
|
259
|
-
var [group, type, scope, json] = intent.split('/');
|
|
260
|
-
if (group !== 'list' || !['prepend', 'append', 'replace', 'remove', 'reorder'].includes(type) || !scope) {
|
|
261
|
-
return null;
|
|
262
|
-
}
|
|
263
|
-
var _payload = JSON.parse(json);
|
|
264
|
-
return {
|
|
265
|
-
// @ts-expect-error
|
|
266
|
-
type,
|
|
267
|
-
scope,
|
|
268
|
-
payload: _payload
|
|
269
|
-
};
|
|
270
|
-
} catch (error) {
|
|
271
|
-
return null;
|
|
272
|
-
}
|
|
273
|
-
}
|
|
274
|
-
function updateList(list, command) {
|
|
275
|
-
switch (command.type) {
|
|
276
|
-
case 'prepend':
|
|
277
|
-
{
|
|
278
|
-
list.unshift(command.payload.defaultValue);
|
|
279
|
-
break;
|
|
280
|
-
}
|
|
281
|
-
case 'append':
|
|
282
|
-
{
|
|
283
|
-
list.push(command.payload.defaultValue);
|
|
284
|
-
break;
|
|
285
|
-
}
|
|
286
|
-
case 'replace':
|
|
287
|
-
{
|
|
288
|
-
list.splice(command.payload.index, 1, command.payload.defaultValue);
|
|
289
|
-
break;
|
|
290
|
-
}
|
|
291
|
-
case 'remove':
|
|
292
|
-
list.splice(command.payload.index, 1);
|
|
293
|
-
break;
|
|
294
|
-
case 'reorder':
|
|
295
|
-
list.splice(command.payload.to, 0, ...list.splice(command.payload.from, 1));
|
|
296
|
-
break;
|
|
297
|
-
default:
|
|
298
|
-
throw new Error('Unknown list command received');
|
|
299
|
-
}
|
|
300
|
-
return list;
|
|
301
|
-
}
|
|
302
|
-
/**
|
|
303
|
-
* Helpers to configure an intent button for modifying a list
|
|
304
|
-
*
|
|
305
|
-
* @see https://conform.guide/api/react#list
|
|
306
|
-
*/
|
|
307
|
-
var list = new Proxy({}, {
|
|
308
|
-
get(_target, type) {
|
|
309
|
-
switch (type) {
|
|
310
|
-
case 'append':
|
|
311
|
-
case 'prepend':
|
|
312
|
-
case 'replace':
|
|
313
|
-
case 'remove':
|
|
314
|
-
case 'reorder':
|
|
315
|
-
return function (scope) {
|
|
316
|
-
var payload = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};
|
|
317
|
-
return {
|
|
318
|
-
name: INTENT,
|
|
319
|
-
value: "list/".concat(type, "/").concat(scope, "/").concat(JSON.stringify(payload)),
|
|
320
|
-
formNoValidate: true
|
|
321
|
-
};
|
|
322
|
-
};
|
|
323
|
-
}
|
|
324
|
-
}
|
|
325
|
-
});
|
|
326
|
-
|
|
327
|
-
/**
|
|
328
|
-
* Validate the form with the Constraint Validation API
|
|
329
|
-
* @see https://conform.guide/api/react#validateconstraint
|
|
330
|
-
*/
|
|
331
|
-
function validateConstraint(options) {
|
|
332
|
-
var _options$formData, _options$formatMessag;
|
|
333
|
-
var formData = (_options$formData = options === null || options === void 0 ? void 0 : options.formData) !== null && _options$formData !== void 0 ? _options$formData : new FormData(options.form);
|
|
334
|
-
var getDefaultErrors = (validity, result) => {
|
|
335
|
-
var errors = [];
|
|
336
|
-
if (validity.valueMissing) errors.push('required');
|
|
337
|
-
if (validity.typeMismatch || validity.badInput) errors.push('type');
|
|
338
|
-
if (validity.tooShort) errors.push('minLength');
|
|
339
|
-
if (validity.rangeUnderflow) errors.push('min');
|
|
340
|
-
if (validity.stepMismatch) errors.push('step');
|
|
341
|
-
if (validity.tooLong) errors.push('maxLength');
|
|
342
|
-
if (validity.rangeOverflow) errors.push('max');
|
|
343
|
-
if (validity.patternMismatch) errors.push('pattern');
|
|
344
|
-
for (var [constraintName, valid] of Object.entries(result)) {
|
|
345
|
-
if (!valid) {
|
|
346
|
-
errors.push(constraintName);
|
|
347
|
-
}
|
|
348
|
-
}
|
|
349
|
-
return errors;
|
|
350
|
-
};
|
|
351
|
-
var formatMessages = (_options$formatMessag = options === null || options === void 0 ? void 0 : options.formatMessages) !== null && _options$formatMessag !== void 0 ? _options$formatMessag : _ref3 => {
|
|
352
|
-
var {
|
|
353
|
-
defaultErrors
|
|
354
|
-
} = _ref3;
|
|
355
|
-
return defaultErrors;
|
|
356
|
-
};
|
|
357
|
-
return parse(formData, {
|
|
358
|
-
resolve(payload, intent) {
|
|
359
|
-
var error = {};
|
|
360
|
-
var constraintPattern = /^constraint[A-Z][^A-Z]*$/;
|
|
361
|
-
var _loop2 = function _loop2(element) {
|
|
362
|
-
if (isFieldElement(element)) {
|
|
363
|
-
var _options$acceptMultip, _options$acceptMultip2;
|
|
364
|
-
var _name3 = element.name !== FORM_ERROR_ELEMENT_NAME ? element.name : '';
|
|
365
|
-
var constraint = Object.entries(element.dataset).reduce((result, _ref4) => {
|
|
366
|
-
var [name, attributeValue = ''] = _ref4;
|
|
367
|
-
if (constraintPattern.test(name)) {
|
|
368
|
-
var _options$constraint;
|
|
369
|
-
var constraintName = name.slice(10).toLowerCase();
|
|
370
|
-
var _validate = (_options$constraint = options.constraint) === null || _options$constraint === void 0 ? void 0 : _options$constraint[constraintName];
|
|
371
|
-
if (typeof _validate === 'function') {
|
|
372
|
-
result[constraintName] = _validate(element.value, {
|
|
373
|
-
formData,
|
|
374
|
-
attributeValue
|
|
375
|
-
});
|
|
376
|
-
} else {
|
|
377
|
-
console.warn("Found an \"".concat(constraintName, "\" constraint with undefined definition; Please specify it on the validateConstraint API."));
|
|
378
|
-
}
|
|
379
|
-
}
|
|
380
|
-
return result;
|
|
381
|
-
}, {});
|
|
382
|
-
var errors = formatMessages({
|
|
383
|
-
name: _name3,
|
|
384
|
-
validity: element.validity,
|
|
385
|
-
constraint,
|
|
386
|
-
defaultErrors: getDefaultErrors(element.validity, constraint)
|
|
387
|
-
});
|
|
388
|
-
var shouldAcceptMultipleErrors = (_options$acceptMultip = options === null || options === void 0 ? void 0 : (_options$acceptMultip2 = options.acceptMultipleErrors) === null || _options$acceptMultip2 === void 0 ? void 0 : _options$acceptMultip2.call(options, {
|
|
389
|
-
name: _name3,
|
|
390
|
-
payload,
|
|
391
|
-
intent
|
|
392
|
-
})) !== null && _options$acceptMultip !== void 0 ? _options$acceptMultip : false;
|
|
393
|
-
if (errors.length > 0) {
|
|
394
|
-
error[_name3] = shouldAcceptMultipleErrors ? errors : errors[0];
|
|
395
|
-
}
|
|
396
|
-
}
|
|
397
|
-
};
|
|
398
|
-
for (var element of options.form.elements) {
|
|
399
|
-
_loop2(element);
|
|
400
|
-
}
|
|
401
|
-
return {
|
|
402
|
-
error
|
|
403
|
-
};
|
|
404
|
-
}
|
|
405
|
-
});
|
|
406
|
-
}
|
|
407
|
-
|
|
408
|
-
export { FORM_ERROR_ELEMENT_NAME, INTENT, VALIDATION_SKIPPED, VALIDATION_UNDEFINED, getErrors, getFormAttributes, getFormData, getFormElement, getName, getPaths, getScope, getValidationMessage, isFieldElement, isFocusedOnIntentButton, list, parse, parseListCommand, reportSubmission, requestIntent, setValue, updateList, validate, validateConstraint };
|
|
1
|
+
export { createSubmitter, focusFirstInvalidControl, focusFormControl, getFormAction, getFormControls, getFormElement, getFormEncType, getFormMethod, isFormControl as isFieldElement, isFocusableFormControl, requestSubmit } from './dom.js';
|
|
2
|
+
export { getErrors, getFormData, formatPaths as getName, getPaths, getValidationMessage } from './formdata.js';
|
|
3
|
+
export { INTENT, getScope, isSubmitting, list, parseListCommand, requestIntent, updateList, validate } from './intent.js';
|
|
4
|
+
export { parse } from './parse.js';
|