@conform-to/react 1.9.0 → 1.9.1
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 +11 -11
- package/dist/future/state.d.ts +2 -1
- package/dist/future/state.js +37 -19
- package/dist/future/state.mjs +36 -19
- package/dist/future/types.d.ts +18 -4
- package/package.json +2 -2
package/README.md
CHANGED
|
@@ -7,23 +7,23 @@
|
|
|
7
7
|
╚══════╝ ╚═════╝ ╚═╝ ╚══╝ ╚═╝ ╚═════╝ ╚═╝ ╚═╝ ╚═╝ ╚═╝
|
|
8
8
|
```
|
|
9
9
|
|
|
10
|
-
Version 1.9.
|
|
10
|
+
Version 1.9.1 / License MIT / Copyright (c) 2025 Edmund Hung
|
|
11
11
|
|
|
12
|
-
|
|
12
|
+
Progressively enhance HTML forms with React. Build resilient, type-safe forms with no hassle using web standards.
|
|
13
13
|
|
|
14
|
-
|
|
14
|
+
## Getting Started
|
|
15
15
|
|
|
16
16
|
Check out the overview and tutorial at our website https://conform.guide
|
|
17
17
|
|
|
18
|
-
|
|
18
|
+
## Features
|
|
19
19
|
|
|
20
|
-
-
|
|
21
|
-
-
|
|
22
|
-
-
|
|
23
|
-
-
|
|
24
|
-
-
|
|
20
|
+
- Full type safety with schema field inference
|
|
21
|
+
- Standard Schema support with enhanced Zod and Valibot integration
|
|
22
|
+
- Progressive enhancement first design with built-in accessibility features
|
|
23
|
+
- Native Server Actions support for Remix and Next.js
|
|
24
|
+
- Built on web standards for flexible composition with other tools
|
|
25
25
|
|
|
26
|
-
|
|
26
|
+
## Documentation
|
|
27
27
|
|
|
28
28
|
- Validation: https://conform.guide/validation
|
|
29
29
|
- Nested object and Array: https://conform.guide/complex-structures
|
|
@@ -31,6 +31,6 @@ Check out the overview and tutorial at our website https://conform.guide
|
|
|
31
31
|
- Intent button: https://conform.guide/intent-button
|
|
32
32
|
- Accessibility Guide: https://conform.guide/accessibility
|
|
33
33
|
|
|
34
|
-
|
|
34
|
+
## Support
|
|
35
35
|
|
|
36
36
|
To report a bug, please open an issue on the repository at https://github.com/edmundhung/conform. For feature requests and questions, you can post them in the Discussions section.
|
package/dist/future/state.d.ts
CHANGED
|
@@ -23,7 +23,8 @@ export declare function isDefaultChecked(context: FormContext<any>, name: string
|
|
|
23
23
|
export declare function isTouched(state: FormState<any>, name?: string): boolean;
|
|
24
24
|
export declare function getDefaultListKey(prefix: string, initialValue: Record<string, unknown> | null, name: string): string[];
|
|
25
25
|
export declare function getListKey(context: FormContext<any>, name: string): string[];
|
|
26
|
-
export declare function
|
|
26
|
+
export declare function getErrors<ErrorShape>(state: FormState<ErrorShape>, name?: string): ErrorShape[] | undefined;
|
|
27
|
+
export declare function getFieldErrors<ErrorShape>(state: FormState<ErrorShape>, name?: string): Record<string, ErrorShape[]>;
|
|
27
28
|
/**
|
|
28
29
|
* Gets validation constraint for a field, with fallback to parent array patterns.
|
|
29
30
|
* e.g. "array[0].key" falls back to "array[].key" if specific constraint not found.
|
package/dist/future/state.js
CHANGED
|
@@ -119,7 +119,7 @@ function getListKey(context, name) {
|
|
|
119
119
|
var _context$state$listKe, _context$state$listKe2, _context$state$intend4;
|
|
120
120
|
return (_context$state$listKe = (_context$state$listKe2 = context.state.listKeys) === null || _context$state$listKe2 === void 0 ? void 0 : _context$state$listKe2[name]) !== null && _context$state$listKe !== void 0 ? _context$state$listKe : getDefaultListKey(context.state.resetKey, (_context$state$intend4 = context.state.intendedValue) !== null && _context$state$intend4 !== void 0 ? _context$state$intend4 : context.defaultValue, name);
|
|
121
121
|
}
|
|
122
|
-
function
|
|
122
|
+
function getErrors(state, name) {
|
|
123
123
|
var _state$serverError;
|
|
124
124
|
var error = (_state$serverError = state.serverError) !== null && _state$serverError !== void 0 ? _state$serverError : state.clientError;
|
|
125
125
|
if (!error || !isTouched(state, name)) {
|
|
@@ -130,6 +130,21 @@ function getError(state, name) {
|
|
|
130
130
|
return errors;
|
|
131
131
|
}
|
|
132
132
|
}
|
|
133
|
+
function getFieldErrors(state, name) {
|
|
134
|
+
var result = {};
|
|
135
|
+
var basePath = future.getPathSegments(name);
|
|
136
|
+
for (var field of state.touchedFields) {
|
|
137
|
+
var relativePath = future.getRelativePath(field, basePath);
|
|
138
|
+
if (!relativePath || relativePath.length === 0) {
|
|
139
|
+
continue;
|
|
140
|
+
}
|
|
141
|
+
var error = getErrors(state, field);
|
|
142
|
+
if (typeof error !== 'undefined') {
|
|
143
|
+
result[future.formatPathSegments(relativePath)] = error;
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
return result;
|
|
147
|
+
}
|
|
133
148
|
|
|
134
149
|
/**
|
|
135
150
|
* Gets validation constraint for a field, with fallback to parent array patterns.
|
|
@@ -160,29 +175,24 @@ function getConstraint(context, name) {
|
|
|
160
175
|
}
|
|
161
176
|
function getFormMetadata(context, options) {
|
|
162
177
|
return {
|
|
178
|
+
key: context.state.resetKey,
|
|
163
179
|
id: context.formId,
|
|
180
|
+
errorId: "".concat(context.formId, "-form-error"),
|
|
181
|
+
descriptionId: "".concat(context.formId, "-form-description"),
|
|
164
182
|
get errors() {
|
|
165
|
-
return
|
|
183
|
+
return getErrors(context.state);
|
|
166
184
|
},
|
|
167
185
|
get fieldErrors() {
|
|
168
|
-
|
|
169
|
-
for (var name of context.state.touchedFields) {
|
|
170
|
-
if (!name) {
|
|
171
|
-
// Skip form-level errors
|
|
172
|
-
continue;
|
|
173
|
-
}
|
|
174
|
-
var error = getError(context.state, name);
|
|
175
|
-
if (typeof error !== 'undefined') {
|
|
176
|
-
result[name] = error;
|
|
177
|
-
}
|
|
178
|
-
}
|
|
179
|
-
return result;
|
|
186
|
+
return getFieldErrors(context.state);
|
|
180
187
|
},
|
|
181
188
|
get touched() {
|
|
182
189
|
return isTouched(context.state);
|
|
183
190
|
},
|
|
191
|
+
get valid() {
|
|
192
|
+
return typeof getErrors(context.state) === 'undefined';
|
|
193
|
+
},
|
|
184
194
|
get invalid() {
|
|
185
|
-
return
|
|
195
|
+
return !this.valid;
|
|
186
196
|
},
|
|
187
197
|
props: {
|
|
188
198
|
id: context.formId,
|
|
@@ -210,12 +220,13 @@ function getFormMetadata(context, options) {
|
|
|
210
220
|
};
|
|
211
221
|
}
|
|
212
222
|
function getField(context, options) {
|
|
213
|
-
var id = "".concat(context.formId, "-").concat(options.name);
|
|
223
|
+
var id = "".concat(context.formId, "-field-").concat(options.name.replace(/[^a-zA-Z0-9._-]/g, '_'));
|
|
214
224
|
var constraint = getConstraint(context, options.name);
|
|
215
225
|
var metadata = {
|
|
216
226
|
id: id,
|
|
217
227
|
descriptionId: "".concat(id, "-description"),
|
|
218
228
|
errorId: "".concat(id, "-error"),
|
|
229
|
+
formId: context.formId,
|
|
219
230
|
required: constraint === null || constraint === void 0 ? void 0 : constraint.required,
|
|
220
231
|
minLength: constraint === null || constraint === void 0 ? void 0 : constraint.minLength,
|
|
221
232
|
maxLength: constraint === null || constraint === void 0 ? void 0 : constraint.maxLength,
|
|
@@ -236,11 +247,17 @@ function getField(context, options) {
|
|
|
236
247
|
get touched() {
|
|
237
248
|
return isTouched(context.state, options.name);
|
|
238
249
|
},
|
|
250
|
+
get valid() {
|
|
251
|
+
return typeof getErrors(context.state, options.name) === 'undefined';
|
|
252
|
+
},
|
|
239
253
|
get invalid() {
|
|
240
|
-
return
|
|
254
|
+
return !this.valid;
|
|
241
255
|
},
|
|
242
256
|
get errors() {
|
|
243
|
-
return
|
|
257
|
+
return getErrors(context.state, options.name);
|
|
258
|
+
},
|
|
259
|
+
get fieldErrors() {
|
|
260
|
+
return getFieldErrors(context.state, options.name);
|
|
244
261
|
}
|
|
245
262
|
};
|
|
246
263
|
return Object.assign(metadata, {
|
|
@@ -288,8 +305,9 @@ exports.getConstraint = getConstraint;
|
|
|
288
305
|
exports.getDefaultListKey = getDefaultListKey;
|
|
289
306
|
exports.getDefaultOptions = getDefaultOptions;
|
|
290
307
|
exports.getDefaultValue = getDefaultValue;
|
|
291
|
-
exports.
|
|
308
|
+
exports.getErrors = getErrors;
|
|
292
309
|
exports.getField = getField;
|
|
310
|
+
exports.getFieldErrors = getFieldErrors;
|
|
293
311
|
exports.getFieldList = getFieldList;
|
|
294
312
|
exports.getFieldset = getFieldset;
|
|
295
313
|
exports.getFormMetadata = getFormMetadata;
|
package/dist/future/state.mjs
CHANGED
|
@@ -115,7 +115,7 @@ function getListKey(context, name) {
|
|
|
115
115
|
var _context$state$listKe, _context$state$listKe2, _context$state$intend4;
|
|
116
116
|
return (_context$state$listKe = (_context$state$listKe2 = context.state.listKeys) === null || _context$state$listKe2 === void 0 ? void 0 : _context$state$listKe2[name]) !== null && _context$state$listKe !== void 0 ? _context$state$listKe : getDefaultListKey(context.state.resetKey, (_context$state$intend4 = context.state.intendedValue) !== null && _context$state$intend4 !== void 0 ? _context$state$intend4 : context.defaultValue, name);
|
|
117
117
|
}
|
|
118
|
-
function
|
|
118
|
+
function getErrors(state, name) {
|
|
119
119
|
var _state$serverError;
|
|
120
120
|
var error = (_state$serverError = state.serverError) !== null && _state$serverError !== void 0 ? _state$serverError : state.clientError;
|
|
121
121
|
if (!error || !isTouched(state, name)) {
|
|
@@ -126,6 +126,21 @@ function getError(state, name) {
|
|
|
126
126
|
return errors;
|
|
127
127
|
}
|
|
128
128
|
}
|
|
129
|
+
function getFieldErrors(state, name) {
|
|
130
|
+
var result = {};
|
|
131
|
+
var basePath = getPathSegments(name);
|
|
132
|
+
for (var field of state.touchedFields) {
|
|
133
|
+
var relativePath = getRelativePath(field, basePath);
|
|
134
|
+
if (!relativePath || relativePath.length === 0) {
|
|
135
|
+
continue;
|
|
136
|
+
}
|
|
137
|
+
var error = getErrors(state, field);
|
|
138
|
+
if (typeof error !== 'undefined') {
|
|
139
|
+
result[formatPathSegments(relativePath)] = error;
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
return result;
|
|
143
|
+
}
|
|
129
144
|
|
|
130
145
|
/**
|
|
131
146
|
* Gets validation constraint for a field, with fallback to parent array patterns.
|
|
@@ -156,29 +171,24 @@ function getConstraint(context, name) {
|
|
|
156
171
|
}
|
|
157
172
|
function getFormMetadata(context, options) {
|
|
158
173
|
return {
|
|
174
|
+
key: context.state.resetKey,
|
|
159
175
|
id: context.formId,
|
|
176
|
+
errorId: "".concat(context.formId, "-form-error"),
|
|
177
|
+
descriptionId: "".concat(context.formId, "-form-description"),
|
|
160
178
|
get errors() {
|
|
161
|
-
return
|
|
179
|
+
return getErrors(context.state);
|
|
162
180
|
},
|
|
163
181
|
get fieldErrors() {
|
|
164
|
-
|
|
165
|
-
for (var name of context.state.touchedFields) {
|
|
166
|
-
if (!name) {
|
|
167
|
-
// Skip form-level errors
|
|
168
|
-
continue;
|
|
169
|
-
}
|
|
170
|
-
var error = getError(context.state, name);
|
|
171
|
-
if (typeof error !== 'undefined') {
|
|
172
|
-
result[name] = error;
|
|
173
|
-
}
|
|
174
|
-
}
|
|
175
|
-
return result;
|
|
182
|
+
return getFieldErrors(context.state);
|
|
176
183
|
},
|
|
177
184
|
get touched() {
|
|
178
185
|
return isTouched(context.state);
|
|
179
186
|
},
|
|
187
|
+
get valid() {
|
|
188
|
+
return typeof getErrors(context.state) === 'undefined';
|
|
189
|
+
},
|
|
180
190
|
get invalid() {
|
|
181
|
-
return
|
|
191
|
+
return !this.valid;
|
|
182
192
|
},
|
|
183
193
|
props: {
|
|
184
194
|
id: context.formId,
|
|
@@ -206,12 +216,13 @@ function getFormMetadata(context, options) {
|
|
|
206
216
|
};
|
|
207
217
|
}
|
|
208
218
|
function getField(context, options) {
|
|
209
|
-
var id = "".concat(context.formId, "-").concat(options.name);
|
|
219
|
+
var id = "".concat(context.formId, "-field-").concat(options.name.replace(/[^a-zA-Z0-9._-]/g, '_'));
|
|
210
220
|
var constraint = getConstraint(context, options.name);
|
|
211
221
|
var metadata = {
|
|
212
222
|
id: id,
|
|
213
223
|
descriptionId: "".concat(id, "-description"),
|
|
214
224
|
errorId: "".concat(id, "-error"),
|
|
225
|
+
formId: context.formId,
|
|
215
226
|
required: constraint === null || constraint === void 0 ? void 0 : constraint.required,
|
|
216
227
|
minLength: constraint === null || constraint === void 0 ? void 0 : constraint.minLength,
|
|
217
228
|
maxLength: constraint === null || constraint === void 0 ? void 0 : constraint.maxLength,
|
|
@@ -232,11 +243,17 @@ function getField(context, options) {
|
|
|
232
243
|
get touched() {
|
|
233
244
|
return isTouched(context.state, options.name);
|
|
234
245
|
},
|
|
246
|
+
get valid() {
|
|
247
|
+
return typeof getErrors(context.state, options.name) === 'undefined';
|
|
248
|
+
},
|
|
235
249
|
get invalid() {
|
|
236
|
-
return
|
|
250
|
+
return !this.valid;
|
|
237
251
|
},
|
|
238
252
|
get errors() {
|
|
239
|
-
return
|
|
253
|
+
return getErrors(context.state, options.name);
|
|
254
|
+
},
|
|
255
|
+
get fieldErrors() {
|
|
256
|
+
return getFieldErrors(context.state, options.name);
|
|
240
257
|
}
|
|
241
258
|
};
|
|
242
259
|
return Object.assign(metadata, {
|
|
@@ -280,4 +297,4 @@ function getFieldList(context, options) {
|
|
|
280
297
|
});
|
|
281
298
|
}
|
|
282
299
|
|
|
283
|
-
export { getConstraint, getDefaultListKey, getDefaultOptions, getDefaultValue,
|
|
300
|
+
export { getConstraint, getDefaultListKey, getDefaultOptions, getDefaultValue, getErrors, getField, getFieldErrors, getFieldList, getFieldset, getFormMetadata, getListKey, initializeState, isDefaultChecked, isTouched, updateState };
|
package/dist/future/types.d.ts
CHANGED
|
@@ -262,15 +262,23 @@ FieldMetadata extends Record<string, unknown>> = {
|
|
|
262
262
|
};
|
|
263
263
|
/** Form-level metadata and state object containing validation status, errors, and field access methods. */
|
|
264
264
|
export type FormMetadata<ErrorShape, FieldMetadata extends Record<string, unknown> = DefaultFieldMetadata<ErrorShape>> = Readonly<{
|
|
265
|
+
/** Unique identifier that changes on form reset */
|
|
266
|
+
key: string;
|
|
265
267
|
/** The form's unique identifier. */
|
|
266
268
|
id: string;
|
|
269
|
+
/** Auto-generated ID for associating form descriptions via aria-describedby. */
|
|
270
|
+
descriptionId: string;
|
|
271
|
+
/** Auto-generated ID for associating form errors via aria-describedby. */
|
|
272
|
+
errorId: string;
|
|
267
273
|
/** Whether any field in the form has been touched (through intent.validate() or the shouldValidate option). */
|
|
268
274
|
touched: boolean;
|
|
269
|
-
/** Whether the form currently has
|
|
275
|
+
/** Whether the form currently has no validation errors. */
|
|
276
|
+
valid: boolean;
|
|
277
|
+
/** @deprecated Use `.valid` instead. This was not an intentionl breaking change and would be removed in the next minor version soon */
|
|
270
278
|
invalid: boolean;
|
|
271
279
|
/** Form-level validation errors, if any exist. */
|
|
272
280
|
errors: ErrorShape[] | undefined;
|
|
273
|
-
/** Object containing
|
|
281
|
+
/** Object containing errors for all touched fields. */
|
|
274
282
|
fieldErrors: Record<string, ErrorShape[]>;
|
|
275
283
|
/** Form props object for spreading onto the <form> element. */
|
|
276
284
|
props: Readonly<{
|
|
@@ -295,12 +303,14 @@ export type FormMetadata<ErrorShape, FieldMetadata extends Record<string, unknow
|
|
|
295
303
|
}>;
|
|
296
304
|
/** Default field metadata object containing field state, validation attributes, and accessibility IDs. */
|
|
297
305
|
export type DefaultFieldMetadata<ErrorShape> = Readonly<ValidationAttributes & {
|
|
298
|
-
/** The field's unique identifier, automatically generated as {formId}-{fieldName}. */
|
|
306
|
+
/** The field's unique identifier, automatically generated as {formId}-field-{fieldName}. */
|
|
299
307
|
id: string;
|
|
300
308
|
/** Auto-generated ID for associating field descriptions via aria-describedby. */
|
|
301
309
|
descriptionId: string;
|
|
302
310
|
/** Auto-generated ID for associating field errors via aria-describedby. */
|
|
303
311
|
errorId: string;
|
|
312
|
+
/** The form's unique identifier for associating field via the `form` attribute. */
|
|
313
|
+
formId: string;
|
|
304
314
|
/** The field's default value as a string. */
|
|
305
315
|
defaultValue: string | undefined;
|
|
306
316
|
/** Default selected options for multi-select fields or checkbox group. */
|
|
@@ -309,10 +319,14 @@ export type DefaultFieldMetadata<ErrorShape> = Readonly<ValidationAttributes & {
|
|
|
309
319
|
defaultChecked: boolean | undefined;
|
|
310
320
|
/** Whether this field has been touched (through intent.validate() or the shouldValidate option). */
|
|
311
321
|
touched: boolean;
|
|
312
|
-
/** Whether this field currently has validation errors. */
|
|
322
|
+
/** Whether this field currently has no validation errors. */
|
|
323
|
+
valid: boolean;
|
|
324
|
+
/** @deprecated Use `.valid` instead. This was not an intentionl breaking change and would be removed in the next minor version soon */
|
|
313
325
|
invalid: boolean;
|
|
314
326
|
/** Array of validation error messages for this field. */
|
|
315
327
|
errors: ErrorShape[] | undefined;
|
|
328
|
+
/** Object containing errors for all touched subfields. */
|
|
329
|
+
fieldErrors: Record<string, ErrorShape[]>;
|
|
316
330
|
}>;
|
|
317
331
|
export type ValidateResult<ErrorShape, Value> = FormError<ErrorShape> | null | {
|
|
318
332
|
error: FormError<ErrorShape> | null;
|
package/package.json
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
"description": "Conform view adapter for react",
|
|
4
4
|
"homepage": "https://conform.guide",
|
|
5
5
|
"license": "MIT",
|
|
6
|
-
"version": "1.9.
|
|
6
|
+
"version": "1.9.1",
|
|
7
7
|
"main": "./dist/index.js",
|
|
8
8
|
"module": "./dist/index.mjs",
|
|
9
9
|
"types": "./dist/index.d.ts",
|
|
@@ -41,7 +41,7 @@
|
|
|
41
41
|
"url": "https://github.com/edmundhung/conform/issues"
|
|
42
42
|
},
|
|
43
43
|
"dependencies": {
|
|
44
|
-
"@conform-to/dom": "1.9.
|
|
44
|
+
"@conform-to/dom": "1.9.1"
|
|
45
45
|
},
|
|
46
46
|
"devDependencies": {
|
|
47
47
|
"@babel/core": "^7.17.8",
|