@conform-to/dom 0.5.0 → 0.6.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/_virtual/_rollupPluginBabelHelpers.js +17 -0
- package/index.d.ts +96 -32
- package/index.js +234 -119
- package/module/_virtual/_rollupPluginBabelHelpers.js +16 -1
- package/module/index.js +228 -116
- package/package.json +1 -1
|
@@ -24,6 +24,7 @@ function _objectSpread2(target) {
|
|
|
24
24
|
return target;
|
|
25
25
|
}
|
|
26
26
|
function _defineProperty(obj, key, value) {
|
|
27
|
+
key = _toPropertyKey(key);
|
|
27
28
|
if (key in obj) {
|
|
28
29
|
Object.defineProperty(obj, key, {
|
|
29
30
|
value: value,
|
|
@@ -36,6 +37,22 @@ function _defineProperty(obj, key, value) {
|
|
|
36
37
|
}
|
|
37
38
|
return obj;
|
|
38
39
|
}
|
|
40
|
+
function _toPrimitive(input, hint) {
|
|
41
|
+
if (typeof input !== "object" || input === null) return input;
|
|
42
|
+
var prim = input[Symbol.toPrimitive];
|
|
43
|
+
if (prim !== undefined) {
|
|
44
|
+
var res = prim.call(input, hint || "default");
|
|
45
|
+
if (typeof res !== "object") return res;
|
|
46
|
+
throw new TypeError("@@toPrimitive must return a primitive value.");
|
|
47
|
+
}
|
|
48
|
+
return (hint === "string" ? String : Number)(input);
|
|
49
|
+
}
|
|
50
|
+
function _toPropertyKey(arg) {
|
|
51
|
+
var key = _toPrimitive(arg, "string");
|
|
52
|
+
return typeof key === "symbol" ? key : String(key);
|
|
53
|
+
}
|
|
39
54
|
|
|
40
55
|
exports.defineProperty = _defineProperty;
|
|
41
56
|
exports.objectSpread2 = _objectSpread2;
|
|
57
|
+
exports.toPrimitive = _toPrimitive;
|
|
58
|
+
exports.toPropertyKey = _toPropertyKey;
|
package/index.d.ts
CHANGED
|
@@ -1,37 +1,42 @@
|
|
|
1
|
-
export
|
|
2
|
-
export
|
|
1
|
+
export type Primitive = null | undefined | string | number | boolean | Date;
|
|
2
|
+
export type FieldElement = HTMLInputElement | HTMLSelectElement | HTMLTextAreaElement | HTMLButtonElement;
|
|
3
3
|
export interface FieldConfig<Schema = unknown> extends FieldConstraint<Schema> {
|
|
4
4
|
id?: string;
|
|
5
5
|
name: string;
|
|
6
6
|
defaultValue?: FieldValue<Schema>;
|
|
7
|
-
initialError?:
|
|
7
|
+
initialError?: Record<string, string | string[]>;
|
|
8
8
|
form?: string;
|
|
9
9
|
errorId?: string;
|
|
10
10
|
}
|
|
11
|
-
export
|
|
11
|
+
export type FieldValue<Schema> = Schema extends Primitive ? string : Schema extends File ? File : Schema extends Array<infer InnerType> ? Array<FieldValue<InnerType>> : Schema extends Record<string, any> ? {
|
|
12
12
|
[Key in keyof Schema]?: FieldValue<Schema[Key]>;
|
|
13
|
-
} :
|
|
14
|
-
export
|
|
13
|
+
} : any;
|
|
14
|
+
export type FieldConstraint<Schema = any> = {
|
|
15
15
|
required?: boolean;
|
|
16
16
|
minLength?: number;
|
|
17
17
|
maxLength?: number;
|
|
18
|
-
min?: Schema extends number ? number : string;
|
|
19
|
-
max?: Schema extends number ? number : string;
|
|
20
|
-
step?: Schema extends number ? number : string;
|
|
18
|
+
min?: Schema extends number ? number : string | number;
|
|
19
|
+
max?: Schema extends number ? number : string | number;
|
|
20
|
+
step?: Schema extends number ? number : string | number;
|
|
21
21
|
multiple?: boolean;
|
|
22
22
|
pattern?: string;
|
|
23
23
|
};
|
|
24
|
-
export
|
|
24
|
+
export type FieldsetConstraint<Schema extends Record<string, any>> = {
|
|
25
25
|
[Key in keyof Schema]?: FieldConstraint<Schema[Key]>;
|
|
26
26
|
};
|
|
27
|
-
export
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
27
|
+
export type Submission<Schema extends Record<string, any> | unknown = unknown> = unknown extends Schema ? {
|
|
28
|
+
intent: string;
|
|
29
|
+
payload: Record<string, any>;
|
|
30
|
+
error: Record<string, string | string[]>;
|
|
31
|
+
} : {
|
|
32
|
+
intent: string;
|
|
33
|
+
payload: Record<string, any>;
|
|
34
|
+
value?: Schema;
|
|
35
|
+
error: Record<string, string | string[]>;
|
|
36
|
+
toJSON(): Submission;
|
|
32
37
|
};
|
|
33
|
-
export interface
|
|
34
|
-
name:
|
|
38
|
+
export interface IntentButtonProps {
|
|
39
|
+
name: '__intent__';
|
|
35
40
|
value: string;
|
|
36
41
|
formNoValidate?: boolean;
|
|
37
42
|
}
|
|
@@ -39,9 +44,19 @@ export declare function isFieldElement(element: unknown): element is FieldElemen
|
|
|
39
44
|
export declare function getFormElements(form: HTMLFormElement): FieldElement[];
|
|
40
45
|
export declare function getPaths(name: string): Array<string | number>;
|
|
41
46
|
export declare function getFormData(form: HTMLFormElement, submitter?: HTMLInputElement | HTMLButtonElement | null): FormData;
|
|
47
|
+
export type FormMethod = 'get' | 'post' | 'put' | 'patch' | 'delete';
|
|
48
|
+
export type FormEncType = 'application/x-www-form-urlencoded' | 'multipart/form-data';
|
|
49
|
+
export declare function getFormAttributes(form: HTMLFormElement, submitter?: HTMLInputElement | HTMLButtonElement | null): {
|
|
50
|
+
action: string;
|
|
51
|
+
encType: FormEncType;
|
|
52
|
+
method: FormMethod;
|
|
53
|
+
};
|
|
42
54
|
export declare function getName(paths: Array<string | number>): string;
|
|
43
|
-
export declare function shouldValidate(
|
|
44
|
-
export declare function
|
|
55
|
+
export declare function shouldValidate(intent: string, name: string): boolean;
|
|
56
|
+
export declare function getValidationMessage(errors?: string | string[]): string;
|
|
57
|
+
export declare function getErrors(message: string | undefined): string[];
|
|
58
|
+
export declare const VALIDATION_UNDEFINED = "__undefined__";
|
|
59
|
+
export declare const VALIDATION_SKIPPED = "__skipped__";
|
|
45
60
|
export declare function reportSubmission(form: HTMLFormElement, submission: Submission): void;
|
|
46
61
|
export declare function setValue<T>(target: any, paths: Array<string | number>, valueFn: (prev?: T) => T): void;
|
|
47
62
|
/**
|
|
@@ -51,20 +66,47 @@ export declare function setValue<T>(target: any, paths: Array<string | number>,
|
|
|
51
66
|
*/
|
|
52
67
|
export declare function requestSubmit(form: HTMLFormElement, submitter?: HTMLButtonElement | HTMLInputElement): void;
|
|
53
68
|
/**
|
|
54
|
-
* Creates
|
|
69
|
+
* Creates an intent button on demand and trigger a form submit by clicking it.
|
|
55
70
|
*/
|
|
56
|
-
export declare function
|
|
71
|
+
export declare function requestIntent(form: HTMLFormElement | undefined, buttonProps: {
|
|
72
|
+
value: string;
|
|
73
|
+
formNoValidate?: boolean;
|
|
74
|
+
}): void;
|
|
57
75
|
/**
|
|
58
76
|
* Returns the properties required to configure a command button for validation
|
|
59
77
|
*
|
|
60
78
|
* @see https://conform.guide/api/react#validate
|
|
61
79
|
*/
|
|
62
|
-
export declare function validate(field?: string):
|
|
80
|
+
export declare function validate(field?: string): IntentButtonProps;
|
|
63
81
|
export declare function getFormElement(element: HTMLFormElement | HTMLFieldSetElement | HTMLInputElement | HTMLSelectElement | HTMLTextAreaElement | HTMLButtonElement | null): HTMLFormElement | null;
|
|
64
82
|
export declare function focus(field: FieldElement): void;
|
|
65
|
-
export declare function
|
|
66
|
-
export declare function parse<Schema
|
|
67
|
-
|
|
83
|
+
export declare function parse(payload: FormData | URLSearchParams): Submission;
|
|
84
|
+
export declare function parse<Schema>(payload: FormData | URLSearchParams, options?: {
|
|
85
|
+
resolve?: (payload: Record<string, any>, intent: string) => {
|
|
86
|
+
value: Schema;
|
|
87
|
+
} | {
|
|
88
|
+
error: Record<string, string | string[]>;
|
|
89
|
+
};
|
|
90
|
+
}): Submission<Schema>;
|
|
91
|
+
export declare function parse<Schema>(payload: FormData | URLSearchParams, options?: {
|
|
92
|
+
resolve?: (payload: Record<string, any>, intent: string) => Promise<{
|
|
93
|
+
value: Schema;
|
|
94
|
+
} | {
|
|
95
|
+
error: Record<string, string | string[]>;
|
|
96
|
+
}>;
|
|
97
|
+
}): Promise<Submission<Schema>>;
|
|
98
|
+
export declare function parse<Schema>(payload: FormData | URLSearchParams, options?: {
|
|
99
|
+
resolve?: (payload: Record<string, any>, intent: string) => ({
|
|
100
|
+
value: Schema;
|
|
101
|
+
} | {
|
|
102
|
+
error: Record<string, string | string[]>;
|
|
103
|
+
}) | Promise<{
|
|
104
|
+
value: Schema;
|
|
105
|
+
} | {
|
|
106
|
+
error: Record<string, string | string[]>;
|
|
107
|
+
}>;
|
|
108
|
+
}): Submission<Schema> | Promise<Submission<Schema>>;
|
|
109
|
+
export type ListCommand<Schema = unknown> = {
|
|
68
110
|
type: 'prepend';
|
|
69
111
|
scope: string;
|
|
70
112
|
payload: {
|
|
@@ -97,27 +139,26 @@ export declare type ListCommand<Schema = unknown> = {
|
|
|
97
139
|
to: number;
|
|
98
140
|
};
|
|
99
141
|
};
|
|
100
|
-
export declare function parseListCommand<Schema = unknown>(
|
|
142
|
+
export declare function parseListCommand<Schema = unknown>(intent: string): ListCommand<Schema> | null;
|
|
101
143
|
export declare function updateList<Schema>(list: Array<Schema>, command: ListCommand<Schema>): Array<Schema>;
|
|
102
|
-
export declare function handleList<Schema>(submission: Submission<Schema>): Submission<Schema>;
|
|
103
144
|
export interface ListCommandButtonBuilder {
|
|
104
145
|
append<Schema>(name: string, payload?: {
|
|
105
146
|
defaultValue: Schema;
|
|
106
|
-
}):
|
|
147
|
+
}): IntentButtonProps;
|
|
107
148
|
prepend<Schema>(name: string, payload?: {
|
|
108
149
|
defaultValue: Schema;
|
|
109
|
-
}):
|
|
150
|
+
}): IntentButtonProps;
|
|
110
151
|
replace<Schema>(name: string, payload: {
|
|
111
152
|
defaultValue: Schema;
|
|
112
153
|
index: number;
|
|
113
|
-
}):
|
|
154
|
+
}): IntentButtonProps;
|
|
114
155
|
remove(name: string, payload: {
|
|
115
156
|
index: number;
|
|
116
|
-
}):
|
|
157
|
+
}): IntentButtonProps;
|
|
117
158
|
reorder(name: string, payload: {
|
|
118
159
|
from: number;
|
|
119
160
|
to: number;
|
|
120
|
-
}):
|
|
161
|
+
}): IntentButtonProps;
|
|
121
162
|
}
|
|
122
163
|
/**
|
|
123
164
|
* Helpers to configure a command button for modifying a list
|
|
@@ -125,3 +166,26 @@ export interface ListCommandButtonBuilder {
|
|
|
125
166
|
* @see https://conform.guide/api/react#list
|
|
126
167
|
*/
|
|
127
168
|
export declare const list: ListCommandButtonBuilder;
|
|
169
|
+
/**
|
|
170
|
+
* Validate the form with the Constraint Validation API
|
|
171
|
+
* @see https://conform.guide/api/react#validateconstraint
|
|
172
|
+
*/
|
|
173
|
+
export declare function validateConstraint(options: {
|
|
174
|
+
form: HTMLFormElement;
|
|
175
|
+
formData?: FormData;
|
|
176
|
+
constraint?: Record<Lowercase<string>, (value: string, context: {
|
|
177
|
+
formData: FormData;
|
|
178
|
+
attributeValue: string;
|
|
179
|
+
}) => boolean>;
|
|
180
|
+
acceptMultipleErrors?: ({ name, intent, payload, }: {
|
|
181
|
+
name: string;
|
|
182
|
+
intent: string;
|
|
183
|
+
payload: Record<string, any>;
|
|
184
|
+
}) => boolean;
|
|
185
|
+
formatMessages?: ({ name, validity, constraint, defaultErrors, }: {
|
|
186
|
+
name: string;
|
|
187
|
+
validity: ValidityState;
|
|
188
|
+
constraint: Record<string, boolean>;
|
|
189
|
+
defaultErrors: string[];
|
|
190
|
+
}) => string[];
|
|
191
|
+
}): Submission;
|
package/index.js
CHANGED
|
@@ -4,6 +4,21 @@ Object.defineProperty(exports, '__esModule', { value: true });
|
|
|
4
4
|
|
|
5
5
|
var _rollupPluginBabelHelpers = require('./_virtual/_rollupPluginBabelHelpers.js');
|
|
6
6
|
|
|
7
|
+
// type Join<K, P> = P extends string | number ?
|
|
8
|
+
// K extends string | number ?
|
|
9
|
+
// `${K}${"" extends P ? "" : "."}${P}`
|
|
10
|
+
// : never : never;
|
|
11
|
+
|
|
12
|
+
// type DottedPaths<T> = T extends object ?
|
|
13
|
+
// { [K in keyof T]-?: K extends string | number ?
|
|
14
|
+
// `${K}` | Join<K, DottedPaths<T[K]>>
|
|
15
|
+
// : never
|
|
16
|
+
// }[keyof T] : ""
|
|
17
|
+
|
|
18
|
+
// type Pathfix<T> = T extends `${infer Prefix}.${number}${infer Postfix}` ? `${Prefix}[${number}]${Pathfix<Postfix>}` : T;
|
|
19
|
+
|
|
20
|
+
// type Path<Schema> = Pathfix<DottedPaths<Schema>> | '';
|
|
21
|
+
|
|
7
22
|
function isFieldElement(element) {
|
|
8
23
|
return element instanceof Element && (element.tagName === 'INPUT' || element.tagName === 'SELECT' || element.tagName === 'TEXTAREA' || element.tagName === 'BUTTON');
|
|
9
24
|
}
|
|
@@ -33,6 +48,18 @@ function getFormData(form, submitter) {
|
|
|
33
48
|
}
|
|
34
49
|
return payload;
|
|
35
50
|
}
|
|
51
|
+
function getFormAttributes(form, submitter) {
|
|
52
|
+
var _ref, _submitter$getAttribu, _ref2, _submitter$getAttribu2, _submitter$getAttribu3;
|
|
53
|
+
var enforce = (value, list) => list.includes(value) ? value : list[0];
|
|
54
|
+
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);
|
|
55
|
+
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';
|
|
56
|
+
var encType = (_submitter$getAttribu3 = submitter === null || submitter === void 0 ? void 0 : submitter.getAttribute('formenctype')) !== null && _submitter$getAttribu3 !== void 0 ? _submitter$getAttribu3 : form.enctype;
|
|
57
|
+
return {
|
|
58
|
+
action,
|
|
59
|
+
encType: enforce(encType, ['application/x-www-form-urlencoded', 'multipart/form-data']),
|
|
60
|
+
method: enforce(method, ['get', 'post', 'put', 'patch', 'delete'])
|
|
61
|
+
};
|
|
62
|
+
}
|
|
36
63
|
function getName(paths) {
|
|
37
64
|
return paths.reduce((name, path) => {
|
|
38
65
|
if (typeof path === 'number') {
|
|
@@ -44,57 +71,70 @@ function getName(paths) {
|
|
|
44
71
|
return [name, path].join('.');
|
|
45
72
|
}, '');
|
|
46
73
|
}
|
|
47
|
-
function shouldValidate(
|
|
48
|
-
|
|
74
|
+
function shouldValidate(intent, name) {
|
|
75
|
+
var _parseListCommand;
|
|
76
|
+
var [type] = intent.split('/', 1);
|
|
77
|
+
switch (type) {
|
|
78
|
+
case 'validate':
|
|
79
|
+
return intent === 'validate' || intent === "validate/".concat(name);
|
|
80
|
+
case 'list':
|
|
81
|
+
return ((_parseListCommand = parseListCommand(intent)) === null || _parseListCommand === void 0 ? void 0 : _parseListCommand.scope) === name;
|
|
82
|
+
default:
|
|
83
|
+
return true;
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
function getValidationMessage(errors) {
|
|
87
|
+
return [].concat(errors !== null && errors !== void 0 ? errors : []).join(String.fromCharCode(31));
|
|
49
88
|
}
|
|
50
|
-
function
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
89
|
+
function getErrors(message) {
|
|
90
|
+
if (!message) {
|
|
91
|
+
return [];
|
|
92
|
+
}
|
|
93
|
+
return message.split(String.fromCharCode(31));
|
|
55
94
|
}
|
|
95
|
+
var VALIDATION_UNDEFINED = '__undefined__';
|
|
96
|
+
var VALIDATION_SKIPPED = '__skipped__';
|
|
56
97
|
function reportSubmission(form, submission) {
|
|
57
|
-
var
|
|
58
|
-
|
|
59
|
-
if (
|
|
60
|
-
|
|
61
|
-
|
|
98
|
+
for (var [_name, message] of Object.entries(submission.error)) {
|
|
99
|
+
// There is no need to create a placeholder button if all we want is to reset the error
|
|
100
|
+
if (message === '') {
|
|
101
|
+
continue;
|
|
102
|
+
}
|
|
62
103
|
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
104
|
+
// We can't use empty string as button name
|
|
105
|
+
// As `form.element.namedItem('')` will always returns null
|
|
106
|
+
var elementName = _name ? _name : '__form__';
|
|
107
|
+
var item = form.elements.namedItem(elementName);
|
|
108
|
+
if (item instanceof RadioNodeList) {
|
|
109
|
+
for (var field of item) {
|
|
110
|
+
if (field.type !== 'radio') {
|
|
111
|
+
console.warn('Repeated field name is not supported.');
|
|
112
|
+
continue;
|
|
72
113
|
}
|
|
73
114
|
}
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
}
|
|
115
|
+
}
|
|
116
|
+
if (item === null) {
|
|
117
|
+
// Create placeholder button to keep the error without contributing to the form data
|
|
118
|
+
var button = document.createElement('button');
|
|
119
|
+
button.name = elementName;
|
|
120
|
+
button.hidden = true;
|
|
121
|
+
button.dataset.conformTouched = 'true';
|
|
122
|
+
form.appendChild(button);
|
|
83
123
|
}
|
|
84
124
|
}
|
|
85
125
|
for (var element of form.elements) {
|
|
86
126
|
if (isFieldElement(element) && element.willValidate) {
|
|
87
127
|
var _elementName = element.name !== '__form__' ? element.name : '';
|
|
88
|
-
var _message =
|
|
89
|
-
var elementShouldValidate = shouldValidate(submission, _elementName);
|
|
128
|
+
var _message = submission.error[_elementName];
|
|
129
|
+
var elementShouldValidate = shouldValidate(submission.intent, _elementName);
|
|
90
130
|
if (elementShouldValidate) {
|
|
91
131
|
element.dataset.conformTouched = 'true';
|
|
92
132
|
}
|
|
93
|
-
if (typeof _message
|
|
133
|
+
if (typeof _message === 'undefined' || ![].concat(_message).includes(VALIDATION_SKIPPED)) {
|
|
94
134
|
var invalidEvent = new Event('invalid', {
|
|
95
135
|
cancelable: true
|
|
96
136
|
});
|
|
97
|
-
element.setCustomValidity(_message
|
|
137
|
+
element.setCustomValidity(getValidationMessage(_message));
|
|
98
138
|
element.dispatchEvent(invalidEvent);
|
|
99
139
|
}
|
|
100
140
|
if (elementShouldValidate && !element.validity.valid) {
|
|
@@ -133,15 +173,15 @@ function requestSubmit(form, submitter) {
|
|
|
133
173
|
}
|
|
134
174
|
|
|
135
175
|
/**
|
|
136
|
-
* Creates
|
|
176
|
+
* Creates an intent button on demand and trigger a form submit by clicking it.
|
|
137
177
|
*/
|
|
138
|
-
function
|
|
178
|
+
function requestIntent(form, buttonProps) {
|
|
139
179
|
if (!form) {
|
|
140
180
|
console.warn('No form element is provided');
|
|
141
181
|
return;
|
|
142
182
|
}
|
|
143
183
|
var button = document.createElement('button');
|
|
144
|
-
button.name =
|
|
184
|
+
button.name = '__intent__';
|
|
145
185
|
button.value = buttonProps.value;
|
|
146
186
|
button.hidden = true;
|
|
147
187
|
if (buttonProps.formNoValidate) {
|
|
@@ -159,8 +199,8 @@ function requestCommand(form, buttonProps) {
|
|
|
159
199
|
*/
|
|
160
200
|
function validate(field) {
|
|
161
201
|
return {
|
|
162
|
-
name: '
|
|
163
|
-
value: field
|
|
202
|
+
name: '__intent__',
|
|
203
|
+
value: field ? "validate/".concat(field) : 'validate',
|
|
164
204
|
formNoValidate: true
|
|
165
205
|
};
|
|
166
206
|
}
|
|
@@ -178,70 +218,80 @@ function focus(field) {
|
|
|
178
218
|
}
|
|
179
219
|
field.focus();
|
|
180
220
|
}
|
|
181
|
-
function
|
|
182
|
-
var prefix = 'conform/';
|
|
183
|
-
if (!name.startsWith(prefix) || name.length <= prefix.length) {
|
|
184
|
-
return null;
|
|
185
|
-
}
|
|
186
|
-
return name.slice(prefix.length);
|
|
187
|
-
}
|
|
188
|
-
function parse(payload) {
|
|
189
|
-
var hasCommand = false;
|
|
221
|
+
function parse(payload, options) {
|
|
190
222
|
var submission = {
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
error:
|
|
223
|
+
intent: 'submit',
|
|
224
|
+
payload: {},
|
|
225
|
+
error: {}
|
|
194
226
|
};
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
if (typeof value !== 'string') {
|
|
200
|
-
throw new Error('The conform command could not be used on a file input');
|
|
201
|
-
}
|
|
202
|
-
if (hasCommand) {
|
|
203
|
-
throw new Error('The conform command could only be set on a button');
|
|
204
|
-
}
|
|
205
|
-
submission = _rollupPluginBabelHelpers.objectSpread2(_rollupPluginBabelHelpers.objectSpread2({}, submission), {}, {
|
|
206
|
-
type: submissionType,
|
|
207
|
-
intent: value
|
|
208
|
-
});
|
|
209
|
-
hasCommand = true;
|
|
210
|
-
} else {
|
|
211
|
-
var paths = getPaths(_name2);
|
|
212
|
-
setValue(submission.value, 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
|
-
});
|
|
227
|
+
var _loop = function _loop(_value) {
|
|
228
|
+
if (_name2 === '__intent__') {
|
|
229
|
+
if (typeof _value !== 'string' || submission.intent !== 'submit') {
|
|
230
|
+
throw new Error('The intent could only be set on a button');
|
|
221
231
|
}
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
232
|
+
submission.intent = _value;
|
|
233
|
+
} else {
|
|
234
|
+
var _paths = getPaths(_name2);
|
|
235
|
+
setValue(submission.payload, _paths, prev => {
|
|
236
|
+
if (!prev) {
|
|
237
|
+
return _value;
|
|
238
|
+
} else if (Array.isArray(prev)) {
|
|
239
|
+
return prev.concat(_value);
|
|
240
|
+
} else {
|
|
241
|
+
return [prev, _value];
|
|
242
|
+
}
|
|
243
|
+
});
|
|
230
244
|
}
|
|
231
|
-
}
|
|
232
|
-
|
|
245
|
+
};
|
|
246
|
+
for (var [_name2, _value] of payload.entries()) {
|
|
247
|
+
_loop(_value);
|
|
248
|
+
}
|
|
249
|
+
var command = parseListCommand(submission.intent);
|
|
250
|
+
if (command) {
|
|
251
|
+
var paths = getPaths(command.scope);
|
|
252
|
+
setValue(submission.payload, paths, list => {
|
|
253
|
+
if (typeof list !== 'undefined' && !Array.isArray(list)) {
|
|
254
|
+
throw new Error('The list command can only be applied to a list');
|
|
255
|
+
}
|
|
256
|
+
return updateList(list !== null && list !== void 0 ? list : [], command);
|
|
257
|
+
});
|
|
258
|
+
}
|
|
259
|
+
if (typeof (options === null || options === void 0 ? void 0 : options.resolve) === 'undefined') {
|
|
260
|
+
return submission;
|
|
233
261
|
}
|
|
234
|
-
|
|
262
|
+
var result = options.resolve(submission.payload, submission.intent);
|
|
263
|
+
var mergeResolveResult = resolved => {
|
|
264
|
+
var result = _rollupPluginBabelHelpers.objectSpread2(_rollupPluginBabelHelpers.objectSpread2(_rollupPluginBabelHelpers.objectSpread2({}, submission), resolved), {}, {
|
|
265
|
+
toJSON() {
|
|
266
|
+
return {
|
|
267
|
+
intent: this.intent,
|
|
268
|
+
payload: this.payload,
|
|
269
|
+
error: this.error
|
|
270
|
+
};
|
|
271
|
+
}
|
|
272
|
+
});
|
|
273
|
+
return result;
|
|
274
|
+
};
|
|
275
|
+
if (result instanceof Promise) {
|
|
276
|
+
return result.then(mergeResolveResult);
|
|
277
|
+
}
|
|
278
|
+
return mergeResolveResult(result);
|
|
235
279
|
}
|
|
236
|
-
function parseListCommand(
|
|
280
|
+
function parseListCommand(intent) {
|
|
237
281
|
try {
|
|
238
|
-
var
|
|
239
|
-
if (
|
|
240
|
-
|
|
282
|
+
var [group, type, scope, json] = intent.split('/');
|
|
283
|
+
if (group !== 'list' || !['prepend', 'append', 'replace', 'remove', 'reorder'].includes(type) || !scope) {
|
|
284
|
+
return null;
|
|
241
285
|
}
|
|
242
|
-
|
|
286
|
+
var _payload = JSON.parse(json);
|
|
287
|
+
return {
|
|
288
|
+
// @ts-expect-error
|
|
289
|
+
type,
|
|
290
|
+
scope,
|
|
291
|
+
payload: _payload
|
|
292
|
+
};
|
|
243
293
|
} catch (error) {
|
|
244
|
-
|
|
294
|
+
return null;
|
|
245
295
|
}
|
|
246
296
|
}
|
|
247
297
|
function updateList(list, command) {
|
|
@@ -272,21 +322,6 @@ function updateList(list, command) {
|
|
|
272
322
|
}
|
|
273
323
|
return list;
|
|
274
324
|
}
|
|
275
|
-
function handleList(submission) {
|
|
276
|
-
var _submission$intent;
|
|
277
|
-
if (submission.type !== 'list') {
|
|
278
|
-
return submission;
|
|
279
|
-
}
|
|
280
|
-
var command = parseListCommand((_submission$intent = submission.intent) !== null && _submission$intent !== void 0 ? _submission$intent : '');
|
|
281
|
-
var paths = getPaths(command.scope);
|
|
282
|
-
setValue(submission.value, paths, list => {
|
|
283
|
-
if (typeof list !== 'undefined' && !Array.isArray(list)) {
|
|
284
|
-
throw new Error('The list command can only be applied to a list');
|
|
285
|
-
}
|
|
286
|
-
return updateList(list !== null && list !== void 0 ? list : [], command);
|
|
287
|
-
});
|
|
288
|
-
return submission;
|
|
289
|
-
}
|
|
290
325
|
/**
|
|
291
326
|
* Helpers to configure a command button for modifying a list
|
|
292
327
|
*
|
|
@@ -303,12 +338,8 @@ var list = new Proxy({}, {
|
|
|
303
338
|
return function (scope) {
|
|
304
339
|
var payload = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};
|
|
305
340
|
return {
|
|
306
|
-
name: '
|
|
307
|
-
value: JSON.stringify(
|
|
308
|
-
type,
|
|
309
|
-
scope,
|
|
310
|
-
payload
|
|
311
|
-
}),
|
|
341
|
+
name: '__intent__',
|
|
342
|
+
value: "list/".concat(type, "/").concat(scope, "/").concat(JSON.stringify(payload)),
|
|
312
343
|
formNoValidate: true
|
|
313
344
|
};
|
|
314
345
|
};
|
|
@@ -316,23 +347,107 @@ var list = new Proxy({}, {
|
|
|
316
347
|
}
|
|
317
348
|
});
|
|
318
349
|
|
|
350
|
+
/**
|
|
351
|
+
* Validate the form with the Constraint Validation API
|
|
352
|
+
* @see https://conform.guide/api/react#validateconstraint
|
|
353
|
+
*/
|
|
354
|
+
function validateConstraint(options) {
|
|
355
|
+
var _options$formData, _options$formatMessag;
|
|
356
|
+
var formData = (_options$formData = options === null || options === void 0 ? void 0 : options.formData) !== null && _options$formData !== void 0 ? _options$formData : new FormData(options.form);
|
|
357
|
+
var getDefaultErrors = (validity, result) => {
|
|
358
|
+
var errors = [];
|
|
359
|
+
if (validity.valueMissing) errors.push('required');
|
|
360
|
+
if (validity.typeMismatch || validity.badInput) errors.push('type');
|
|
361
|
+
if (validity.tooShort) errors.push('minLength');
|
|
362
|
+
if (validity.rangeUnderflow) errors.push('min');
|
|
363
|
+
if (validity.stepMismatch) errors.push('step');
|
|
364
|
+
if (validity.tooLong) errors.push('maxLength');
|
|
365
|
+
if (validity.rangeOverflow) errors.push('max');
|
|
366
|
+
if (validity.patternMismatch) errors.push('pattern');
|
|
367
|
+
for (var [constraintName, valid] of Object.entries(result)) {
|
|
368
|
+
if (!valid) {
|
|
369
|
+
errors.push(constraintName);
|
|
370
|
+
}
|
|
371
|
+
}
|
|
372
|
+
return errors;
|
|
373
|
+
};
|
|
374
|
+
var formatMessages = (_options$formatMessag = options === null || options === void 0 ? void 0 : options.formatMessages) !== null && _options$formatMessag !== void 0 ? _options$formatMessag : _ref3 => {
|
|
375
|
+
var {
|
|
376
|
+
defaultErrors
|
|
377
|
+
} = _ref3;
|
|
378
|
+
return defaultErrors;
|
|
379
|
+
};
|
|
380
|
+
return parse(formData, {
|
|
381
|
+
resolve(payload, intent) {
|
|
382
|
+
var error = {};
|
|
383
|
+
var constraintPattern = /^constraint[A-Z][^A-Z]*$/;
|
|
384
|
+
var _loop2 = function _loop2(element) {
|
|
385
|
+
if (isFieldElement(element)) {
|
|
386
|
+
var _options$acceptMultip, _options$acceptMultip2;
|
|
387
|
+
var _name3 = element.name === '__form__' ? '' : element.name;
|
|
388
|
+
var constraint = Object.entries(element.dataset).reduce((result, _ref4) => {
|
|
389
|
+
var [name, attributeValue = ''] = _ref4;
|
|
390
|
+
if (constraintPattern.test(name)) {
|
|
391
|
+
var _options$constraint;
|
|
392
|
+
var constraintName = name.slice(10).toLowerCase();
|
|
393
|
+
var _validate = (_options$constraint = options.constraint) === null || _options$constraint === void 0 ? void 0 : _options$constraint[constraintName];
|
|
394
|
+
if (typeof _validate === 'function') {
|
|
395
|
+
result[constraintName] = _validate(element.value, {
|
|
396
|
+
formData,
|
|
397
|
+
attributeValue
|
|
398
|
+
});
|
|
399
|
+
} else {
|
|
400
|
+
console.warn("Found an \"".concat(constraintName, "\" constraint with undefined definition; Please specify it on the validateConstraint API."));
|
|
401
|
+
}
|
|
402
|
+
}
|
|
403
|
+
return result;
|
|
404
|
+
}, {});
|
|
405
|
+
var errors = formatMessages({
|
|
406
|
+
name: _name3,
|
|
407
|
+
validity: element.validity,
|
|
408
|
+
constraint,
|
|
409
|
+
defaultErrors: getDefaultErrors(element.validity, constraint)
|
|
410
|
+
});
|
|
411
|
+
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, {
|
|
412
|
+
name: _name3,
|
|
413
|
+
payload,
|
|
414
|
+
intent
|
|
415
|
+
})) !== null && _options$acceptMultip !== void 0 ? _options$acceptMultip : false;
|
|
416
|
+
if (errors.length > 0) {
|
|
417
|
+
error[_name3] = shouldAcceptMultipleErrors ? errors : errors[0];
|
|
418
|
+
}
|
|
419
|
+
}
|
|
420
|
+
};
|
|
421
|
+
for (var element of options.form.elements) {
|
|
422
|
+
_loop2(element);
|
|
423
|
+
}
|
|
424
|
+
return {
|
|
425
|
+
error
|
|
426
|
+
};
|
|
427
|
+
}
|
|
428
|
+
});
|
|
429
|
+
}
|
|
430
|
+
|
|
431
|
+
exports.VALIDATION_SKIPPED = VALIDATION_SKIPPED;
|
|
432
|
+
exports.VALIDATION_UNDEFINED = VALIDATION_UNDEFINED;
|
|
319
433
|
exports.focus = focus;
|
|
434
|
+
exports.getErrors = getErrors;
|
|
435
|
+
exports.getFormAttributes = getFormAttributes;
|
|
320
436
|
exports.getFormData = getFormData;
|
|
321
437
|
exports.getFormElement = getFormElement;
|
|
322
438
|
exports.getFormElements = getFormElements;
|
|
323
439
|
exports.getName = getName;
|
|
324
440
|
exports.getPaths = getPaths;
|
|
325
|
-
exports.
|
|
326
|
-
exports.handleList = handleList;
|
|
327
|
-
exports.hasError = hasError;
|
|
441
|
+
exports.getValidationMessage = getValidationMessage;
|
|
328
442
|
exports.isFieldElement = isFieldElement;
|
|
329
443
|
exports.list = list;
|
|
330
444
|
exports.parse = parse;
|
|
331
445
|
exports.parseListCommand = parseListCommand;
|
|
332
446
|
exports.reportSubmission = reportSubmission;
|
|
333
|
-
exports.
|
|
447
|
+
exports.requestIntent = requestIntent;
|
|
334
448
|
exports.requestSubmit = requestSubmit;
|
|
335
449
|
exports.setValue = setValue;
|
|
336
450
|
exports.shouldValidate = shouldValidate;
|
|
337
451
|
exports.updateList = updateList;
|
|
338
452
|
exports.validate = validate;
|
|
453
|
+
exports.validateConstraint = validateConstraint;
|
|
@@ -20,6 +20,7 @@ function _objectSpread2(target) {
|
|
|
20
20
|
return target;
|
|
21
21
|
}
|
|
22
22
|
function _defineProperty(obj, key, value) {
|
|
23
|
+
key = _toPropertyKey(key);
|
|
23
24
|
if (key in obj) {
|
|
24
25
|
Object.defineProperty(obj, key, {
|
|
25
26
|
value: value,
|
|
@@ -32,5 +33,19 @@ function _defineProperty(obj, key, value) {
|
|
|
32
33
|
}
|
|
33
34
|
return obj;
|
|
34
35
|
}
|
|
36
|
+
function _toPrimitive(input, hint) {
|
|
37
|
+
if (typeof input !== "object" || input === null) return input;
|
|
38
|
+
var prim = input[Symbol.toPrimitive];
|
|
39
|
+
if (prim !== undefined) {
|
|
40
|
+
var res = prim.call(input, hint || "default");
|
|
41
|
+
if (typeof res !== "object") return res;
|
|
42
|
+
throw new TypeError("@@toPrimitive must return a primitive value.");
|
|
43
|
+
}
|
|
44
|
+
return (hint === "string" ? String : Number)(input);
|
|
45
|
+
}
|
|
46
|
+
function _toPropertyKey(arg) {
|
|
47
|
+
var key = _toPrimitive(arg, "string");
|
|
48
|
+
return typeof key === "symbol" ? key : String(key);
|
|
49
|
+
}
|
|
35
50
|
|
|
36
|
-
export { _defineProperty as defineProperty, _objectSpread2 as objectSpread2 };
|
|
51
|
+
export { _defineProperty as defineProperty, _objectSpread2 as objectSpread2, _toPrimitive as toPrimitive, _toPropertyKey as toPropertyKey };
|
package/module/index.js
CHANGED
|
@@ -1,5 +1,20 @@
|
|
|
1
1
|
import { objectSpread2 as _objectSpread2 } from './_virtual/_rollupPluginBabelHelpers.js';
|
|
2
2
|
|
|
3
|
+
// type Join<K, P> = P extends string | number ?
|
|
4
|
+
// K extends string | number ?
|
|
5
|
+
// `${K}${"" extends P ? "" : "."}${P}`
|
|
6
|
+
// : never : never;
|
|
7
|
+
|
|
8
|
+
// type DottedPaths<T> = T extends object ?
|
|
9
|
+
// { [K in keyof T]-?: K extends string | number ?
|
|
10
|
+
// `${K}` | Join<K, DottedPaths<T[K]>>
|
|
11
|
+
// : never
|
|
12
|
+
// }[keyof T] : ""
|
|
13
|
+
|
|
14
|
+
// type Pathfix<T> = T extends `${infer Prefix}.${number}${infer Postfix}` ? `${Prefix}[${number}]${Pathfix<Postfix>}` : T;
|
|
15
|
+
|
|
16
|
+
// type Path<Schema> = Pathfix<DottedPaths<Schema>> | '';
|
|
17
|
+
|
|
3
18
|
function isFieldElement(element) {
|
|
4
19
|
return element instanceof Element && (element.tagName === 'INPUT' || element.tagName === 'SELECT' || element.tagName === 'TEXTAREA' || element.tagName === 'BUTTON');
|
|
5
20
|
}
|
|
@@ -29,6 +44,18 @@ function getFormData(form, submitter) {
|
|
|
29
44
|
}
|
|
30
45
|
return payload;
|
|
31
46
|
}
|
|
47
|
+
function getFormAttributes(form, submitter) {
|
|
48
|
+
var _ref, _submitter$getAttribu, _ref2, _submitter$getAttribu2, _submitter$getAttribu3;
|
|
49
|
+
var enforce = (value, list) => list.includes(value) ? value : list[0];
|
|
50
|
+
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);
|
|
51
|
+
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';
|
|
52
|
+
var encType = (_submitter$getAttribu3 = submitter === null || submitter === void 0 ? void 0 : submitter.getAttribute('formenctype')) !== null && _submitter$getAttribu3 !== void 0 ? _submitter$getAttribu3 : form.enctype;
|
|
53
|
+
return {
|
|
54
|
+
action,
|
|
55
|
+
encType: enforce(encType, ['application/x-www-form-urlencoded', 'multipart/form-data']),
|
|
56
|
+
method: enforce(method, ['get', 'post', 'put', 'patch', 'delete'])
|
|
57
|
+
};
|
|
58
|
+
}
|
|
32
59
|
function getName(paths) {
|
|
33
60
|
return paths.reduce((name, path) => {
|
|
34
61
|
if (typeof path === 'number') {
|
|
@@ -40,57 +67,70 @@ function getName(paths) {
|
|
|
40
67
|
return [name, path].join('.');
|
|
41
68
|
}, '');
|
|
42
69
|
}
|
|
43
|
-
function shouldValidate(
|
|
44
|
-
|
|
70
|
+
function shouldValidate(intent, name) {
|
|
71
|
+
var _parseListCommand;
|
|
72
|
+
var [type] = intent.split('/', 1);
|
|
73
|
+
switch (type) {
|
|
74
|
+
case 'validate':
|
|
75
|
+
return intent === 'validate' || intent === "validate/".concat(name);
|
|
76
|
+
case 'list':
|
|
77
|
+
return ((_parseListCommand = parseListCommand(intent)) === null || _parseListCommand === void 0 ? void 0 : _parseListCommand.scope) === name;
|
|
78
|
+
default:
|
|
79
|
+
return true;
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
function getValidationMessage(errors) {
|
|
83
|
+
return [].concat(errors !== null && errors !== void 0 ? errors : []).join(String.fromCharCode(31));
|
|
45
84
|
}
|
|
46
|
-
function
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
85
|
+
function getErrors(message) {
|
|
86
|
+
if (!message) {
|
|
87
|
+
return [];
|
|
88
|
+
}
|
|
89
|
+
return message.split(String.fromCharCode(31));
|
|
51
90
|
}
|
|
91
|
+
var VALIDATION_UNDEFINED = '__undefined__';
|
|
92
|
+
var VALIDATION_SKIPPED = '__skipped__';
|
|
52
93
|
function reportSubmission(form, submission) {
|
|
53
|
-
var
|
|
54
|
-
|
|
55
|
-
if (
|
|
56
|
-
|
|
57
|
-
|
|
94
|
+
for (var [_name, message] of Object.entries(submission.error)) {
|
|
95
|
+
// There is no need to create a placeholder button if all we want is to reset the error
|
|
96
|
+
if (message === '') {
|
|
97
|
+
continue;
|
|
98
|
+
}
|
|
58
99
|
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
100
|
+
// We can't use empty string as button name
|
|
101
|
+
// As `form.element.namedItem('')` will always returns null
|
|
102
|
+
var elementName = _name ? _name : '__form__';
|
|
103
|
+
var item = form.elements.namedItem(elementName);
|
|
104
|
+
if (item instanceof RadioNodeList) {
|
|
105
|
+
for (var field of item) {
|
|
106
|
+
if (field.type !== 'radio') {
|
|
107
|
+
console.warn('Repeated field name is not supported.');
|
|
108
|
+
continue;
|
|
68
109
|
}
|
|
69
110
|
}
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
}
|
|
111
|
+
}
|
|
112
|
+
if (item === null) {
|
|
113
|
+
// Create placeholder button to keep the error without contributing to the form data
|
|
114
|
+
var button = document.createElement('button');
|
|
115
|
+
button.name = elementName;
|
|
116
|
+
button.hidden = true;
|
|
117
|
+
button.dataset.conformTouched = 'true';
|
|
118
|
+
form.appendChild(button);
|
|
79
119
|
}
|
|
80
120
|
}
|
|
81
121
|
for (var element of form.elements) {
|
|
82
122
|
if (isFieldElement(element) && element.willValidate) {
|
|
83
123
|
var _elementName = element.name !== '__form__' ? element.name : '';
|
|
84
|
-
var _message =
|
|
85
|
-
var elementShouldValidate = shouldValidate(submission, _elementName);
|
|
124
|
+
var _message = submission.error[_elementName];
|
|
125
|
+
var elementShouldValidate = shouldValidate(submission.intent, _elementName);
|
|
86
126
|
if (elementShouldValidate) {
|
|
87
127
|
element.dataset.conformTouched = 'true';
|
|
88
128
|
}
|
|
89
|
-
if (typeof _message
|
|
129
|
+
if (typeof _message === 'undefined' || ![].concat(_message).includes(VALIDATION_SKIPPED)) {
|
|
90
130
|
var invalidEvent = new Event('invalid', {
|
|
91
131
|
cancelable: true
|
|
92
132
|
});
|
|
93
|
-
element.setCustomValidity(_message
|
|
133
|
+
element.setCustomValidity(getValidationMessage(_message));
|
|
94
134
|
element.dispatchEvent(invalidEvent);
|
|
95
135
|
}
|
|
96
136
|
if (elementShouldValidate && !element.validity.valid) {
|
|
@@ -129,15 +169,15 @@ function requestSubmit(form, submitter) {
|
|
|
129
169
|
}
|
|
130
170
|
|
|
131
171
|
/**
|
|
132
|
-
* Creates
|
|
172
|
+
* Creates an intent button on demand and trigger a form submit by clicking it.
|
|
133
173
|
*/
|
|
134
|
-
function
|
|
174
|
+
function requestIntent(form, buttonProps) {
|
|
135
175
|
if (!form) {
|
|
136
176
|
console.warn('No form element is provided');
|
|
137
177
|
return;
|
|
138
178
|
}
|
|
139
179
|
var button = document.createElement('button');
|
|
140
|
-
button.name =
|
|
180
|
+
button.name = '__intent__';
|
|
141
181
|
button.value = buttonProps.value;
|
|
142
182
|
button.hidden = true;
|
|
143
183
|
if (buttonProps.formNoValidate) {
|
|
@@ -155,8 +195,8 @@ function requestCommand(form, buttonProps) {
|
|
|
155
195
|
*/
|
|
156
196
|
function validate(field) {
|
|
157
197
|
return {
|
|
158
|
-
name: '
|
|
159
|
-
value: field
|
|
198
|
+
name: '__intent__',
|
|
199
|
+
value: field ? "validate/".concat(field) : 'validate',
|
|
160
200
|
formNoValidate: true
|
|
161
201
|
};
|
|
162
202
|
}
|
|
@@ -174,70 +214,80 @@ function focus(field) {
|
|
|
174
214
|
}
|
|
175
215
|
field.focus();
|
|
176
216
|
}
|
|
177
|
-
function
|
|
178
|
-
var prefix = 'conform/';
|
|
179
|
-
if (!name.startsWith(prefix) || name.length <= prefix.length) {
|
|
180
|
-
return null;
|
|
181
|
-
}
|
|
182
|
-
return name.slice(prefix.length);
|
|
183
|
-
}
|
|
184
|
-
function parse(payload) {
|
|
185
|
-
var hasCommand = false;
|
|
217
|
+
function parse(payload, options) {
|
|
186
218
|
var submission = {
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
error:
|
|
219
|
+
intent: 'submit',
|
|
220
|
+
payload: {},
|
|
221
|
+
error: {}
|
|
190
222
|
};
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
if (typeof value !== 'string') {
|
|
196
|
-
throw new Error('The conform command could not be used on a file input');
|
|
197
|
-
}
|
|
198
|
-
if (hasCommand) {
|
|
199
|
-
throw new Error('The conform command could only be set on a button');
|
|
200
|
-
}
|
|
201
|
-
submission = _objectSpread2(_objectSpread2({}, submission), {}, {
|
|
202
|
-
type: submissionType,
|
|
203
|
-
intent: value
|
|
204
|
-
});
|
|
205
|
-
hasCommand = true;
|
|
206
|
-
} else {
|
|
207
|
-
var paths = getPaths(_name2);
|
|
208
|
-
setValue(submission.value, paths, prev => {
|
|
209
|
-
if (!prev) {
|
|
210
|
-
return value;
|
|
211
|
-
} else if (Array.isArray(prev)) {
|
|
212
|
-
return prev.concat(value);
|
|
213
|
-
} else {
|
|
214
|
-
return [prev, value];
|
|
215
|
-
}
|
|
216
|
-
});
|
|
223
|
+
var _loop = function _loop(_value) {
|
|
224
|
+
if (_name2 === '__intent__') {
|
|
225
|
+
if (typeof _value !== 'string' || submission.intent !== 'submit') {
|
|
226
|
+
throw new Error('The intent could only be set on a button');
|
|
217
227
|
}
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
228
|
+
submission.intent = _value;
|
|
229
|
+
} else {
|
|
230
|
+
var _paths = getPaths(_name2);
|
|
231
|
+
setValue(submission.payload, _paths, prev => {
|
|
232
|
+
if (!prev) {
|
|
233
|
+
return _value;
|
|
234
|
+
} else if (Array.isArray(prev)) {
|
|
235
|
+
return prev.concat(_value);
|
|
236
|
+
} else {
|
|
237
|
+
return [prev, _value];
|
|
238
|
+
}
|
|
239
|
+
});
|
|
226
240
|
}
|
|
227
|
-
}
|
|
228
|
-
|
|
241
|
+
};
|
|
242
|
+
for (var [_name2, _value] of payload.entries()) {
|
|
243
|
+
_loop(_value);
|
|
244
|
+
}
|
|
245
|
+
var command = parseListCommand(submission.intent);
|
|
246
|
+
if (command) {
|
|
247
|
+
var paths = getPaths(command.scope);
|
|
248
|
+
setValue(submission.payload, paths, list => {
|
|
249
|
+
if (typeof list !== 'undefined' && !Array.isArray(list)) {
|
|
250
|
+
throw new Error('The list command can only be applied to a list');
|
|
251
|
+
}
|
|
252
|
+
return updateList(list !== null && list !== void 0 ? list : [], command);
|
|
253
|
+
});
|
|
254
|
+
}
|
|
255
|
+
if (typeof (options === null || options === void 0 ? void 0 : options.resolve) === 'undefined') {
|
|
256
|
+
return submission;
|
|
229
257
|
}
|
|
230
|
-
|
|
258
|
+
var result = options.resolve(submission.payload, submission.intent);
|
|
259
|
+
var mergeResolveResult = resolved => {
|
|
260
|
+
var result = _objectSpread2(_objectSpread2(_objectSpread2({}, submission), resolved), {}, {
|
|
261
|
+
toJSON() {
|
|
262
|
+
return {
|
|
263
|
+
intent: this.intent,
|
|
264
|
+
payload: this.payload,
|
|
265
|
+
error: this.error
|
|
266
|
+
};
|
|
267
|
+
}
|
|
268
|
+
});
|
|
269
|
+
return result;
|
|
270
|
+
};
|
|
271
|
+
if (result instanceof Promise) {
|
|
272
|
+
return result.then(mergeResolveResult);
|
|
273
|
+
}
|
|
274
|
+
return mergeResolveResult(result);
|
|
231
275
|
}
|
|
232
|
-
function parseListCommand(
|
|
276
|
+
function parseListCommand(intent) {
|
|
233
277
|
try {
|
|
234
|
-
var
|
|
235
|
-
if (
|
|
236
|
-
|
|
278
|
+
var [group, type, scope, json] = intent.split('/');
|
|
279
|
+
if (group !== 'list' || !['prepend', 'append', 'replace', 'remove', 'reorder'].includes(type) || !scope) {
|
|
280
|
+
return null;
|
|
237
281
|
}
|
|
238
|
-
|
|
282
|
+
var _payload = JSON.parse(json);
|
|
283
|
+
return {
|
|
284
|
+
// @ts-expect-error
|
|
285
|
+
type,
|
|
286
|
+
scope,
|
|
287
|
+
payload: _payload
|
|
288
|
+
};
|
|
239
289
|
} catch (error) {
|
|
240
|
-
|
|
290
|
+
return null;
|
|
241
291
|
}
|
|
242
292
|
}
|
|
243
293
|
function updateList(list, command) {
|
|
@@ -268,21 +318,6 @@ function updateList(list, command) {
|
|
|
268
318
|
}
|
|
269
319
|
return list;
|
|
270
320
|
}
|
|
271
|
-
function handleList(submission) {
|
|
272
|
-
var _submission$intent;
|
|
273
|
-
if (submission.type !== 'list') {
|
|
274
|
-
return submission;
|
|
275
|
-
}
|
|
276
|
-
var command = parseListCommand((_submission$intent = submission.intent) !== null && _submission$intent !== void 0 ? _submission$intent : '');
|
|
277
|
-
var paths = getPaths(command.scope);
|
|
278
|
-
setValue(submission.value, paths, list => {
|
|
279
|
-
if (typeof list !== 'undefined' && !Array.isArray(list)) {
|
|
280
|
-
throw new Error('The list command can only be applied to a list');
|
|
281
|
-
}
|
|
282
|
-
return updateList(list !== null && list !== void 0 ? list : [], command);
|
|
283
|
-
});
|
|
284
|
-
return submission;
|
|
285
|
-
}
|
|
286
321
|
/**
|
|
287
322
|
* Helpers to configure a command button for modifying a list
|
|
288
323
|
*
|
|
@@ -299,12 +334,8 @@ var list = new Proxy({}, {
|
|
|
299
334
|
return function (scope) {
|
|
300
335
|
var payload = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};
|
|
301
336
|
return {
|
|
302
|
-
name: '
|
|
303
|
-
value: JSON.stringify(
|
|
304
|
-
type,
|
|
305
|
-
scope,
|
|
306
|
-
payload
|
|
307
|
-
}),
|
|
337
|
+
name: '__intent__',
|
|
338
|
+
value: "list/".concat(type, "/").concat(scope, "/").concat(JSON.stringify(payload)),
|
|
308
339
|
formNoValidate: true
|
|
309
340
|
};
|
|
310
341
|
};
|
|
@@ -312,4 +343,85 @@ var list = new Proxy({}, {
|
|
|
312
343
|
}
|
|
313
344
|
});
|
|
314
345
|
|
|
315
|
-
|
|
346
|
+
/**
|
|
347
|
+
* Validate the form with the Constraint Validation API
|
|
348
|
+
* @see https://conform.guide/api/react#validateconstraint
|
|
349
|
+
*/
|
|
350
|
+
function validateConstraint(options) {
|
|
351
|
+
var _options$formData, _options$formatMessag;
|
|
352
|
+
var formData = (_options$formData = options === null || options === void 0 ? void 0 : options.formData) !== null && _options$formData !== void 0 ? _options$formData : new FormData(options.form);
|
|
353
|
+
var getDefaultErrors = (validity, result) => {
|
|
354
|
+
var errors = [];
|
|
355
|
+
if (validity.valueMissing) errors.push('required');
|
|
356
|
+
if (validity.typeMismatch || validity.badInput) errors.push('type');
|
|
357
|
+
if (validity.tooShort) errors.push('minLength');
|
|
358
|
+
if (validity.rangeUnderflow) errors.push('min');
|
|
359
|
+
if (validity.stepMismatch) errors.push('step');
|
|
360
|
+
if (validity.tooLong) errors.push('maxLength');
|
|
361
|
+
if (validity.rangeOverflow) errors.push('max');
|
|
362
|
+
if (validity.patternMismatch) errors.push('pattern');
|
|
363
|
+
for (var [constraintName, valid] of Object.entries(result)) {
|
|
364
|
+
if (!valid) {
|
|
365
|
+
errors.push(constraintName);
|
|
366
|
+
}
|
|
367
|
+
}
|
|
368
|
+
return errors;
|
|
369
|
+
};
|
|
370
|
+
var formatMessages = (_options$formatMessag = options === null || options === void 0 ? void 0 : options.formatMessages) !== null && _options$formatMessag !== void 0 ? _options$formatMessag : _ref3 => {
|
|
371
|
+
var {
|
|
372
|
+
defaultErrors
|
|
373
|
+
} = _ref3;
|
|
374
|
+
return defaultErrors;
|
|
375
|
+
};
|
|
376
|
+
return parse(formData, {
|
|
377
|
+
resolve(payload, intent) {
|
|
378
|
+
var error = {};
|
|
379
|
+
var constraintPattern = /^constraint[A-Z][^A-Z]*$/;
|
|
380
|
+
var _loop2 = function _loop2(element) {
|
|
381
|
+
if (isFieldElement(element)) {
|
|
382
|
+
var _options$acceptMultip, _options$acceptMultip2;
|
|
383
|
+
var _name3 = element.name === '__form__' ? '' : element.name;
|
|
384
|
+
var constraint = Object.entries(element.dataset).reduce((result, _ref4) => {
|
|
385
|
+
var [name, attributeValue = ''] = _ref4;
|
|
386
|
+
if (constraintPattern.test(name)) {
|
|
387
|
+
var _options$constraint;
|
|
388
|
+
var constraintName = name.slice(10).toLowerCase();
|
|
389
|
+
var _validate = (_options$constraint = options.constraint) === null || _options$constraint === void 0 ? void 0 : _options$constraint[constraintName];
|
|
390
|
+
if (typeof _validate === 'function') {
|
|
391
|
+
result[constraintName] = _validate(element.value, {
|
|
392
|
+
formData,
|
|
393
|
+
attributeValue
|
|
394
|
+
});
|
|
395
|
+
} else {
|
|
396
|
+
console.warn("Found an \"".concat(constraintName, "\" constraint with undefined definition; Please specify it on the validateConstraint API."));
|
|
397
|
+
}
|
|
398
|
+
}
|
|
399
|
+
return result;
|
|
400
|
+
}, {});
|
|
401
|
+
var errors = formatMessages({
|
|
402
|
+
name: _name3,
|
|
403
|
+
validity: element.validity,
|
|
404
|
+
constraint,
|
|
405
|
+
defaultErrors: getDefaultErrors(element.validity, constraint)
|
|
406
|
+
});
|
|
407
|
+
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, {
|
|
408
|
+
name: _name3,
|
|
409
|
+
payload,
|
|
410
|
+
intent
|
|
411
|
+
})) !== null && _options$acceptMultip !== void 0 ? _options$acceptMultip : false;
|
|
412
|
+
if (errors.length > 0) {
|
|
413
|
+
error[_name3] = shouldAcceptMultipleErrors ? errors : errors[0];
|
|
414
|
+
}
|
|
415
|
+
}
|
|
416
|
+
};
|
|
417
|
+
for (var element of options.form.elements) {
|
|
418
|
+
_loop2(element);
|
|
419
|
+
}
|
|
420
|
+
return {
|
|
421
|
+
error
|
|
422
|
+
};
|
|
423
|
+
}
|
|
424
|
+
});
|
|
425
|
+
}
|
|
426
|
+
|
|
427
|
+
export { VALIDATION_SKIPPED, VALIDATION_UNDEFINED, focus, getErrors, getFormAttributes, getFormData, getFormElement, getFormElements, getName, getPaths, getValidationMessage, isFieldElement, list, parse, parseListCommand, reportSubmission, requestIntent, requestSubmit, setValue, shouldValidate, updateList, validate, validateConstraint };
|
package/package.json
CHANGED