@conform-to/dom 0.3.0 → 0.4.0-pre.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/index.d.ts +31 -39
- package/index.js +164 -155
- package/module/index.js +157 -147
- package/package.json +1 -1
package/index.d.ts
CHANGED
|
@@ -3,18 +3,12 @@ export declare type FieldElement = HTMLInputElement | HTMLSelectElement | HTMLTe
|
|
|
3
3
|
export interface FieldConfig<Schema = unknown> extends FieldConstraint {
|
|
4
4
|
name: string;
|
|
5
5
|
defaultValue?: FieldValue<Schema>;
|
|
6
|
-
initialError?:
|
|
6
|
+
initialError?: Array<[string, string]>;
|
|
7
7
|
form?: string;
|
|
8
8
|
}
|
|
9
9
|
export declare type FieldValue<Schema> = Schema extends Primitive | File ? string : Schema extends Array<infer InnerType> ? Array<FieldValue<InnerType>> : Schema extends Record<string, any> ? {
|
|
10
10
|
[Key in keyof Schema]?: FieldValue<Schema[Key]>;
|
|
11
11
|
} : unknown;
|
|
12
|
-
export interface FieldError<Schema> {
|
|
13
|
-
message?: string;
|
|
14
|
-
details?: Schema extends Primitive | File ? never : Schema extends Array<infer InnerType> ? Array<FieldError<InnerType>> : Schema extends Record<string, any> ? {
|
|
15
|
-
[Key in keyof Schema]?: FieldError<Schema[Key]>;
|
|
16
|
-
} : unknown;
|
|
17
|
-
}
|
|
18
12
|
export declare type FieldConstraint = {
|
|
19
13
|
required?: boolean;
|
|
20
14
|
minLength?: number;
|
|
@@ -28,69 +22,67 @@ export declare type FieldConstraint = {
|
|
|
28
22
|
export declare type FieldsetConstraint<Schema extends Record<string, any>> = {
|
|
29
23
|
[Key in keyof Schema]?: FieldConstraint;
|
|
30
24
|
};
|
|
31
|
-
export declare type Schema
|
|
32
|
-
|
|
33
|
-
constraint: FieldsetConstraint<Shape>;
|
|
34
|
-
validate: (element: HTMLFormElement, submitter?: HTMLInputElement | HTMLButtonElement | null) => void;
|
|
35
|
-
parse: (payload: FormData | URLSearchParams) => Submission<Shape>;
|
|
36
|
-
};
|
|
37
|
-
export interface FormState<Schema extends Record<string, any>> {
|
|
25
|
+
export declare type FormState<Schema = unknown> = {
|
|
26
|
+
scope: string[];
|
|
38
27
|
value: FieldValue<Schema>;
|
|
39
|
-
error:
|
|
40
|
-
}
|
|
41
|
-
export declare type Submission<T extends Record<string, unknown>> = {
|
|
42
|
-
state: 'modified';
|
|
43
|
-
form: FormState<T>;
|
|
44
|
-
} | {
|
|
45
|
-
state: 'rejected';
|
|
46
|
-
form: FormState<T>;
|
|
47
|
-
} | {
|
|
48
|
-
state: 'accepted';
|
|
49
|
-
data: T;
|
|
50
|
-
form: FormState<T>;
|
|
28
|
+
error: Array<[string, string]>;
|
|
51
29
|
};
|
|
30
|
+
export declare type Submission<Schema = unknown> = FormState<Schema> & ({
|
|
31
|
+
type?: undefined;
|
|
32
|
+
} | {
|
|
33
|
+
type: string;
|
|
34
|
+
data: string;
|
|
35
|
+
});
|
|
52
36
|
export declare function isFieldElement(element: unknown): element is FieldElement;
|
|
53
|
-
export declare function getPaths(name
|
|
37
|
+
export declare function getPaths(name: string): Array<string | number>;
|
|
54
38
|
export declare function getFormData(form: HTMLFormElement, submitter?: HTMLInputElement | HTMLButtonElement | null): FormData;
|
|
55
39
|
export declare function getName(paths: Array<string | number>): string;
|
|
56
|
-
export declare function
|
|
57
|
-
export declare function
|
|
40
|
+
export declare function hasError(error: Array<[string, string]>, name: string): boolean;
|
|
41
|
+
export declare function reportValidity(form: HTMLFormElement, state: FormState): boolean;
|
|
58
42
|
export declare function setValue<T>(target: any, paths: Array<string | number>, valueFn: (prev?: T) => T): void;
|
|
59
|
-
export declare function
|
|
60
|
-
export declare function
|
|
61
|
-
export declare function createSubmission(payload: FormData | URLSearchParams): Submission<Record<string, unknown>>;
|
|
62
|
-
export declare function createValidate(handler: (field: HTMLInputElement | HTMLSelectElement | HTMLTextAreaElement | HTMLButtonElement, formData: FormData) => void): (form: HTMLFormElement, submitter?: HTMLInputElement | HTMLButtonElement | null) => void;
|
|
43
|
+
export declare function requestSubmit(form: HTMLFormElement, submitter?: HTMLButtonElement | HTMLInputElement): void;
|
|
44
|
+
export declare function requestValidate(form: HTMLFormElement, field?: string): void;
|
|
63
45
|
export declare function getFormElement(element: HTMLFormElement | HTMLFieldSetElement | HTMLInputElement | HTMLSelectElement | HTMLTextAreaElement | HTMLButtonElement | null): HTMLFormElement | null;
|
|
64
|
-
export declare
|
|
46
|
+
export declare function focusFirstInvalidField(form: HTMLFormElement, fields?: string[]): void;
|
|
47
|
+
export declare function getSubmissionType(name: string): string | null;
|
|
48
|
+
export declare function parse<Schema extends Record<string, any>>(payload: FormData | URLSearchParams): Submission<Schema>;
|
|
49
|
+
export declare type Command = {
|
|
50
|
+
name: string;
|
|
51
|
+
value: string;
|
|
52
|
+
};
|
|
53
|
+
export declare type ListCommand<Schema = unknown> = {
|
|
65
54
|
type: 'prepend';
|
|
55
|
+
scope: string;
|
|
66
56
|
payload: {
|
|
67
57
|
defaultValue: Schema;
|
|
68
58
|
};
|
|
69
59
|
} | {
|
|
70
60
|
type: 'append';
|
|
61
|
+
scope: string;
|
|
71
62
|
payload: {
|
|
72
63
|
defaultValue: Schema;
|
|
73
64
|
};
|
|
74
65
|
} | {
|
|
75
66
|
type: 'replace';
|
|
67
|
+
scope: string;
|
|
76
68
|
payload: {
|
|
77
69
|
defaultValue: Schema;
|
|
78
70
|
index: number;
|
|
79
71
|
};
|
|
80
72
|
} | {
|
|
81
73
|
type: 'remove';
|
|
74
|
+
scope: string;
|
|
82
75
|
payload: {
|
|
83
76
|
index: number;
|
|
84
77
|
};
|
|
85
78
|
} | {
|
|
86
79
|
type: 'reorder';
|
|
80
|
+
scope: string;
|
|
87
81
|
payload: {
|
|
88
82
|
from: number;
|
|
89
83
|
to: number;
|
|
90
84
|
};
|
|
91
85
|
};
|
|
92
|
-
export declare
|
|
93
|
-
export declare function
|
|
94
|
-
export declare function
|
|
95
|
-
export declare function updateList<Type>(list: Array<Type>, command: ListCommand<Type>): Array<Type>;
|
|
96
|
-
export declare function applyListCommand(payload: FormData | URLSearchParams): FormData | URLSearchParams;
|
|
86
|
+
export declare function parseListCommand<Schema = unknown>(data: string): ListCommand<Schema>;
|
|
87
|
+
export declare function updateList<Schema>(list: Array<Schema>, command: ListCommand<Schema>): Array<Schema>;
|
|
88
|
+
export declare function handleList<Schema>(submission: Submission<Schema>): Submission<Schema>;
|
package/index.js
CHANGED
|
@@ -2,11 +2,13 @@
|
|
|
2
2
|
|
|
3
3
|
Object.defineProperty(exports, '__esModule', { value: true });
|
|
4
4
|
|
|
5
|
+
var _rollupPluginBabelHelpers = require('./_virtual/_rollupPluginBabelHelpers.js');
|
|
6
|
+
|
|
5
7
|
function isFieldElement(element) {
|
|
6
8
|
return element instanceof Element && (element.tagName === 'INPUT' || element.tagName === 'SELECT' || element.tagName === 'TEXTAREA' || element.tagName === 'BUTTON');
|
|
7
9
|
}
|
|
8
10
|
function getPaths(name) {
|
|
9
|
-
var pattern = /(\w
|
|
11
|
+
var pattern = /(\w*)\[(\d+)\]/;
|
|
10
12
|
|
|
11
13
|
if (!name) {
|
|
12
14
|
return [];
|
|
@@ -19,6 +21,10 @@ function getPaths(name) {
|
|
|
19
21
|
return key;
|
|
20
22
|
}
|
|
21
23
|
|
|
24
|
+
if (matches[1] === '') {
|
|
25
|
+
return Number(matches[2]);
|
|
26
|
+
}
|
|
27
|
+
|
|
22
28
|
return [matches[1], Number(matches[2])];
|
|
23
29
|
});
|
|
24
30
|
}
|
|
@@ -33,40 +39,37 @@ function getFormData(form, submitter) {
|
|
|
33
39
|
}
|
|
34
40
|
function getName(paths) {
|
|
35
41
|
return paths.reduce((name, path) => {
|
|
36
|
-
if (name === '' || path === '') {
|
|
37
|
-
return [name, path].join('');
|
|
38
|
-
}
|
|
39
|
-
|
|
40
42
|
if (typeof path === 'number') {
|
|
41
43
|
return "".concat(name, "[").concat(path, "]");
|
|
42
44
|
}
|
|
43
45
|
|
|
46
|
+
if (name === '' || path === '') {
|
|
47
|
+
return [name, path].join('');
|
|
48
|
+
}
|
|
49
|
+
|
|
44
50
|
return [name, path].join('.');
|
|
45
51
|
}, '');
|
|
46
52
|
}
|
|
47
|
-
function
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
if (paths.length > 1) {
|
|
53
|
-
return null;
|
|
54
|
-
}
|
|
55
|
-
|
|
56
|
-
return typeof paths[0] === 'string' ? paths[0] : null;
|
|
53
|
+
function hasError(error, name) {
|
|
54
|
+
return typeof error.find(_ref => {
|
|
55
|
+
var [fieldName, message] = _ref;
|
|
56
|
+
return fieldName === name && message !== '';
|
|
57
|
+
}) !== 'undefined';
|
|
57
58
|
}
|
|
58
|
-
function
|
|
59
|
-
var firstErrorByName = Object.fromEntries([...
|
|
59
|
+
function reportValidity(form, state) {
|
|
60
|
+
var firstErrorByName = Object.fromEntries([...state.error].reverse());
|
|
60
61
|
|
|
61
|
-
for (var
|
|
62
|
-
var _firstErrorByName$
|
|
62
|
+
for (var element of form.elements) {
|
|
63
|
+
var _firstErrorByName$ele;
|
|
63
64
|
|
|
64
|
-
if (!isFieldElement(
|
|
65
|
+
if (!isFieldElement(element) || !state.scope.includes(element.name)) {
|
|
65
66
|
continue;
|
|
66
67
|
}
|
|
67
68
|
|
|
68
|
-
|
|
69
|
+
element.setCustomValidity((_firstErrorByName$ele = firstErrorByName[element.name]) !== null && _firstErrorByName$ele !== void 0 ? _firstErrorByName$ele : '');
|
|
69
70
|
}
|
|
71
|
+
|
|
72
|
+
return form.reportValidity();
|
|
70
73
|
}
|
|
71
74
|
function setValue(target, paths, valueFn) {
|
|
72
75
|
var length = paths.length;
|
|
@@ -84,115 +87,145 @@ function setValue(target, paths, valueFn) {
|
|
|
84
87
|
pointer = pointer[key];
|
|
85
88
|
}
|
|
86
89
|
}
|
|
87
|
-
function
|
|
88
|
-
var
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
90
|
+
function requestSubmit(form, submitter) {
|
|
91
|
+
var submitEvent = new SubmitEvent('submit', {
|
|
92
|
+
bubbles: true,
|
|
93
|
+
cancelable: true,
|
|
94
|
+
submitter
|
|
95
|
+
});
|
|
96
|
+
form.dispatchEvent(submitEvent);
|
|
97
|
+
}
|
|
98
|
+
function requestValidate(form, field) {
|
|
99
|
+
var button = document.createElement('button');
|
|
100
|
+
button.name = 'conform/validate';
|
|
101
|
+
button.value = field !== null && field !== void 0 ? field : '';
|
|
102
|
+
button.hidden = true;
|
|
103
|
+
form.appendChild(button);
|
|
104
|
+
requestSubmit(form, button);
|
|
105
|
+
form.removeChild(button);
|
|
106
|
+
}
|
|
107
|
+
function getFormElement(element) {
|
|
108
|
+
var form = element instanceof HTMLFormElement ? element : element === null || element === void 0 ? void 0 : element.form;
|
|
109
|
+
|
|
110
|
+
if (!form) {
|
|
111
|
+
return null;
|
|
101
112
|
}
|
|
102
113
|
|
|
103
|
-
return
|
|
114
|
+
return form;
|
|
104
115
|
}
|
|
105
|
-
function
|
|
106
|
-
var
|
|
107
|
-
|
|
108
|
-
var _loop = function _loop(key, value) {
|
|
109
|
-
var paths = getPaths(key);
|
|
110
|
-
setValue(result, paths, prev => {
|
|
111
|
-
if (prev) {
|
|
112
|
-
throw new Error('Entry with the same name is not supported');
|
|
113
|
-
}
|
|
116
|
+
function focusFirstInvalidField(form, fields) {
|
|
117
|
+
var currentFocus = document.activeElement;
|
|
114
118
|
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
}
|
|
119
|
+
if (!isFieldElement(currentFocus) || currentFocus.tagName !== 'BUTTON' || currentFocus.form !== form) {
|
|
120
|
+
return;
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
for (var field of form.elements) {
|
|
124
|
+
if (isFieldElement(field)) {
|
|
125
|
+
// Focus on the first non button field
|
|
126
|
+
if (!field.validity.valid && field.dataset.conformTouched && field.tagName !== 'BUTTON' && (!fields || fields.includes(field.name))) {
|
|
127
|
+
field.focus();
|
|
128
|
+
break;
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
function getSubmissionType(name) {
|
|
134
|
+
var prefix = 'conform/';
|
|
118
135
|
|
|
119
|
-
|
|
120
|
-
|
|
136
|
+
if (!name.startsWith(prefix) || name.length <= prefix.length) {
|
|
137
|
+
return null;
|
|
121
138
|
}
|
|
122
139
|
|
|
123
|
-
return
|
|
140
|
+
return name.slice(prefix.length);
|
|
124
141
|
}
|
|
125
|
-
function
|
|
126
|
-
var
|
|
142
|
+
function parse(payload) {
|
|
143
|
+
var submission = {
|
|
144
|
+
value: {},
|
|
145
|
+
error: [],
|
|
146
|
+
scope: ['']
|
|
147
|
+
};
|
|
127
148
|
|
|
128
149
|
try {
|
|
129
|
-
var
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
form: {
|
|
136
|
-
value,
|
|
137
|
-
error: {}
|
|
150
|
+
var _loop = function _loop(name, value) {
|
|
151
|
+
var submissionType = getSubmissionType(name);
|
|
152
|
+
|
|
153
|
+
if (submissionType) {
|
|
154
|
+
if (typeof value !== 'string') {
|
|
155
|
+
throw new Error('The conform command could not be used on a file input');
|
|
138
156
|
}
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
return {
|
|
143
|
-
state: 'rejected',
|
|
144
|
-
form: {
|
|
145
|
-
value,
|
|
146
|
-
error: {
|
|
147
|
-
message: e instanceof Error ? e.message : 'Submission failed'
|
|
157
|
+
|
|
158
|
+
if (typeof submission.type !== 'undefined') {
|
|
159
|
+
throw new Error('The conform command could only be set on a button');
|
|
148
160
|
}
|
|
161
|
+
|
|
162
|
+
submission = _rollupPluginBabelHelpers.objectSpread2(_rollupPluginBabelHelpers.objectSpread2({}, submission), {}, {
|
|
163
|
+
type: submissionType,
|
|
164
|
+
data: value
|
|
165
|
+
});
|
|
166
|
+
} else {
|
|
167
|
+
var paths = getPaths(name);
|
|
168
|
+
var scopes = paths.reduce((result, path) => {
|
|
169
|
+
if (result.length === 0) {
|
|
170
|
+
if (typeof path !== 'string') {
|
|
171
|
+
throw new Error("Invalid name received: ".concat(name));
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
result.push(path);
|
|
175
|
+
} else {
|
|
176
|
+
var [lastName] = result.slice(-1);
|
|
177
|
+
result.push(getName([lastName, path]));
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
return result;
|
|
181
|
+
}, []);
|
|
182
|
+
submission.scope.push(...scopes);
|
|
183
|
+
setValue(submission.value, paths, prev => {
|
|
184
|
+
if (prev) {
|
|
185
|
+
throw new Error('Entry with the same name is not supported');
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
return value;
|
|
189
|
+
});
|
|
149
190
|
}
|
|
150
191
|
};
|
|
151
|
-
}
|
|
152
192
|
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
data: value,
|
|
156
|
-
form: {
|
|
157
|
-
value,
|
|
158
|
-
error: {}
|
|
193
|
+
for (var [name, value] of payload.entries()) {
|
|
194
|
+
_loop(name, value);
|
|
159
195
|
}
|
|
160
|
-
};
|
|
161
|
-
}
|
|
162
|
-
function createValidate(handler) {
|
|
163
|
-
return (form, submitter) => {
|
|
164
|
-
var formData = getFormData(form, submitter);
|
|
165
196
|
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
197
|
+
switch (submission.type) {
|
|
198
|
+
case 'validate':
|
|
199
|
+
if (typeof submission.data !== 'undefined' && submission.data !== '') {
|
|
200
|
+
submission.scope = [submission.data];
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
break;
|
|
204
|
+
|
|
205
|
+
case 'list':
|
|
206
|
+
submission = handleList(submission);
|
|
207
|
+
break;
|
|
170
208
|
}
|
|
171
|
-
}
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
var form = element instanceof HTMLFormElement ? element : element === null || element === void 0 ? void 0 : element.form;
|
|
209
|
+
} catch (e) {
|
|
210
|
+
submission.error.push(['', e instanceof Error ? e.message : 'Invalid payload received']);
|
|
211
|
+
} // Remove duplicates
|
|
175
212
|
|
|
176
|
-
if (!form) {
|
|
177
|
-
return null;
|
|
178
|
-
}
|
|
179
213
|
|
|
180
|
-
|
|
214
|
+
submission.scope = Array.from(new Set(submission.scope));
|
|
215
|
+
return submission;
|
|
181
216
|
}
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
payload: JSON.parse(json)
|
|
195
|
-
}];
|
|
217
|
+
function parseListCommand(data) {
|
|
218
|
+
try {
|
|
219
|
+
var command = JSON.parse(data);
|
|
220
|
+
|
|
221
|
+
if (typeof command.type !== 'string' || !['prepend', 'append', 'replace', 'remove', 'reorder'].includes(command.type)) {
|
|
222
|
+
throw new Error('Unsupported list command type');
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
return command;
|
|
226
|
+
} catch (error) {
|
|
227
|
+
throw new Error("Invalid list command: \"".concat(data, "\"; ").concat(error));
|
|
228
|
+
}
|
|
196
229
|
}
|
|
197
230
|
function updateList(list, command) {
|
|
198
231
|
switch (command.type) {
|
|
@@ -223,67 +256,43 @@ function updateList(list, command) {
|
|
|
223
256
|
break;
|
|
224
257
|
|
|
225
258
|
default:
|
|
226
|
-
throw new Error('
|
|
259
|
+
throw new Error('Unknown list command received');
|
|
227
260
|
}
|
|
228
261
|
|
|
229
262
|
return list;
|
|
230
263
|
}
|
|
231
|
-
function
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
if (!command) {
|
|
235
|
-
return payload;
|
|
236
|
-
}
|
|
237
|
-
|
|
238
|
-
payload.delete(listCommandKey);
|
|
239
|
-
|
|
240
|
-
if (command instanceof File) {
|
|
241
|
-
throw new Error("The \"".concat(listCommandKey, "\" key could not be used for file upload"));
|
|
264
|
+
function handleList(submission) {
|
|
265
|
+
if (submission.type !== 'list') {
|
|
266
|
+
return submission;
|
|
242
267
|
}
|
|
243
268
|
|
|
244
|
-
var
|
|
245
|
-
var
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
if (name.startsWith(key)) {
|
|
250
|
-
entries.push([name.replace(key, 'list'), value]);
|
|
251
|
-
} else {
|
|
252
|
-
result.append(name, value);
|
|
269
|
+
var command = parseListCommand(submission.data);
|
|
270
|
+
var paths = getPaths(command.scope);
|
|
271
|
+
setValue(submission.value, paths, list => {
|
|
272
|
+
if (!Array.isArray(list)) {
|
|
273
|
+
throw new Error('The list command can only be applied to a list');
|
|
253
274
|
}
|
|
254
|
-
}
|
|
255
|
-
|
|
256
|
-
var {
|
|
257
|
-
list
|
|
258
|
-
} = unflatten(entries);
|
|
259
|
-
|
|
260
|
-
if (!Array.isArray(list)) {
|
|
261
|
-
throw new Error('The list command can only be applied to a list');
|
|
262
|
-
}
|
|
263
275
|
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
}
|
|
269
|
-
|
|
270
|
-
return result;
|
|
276
|
+
return updateList(list, command);
|
|
277
|
+
});
|
|
278
|
+
return _rollupPluginBabelHelpers.objectSpread2(_rollupPluginBabelHelpers.objectSpread2({}, submission), {}, {
|
|
279
|
+
scope: [command.scope]
|
|
280
|
+
});
|
|
271
281
|
}
|
|
272
282
|
|
|
273
|
-
exports.
|
|
274
|
-
exports.createSubmission = createSubmission;
|
|
275
|
-
exports.createValidate = createValidate;
|
|
276
|
-
exports.flatten = flatten;
|
|
283
|
+
exports.focusFirstInvalidField = focusFirstInvalidField;
|
|
277
284
|
exports.getFormData = getFormData;
|
|
278
285
|
exports.getFormElement = getFormElement;
|
|
279
|
-
exports.getKey = getKey;
|
|
280
286
|
exports.getName = getName;
|
|
281
287
|
exports.getPaths = getPaths;
|
|
288
|
+
exports.getSubmissionType = getSubmissionType;
|
|
289
|
+
exports.handleList = handleList;
|
|
290
|
+
exports.hasError = hasError;
|
|
282
291
|
exports.isFieldElement = isFieldElement;
|
|
283
|
-
exports.
|
|
292
|
+
exports.parse = parse;
|
|
284
293
|
exports.parseListCommand = parseListCommand;
|
|
285
|
-
exports.
|
|
286
|
-
exports.
|
|
294
|
+
exports.reportValidity = reportValidity;
|
|
295
|
+
exports.requestSubmit = requestSubmit;
|
|
296
|
+
exports.requestValidate = requestValidate;
|
|
287
297
|
exports.setValue = setValue;
|
|
288
|
-
exports.unflatten = unflatten;
|
|
289
298
|
exports.updateList = updateList;
|
package/module/index.js
CHANGED
|
@@ -1,8 +1,10 @@
|
|
|
1
|
+
import { objectSpread2 as _objectSpread2 } from './_virtual/_rollupPluginBabelHelpers.js';
|
|
2
|
+
|
|
1
3
|
function isFieldElement(element) {
|
|
2
4
|
return element instanceof Element && (element.tagName === 'INPUT' || element.tagName === 'SELECT' || element.tagName === 'TEXTAREA' || element.tagName === 'BUTTON');
|
|
3
5
|
}
|
|
4
6
|
function getPaths(name) {
|
|
5
|
-
var pattern = /(\w
|
|
7
|
+
var pattern = /(\w*)\[(\d+)\]/;
|
|
6
8
|
|
|
7
9
|
if (!name) {
|
|
8
10
|
return [];
|
|
@@ -15,6 +17,10 @@ function getPaths(name) {
|
|
|
15
17
|
return key;
|
|
16
18
|
}
|
|
17
19
|
|
|
20
|
+
if (matches[1] === '') {
|
|
21
|
+
return Number(matches[2]);
|
|
22
|
+
}
|
|
23
|
+
|
|
18
24
|
return [matches[1], Number(matches[2])];
|
|
19
25
|
});
|
|
20
26
|
}
|
|
@@ -29,40 +35,37 @@ function getFormData(form, submitter) {
|
|
|
29
35
|
}
|
|
30
36
|
function getName(paths) {
|
|
31
37
|
return paths.reduce((name, path) => {
|
|
32
|
-
if (name === '' || path === '') {
|
|
33
|
-
return [name, path].join('');
|
|
34
|
-
}
|
|
35
|
-
|
|
36
38
|
if (typeof path === 'number') {
|
|
37
39
|
return "".concat(name, "[").concat(path, "]");
|
|
38
40
|
}
|
|
39
41
|
|
|
42
|
+
if (name === '' || path === '') {
|
|
43
|
+
return [name, path].join('');
|
|
44
|
+
}
|
|
45
|
+
|
|
40
46
|
return [name, path].join('.');
|
|
41
47
|
}, '');
|
|
42
48
|
}
|
|
43
|
-
function
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
if (paths.length > 1) {
|
|
49
|
-
return null;
|
|
50
|
-
}
|
|
51
|
-
|
|
52
|
-
return typeof paths[0] === 'string' ? paths[0] : null;
|
|
49
|
+
function hasError(error, name) {
|
|
50
|
+
return typeof error.find(_ref => {
|
|
51
|
+
var [fieldName, message] = _ref;
|
|
52
|
+
return fieldName === name && message !== '';
|
|
53
|
+
}) !== 'undefined';
|
|
53
54
|
}
|
|
54
|
-
function
|
|
55
|
-
var firstErrorByName = Object.fromEntries([...
|
|
55
|
+
function reportValidity(form, state) {
|
|
56
|
+
var firstErrorByName = Object.fromEntries([...state.error].reverse());
|
|
56
57
|
|
|
57
|
-
for (var
|
|
58
|
-
var _firstErrorByName$
|
|
58
|
+
for (var element of form.elements) {
|
|
59
|
+
var _firstErrorByName$ele;
|
|
59
60
|
|
|
60
|
-
if (!isFieldElement(
|
|
61
|
+
if (!isFieldElement(element) || !state.scope.includes(element.name)) {
|
|
61
62
|
continue;
|
|
62
63
|
}
|
|
63
64
|
|
|
64
|
-
|
|
65
|
+
element.setCustomValidity((_firstErrorByName$ele = firstErrorByName[element.name]) !== null && _firstErrorByName$ele !== void 0 ? _firstErrorByName$ele : '');
|
|
65
66
|
}
|
|
67
|
+
|
|
68
|
+
return form.reportValidity();
|
|
66
69
|
}
|
|
67
70
|
function setValue(target, paths, valueFn) {
|
|
68
71
|
var length = paths.length;
|
|
@@ -80,115 +83,145 @@ function setValue(target, paths, valueFn) {
|
|
|
80
83
|
pointer = pointer[key];
|
|
81
84
|
}
|
|
82
85
|
}
|
|
83
|
-
function
|
|
84
|
-
var
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
86
|
+
function requestSubmit(form, submitter) {
|
|
87
|
+
var submitEvent = new SubmitEvent('submit', {
|
|
88
|
+
bubbles: true,
|
|
89
|
+
cancelable: true,
|
|
90
|
+
submitter
|
|
91
|
+
});
|
|
92
|
+
form.dispatchEvent(submitEvent);
|
|
93
|
+
}
|
|
94
|
+
function requestValidate(form, field) {
|
|
95
|
+
var button = document.createElement('button');
|
|
96
|
+
button.name = 'conform/validate';
|
|
97
|
+
button.value = field !== null && field !== void 0 ? field : '';
|
|
98
|
+
button.hidden = true;
|
|
99
|
+
form.appendChild(button);
|
|
100
|
+
requestSubmit(form, button);
|
|
101
|
+
form.removeChild(button);
|
|
102
|
+
}
|
|
103
|
+
function getFormElement(element) {
|
|
104
|
+
var form = element instanceof HTMLFormElement ? element : element === null || element === void 0 ? void 0 : element.form;
|
|
105
|
+
|
|
106
|
+
if (!form) {
|
|
107
|
+
return null;
|
|
97
108
|
}
|
|
98
109
|
|
|
99
|
-
return
|
|
110
|
+
return form;
|
|
100
111
|
}
|
|
101
|
-
function
|
|
102
|
-
var
|
|
103
|
-
|
|
104
|
-
var _loop = function _loop(key, value) {
|
|
105
|
-
var paths = getPaths(key);
|
|
106
|
-
setValue(result, paths, prev => {
|
|
107
|
-
if (prev) {
|
|
108
|
-
throw new Error('Entry with the same name is not supported');
|
|
109
|
-
}
|
|
112
|
+
function focusFirstInvalidField(form, fields) {
|
|
113
|
+
var currentFocus = document.activeElement;
|
|
110
114
|
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
}
|
|
115
|
+
if (!isFieldElement(currentFocus) || currentFocus.tagName !== 'BUTTON' || currentFocus.form !== form) {
|
|
116
|
+
return;
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
for (var field of form.elements) {
|
|
120
|
+
if (isFieldElement(field)) {
|
|
121
|
+
// Focus on the first non button field
|
|
122
|
+
if (!field.validity.valid && field.dataset.conformTouched && field.tagName !== 'BUTTON' && (!fields || fields.includes(field.name))) {
|
|
123
|
+
field.focus();
|
|
124
|
+
break;
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
function getSubmissionType(name) {
|
|
130
|
+
var prefix = 'conform/';
|
|
114
131
|
|
|
115
|
-
|
|
116
|
-
|
|
132
|
+
if (!name.startsWith(prefix) || name.length <= prefix.length) {
|
|
133
|
+
return null;
|
|
117
134
|
}
|
|
118
135
|
|
|
119
|
-
return
|
|
136
|
+
return name.slice(prefix.length);
|
|
120
137
|
}
|
|
121
|
-
function
|
|
122
|
-
var
|
|
138
|
+
function parse(payload) {
|
|
139
|
+
var submission = {
|
|
140
|
+
value: {},
|
|
141
|
+
error: [],
|
|
142
|
+
scope: ['']
|
|
143
|
+
};
|
|
123
144
|
|
|
124
145
|
try {
|
|
125
|
-
var
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
form: {
|
|
132
|
-
value,
|
|
133
|
-
error: {}
|
|
146
|
+
var _loop = function _loop(name, value) {
|
|
147
|
+
var submissionType = getSubmissionType(name);
|
|
148
|
+
|
|
149
|
+
if (submissionType) {
|
|
150
|
+
if (typeof value !== 'string') {
|
|
151
|
+
throw new Error('The conform command could not be used on a file input');
|
|
134
152
|
}
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
return {
|
|
139
|
-
state: 'rejected',
|
|
140
|
-
form: {
|
|
141
|
-
value,
|
|
142
|
-
error: {
|
|
143
|
-
message: e instanceof Error ? e.message : 'Submission failed'
|
|
153
|
+
|
|
154
|
+
if (typeof submission.type !== 'undefined') {
|
|
155
|
+
throw new Error('The conform command could only be set on a button');
|
|
144
156
|
}
|
|
157
|
+
|
|
158
|
+
submission = _objectSpread2(_objectSpread2({}, submission), {}, {
|
|
159
|
+
type: submissionType,
|
|
160
|
+
data: value
|
|
161
|
+
});
|
|
162
|
+
} else {
|
|
163
|
+
var paths = getPaths(name);
|
|
164
|
+
var scopes = paths.reduce((result, path) => {
|
|
165
|
+
if (result.length === 0) {
|
|
166
|
+
if (typeof path !== 'string') {
|
|
167
|
+
throw new Error("Invalid name received: ".concat(name));
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
result.push(path);
|
|
171
|
+
} else {
|
|
172
|
+
var [lastName] = result.slice(-1);
|
|
173
|
+
result.push(getName([lastName, path]));
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
return result;
|
|
177
|
+
}, []);
|
|
178
|
+
submission.scope.push(...scopes);
|
|
179
|
+
setValue(submission.value, paths, prev => {
|
|
180
|
+
if (prev) {
|
|
181
|
+
throw new Error('Entry with the same name is not supported');
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
return value;
|
|
185
|
+
});
|
|
145
186
|
}
|
|
146
187
|
};
|
|
147
|
-
}
|
|
148
188
|
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
data: value,
|
|
152
|
-
form: {
|
|
153
|
-
value,
|
|
154
|
-
error: {}
|
|
189
|
+
for (var [name, value] of payload.entries()) {
|
|
190
|
+
_loop(name, value);
|
|
155
191
|
}
|
|
156
|
-
};
|
|
157
|
-
}
|
|
158
|
-
function createValidate(handler) {
|
|
159
|
-
return (form, submitter) => {
|
|
160
|
-
var formData = getFormData(form, submitter);
|
|
161
192
|
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
193
|
+
switch (submission.type) {
|
|
194
|
+
case 'validate':
|
|
195
|
+
if (typeof submission.data !== 'undefined' && submission.data !== '') {
|
|
196
|
+
submission.scope = [submission.data];
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
break;
|
|
200
|
+
|
|
201
|
+
case 'list':
|
|
202
|
+
submission = handleList(submission);
|
|
203
|
+
break;
|
|
166
204
|
}
|
|
167
|
-
}
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
var form = element instanceof HTMLFormElement ? element : element === null || element === void 0 ? void 0 : element.form;
|
|
205
|
+
} catch (e) {
|
|
206
|
+
submission.error.push(['', e instanceof Error ? e.message : 'Invalid payload received']);
|
|
207
|
+
} // Remove duplicates
|
|
171
208
|
|
|
172
|
-
if (!form) {
|
|
173
|
-
return null;
|
|
174
|
-
}
|
|
175
209
|
|
|
176
|
-
|
|
210
|
+
submission.scope = Array.from(new Set(submission.scope));
|
|
211
|
+
return submission;
|
|
177
212
|
}
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
payload: JSON.parse(json)
|
|
191
|
-
}];
|
|
213
|
+
function parseListCommand(data) {
|
|
214
|
+
try {
|
|
215
|
+
var command = JSON.parse(data);
|
|
216
|
+
|
|
217
|
+
if (typeof command.type !== 'string' || !['prepend', 'append', 'replace', 'remove', 'reorder'].includes(command.type)) {
|
|
218
|
+
throw new Error('Unsupported list command type');
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
return command;
|
|
222
|
+
} catch (error) {
|
|
223
|
+
throw new Error("Invalid list command: \"".concat(data, "\"; ").concat(error));
|
|
224
|
+
}
|
|
192
225
|
}
|
|
193
226
|
function updateList(list, command) {
|
|
194
227
|
switch (command.type) {
|
|
@@ -219,51 +252,28 @@ function updateList(list, command) {
|
|
|
219
252
|
break;
|
|
220
253
|
|
|
221
254
|
default:
|
|
222
|
-
throw new Error('
|
|
255
|
+
throw new Error('Unknown list command received');
|
|
223
256
|
}
|
|
224
257
|
|
|
225
258
|
return list;
|
|
226
259
|
}
|
|
227
|
-
function
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
if (!command) {
|
|
231
|
-
return payload;
|
|
232
|
-
}
|
|
233
|
-
|
|
234
|
-
payload.delete(listCommandKey);
|
|
235
|
-
|
|
236
|
-
if (command instanceof File) {
|
|
237
|
-
throw new Error("The \"".concat(listCommandKey, "\" key could not be used for file upload"));
|
|
260
|
+
function handleList(submission) {
|
|
261
|
+
if (submission.type !== 'list') {
|
|
262
|
+
return submission;
|
|
238
263
|
}
|
|
239
264
|
|
|
240
|
-
var
|
|
241
|
-
var
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
if (name.startsWith(key)) {
|
|
246
|
-
entries.push([name.replace(key, 'list'), value]);
|
|
247
|
-
} else {
|
|
248
|
-
result.append(name, value);
|
|
265
|
+
var command = parseListCommand(submission.data);
|
|
266
|
+
var paths = getPaths(command.scope);
|
|
267
|
+
setValue(submission.value, paths, list => {
|
|
268
|
+
if (!Array.isArray(list)) {
|
|
269
|
+
throw new Error('The list command can only be applied to a list');
|
|
249
270
|
}
|
|
250
|
-
}
|
|
251
|
-
|
|
252
|
-
var {
|
|
253
|
-
list
|
|
254
|
-
} = unflatten(entries);
|
|
255
|
-
|
|
256
|
-
if (!Array.isArray(list)) {
|
|
257
|
-
throw new Error('The list command can only be applied to a list');
|
|
258
|
-
}
|
|
259
271
|
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
}
|
|
265
|
-
|
|
266
|
-
return result;
|
|
272
|
+
return updateList(list, command);
|
|
273
|
+
});
|
|
274
|
+
return _objectSpread2(_objectSpread2({}, submission), {}, {
|
|
275
|
+
scope: [command.scope]
|
|
276
|
+
});
|
|
267
277
|
}
|
|
268
278
|
|
|
269
|
-
export {
|
|
279
|
+
export { focusFirstInvalidField, getFormData, getFormElement, getName, getPaths, getSubmissionType, handleList, hasError, isFieldElement, parse, parseListCommand, reportValidity, requestSubmit, requestValidate, setValue, updateList };
|
package/package.json
CHANGED