@conform-to/dom 1.17.1 → 1.19.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +1 -1
- package/dist/dom.d.ts +13 -9
- package/dist/dom.js +93 -26
- package/dist/dom.mjs +92 -26
- package/dist/form.js +23 -23
- package/dist/form.mjs +24 -24
- package/dist/formdata.d.ts +106 -69
- package/dist/formdata.js +186 -142
- package/dist/formdata.mjs +177 -136
- package/dist/future/index.d.ts +3 -3
- package/dist/future/index.js +9 -7
- package/dist/future/index.mjs +2 -2
- package/dist/index.d.ts +1 -1
- package/dist/index.js +4 -3
- package/dist/index.mjs +1 -1
- package/dist/standard-schema.d.ts +2 -14
- package/dist/standard-schema.js +4 -9
- package/dist/standard-schema.mjs +5 -10
- package/dist/submission.js +11 -11
- package/dist/submission.mjs +12 -12
- package/dist/types.d.ts +40 -20
- package/package.json +1 -1
package/dist/form.mjs
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { objectSpread2 as _objectSpread2 } from './_virtual/_rollupPluginBabelHelpers.mjs';
|
|
2
|
-
import {
|
|
2
|
+
import { appendPath, getPathValue, isPathPrefix, setPathValue, getFormData, parsePath, getRelativePath, formatPath } from './formdata.mjs';
|
|
3
3
|
import { getFormAction, getFormEncType, getFormMethod, isFieldElement, requestSubmit, isDirtyInput, updateField } from './dom.mjs';
|
|
4
4
|
import { generateId, clone, isPlainObject, invariant } from './util.mjs';
|
|
5
5
|
import { serialize, flatten, setListState, setListValue, setState, normalize, INTENT, serializeIntent, root, getSubmissionContext } from './submission.mjs';
|
|
@@ -39,7 +39,7 @@ function getDefaultKey(defaultValue, prefix) {
|
|
|
39
39
|
var [key, value] = _ref2;
|
|
40
40
|
if (Array.isArray(value)) {
|
|
41
41
|
for (var i = 0; i < value.length; i++) {
|
|
42
|
-
result[
|
|
42
|
+
result[appendPath(key, i)] = generateId();
|
|
43
43
|
}
|
|
44
44
|
}
|
|
45
45
|
return result;
|
|
@@ -72,7 +72,7 @@ function handleIntent(meta, intent, fields, initialized) {
|
|
|
72
72
|
validated,
|
|
73
73
|
value
|
|
74
74
|
} = intent.payload;
|
|
75
|
-
var _name2 =
|
|
75
|
+
var _name2 = appendPath(intent.payload.name, intent.payload.index);
|
|
76
76
|
if (typeof value !== 'undefined') {
|
|
77
77
|
updateValue(meta, _name2 !== null && _name2 !== void 0 ? _name2 : '', value);
|
|
78
78
|
}
|
|
@@ -101,8 +101,8 @@ function handleIntent(meta, intent, fields, initialized) {
|
|
|
101
101
|
}
|
|
102
102
|
case 'reset':
|
|
103
103
|
{
|
|
104
|
-
var _name3 =
|
|
105
|
-
var _value =
|
|
104
|
+
var _name3 = appendPath(intent.payload.name, intent.payload.index);
|
|
105
|
+
var _value = getPathValue(meta.defaultValue, _name3);
|
|
106
106
|
updateValue(meta, _name3, _value);
|
|
107
107
|
if (_name3) {
|
|
108
108
|
setState(meta.validated, _name3, () => undefined);
|
|
@@ -137,7 +137,7 @@ function handleIntent(meta, intent, fields, initialized) {
|
|
|
137
137
|
var validatedFields = (_fields$filter = fields === null || fields === void 0 ? void 0 : fields.filter(name => meta.validated[name])) !== null && _fields$filter !== void 0 ? _fields$filter : [];
|
|
138
138
|
meta.error = Object.entries(meta.error).reduce((result, _ref3) => {
|
|
139
139
|
var [name, error] = _ref3;
|
|
140
|
-
if (meta.validated[name] || validatedFields.some(field =>
|
|
140
|
+
if (meta.validated[name] || validatedFields.some(field => isPathPrefix(name, field))) {
|
|
141
141
|
result[name] = error;
|
|
142
142
|
}
|
|
143
143
|
return result;
|
|
@@ -155,8 +155,8 @@ function updateValue(meta, name, value) {
|
|
|
155
155
|
meta.initialValue = clone(meta.initialValue);
|
|
156
156
|
meta.value = clone(meta.value);
|
|
157
157
|
meta.key = clone(meta.key);
|
|
158
|
-
|
|
159
|
-
|
|
158
|
+
setPathValue(meta.initialValue, name, () => value);
|
|
159
|
+
setPathValue(meta.value, name, () => value);
|
|
160
160
|
if (isPlainObject(value) || Array.isArray(value)) {
|
|
161
161
|
setState(meta.key, name, () => undefined);
|
|
162
162
|
Object.assign(meta.key, getDefaultKey(value, name));
|
|
@@ -181,11 +181,11 @@ function createValueProxy(value) {
|
|
|
181
181
|
if (name === '') {
|
|
182
182
|
return val;
|
|
183
183
|
}
|
|
184
|
-
var path =
|
|
185
|
-
var basename =
|
|
186
|
-
var key =
|
|
184
|
+
var path = parsePath(name);
|
|
185
|
+
var basename = formatPath(path.slice(0, -1));
|
|
186
|
+
var key = formatPath(path.slice(-1));
|
|
187
187
|
var parentValue = proxy[basename];
|
|
188
|
-
return
|
|
188
|
+
return getPathValue(parentValue, key);
|
|
189
189
|
});
|
|
190
190
|
}
|
|
191
191
|
function createConstraintProxy(constraint) {
|
|
@@ -193,7 +193,7 @@ function createConstraintProxy(constraint) {
|
|
|
193
193
|
var _result;
|
|
194
194
|
var result = constraint[name];
|
|
195
195
|
if (!result) {
|
|
196
|
-
var path =
|
|
196
|
+
var path = parsePath(name);
|
|
197
197
|
for (var i = path.length - 1; i >= 0; i--) {
|
|
198
198
|
var segment = path[i];
|
|
199
199
|
|
|
@@ -206,7 +206,7 @@ function createConstraintProxy(constraint) {
|
|
|
206
206
|
break;
|
|
207
207
|
}
|
|
208
208
|
}
|
|
209
|
-
var alternative =
|
|
209
|
+
var alternative = formatPath(path);
|
|
210
210
|
if (name !== alternative) {
|
|
211
211
|
result = proxy[alternative];
|
|
212
212
|
}
|
|
@@ -217,11 +217,11 @@ function createConstraintProxy(constraint) {
|
|
|
217
217
|
function createKeyProxy(key) {
|
|
218
218
|
return createStateProxy((name, proxy) => {
|
|
219
219
|
var currentKey = key[name];
|
|
220
|
-
var segments =
|
|
220
|
+
var segments = parsePath(name);
|
|
221
221
|
if (segments.length === 0) {
|
|
222
222
|
return currentKey;
|
|
223
223
|
}
|
|
224
|
-
var parentKey = proxy[
|
|
224
|
+
var parentKey = proxy[formatPath(segments.slice(0, -1))];
|
|
225
225
|
if (typeof parentKey === 'undefined') {
|
|
226
226
|
return currentKey;
|
|
227
227
|
}
|
|
@@ -235,7 +235,7 @@ function createValidProxy(error) {
|
|
|
235
235
|
return keys.length === 0;
|
|
236
236
|
}
|
|
237
237
|
for (var key of keys) {
|
|
238
|
-
if (
|
|
238
|
+
if (isPathPrefix(key, name) && typeof error[key] !== 'undefined') {
|
|
239
239
|
return false;
|
|
240
240
|
}
|
|
241
241
|
}
|
|
@@ -266,7 +266,7 @@ function shouldNotify(prev, next, cache, scope) {
|
|
|
266
266
|
var names = (_scope$name = scope.name) !== null && _scope$name !== void 0 ? _scope$name : [];
|
|
267
267
|
var list = prefixes.length === 0 ? names : Array.from(new Set([...Object.keys(prev), ...Object.keys(next)]));
|
|
268
268
|
var _loop = function _loop(_name4) {
|
|
269
|
-
if (prefixes.length === 0 || names.includes(_name4) || prefixes.some(prefix =>
|
|
269
|
+
if (prefixes.length === 0 || names.includes(_name4) || prefixes.some(prefix => isPathPrefix(_name4, prefix))) {
|
|
270
270
|
var _cache$_name;
|
|
271
271
|
(_cache$_name = cache[_name4]) !== null && _cache$_name !== void 0 ? _cache$_name : cache[_name4] = compareFn(prev[_name4], next[_name4]);
|
|
272
272
|
if (cache[_name4]) {
|
|
@@ -576,13 +576,13 @@ function createFormContext(options) {
|
|
|
576
576
|
switch (intent.type) {
|
|
577
577
|
case 'update':
|
|
578
578
|
{
|
|
579
|
-
var _name5 =
|
|
580
|
-
var baseSegments =
|
|
579
|
+
var _name5 = appendPath(intent.payload.name, intent.payload.index);
|
|
580
|
+
var baseSegments = parsePath(_name5);
|
|
581
581
|
for (var element of formElement.elements) {
|
|
582
582
|
if (isFieldElement(element)) {
|
|
583
583
|
var paths = getRelativePath(element.name, baseSegments);
|
|
584
584
|
if (paths) {
|
|
585
|
-
var value =
|
|
585
|
+
var value = getPathValue(intent.payload.value, paths);
|
|
586
586
|
var inputValue = typeof value === 'string' || Array.isArray(value) && value.every(item => typeof item === 'string') ? value : undefined;
|
|
587
587
|
if (typeof inputValue !== 'undefined' || _name5 === '' && paths.length > 1) {
|
|
588
588
|
updateField(element, {
|
|
@@ -599,10 +599,10 @@ function createFormContext(options) {
|
|
|
599
599
|
}
|
|
600
600
|
case 'reset':
|
|
601
601
|
{
|
|
602
|
-
var prefix =
|
|
602
|
+
var prefix = appendPath(intent.payload.name, intent.payload.index);
|
|
603
603
|
for (var _element of formElement.elements) {
|
|
604
|
-
if (isFieldElement(_element) && _element.name &&
|
|
605
|
-
var _value2 =
|
|
604
|
+
if (isFieldElement(_element) && _element.name && isPathPrefix(_element.name, prefix)) {
|
|
605
|
+
var _value2 = getPathValue(meta.defaultValue, _element.name);
|
|
606
606
|
var defaultValue = typeof _value2 === 'string' || Array.isArray(_value2) && _value2.every(item => typeof item === 'string') ? _value2 : undefined;
|
|
607
607
|
if (typeof defaultValue === 'undefined' && !_element.dataset.conform && 'defaultValue' in _element && !isDirtyInput(_element)) {
|
|
608
608
|
continue;
|
package/dist/formdata.d.ts
CHANGED
|
@@ -1,36 +1,47 @@
|
|
|
1
|
-
import type { FormValue, FieldName, JsonPrimitive,
|
|
2
|
-
import type { StandardSchemaIssue } from './standard-schema';
|
|
1
|
+
import type { FormError, FormValue, FieldName, JsonPrimitive, CustomSerialize, Submission, SubmissionResult, UnknownObject, Serialize, StandardSchemaError, CustomError } from './types';
|
|
3
2
|
export declare const DEFAULT_INTENT_NAME = "__INTENT__";
|
|
3
|
+
/**
|
|
4
|
+
* Returns whether an error payload contains a meaningful value.
|
|
5
|
+
* Empty strings and empty arrays are treated as no error.
|
|
6
|
+
*/
|
|
7
|
+
export declare function hasError<ErrorShape>(error: ErrorShape | null | undefined): error is ErrorShape;
|
|
8
|
+
/**
|
|
9
|
+
* Normalizes a form error object by removing empty error payloads such as
|
|
10
|
+
* empty strings and empty arrays.
|
|
11
|
+
*
|
|
12
|
+
* Returns `null` when no form-level or field-level errors remain.
|
|
13
|
+
*/
|
|
14
|
+
export declare function normalizeFormError<ErrorShape>(error: CustomError<ErrorShape> | null): FormError<ErrorShape> | null;
|
|
4
15
|
/**
|
|
5
16
|
* Construct a form data with the submitter value.
|
|
6
17
|
* It utilizes the submitter argument on the FormData constructor from modern browsers
|
|
7
18
|
* with fallback to append the submitter value in case it is not unsupported.
|
|
8
19
|
*
|
|
9
|
-
*
|
|
20
|
+
* See https://developer.mozilla.org/en-US/docs/Web/API/FormData/FormData#parameters
|
|
10
21
|
*/
|
|
11
22
|
export declare function getFormData(form: HTMLFormElement, submitter?: HTMLElement | null): FormData;
|
|
12
23
|
/**
|
|
13
24
|
* Convert a string path into an array of segments.
|
|
14
25
|
*
|
|
15
|
-
*
|
|
26
|
+
* **Example:**
|
|
16
27
|
* ```js
|
|
17
|
-
*
|
|
18
|
-
*
|
|
19
|
-
*
|
|
28
|
+
* parsePath("object.key"); // → ['object', 'key']
|
|
29
|
+
* parsePath("array[0].content"); // → ['array', 0, 'content']
|
|
30
|
+
* parsePath("todos[]"); // → ['todos', '']
|
|
20
31
|
* ```
|
|
21
32
|
*/
|
|
22
|
-
export declare function
|
|
33
|
+
export declare function parsePath(path: string | undefined): Array<string | number>;
|
|
23
34
|
/**
|
|
24
35
|
* Returns a formatted name from the path segments based on the dot and bracket notation.
|
|
25
36
|
*
|
|
26
|
-
*
|
|
37
|
+
* **Example:**
|
|
27
38
|
* ```js
|
|
28
|
-
*
|
|
29
|
-
*
|
|
30
|
-
*
|
|
39
|
+
* formatPath(['object', 'key']); // → "object.key"
|
|
40
|
+
* formatPath(['array', 0, 'content']); // → "array[0].content"
|
|
41
|
+
* formatPath(['todos', '']); // → "todos[]"
|
|
31
42
|
* ```
|
|
32
43
|
*/
|
|
33
|
-
export declare function
|
|
44
|
+
export declare function formatPath(segments: Readonly<Array<string | number>>): string;
|
|
34
45
|
/**
|
|
35
46
|
* Append one more segment onto an existing path string.
|
|
36
47
|
*
|
|
@@ -39,53 +50,54 @@ export declare function formatPathSegments(segments: Readonly<Array<string | num
|
|
|
39
50
|
* - segment = `number` ⇒ bracket notation "[n]"
|
|
40
51
|
* - segment = `string` ⇒ dot-notation ".prop"
|
|
41
52
|
*/
|
|
42
|
-
export declare function
|
|
53
|
+
export declare function appendPath(path: string | undefined, segment: string | number | undefined): string;
|
|
43
54
|
/**
|
|
44
55
|
* Returns true if `prefix` is a valid leading path of `name`.
|
|
45
56
|
*
|
|
46
|
-
*
|
|
57
|
+
* **Example:**
|
|
47
58
|
* ```js
|
|
48
|
-
*
|
|
49
|
-
*
|
|
50
|
-
*
|
|
51
|
-
*
|
|
52
|
-
*
|
|
59
|
+
* isPathPrefix("foo.bar.baz", "foo.bar") // → true
|
|
60
|
+
* isPathPrefix("foo.bar[3].baz", "foo.bar[3]") // → true
|
|
61
|
+
* isPathPrefix("foo.bar[3].baz", "foo.bar") // → true
|
|
62
|
+
* isPathPrefix("foo.bar[3].baz", "foo.baz") // → false
|
|
63
|
+
* isPathPrefix("foo", "foo.bar") // → false
|
|
53
64
|
* ```
|
|
54
65
|
*/
|
|
55
|
-
export declare function
|
|
66
|
+
export declare function isPathPrefix(name: string, prefix: string): boolean;
|
|
56
67
|
/**
|
|
57
|
-
* Return the segments of `
|
|
68
|
+
* Return the segments of `fullPath` that come after the `basePath` prefix.
|
|
58
69
|
*
|
|
59
|
-
* @param
|
|
60
|
-
* @param basePath
|
|
61
|
-
* @returns The “tail” segments, or `null` if `
|
|
70
|
+
* @param fullPath Full path as a dot/bracket string or array of segments
|
|
71
|
+
* @param basePath Base path as a dot/bracket string or array of segments
|
|
72
|
+
* @returns The “tail” segments, or `null` if `fullPath` isn’t nested under `basePath`
|
|
62
73
|
*
|
|
63
|
-
*
|
|
74
|
+
* **Example:**
|
|
64
75
|
* ```js
|
|
65
76
|
* getRelativePath("foo.bar[0].qux", ["foo","bar"]) // → [0, "qux"]
|
|
66
77
|
* getRelativePath("a.b.c.d", ["a","b"]) // → ["c","d"]
|
|
67
78
|
* getRelativePath("foo", ["foo","bar"]) // → null
|
|
68
79
|
* ```
|
|
69
80
|
*/
|
|
70
|
-
export declare function getRelativePath(
|
|
81
|
+
export declare function getRelativePath(fullPath: string | Array<string | number>, basePath: string | Array<string | number>): Array<string | number> | null;
|
|
71
82
|
/**
|
|
72
83
|
* Assign a value to a target object by following the path segments.
|
|
73
84
|
*/
|
|
74
|
-
export declare function
|
|
85
|
+
export declare function setPathValue<T extends Record<string, any>>(target: T, pathOrSegments: string | Array<string | number>, valueOrFn: unknown | ((current: unknown) => unknown), options?: {
|
|
75
86
|
clone?: boolean;
|
|
76
87
|
silent?: boolean;
|
|
77
88
|
}): T;
|
|
78
89
|
/**
|
|
79
90
|
* Retrive the value from a target object by following the path segments.
|
|
80
91
|
*/
|
|
81
|
-
export declare function
|
|
92
|
+
export declare function getPathValue(target: unknown, pathOrSegments: string | Array<string | number>): unknown;
|
|
82
93
|
/**
|
|
83
94
|
* Parse `FormData` or `URLSearchParams` into a submission object.
|
|
84
95
|
* This function structures the form values based on the naming convention.
|
|
85
96
|
* It also includes all the field names and extracts the intent from the submission.
|
|
86
97
|
*
|
|
87
|
-
*
|
|
88
|
-
*
|
|
98
|
+
* See https://conform.guide/api/react/future/parseSubmission
|
|
99
|
+
*
|
|
100
|
+
* **Example:**
|
|
89
101
|
* ```ts
|
|
90
102
|
* const formData = new FormData();
|
|
91
103
|
*
|
|
@@ -122,8 +134,11 @@ export declare function parseSubmission(formData: FormData | URLSearchParams, op
|
|
|
122
134
|
skipEntry?: (name: string) => boolean;
|
|
123
135
|
/**
|
|
124
136
|
* Whether to strip empty values (empty strings, empty files, arrays with all empty values)
|
|
125
|
-
* from the submission payload. Defaults to `
|
|
126
|
-
*
|
|
137
|
+
* from the submission payload. Defaults to `false`.
|
|
138
|
+
*
|
|
139
|
+
* @deprecated This option will be removed in a future minor release.
|
|
140
|
+
* If you are using a schema library like Zod or Valibot, our integration
|
|
141
|
+
* already strips empty values for you. There is no need to use this option.
|
|
127
142
|
*/
|
|
128
143
|
stripEmptyValues?: boolean;
|
|
129
144
|
}): Submission;
|
|
@@ -133,8 +148,9 @@ export declare function parseSubmission(formData: FormData | URLSearchParams, op
|
|
|
133
148
|
* file inputs cannot be initialized with files.
|
|
134
149
|
* You can specify `keepFiles: true` to keep the files if needed.
|
|
135
150
|
*
|
|
136
|
-
*
|
|
137
|
-
*
|
|
151
|
+
* See https://conform.guide/api/react/future/report
|
|
152
|
+
*
|
|
153
|
+
* **Example:**
|
|
138
154
|
* ```ts
|
|
139
155
|
* // Report the submission with the field errors
|
|
140
156
|
* report(submission, {
|
|
@@ -158,55 +174,61 @@ export declare function parseSubmission(formData: FormData | URLSearchParams, op
|
|
|
158
174
|
* })
|
|
159
175
|
* ```
|
|
160
176
|
*/
|
|
161
|
-
export declare function report<ErrorShape
|
|
177
|
+
export declare function report<ErrorShape>(submission: Submission, options: {
|
|
162
178
|
keepFiles?: false;
|
|
163
|
-
error
|
|
164
|
-
issues?: undefined;
|
|
165
|
-
formErrors?: ErrorShape[];
|
|
166
|
-
fieldErrors?: Record<string, ErrorShape[]>;
|
|
167
|
-
} | null;
|
|
179
|
+
error: CustomError<ErrorShape>;
|
|
168
180
|
value?: Record<string, FormValue> | null;
|
|
169
181
|
hideFields?: string[];
|
|
170
182
|
reset?: boolean;
|
|
171
183
|
}): SubmissionResult<ErrorShape, Exclude<JsonPrimitive | FormDataEntryValue, File>>;
|
|
172
|
-
export declare function report<ErrorShape
|
|
184
|
+
export declare function report<ErrorShape>(submission: Submission, options: {
|
|
173
185
|
keepFiles: true;
|
|
174
|
-
error
|
|
175
|
-
issues?: undefined;
|
|
176
|
-
formErrors?: ErrorShape[];
|
|
177
|
-
fieldErrors?: Record<string, ErrorShape[]>;
|
|
178
|
-
} | null;
|
|
186
|
+
error: CustomError<ErrorShape>;
|
|
179
187
|
value?: Record<string, FormValue> | null;
|
|
180
188
|
hideFields?: string[];
|
|
181
189
|
reset?: boolean;
|
|
182
190
|
}): SubmissionResult<ErrorShape>;
|
|
183
|
-
export declare function report(submission: Submission, options
|
|
191
|
+
export declare function report(submission: Submission, options: {
|
|
184
192
|
keepFiles?: false;
|
|
185
|
-
error
|
|
186
|
-
issues: ReadonlyArray<StandardSchemaIssue>;
|
|
187
|
-
formErrors?: string[];
|
|
188
|
-
fieldErrors?: Record<string, string[]>;
|
|
189
|
-
};
|
|
193
|
+
error: StandardSchemaError;
|
|
190
194
|
value?: Record<string, FormValue> | null;
|
|
191
195
|
hideFields?: string[];
|
|
192
196
|
reset?: boolean;
|
|
193
|
-
}): SubmissionResult<string, Exclude<JsonPrimitive | FormDataEntryValue, File>>;
|
|
197
|
+
}): SubmissionResult<string[], Exclude<JsonPrimitive | FormDataEntryValue, File>>;
|
|
198
|
+
export declare function report(submission: Submission, options: {
|
|
199
|
+
keepFiles: true;
|
|
200
|
+
error: StandardSchemaError;
|
|
201
|
+
value?: Record<string, FormValue> | null;
|
|
202
|
+
hideFields?: string[];
|
|
203
|
+
reset?: boolean;
|
|
204
|
+
}): SubmissionResult<string[]>;
|
|
194
205
|
export declare function report(submission: Submission, options?: {
|
|
206
|
+
keepFiles?: false;
|
|
207
|
+
error?: null;
|
|
208
|
+
value?: Record<string, FormValue> | null;
|
|
209
|
+
hideFields?: string[];
|
|
210
|
+
reset?: boolean;
|
|
211
|
+
}): SubmissionResult<never, Exclude<JsonPrimitive | FormDataEntryValue, File>>;
|
|
212
|
+
export declare function report(submission: Submission, options: {
|
|
195
213
|
keepFiles: true;
|
|
196
|
-
error?:
|
|
197
|
-
issues: ReadonlyArray<StandardSchemaIssue>;
|
|
198
|
-
formErrors?: string[];
|
|
199
|
-
fieldErrors?: Record<string, string[]>;
|
|
200
|
-
};
|
|
214
|
+
error?: null;
|
|
201
215
|
value?: Record<string, FormValue> | null;
|
|
202
216
|
hideFields?: string[];
|
|
203
217
|
reset?: boolean;
|
|
204
|
-
}): SubmissionResult<
|
|
218
|
+
}): SubmissionResult<never>;
|
|
219
|
+
export declare function report<ErrorShape>(submission: Submission, options?: {
|
|
220
|
+
keepFiles?: boolean;
|
|
221
|
+
error?: CustomError<ErrorShape> | null;
|
|
222
|
+
value?: Record<string, FormValue> | null;
|
|
223
|
+
hideFields?: string[];
|
|
224
|
+
reset?: boolean;
|
|
225
|
+
}): SubmissionResult<ErrorShape>;
|
|
205
226
|
/**
|
|
206
227
|
* A utility function that checks whether the current form data differs from the default values.
|
|
207
228
|
*
|
|
208
|
-
*
|
|
209
|
-
*
|
|
229
|
+
* See https://conform.guide/api/react/future/isDirty
|
|
230
|
+
*
|
|
231
|
+
* **Example: Enable a submit button only if the form is dirty**
|
|
210
232
|
*
|
|
211
233
|
* ```tsx
|
|
212
234
|
* const dirty = useFormData(
|
|
@@ -248,19 +270,19 @@ formData: FormData | URLSearchParams | FormValue | null, options?: {
|
|
|
248
270
|
* - Returned as-is
|
|
249
271
|
* - boolean:
|
|
250
272
|
* - true → 'on'
|
|
251
|
-
* - false →
|
|
273
|
+
* - false → null
|
|
252
274
|
* - number / bigint:
|
|
253
275
|
* - Converted to string using `.toString()`
|
|
254
276
|
* - Date:
|
|
255
|
-
* - Converted to
|
|
277
|
+
* - Converted to UTC datetime string without trailing `Z` (e.g. `2026-01-01T12:00:00.000`)
|
|
256
278
|
*/
|
|
257
|
-
serialize?:
|
|
279
|
+
serialize?: CustomSerialize;
|
|
258
280
|
/**
|
|
259
281
|
* A function to exclude specific fields from the comparison.
|
|
260
282
|
* Useful for ignoring hidden inputs like CSRF tokens or internal fields added by frameworks
|
|
261
283
|
* (e.g. Next.js uses hidden inputs to support server actions).
|
|
262
284
|
*
|
|
263
|
-
*
|
|
285
|
+
* **Example:**
|
|
264
286
|
* ```ts
|
|
265
287
|
* isDirty(formData, {
|
|
266
288
|
* skipEntry: (name) => name === 'csrf-token',
|
|
@@ -278,17 +300,31 @@ formData: FormData | URLSearchParams | FormValue | null, options?: {
|
|
|
278
300
|
* - null -> '' (empty string)
|
|
279
301
|
* - boolean -> 'on' | '' (checked semantics)
|
|
280
302
|
* - number | bigint -> value.toString()
|
|
281
|
-
* - Date -> value.toISOString()
|
|
303
|
+
* - Date -> value.toISOString() without trailing `Z`
|
|
282
304
|
* - File -> File
|
|
283
305
|
* - FileList -> File[]
|
|
284
306
|
* - Array -> string[] or File[] if all items serialize to the same kind; otherwise undefined
|
|
285
307
|
* - anything else -> undefined
|
|
286
308
|
*/
|
|
287
|
-
export declare function
|
|
309
|
+
export declare function defaultSerialize(value: unknown): ReturnType<Serialize>;
|
|
310
|
+
/**
|
|
311
|
+
* Recursively serializes a value using the provided serialize function,
|
|
312
|
+
* collapsing empty leaves (`null`, `''`, empty files) to `undefined`
|
|
313
|
+
* and removing empty containers (objects with no remaining keys, empty arrays).
|
|
314
|
+
*
|
|
315
|
+
* When serialize returns `undefined` for a value (i.e. it can't be represented
|
|
316
|
+
* as form data), the raw value is kept and recursed into if it's an object or array.
|
|
317
|
+
*
|
|
318
|
+
* Single-element arrays where the element is a string or undefined are unwrapped
|
|
319
|
+
* to handle the case where a multi-value field (e.g. checkboxes) has only one value.
|
|
320
|
+
*/
|
|
321
|
+
export declare function normalize(value: unknown, serialize?: Serialize, name?: string): unknown;
|
|
288
322
|
/**
|
|
289
323
|
* Retrieve a field value from FormData with optional type guards.
|
|
290
324
|
*
|
|
291
|
-
*
|
|
325
|
+
* **Example:**
|
|
326
|
+
*
|
|
327
|
+
* ```ts
|
|
292
328
|
* // Basic field access: return `unknown`
|
|
293
329
|
* const email = getFieldValue(formData, 'email');
|
|
294
330
|
* // String type: returns `string`
|
|
@@ -303,6 +339,7 @@ export declare function serialize(value: unknown): SerializedValue | null | unde
|
|
|
303
339
|
* const items = getFieldValue<Item[]>(formData, 'items', { type: 'object', array: true });
|
|
304
340
|
* // Optional string type: returns `string | undefined`
|
|
305
341
|
* const bio = getFieldValue(formData, 'bio', { type: 'string', optional: true });
|
|
342
|
+
* ```
|
|
306
343
|
*/
|
|
307
344
|
export declare function getFieldValue<FieldShape extends Array<Record<string, unknown>>>(formData: FormData | URLSearchParams, name: FieldName<FieldShape>, options: {
|
|
308
345
|
type: 'object';
|