@conform-to/dom 0.4.1 → 0.5.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 +51 -9
- package/index.js +131 -38
- package/module/index.js +127 -36
- package/package.json +1 -1
package/index.d.ts
CHANGED
|
@@ -1,12 +1,14 @@
|
|
|
1
1
|
export declare type Primitive = null | undefined | string | number | boolean | Date;
|
|
2
2
|
export declare type FieldElement = HTMLInputElement | HTMLSelectElement | HTMLTextAreaElement | HTMLButtonElement;
|
|
3
3
|
export interface FieldConfig<Schema = unknown> extends FieldConstraint<Schema> {
|
|
4
|
+
id?: string;
|
|
4
5
|
name: string;
|
|
5
6
|
defaultValue?: FieldValue<Schema>;
|
|
6
7
|
initialError?: Array<[string, string]>;
|
|
7
8
|
form?: string;
|
|
9
|
+
errorId?: string;
|
|
8
10
|
}
|
|
9
|
-
export declare type FieldValue<Schema> = Schema extends Primitive
|
|
11
|
+
export declare type FieldValue<Schema> = Schema extends Primitive ? string : Schema extends File ? File : Schema extends Array<infer InnerType> ? Array<FieldValue<InnerType>> : Schema extends Record<string, any> ? {
|
|
10
12
|
[Key in keyof Schema]?: FieldValue<Schema[Key]>;
|
|
11
13
|
} : unknown;
|
|
12
14
|
export declare type FieldConstraint<Schema = any> = {
|
|
@@ -28,25 +30,40 @@ export declare type Submission<Schema = unknown> = {
|
|
|
28
30
|
value: FieldValue<Schema>;
|
|
29
31
|
error: Array<[string, string]>;
|
|
30
32
|
};
|
|
33
|
+
export interface CommandButtonProps<Name extends string = string> {
|
|
34
|
+
name: `conform/${Name}`;
|
|
35
|
+
value: string;
|
|
36
|
+
formNoValidate?: boolean;
|
|
37
|
+
}
|
|
31
38
|
export declare function isFieldElement(element: unknown): element is FieldElement;
|
|
32
39
|
export declare function getFormElements(form: HTMLFormElement): FieldElement[];
|
|
33
40
|
export declare function getPaths(name: string): Array<string | number>;
|
|
34
41
|
export declare function getFormData(form: HTMLFormElement, submitter?: HTMLInputElement | HTMLButtonElement | null): FormData;
|
|
35
42
|
export declare function getName(paths: Array<string | number>): string;
|
|
36
|
-
export declare function shouldValidate(submission: Submission, name
|
|
43
|
+
export declare function shouldValidate(submission: Submission, name: string): boolean;
|
|
37
44
|
export declare function hasError(error: Array<[string, string]>, name?: string): boolean;
|
|
38
|
-
export declare function
|
|
45
|
+
export declare function reportSubmission(form: HTMLFormElement, submission: Submission): void;
|
|
39
46
|
export declare function setValue<T>(target: any, paths: Array<string | number>, valueFn: (prev?: T) => T): void;
|
|
47
|
+
/**
|
|
48
|
+
* The ponyfill of `HTMLFormElement.requestSubmit()`
|
|
49
|
+
* @see https://developer.mozilla.org/en-US/docs/Web/API/HTMLFormElement/requestSubmit
|
|
50
|
+
* @see https://caniuse.com/?search=requestSubmit
|
|
51
|
+
*/
|
|
40
52
|
export declare function requestSubmit(form: HTMLFormElement, submitter?: HTMLButtonElement | HTMLInputElement): void;
|
|
41
|
-
|
|
53
|
+
/**
|
|
54
|
+
* Creates a command button on demand and trigger a form submit by clicking it.
|
|
55
|
+
*/
|
|
56
|
+
export declare function requestCommand(form: HTMLFormElement | undefined, buttonProps: CommandButtonProps): void;
|
|
57
|
+
/**
|
|
58
|
+
* Returns the properties required to configure a command button for validation
|
|
59
|
+
*
|
|
60
|
+
* @see https://conform.guide/api/react#validate
|
|
61
|
+
*/
|
|
62
|
+
export declare function validate(field?: string): CommandButtonProps<'validate'>;
|
|
42
63
|
export declare function getFormElement(element: HTMLFormElement | HTMLFieldSetElement | HTMLInputElement | HTMLSelectElement | HTMLTextAreaElement | HTMLButtonElement | null): HTMLFormElement | null;
|
|
43
|
-
export declare function
|
|
64
|
+
export declare function focus(field: FieldElement): void;
|
|
44
65
|
export declare function getSubmissionType(name: string): string | null;
|
|
45
66
|
export declare function parse<Schema extends Record<string, any>>(payload: FormData | URLSearchParams): Submission<Schema>;
|
|
46
|
-
export declare type Command = {
|
|
47
|
-
name: string;
|
|
48
|
-
value: string;
|
|
49
|
-
};
|
|
50
67
|
export declare type ListCommand<Schema = unknown> = {
|
|
51
68
|
type: 'prepend';
|
|
52
69
|
scope: string;
|
|
@@ -83,3 +100,28 @@ export declare type ListCommand<Schema = unknown> = {
|
|
|
83
100
|
export declare function parseListCommand<Schema = unknown>(data: string): ListCommand<Schema>;
|
|
84
101
|
export declare function updateList<Schema>(list: Array<Schema>, command: ListCommand<Schema>): Array<Schema>;
|
|
85
102
|
export declare function handleList<Schema>(submission: Submission<Schema>): Submission<Schema>;
|
|
103
|
+
export interface ListCommandButtonBuilder {
|
|
104
|
+
append<Schema>(name: string, payload?: {
|
|
105
|
+
defaultValue: Schema;
|
|
106
|
+
}): CommandButtonProps<'list'>;
|
|
107
|
+
prepend<Schema>(name: string, payload?: {
|
|
108
|
+
defaultValue: Schema;
|
|
109
|
+
}): CommandButtonProps<'list'>;
|
|
110
|
+
replace<Schema>(name: string, payload: {
|
|
111
|
+
defaultValue: Schema;
|
|
112
|
+
index: number;
|
|
113
|
+
}): CommandButtonProps<'list'>;
|
|
114
|
+
remove(name: string, payload: {
|
|
115
|
+
index: number;
|
|
116
|
+
}): CommandButtonProps<'list'>;
|
|
117
|
+
reorder(name: string, payload: {
|
|
118
|
+
from: number;
|
|
119
|
+
to: number;
|
|
120
|
+
}): CommandButtonProps<'list'>;
|
|
121
|
+
}
|
|
122
|
+
/**
|
|
123
|
+
* Helpers to configure a command button for modifying a list
|
|
124
|
+
*
|
|
125
|
+
* @see https://conform.guide/api/react#list
|
|
126
|
+
*/
|
|
127
|
+
export declare const list: ListCommandButtonBuilder;
|
package/index.js
CHANGED
|
@@ -45,7 +45,7 @@ function getName(paths) {
|
|
|
45
45
|
}, '');
|
|
46
46
|
}
|
|
47
47
|
function shouldValidate(submission, name) {
|
|
48
|
-
return submission.type === 'submit' || submission.type === 'validate' && (
|
|
48
|
+
return submission.type === 'submit' || submission.type === 'validate' && (submission.intent === '' || submission.intent === name);
|
|
49
49
|
}
|
|
50
50
|
function hasError(error, name) {
|
|
51
51
|
return typeof error.find(_ref => {
|
|
@@ -53,13 +53,52 @@ function hasError(error, name) {
|
|
|
53
53
|
return (typeof name === 'undefined' || name === fieldName) && message !== '';
|
|
54
54
|
}) !== 'undefined';
|
|
55
55
|
}
|
|
56
|
-
function
|
|
57
|
-
var
|
|
56
|
+
function reportSubmission(form, submission) {
|
|
57
|
+
var messageByName = new Map();
|
|
58
|
+
for (var [_name, message] of submission.error) {
|
|
59
|
+
if (!messageByName.has(_name)) {
|
|
60
|
+
// Only keep the first error message (for now)
|
|
61
|
+
messageByName.set(_name, message);
|
|
62
|
+
|
|
63
|
+
// We can't use empty string as button name
|
|
64
|
+
// As `form.element.namedItem('')` will always returns null
|
|
65
|
+
var elementName = _name ? _name : '__form__';
|
|
66
|
+
var item = form.elements.namedItem(elementName);
|
|
67
|
+
if (item instanceof RadioNodeList) {
|
|
68
|
+
for (var field of item) {
|
|
69
|
+
if (field.type !== 'radio') {
|
|
70
|
+
throw new Error('Repeated field name is not supported');
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
if (item === null) {
|
|
75
|
+
// Create placeholder button to keep the error without contributing to the form data
|
|
76
|
+
var button = document.createElement('button');
|
|
77
|
+
button.name = elementName;
|
|
78
|
+
button.hidden = true;
|
|
79
|
+
button.dataset.conformTouched = 'true';
|
|
80
|
+
item = button;
|
|
81
|
+
form.appendChild(button);
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
}
|
|
58
85
|
for (var element of form.elements) {
|
|
59
|
-
if (isFieldElement(element)) {
|
|
60
|
-
var
|
|
61
|
-
|
|
62
|
-
|
|
86
|
+
if (isFieldElement(element) && element.willValidate) {
|
|
87
|
+
var _elementName = element.name !== '__form__' ? element.name : '';
|
|
88
|
+
var _message = messageByName.get(_elementName);
|
|
89
|
+
var elementShouldValidate = shouldValidate(submission, _elementName);
|
|
90
|
+
if (elementShouldValidate) {
|
|
91
|
+
element.dataset.conformTouched = 'true';
|
|
92
|
+
}
|
|
93
|
+
if (typeof _message !== 'undefined' || elementShouldValidate) {
|
|
94
|
+
var invalidEvent = new Event('invalid', {
|
|
95
|
+
cancelable: true
|
|
96
|
+
});
|
|
97
|
+
element.setCustomValidity(_message !== null && _message !== void 0 ? _message : '');
|
|
98
|
+
element.dispatchEvent(invalidEvent);
|
|
99
|
+
}
|
|
100
|
+
if (elementShouldValidate && !element.validity.valid) {
|
|
101
|
+
focus(element);
|
|
63
102
|
}
|
|
64
103
|
}
|
|
65
104
|
}
|
|
@@ -78,6 +117,12 @@ function setValue(target, paths, valueFn) {
|
|
|
78
117
|
pointer = pointer[key];
|
|
79
118
|
}
|
|
80
119
|
}
|
|
120
|
+
|
|
121
|
+
/**
|
|
122
|
+
* The ponyfill of `HTMLFormElement.requestSubmit()`
|
|
123
|
+
* @see https://developer.mozilla.org/en-US/docs/Web/API/HTMLFormElement/requestSubmit
|
|
124
|
+
* @see https://caniuse.com/?search=requestSubmit
|
|
125
|
+
*/
|
|
81
126
|
function requestSubmit(form, submitter) {
|
|
82
127
|
var submitEvent = new SubmitEvent('submit', {
|
|
83
128
|
bubbles: true,
|
|
@@ -86,16 +131,39 @@ function requestSubmit(form, submitter) {
|
|
|
86
131
|
});
|
|
87
132
|
form.dispatchEvent(submitEvent);
|
|
88
133
|
}
|
|
89
|
-
|
|
134
|
+
|
|
135
|
+
/**
|
|
136
|
+
* Creates a command button on demand and trigger a form submit by clicking it.
|
|
137
|
+
*/
|
|
138
|
+
function requestCommand(form, buttonProps) {
|
|
139
|
+
if (!form) {
|
|
140
|
+
console.warn('No form element is provided');
|
|
141
|
+
return;
|
|
142
|
+
}
|
|
90
143
|
var button = document.createElement('button');
|
|
91
|
-
button.name =
|
|
92
|
-
button.value =
|
|
93
|
-
button.formNoValidate = true;
|
|
144
|
+
button.name = buttonProps.name;
|
|
145
|
+
button.value = buttonProps.value;
|
|
94
146
|
button.hidden = true;
|
|
147
|
+
if (buttonProps.formNoValidate) {
|
|
148
|
+
button.formNoValidate = true;
|
|
149
|
+
}
|
|
95
150
|
form.appendChild(button);
|
|
96
|
-
|
|
151
|
+
button.click();
|
|
97
152
|
form.removeChild(button);
|
|
98
153
|
}
|
|
154
|
+
|
|
155
|
+
/**
|
|
156
|
+
* Returns the properties required to configure a command button for validation
|
|
157
|
+
*
|
|
158
|
+
* @see https://conform.guide/api/react#validate
|
|
159
|
+
*/
|
|
160
|
+
function validate(field) {
|
|
161
|
+
return {
|
|
162
|
+
name: 'conform/validate',
|
|
163
|
+
value: field !== null && field !== void 0 ? field : '',
|
|
164
|
+
formNoValidate: true
|
|
165
|
+
};
|
|
166
|
+
}
|
|
99
167
|
function getFormElement(element) {
|
|
100
168
|
var form = element instanceof HTMLFormElement ? element : element === null || element === void 0 ? void 0 : element.form;
|
|
101
169
|
if (!form) {
|
|
@@ -103,20 +171,12 @@ function getFormElement(element) {
|
|
|
103
171
|
}
|
|
104
172
|
return form;
|
|
105
173
|
}
|
|
106
|
-
function
|
|
174
|
+
function focus(field) {
|
|
107
175
|
var currentFocus = document.activeElement;
|
|
108
|
-
if (!isFieldElement(currentFocus) || currentFocus.tagName !== 'BUTTON' || currentFocus.form !== form) {
|
|
176
|
+
if (!isFieldElement(currentFocus) || currentFocus.tagName !== 'BUTTON' || currentFocus.form !== field.form) {
|
|
109
177
|
return;
|
|
110
178
|
}
|
|
111
|
-
|
|
112
|
-
if (isFieldElement(field)) {
|
|
113
|
-
// Focus on the first non button field
|
|
114
|
-
if (!field.validity.valid && field.dataset.conformTouched && field.tagName !== 'BUTTON') {
|
|
115
|
-
field.focus();
|
|
116
|
-
break;
|
|
117
|
-
}
|
|
118
|
-
}
|
|
119
|
-
}
|
|
179
|
+
field.focus();
|
|
120
180
|
}
|
|
121
181
|
function getSubmissionType(name) {
|
|
122
182
|
var prefix = 'conform/';
|
|
@@ -133,8 +193,8 @@ function parse(payload) {
|
|
|
133
193
|
error: []
|
|
134
194
|
};
|
|
135
195
|
try {
|
|
136
|
-
var _loop = function _loop(
|
|
137
|
-
var submissionType = getSubmissionType(
|
|
196
|
+
var _loop = function _loop(value, _name2) {
|
|
197
|
+
var submissionType = getSubmissionType(_name2);
|
|
138
198
|
if (submissionType) {
|
|
139
199
|
if (typeof value !== 'string') {
|
|
140
200
|
throw new Error('The conform command could not be used on a file input');
|
|
@@ -148,17 +208,20 @@ function parse(payload) {
|
|
|
148
208
|
});
|
|
149
209
|
hasCommand = true;
|
|
150
210
|
} else {
|
|
151
|
-
var paths = getPaths(
|
|
211
|
+
var paths = getPaths(_name2);
|
|
152
212
|
setValue(submission.value, paths, prev => {
|
|
153
|
-
if (prev) {
|
|
154
|
-
|
|
213
|
+
if (!prev) {
|
|
214
|
+
return value;
|
|
215
|
+
} else if (Array.isArray(prev)) {
|
|
216
|
+
return prev.concat(value);
|
|
217
|
+
} else {
|
|
218
|
+
return [prev, value];
|
|
155
219
|
}
|
|
156
|
-
return value;
|
|
157
220
|
});
|
|
158
221
|
}
|
|
159
222
|
};
|
|
160
|
-
for (var [
|
|
161
|
-
_loop(
|
|
223
|
+
for (var [_name2, value] of payload.entries()) {
|
|
224
|
+
_loop(value, _name2);
|
|
162
225
|
}
|
|
163
226
|
switch (submission.type) {
|
|
164
227
|
case 'list':
|
|
@@ -173,8 +236,8 @@ function parse(payload) {
|
|
|
173
236
|
function parseListCommand(data) {
|
|
174
237
|
try {
|
|
175
238
|
var command = JSON.parse(data);
|
|
176
|
-
if (typeof command.type !== 'string' || !['prepend', 'append', 'replace', 'remove', 'reorder'].includes(command.type)) {
|
|
177
|
-
throw new Error(
|
|
239
|
+
if (typeof command.type !== 'string' || !['prepend', 'append', 'replace', 'remove', 'reorder', 'combine'].includes(command.type)) {
|
|
240
|
+
throw new Error("Unknown list command received: ".concat(command.type));
|
|
178
241
|
}
|
|
179
242
|
return command;
|
|
180
243
|
} catch (error) {
|
|
@@ -217,15 +280,43 @@ function handleList(submission) {
|
|
|
217
280
|
var command = parseListCommand((_submission$intent = submission.intent) !== null && _submission$intent !== void 0 ? _submission$intent : '');
|
|
218
281
|
var paths = getPaths(command.scope);
|
|
219
282
|
setValue(submission.value, paths, list => {
|
|
220
|
-
if (!Array.isArray(list)) {
|
|
283
|
+
if (typeof list !== 'undefined' && !Array.isArray(list)) {
|
|
221
284
|
throw new Error('The list command can only be applied to a list');
|
|
222
285
|
}
|
|
223
|
-
return updateList(list, command);
|
|
286
|
+
return updateList(list !== null && list !== void 0 ? list : [], command);
|
|
224
287
|
});
|
|
225
288
|
return submission;
|
|
226
289
|
}
|
|
290
|
+
/**
|
|
291
|
+
* Helpers to configure a command button for modifying a list
|
|
292
|
+
*
|
|
293
|
+
* @see https://conform.guide/api/react#list
|
|
294
|
+
*/
|
|
295
|
+
var list = new Proxy({}, {
|
|
296
|
+
get(_target, type) {
|
|
297
|
+
switch (type) {
|
|
298
|
+
case 'append':
|
|
299
|
+
case 'prepend':
|
|
300
|
+
case 'replace':
|
|
301
|
+
case 'remove':
|
|
302
|
+
case 'reorder':
|
|
303
|
+
return function (scope) {
|
|
304
|
+
var payload = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};
|
|
305
|
+
return {
|
|
306
|
+
name: 'conform/list',
|
|
307
|
+
value: JSON.stringify({
|
|
308
|
+
type,
|
|
309
|
+
scope,
|
|
310
|
+
payload
|
|
311
|
+
}),
|
|
312
|
+
formNoValidate: true
|
|
313
|
+
};
|
|
314
|
+
};
|
|
315
|
+
}
|
|
316
|
+
}
|
|
317
|
+
});
|
|
227
318
|
|
|
228
|
-
exports.
|
|
319
|
+
exports.focus = focus;
|
|
229
320
|
exports.getFormData = getFormData;
|
|
230
321
|
exports.getFormElement = getFormElement;
|
|
231
322
|
exports.getFormElements = getFormElements;
|
|
@@ -235,11 +326,13 @@ exports.getSubmissionType = getSubmissionType;
|
|
|
235
326
|
exports.handleList = handleList;
|
|
236
327
|
exports.hasError = hasError;
|
|
237
328
|
exports.isFieldElement = isFieldElement;
|
|
329
|
+
exports.list = list;
|
|
238
330
|
exports.parse = parse;
|
|
239
331
|
exports.parseListCommand = parseListCommand;
|
|
332
|
+
exports.reportSubmission = reportSubmission;
|
|
333
|
+
exports.requestCommand = requestCommand;
|
|
240
334
|
exports.requestSubmit = requestSubmit;
|
|
241
|
-
exports.requestValidate = requestValidate;
|
|
242
|
-
exports.setFormError = setFormError;
|
|
243
335
|
exports.setValue = setValue;
|
|
244
336
|
exports.shouldValidate = shouldValidate;
|
|
245
337
|
exports.updateList = updateList;
|
|
338
|
+
exports.validate = validate;
|
package/module/index.js
CHANGED
|
@@ -41,7 +41,7 @@ function getName(paths) {
|
|
|
41
41
|
}, '');
|
|
42
42
|
}
|
|
43
43
|
function shouldValidate(submission, name) {
|
|
44
|
-
return submission.type === 'submit' || submission.type === 'validate' && (
|
|
44
|
+
return submission.type === 'submit' || submission.type === 'validate' && (submission.intent === '' || submission.intent === name);
|
|
45
45
|
}
|
|
46
46
|
function hasError(error, name) {
|
|
47
47
|
return typeof error.find(_ref => {
|
|
@@ -49,13 +49,52 @@ function hasError(error, name) {
|
|
|
49
49
|
return (typeof name === 'undefined' || name === fieldName) && message !== '';
|
|
50
50
|
}) !== 'undefined';
|
|
51
51
|
}
|
|
52
|
-
function
|
|
53
|
-
var
|
|
52
|
+
function reportSubmission(form, submission) {
|
|
53
|
+
var messageByName = new Map();
|
|
54
|
+
for (var [_name, message] of submission.error) {
|
|
55
|
+
if (!messageByName.has(_name)) {
|
|
56
|
+
// Only keep the first error message (for now)
|
|
57
|
+
messageByName.set(_name, message);
|
|
58
|
+
|
|
59
|
+
// We can't use empty string as button name
|
|
60
|
+
// As `form.element.namedItem('')` will always returns null
|
|
61
|
+
var elementName = _name ? _name : '__form__';
|
|
62
|
+
var item = form.elements.namedItem(elementName);
|
|
63
|
+
if (item instanceof RadioNodeList) {
|
|
64
|
+
for (var field of item) {
|
|
65
|
+
if (field.type !== 'radio') {
|
|
66
|
+
throw new Error('Repeated field name is not supported');
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
if (item === null) {
|
|
71
|
+
// Create placeholder button to keep the error without contributing to the form data
|
|
72
|
+
var button = document.createElement('button');
|
|
73
|
+
button.name = elementName;
|
|
74
|
+
button.hidden = true;
|
|
75
|
+
button.dataset.conformTouched = 'true';
|
|
76
|
+
item = button;
|
|
77
|
+
form.appendChild(button);
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
}
|
|
54
81
|
for (var element of form.elements) {
|
|
55
|
-
if (isFieldElement(element)) {
|
|
56
|
-
var
|
|
57
|
-
|
|
58
|
-
|
|
82
|
+
if (isFieldElement(element) && element.willValidate) {
|
|
83
|
+
var _elementName = element.name !== '__form__' ? element.name : '';
|
|
84
|
+
var _message = messageByName.get(_elementName);
|
|
85
|
+
var elementShouldValidate = shouldValidate(submission, _elementName);
|
|
86
|
+
if (elementShouldValidate) {
|
|
87
|
+
element.dataset.conformTouched = 'true';
|
|
88
|
+
}
|
|
89
|
+
if (typeof _message !== 'undefined' || elementShouldValidate) {
|
|
90
|
+
var invalidEvent = new Event('invalid', {
|
|
91
|
+
cancelable: true
|
|
92
|
+
});
|
|
93
|
+
element.setCustomValidity(_message !== null && _message !== void 0 ? _message : '');
|
|
94
|
+
element.dispatchEvent(invalidEvent);
|
|
95
|
+
}
|
|
96
|
+
if (elementShouldValidate && !element.validity.valid) {
|
|
97
|
+
focus(element);
|
|
59
98
|
}
|
|
60
99
|
}
|
|
61
100
|
}
|
|
@@ -74,6 +113,12 @@ function setValue(target, paths, valueFn) {
|
|
|
74
113
|
pointer = pointer[key];
|
|
75
114
|
}
|
|
76
115
|
}
|
|
116
|
+
|
|
117
|
+
/**
|
|
118
|
+
* The ponyfill of `HTMLFormElement.requestSubmit()`
|
|
119
|
+
* @see https://developer.mozilla.org/en-US/docs/Web/API/HTMLFormElement/requestSubmit
|
|
120
|
+
* @see https://caniuse.com/?search=requestSubmit
|
|
121
|
+
*/
|
|
77
122
|
function requestSubmit(form, submitter) {
|
|
78
123
|
var submitEvent = new SubmitEvent('submit', {
|
|
79
124
|
bubbles: true,
|
|
@@ -82,16 +127,39 @@ function requestSubmit(form, submitter) {
|
|
|
82
127
|
});
|
|
83
128
|
form.dispatchEvent(submitEvent);
|
|
84
129
|
}
|
|
85
|
-
|
|
130
|
+
|
|
131
|
+
/**
|
|
132
|
+
* Creates a command button on demand and trigger a form submit by clicking it.
|
|
133
|
+
*/
|
|
134
|
+
function requestCommand(form, buttonProps) {
|
|
135
|
+
if (!form) {
|
|
136
|
+
console.warn('No form element is provided');
|
|
137
|
+
return;
|
|
138
|
+
}
|
|
86
139
|
var button = document.createElement('button');
|
|
87
|
-
button.name =
|
|
88
|
-
button.value =
|
|
89
|
-
button.formNoValidate = true;
|
|
140
|
+
button.name = buttonProps.name;
|
|
141
|
+
button.value = buttonProps.value;
|
|
90
142
|
button.hidden = true;
|
|
143
|
+
if (buttonProps.formNoValidate) {
|
|
144
|
+
button.formNoValidate = true;
|
|
145
|
+
}
|
|
91
146
|
form.appendChild(button);
|
|
92
|
-
|
|
147
|
+
button.click();
|
|
93
148
|
form.removeChild(button);
|
|
94
149
|
}
|
|
150
|
+
|
|
151
|
+
/**
|
|
152
|
+
* Returns the properties required to configure a command button for validation
|
|
153
|
+
*
|
|
154
|
+
* @see https://conform.guide/api/react#validate
|
|
155
|
+
*/
|
|
156
|
+
function validate(field) {
|
|
157
|
+
return {
|
|
158
|
+
name: 'conform/validate',
|
|
159
|
+
value: field !== null && field !== void 0 ? field : '',
|
|
160
|
+
formNoValidate: true
|
|
161
|
+
};
|
|
162
|
+
}
|
|
95
163
|
function getFormElement(element) {
|
|
96
164
|
var form = element instanceof HTMLFormElement ? element : element === null || element === void 0 ? void 0 : element.form;
|
|
97
165
|
if (!form) {
|
|
@@ -99,20 +167,12 @@ function getFormElement(element) {
|
|
|
99
167
|
}
|
|
100
168
|
return form;
|
|
101
169
|
}
|
|
102
|
-
function
|
|
170
|
+
function focus(field) {
|
|
103
171
|
var currentFocus = document.activeElement;
|
|
104
|
-
if (!isFieldElement(currentFocus) || currentFocus.tagName !== 'BUTTON' || currentFocus.form !== form) {
|
|
172
|
+
if (!isFieldElement(currentFocus) || currentFocus.tagName !== 'BUTTON' || currentFocus.form !== field.form) {
|
|
105
173
|
return;
|
|
106
174
|
}
|
|
107
|
-
|
|
108
|
-
if (isFieldElement(field)) {
|
|
109
|
-
// Focus on the first non button field
|
|
110
|
-
if (!field.validity.valid && field.dataset.conformTouched && field.tagName !== 'BUTTON') {
|
|
111
|
-
field.focus();
|
|
112
|
-
break;
|
|
113
|
-
}
|
|
114
|
-
}
|
|
115
|
-
}
|
|
175
|
+
field.focus();
|
|
116
176
|
}
|
|
117
177
|
function getSubmissionType(name) {
|
|
118
178
|
var prefix = 'conform/';
|
|
@@ -129,8 +189,8 @@ function parse(payload) {
|
|
|
129
189
|
error: []
|
|
130
190
|
};
|
|
131
191
|
try {
|
|
132
|
-
var _loop = function _loop(
|
|
133
|
-
var submissionType = getSubmissionType(
|
|
192
|
+
var _loop = function _loop(value, _name2) {
|
|
193
|
+
var submissionType = getSubmissionType(_name2);
|
|
134
194
|
if (submissionType) {
|
|
135
195
|
if (typeof value !== 'string') {
|
|
136
196
|
throw new Error('The conform command could not be used on a file input');
|
|
@@ -144,17 +204,20 @@ function parse(payload) {
|
|
|
144
204
|
});
|
|
145
205
|
hasCommand = true;
|
|
146
206
|
} else {
|
|
147
|
-
var paths = getPaths(
|
|
207
|
+
var paths = getPaths(_name2);
|
|
148
208
|
setValue(submission.value, paths, prev => {
|
|
149
|
-
if (prev) {
|
|
150
|
-
|
|
209
|
+
if (!prev) {
|
|
210
|
+
return value;
|
|
211
|
+
} else if (Array.isArray(prev)) {
|
|
212
|
+
return prev.concat(value);
|
|
213
|
+
} else {
|
|
214
|
+
return [prev, value];
|
|
151
215
|
}
|
|
152
|
-
return value;
|
|
153
216
|
});
|
|
154
217
|
}
|
|
155
218
|
};
|
|
156
|
-
for (var [
|
|
157
|
-
_loop(
|
|
219
|
+
for (var [_name2, value] of payload.entries()) {
|
|
220
|
+
_loop(value, _name2);
|
|
158
221
|
}
|
|
159
222
|
switch (submission.type) {
|
|
160
223
|
case 'list':
|
|
@@ -169,8 +232,8 @@ function parse(payload) {
|
|
|
169
232
|
function parseListCommand(data) {
|
|
170
233
|
try {
|
|
171
234
|
var command = JSON.parse(data);
|
|
172
|
-
if (typeof command.type !== 'string' || !['prepend', 'append', 'replace', 'remove', 'reorder'].includes(command.type)) {
|
|
173
|
-
throw new Error(
|
|
235
|
+
if (typeof command.type !== 'string' || !['prepend', 'append', 'replace', 'remove', 'reorder', 'combine'].includes(command.type)) {
|
|
236
|
+
throw new Error("Unknown list command received: ".concat(command.type));
|
|
174
237
|
}
|
|
175
238
|
return command;
|
|
176
239
|
} catch (error) {
|
|
@@ -213,12 +276,40 @@ function handleList(submission) {
|
|
|
213
276
|
var command = parseListCommand((_submission$intent = submission.intent) !== null && _submission$intent !== void 0 ? _submission$intent : '');
|
|
214
277
|
var paths = getPaths(command.scope);
|
|
215
278
|
setValue(submission.value, paths, list => {
|
|
216
|
-
if (!Array.isArray(list)) {
|
|
279
|
+
if (typeof list !== 'undefined' && !Array.isArray(list)) {
|
|
217
280
|
throw new Error('The list command can only be applied to a list');
|
|
218
281
|
}
|
|
219
|
-
return updateList(list, command);
|
|
282
|
+
return updateList(list !== null && list !== void 0 ? list : [], command);
|
|
220
283
|
});
|
|
221
284
|
return submission;
|
|
222
285
|
}
|
|
286
|
+
/**
|
|
287
|
+
* Helpers to configure a command button for modifying a list
|
|
288
|
+
*
|
|
289
|
+
* @see https://conform.guide/api/react#list
|
|
290
|
+
*/
|
|
291
|
+
var list = new Proxy({}, {
|
|
292
|
+
get(_target, type) {
|
|
293
|
+
switch (type) {
|
|
294
|
+
case 'append':
|
|
295
|
+
case 'prepend':
|
|
296
|
+
case 'replace':
|
|
297
|
+
case 'remove':
|
|
298
|
+
case 'reorder':
|
|
299
|
+
return function (scope) {
|
|
300
|
+
var payload = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};
|
|
301
|
+
return {
|
|
302
|
+
name: 'conform/list',
|
|
303
|
+
value: JSON.stringify({
|
|
304
|
+
type,
|
|
305
|
+
scope,
|
|
306
|
+
payload
|
|
307
|
+
}),
|
|
308
|
+
formNoValidate: true
|
|
309
|
+
};
|
|
310
|
+
};
|
|
311
|
+
}
|
|
312
|
+
}
|
|
313
|
+
});
|
|
223
314
|
|
|
224
|
-
export {
|
|
315
|
+
export { focus, getFormData, getFormElement, getFormElements, getName, getPaths, getSubmissionType, handleList, hasError, isFieldElement, list, parse, parseListCommand, reportSubmission, requestCommand, requestSubmit, setValue, shouldValidate, updateList, validate };
|
package/package.json
CHANGED