@conform-to/react 1.15.0 → 1.16.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/future/dom.js +11 -2
- package/dist/future/dom.mjs +12 -3
- package/dist/future/forms.d.ts +43 -0
- package/dist/future/forms.js +322 -0
- package/dist/future/forms.mjs +318 -0
- package/dist/future/hooks.d.ts +25 -4
- package/dist/future/hooks.js +21 -8
- package/dist/future/hooks.mjs +21 -8
- package/dist/future/index.d.ts +3 -1
- package/dist/future/index.js +4 -0
- package/dist/future/index.mjs +2 -0
- package/dist/future/state.d.ts +28 -13
- package/dist/future/state.js +37 -33
- package/dist/future/state.mjs +39 -35
- package/dist/future/types.d.ts +275 -28
- package/dist/future/util.d.ts +44 -1
- package/dist/future/util.js +67 -5
- package/dist/future/util.mjs +64 -6
- package/dist/integrations.d.ts +3 -3
- package/package.json +2 -2
package/README.md
CHANGED
|
@@ -7,7 +7,7 @@
|
|
|
7
7
|
╚══════╝ ╚═════╝ ╚═╝ ╚══╝ ╚═╝ ╚═════╝ ╚═╝ ╚═╝ ╚═╝ ╚═╝
|
|
8
8
|
```
|
|
9
9
|
|
|
10
|
-
Version 1.
|
|
10
|
+
Version 1.16.0 / 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
|
|
package/dist/future/dom.js
CHANGED
|
@@ -29,9 +29,18 @@ function initializeField(element, options) {
|
|
|
29
29
|
}
|
|
30
30
|
var defaultValue = typeof (options === null || options === void 0 ? void 0 : options.value) === 'string' || typeof (options === null || options === void 0 ? void 0 : options.defaultChecked) === 'boolean' ? options.defaultChecked ? (_options$value = options.value) !== null && _options$value !== void 0 ? _options$value : 'on' : null : options === null || options === void 0 ? void 0 : options.defaultValue;
|
|
31
31
|
|
|
32
|
-
//
|
|
32
|
+
// Use change helper to set value and dispatch events
|
|
33
|
+
// This syncs React's internal value tracker so subsequent
|
|
34
|
+
// programmatic changes will properly trigger onChange
|
|
35
|
+
if (defaultValue !== undefined) {
|
|
36
|
+
future.change(element, defaultValue, {
|
|
37
|
+
// To avoid triggering validation on initialization
|
|
38
|
+
preventDefault: true
|
|
39
|
+
});
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
// Set the default value after change to preserve it for form reset
|
|
33
43
|
future.updateField(element, {
|
|
34
|
-
value: defaultValue,
|
|
35
44
|
defaultValue
|
|
36
45
|
});
|
|
37
46
|
element.dataset.conform = 'initialized';
|
package/dist/future/dom.mjs
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { updateField, isGlobalInstance, isFieldElement, requestIntent, getValueAtPath
|
|
1
|
+
import { change, updateField, isGlobalInstance, isFieldElement, requestIntent, getValueAtPath } from '@conform-to/dom/future';
|
|
2
2
|
import { serializeIntent } from './intent.mjs';
|
|
3
3
|
|
|
4
4
|
function getFormElement(formRef) {
|
|
@@ -25,9 +25,18 @@ function initializeField(element, options) {
|
|
|
25
25
|
}
|
|
26
26
|
var defaultValue = typeof (options === null || options === void 0 ? void 0 : options.value) === 'string' || typeof (options === null || options === void 0 ? void 0 : options.defaultChecked) === 'boolean' ? options.defaultChecked ? (_options$value = options.value) !== null && _options$value !== void 0 ? _options$value : 'on' : null : options === null || options === void 0 ? void 0 : options.defaultValue;
|
|
27
27
|
|
|
28
|
-
//
|
|
28
|
+
// Use change helper to set value and dispatch events
|
|
29
|
+
// This syncs React's internal value tracker so subsequent
|
|
30
|
+
// programmatic changes will properly trigger onChange
|
|
31
|
+
if (defaultValue !== undefined) {
|
|
32
|
+
change(element, defaultValue, {
|
|
33
|
+
// To avoid triggering validation on initialization
|
|
34
|
+
preventDefault: true
|
|
35
|
+
});
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
// Set the default value after change to preserve it for form reset
|
|
29
39
|
updateField(element, {
|
|
30
|
-
value: defaultValue,
|
|
31
40
|
defaultValue
|
|
32
41
|
});
|
|
33
42
|
element.dataset.conform = 'initialized';
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import { FieldName } from '@conform-to/dom';
|
|
2
|
+
import { StandardSchemaV1 } from './standard-schema';
|
|
3
|
+
import { FormRef, FormsConfig, FormContext, FormMetadata, FormOptions, Fieldset, FieldMetadata, InferOutput, InferInput, IntentDispatcher } from './types';
|
|
4
|
+
export declare function configureForms<BaseErrorShape = string, BaseSchema = StandardSchemaV1, CustomFormMetadata extends Record<string, unknown> = {}, CustomFieldMetadata extends Record<string, unknown> = {}>(config?: Partial<FormsConfig<BaseErrorShape, BaseSchema, CustomFormMetadata, CustomFieldMetadata>>): {
|
|
5
|
+
FormProvider: (props: {
|
|
6
|
+
context: FormContext<BaseErrorShape>;
|
|
7
|
+
children: React.ReactNode;
|
|
8
|
+
}) => React.ReactElement;
|
|
9
|
+
useForm: {
|
|
10
|
+
<Schema extends BaseSchema, ErrorShape extends BaseErrorShape = BaseErrorShape, Value = InferOutput<Schema>>(schema: Schema, options: FormOptions<InferInput<Schema> extends Record<string, any> ? InferInput<Schema> : never, ErrorShape, Value, Schema, string extends ErrorShape ? never : "onValidate">): {
|
|
11
|
+
form: FormMetadata<ErrorShape, CustomFormMetadata, CustomFieldMetadata>;
|
|
12
|
+
fields: Fieldset<InferInput<Schema>, ErrorShape, CustomFieldMetadata>;
|
|
13
|
+
intent: IntentDispatcher<InferInput<Schema> extends Record<string, any> ? InferInput<Schema> : never>;
|
|
14
|
+
};
|
|
15
|
+
<FormShape extends Record<string, any> = Record<string, any>, ErrorShape extends BaseErrorShape = BaseErrorShape, Value = undefined>(options: FormOptions<FormShape, ErrorShape, Value, undefined, undefined extends Value ? "onValidate" : never> & {
|
|
16
|
+
/**
|
|
17
|
+
* @deprecated Use `useForm(schema, options)` instead for better type inference.
|
|
18
|
+
*
|
|
19
|
+
* Optional standard schema for validation (e.g., Zod, Valibot, Yup).
|
|
20
|
+
* Removes the need for manual onValidate setup.
|
|
21
|
+
*/
|
|
22
|
+
schema: StandardSchemaV1<FormShape, Value>;
|
|
23
|
+
}): {
|
|
24
|
+
form: FormMetadata<ErrorShape, CustomFormMetadata, CustomFieldMetadata>;
|
|
25
|
+
fields: Fieldset<FormShape, ErrorShape, CustomFieldMetadata>;
|
|
26
|
+
intent: IntentDispatcher<FormShape>;
|
|
27
|
+
};
|
|
28
|
+
<FormShape extends Record<string, any> = Record<string, any>, ErrorShape extends BaseErrorShape = BaseErrorShape, Value = undefined>(options: FormOptions<FormShape, ErrorShape, Value, undefined, "onValidate">): {
|
|
29
|
+
form: FormMetadata<ErrorShape, CustomFormMetadata, CustomFieldMetadata>;
|
|
30
|
+
fields: Fieldset<FormShape, ErrorShape, CustomFieldMetadata>;
|
|
31
|
+
intent: IntentDispatcher<FormShape>;
|
|
32
|
+
};
|
|
33
|
+
};
|
|
34
|
+
useFormMetadata: (options?: {
|
|
35
|
+
formId?: string;
|
|
36
|
+
}) => FormMetadata<BaseErrorShape, CustomFormMetadata, CustomFieldMetadata>;
|
|
37
|
+
useField: <FieldShape = any>(name: FieldName<FieldShape>, options?: {
|
|
38
|
+
formId?: string;
|
|
39
|
+
}) => FieldMetadata<FieldShape, BaseErrorShape, CustomFieldMetadata>;
|
|
40
|
+
useIntent: <FormShape extends Record<string, any>>(formRef: FormRef) => IntentDispatcher<FormShape>;
|
|
41
|
+
config: FormsConfig<BaseErrorShape, BaseSchema, CustomFormMetadata, CustomFieldMetadata>;
|
|
42
|
+
};
|
|
43
|
+
//# sourceMappingURL=forms.d.ts.map
|
|
@@ -0,0 +1,322 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
Object.defineProperty(exports, '__esModule', { value: true });
|
|
4
|
+
|
|
5
|
+
var _rollupPluginBabelHelpers = require('../_virtual/_rollupPluginBabelHelpers.js');
|
|
6
|
+
var dom$1 = require('@conform-to/dom');
|
|
7
|
+
var future = require('@conform-to/dom/future');
|
|
8
|
+
var react = require('react');
|
|
9
|
+
var dom = require('./dom.js');
|
|
10
|
+
var hooks = require('./hooks.js');
|
|
11
|
+
var state = require('./state.js');
|
|
12
|
+
var util = require('./util.js');
|
|
13
|
+
var jsxRuntime = require('react/jsx-runtime');
|
|
14
|
+
|
|
15
|
+
function configureForms() {
|
|
16
|
+
var _config$intentName, _config$serialize, _config$shouldValidat, _ref, _config$shouldRevalid, _config$isSchema, _config$validateSchem;
|
|
17
|
+
var config = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};
|
|
18
|
+
/**
|
|
19
|
+
* Global configuration with defaults applied
|
|
20
|
+
*/
|
|
21
|
+
var globalConfig = _rollupPluginBabelHelpers.objectSpread2(_rollupPluginBabelHelpers.objectSpread2({}, config), {}, {
|
|
22
|
+
intentName: (_config$intentName = config.intentName) !== null && _config$intentName !== void 0 ? _config$intentName : future.DEFAULT_INTENT_NAME,
|
|
23
|
+
serialize: (_config$serialize = config.serialize) !== null && _config$serialize !== void 0 ? _config$serialize : future.serialize,
|
|
24
|
+
shouldValidate: (_config$shouldValidat = config.shouldValidate) !== null && _config$shouldValidat !== void 0 ? _config$shouldValidat : 'onSubmit',
|
|
25
|
+
shouldRevalidate: (_ref = (_config$shouldRevalid = config.shouldRevalidate) !== null && _config$shouldRevalid !== void 0 ? _config$shouldRevalid : config.shouldValidate) !== null && _ref !== void 0 ? _ref : 'onSubmit',
|
|
26
|
+
isSchema: (_config$isSchema = config.isSchema) !== null && _config$isSchema !== void 0 ? _config$isSchema : util.isStandardSchemaV1,
|
|
27
|
+
validateSchema: (_config$validateSchem = config.validateSchema) !== null && _config$validateSchem !== void 0 ? _config$validateSchem : util.validateStandardSchemaV1
|
|
28
|
+
});
|
|
29
|
+
|
|
30
|
+
/**
|
|
31
|
+
* React context
|
|
32
|
+
*/
|
|
33
|
+
var ReactFormContext = /*#__PURE__*/react.createContext([]);
|
|
34
|
+
|
|
35
|
+
/**
|
|
36
|
+
* Provides form context to child components.
|
|
37
|
+
* Stacks contexts to support nested forms, with latest context taking priority.
|
|
38
|
+
*/
|
|
39
|
+
function FormProvider(props) {
|
|
40
|
+
var stack = react.useContext(ReactFormContext);
|
|
41
|
+
var value = react.useMemo(
|
|
42
|
+
// Put the latest form context first to ensure that to be the first one found
|
|
43
|
+
() => [props.context].concat(stack), [stack, props.context]);
|
|
44
|
+
return /*#__PURE__*/jsxRuntime.jsx(ReactFormContext.Provider, {
|
|
45
|
+
value: value,
|
|
46
|
+
children: props.children
|
|
47
|
+
});
|
|
48
|
+
}
|
|
49
|
+
function useFormContext(formId) {
|
|
50
|
+
var contexts = react.useContext(ReactFormContext);
|
|
51
|
+
var context = formId ? contexts.find(context => formId === context.formId) : contexts[0];
|
|
52
|
+
if (!context) {
|
|
53
|
+
throw new Error('No form context found. ' + 'Wrap your component with <FormProvider context={form.context}> ' + 'where `form` is returned from useForm().');
|
|
54
|
+
}
|
|
55
|
+
return context;
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
/**
|
|
59
|
+
* The main React hook for form management. Handles form state, validation, and submission
|
|
60
|
+
* while providing access to form metadata, field objects, and form actions.
|
|
61
|
+
*
|
|
62
|
+
* It can be called in two ways:
|
|
63
|
+
* - **Schema first**: Pass a schema as the first argument for automatic validation with type inference
|
|
64
|
+
* - **Manual configuration**: Pass options with custom `onValidate` handler for manual validation
|
|
65
|
+
*
|
|
66
|
+
* @see https://conform.guide/api/react/future/useForm
|
|
67
|
+
* @example Schema first setup with zod:
|
|
68
|
+
*
|
|
69
|
+
* ```tsx
|
|
70
|
+
* const { form, fields } = useForm(zodSchema, {
|
|
71
|
+
* lastResult,
|
|
72
|
+
* shouldValidate: 'onBlur',
|
|
73
|
+
* });
|
|
74
|
+
*
|
|
75
|
+
* return (
|
|
76
|
+
* <form {...form.props}>
|
|
77
|
+
* <input name={fields.email.name} defaultValue={fields.email.defaultValue} />
|
|
78
|
+
* <div>{fields.email.errors}</div>
|
|
79
|
+
* </form>
|
|
80
|
+
* );
|
|
81
|
+
* ```
|
|
82
|
+
*
|
|
83
|
+
* @example Manual configuration setup with custom validation:
|
|
84
|
+
*
|
|
85
|
+
* ```tsx
|
|
86
|
+
* const { form, fields } = useForm({
|
|
87
|
+
* onValidate({ payload, error }) {
|
|
88
|
+
* if (!payload.email) {
|
|
89
|
+
* error.fieldErrors.email = ['Required'];
|
|
90
|
+
* }
|
|
91
|
+
* return error;
|
|
92
|
+
* }
|
|
93
|
+
* });
|
|
94
|
+
*
|
|
95
|
+
* return (
|
|
96
|
+
* <form {...form.props}>
|
|
97
|
+
* <input name={fields.email.name} defaultValue={fields.email.defaultValue} />
|
|
98
|
+
* <div>{fields.email.errors}</div>
|
|
99
|
+
* </form>
|
|
100
|
+
* );
|
|
101
|
+
* ```
|
|
102
|
+
*/
|
|
103
|
+
|
|
104
|
+
/**
|
|
105
|
+
* @deprecated Use `useForm(schema, options)` instead for better type inference.
|
|
106
|
+
*/
|
|
107
|
+
|
|
108
|
+
function useForm(schemaOrOptions, maybeOptions) {
|
|
109
|
+
var _options$constraint, _globalConfig$getCons, _options$id, _options$onError;
|
|
110
|
+
var schema;
|
|
111
|
+
var options;
|
|
112
|
+
if (globalConfig.isSchema(schemaOrOptions)) {
|
|
113
|
+
schema = schemaOrOptions;
|
|
114
|
+
options = maybeOptions !== null && maybeOptions !== void 0 ? maybeOptions : {};
|
|
115
|
+
} else {
|
|
116
|
+
options = schemaOrOptions;
|
|
117
|
+
}
|
|
118
|
+
var constraint = (_options$constraint = options.constraint) !== null && _options$constraint !== void 0 ? _options$constraint : schema ? (_globalConfig$getCons = globalConfig.getConstraints) === null || _globalConfig$getCons === void 0 ? void 0 : _globalConfig$getCons.call(globalConfig, schema) : undefined;
|
|
119
|
+
var optionsRef = hooks.useLatest(options);
|
|
120
|
+
var fallbackId = react.useId();
|
|
121
|
+
var formId = (_options$id = options.id) !== null && _options$id !== void 0 ? _options$id : "form-".concat(fallbackId);
|
|
122
|
+
var [state$1, handleSubmit] = hooks.useConform(formId, _rollupPluginBabelHelpers.objectSpread2(_rollupPluginBabelHelpers.objectSpread2({}, options), {}, {
|
|
123
|
+
serialize: globalConfig.serialize,
|
|
124
|
+
intentName: globalConfig.intentName,
|
|
125
|
+
onError: (_options$onError = options.onError) !== null && _options$onError !== void 0 ? _options$onError : dom.focusFirstInvalidField,
|
|
126
|
+
onValidate(ctx) {
|
|
127
|
+
var _options$onValidate, _options$onValidate2, _options;
|
|
128
|
+
if (schema) {
|
|
129
|
+
var schemaResult = globalConfig.validateSchema(schema, ctx.payload, options.schemaOptions);
|
|
130
|
+
if (schemaResult instanceof Promise) {
|
|
131
|
+
return schemaResult.then(resolvedResult => {
|
|
132
|
+
if (typeof options.onValidate === 'function') {
|
|
133
|
+
throw new Error('The "onValidate" handler is not supported when used with asynchronous schema validation.');
|
|
134
|
+
}
|
|
135
|
+
return resolvedResult;
|
|
136
|
+
});
|
|
137
|
+
}
|
|
138
|
+
if (!options.onValidate) {
|
|
139
|
+
return schemaResult;
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
// Update the schema error in the context
|
|
143
|
+
if (schemaResult.error) {
|
|
144
|
+
ctx.error = schemaResult.error;
|
|
145
|
+
}
|
|
146
|
+
var schemaValue = schemaResult.value;
|
|
147
|
+
ctx.schemaValue = schemaValue;
|
|
148
|
+
var validateResult = util.resolveValidateResult(options.onValidate(ctx));
|
|
149
|
+
if (validateResult.syncResult) {
|
|
150
|
+
var _validateResult$syncR, _validateResult$syncR2;
|
|
151
|
+
(_validateResult$syncR2 = (_validateResult$syncR = validateResult.syncResult).value) !== null && _validateResult$syncR2 !== void 0 ? _validateResult$syncR2 : _validateResult$syncR.value = schemaValue;
|
|
152
|
+
}
|
|
153
|
+
if (validateResult.asyncResult) {
|
|
154
|
+
validateResult.asyncResult = validateResult.asyncResult.then(result => {
|
|
155
|
+
var _result$value;
|
|
156
|
+
(_result$value = result.value) !== null && _result$value !== void 0 ? _result$value : result.value = schemaValue;
|
|
157
|
+
return result;
|
|
158
|
+
});
|
|
159
|
+
}
|
|
160
|
+
return [validateResult.syncResult, validateResult.asyncResult];
|
|
161
|
+
}
|
|
162
|
+
return (_options$onValidate = (_options$onValidate2 = (_options = options).onValidate) === null || _options$onValidate2 === void 0 ? void 0 : _options$onValidate2.call(_options, ctx)) !== null && _options$onValidate !== void 0 ? _options$onValidate : {
|
|
163
|
+
// To avoid conform falling back to server validation,
|
|
164
|
+
// if neither schema nor validation handler is provided,
|
|
165
|
+
// we just treat it as a valid client submission
|
|
166
|
+
error: null
|
|
167
|
+
};
|
|
168
|
+
}
|
|
169
|
+
}));
|
|
170
|
+
var intent = useIntent(formId);
|
|
171
|
+
var context = react.useMemo(() => ({
|
|
172
|
+
formId,
|
|
173
|
+
state: state$1,
|
|
174
|
+
constraint: constraint !== null && constraint !== void 0 ? constraint : null,
|
|
175
|
+
handleSubmit,
|
|
176
|
+
handleInput(event) {
|
|
177
|
+
var _optionsRef$current$o, _optionsRef$current, _optionsRef$current$s, _ref2, _optionsRef$current$s2;
|
|
178
|
+
if (!dom$1.isFieldElement(event.target) || event.target.name === '' || event.target.form === null || event.target.form !== dom.getFormElement(formId)) {
|
|
179
|
+
return;
|
|
180
|
+
}
|
|
181
|
+
(_optionsRef$current$o = (_optionsRef$current = optionsRef.current).onInput) === null || _optionsRef$current$o === void 0 || _optionsRef$current$o.call(_optionsRef$current, _rollupPluginBabelHelpers.objectSpread2(_rollupPluginBabelHelpers.objectSpread2({}, event), {}, {
|
|
182
|
+
target: event.target,
|
|
183
|
+
currentTarget: event.target.form
|
|
184
|
+
}));
|
|
185
|
+
if (event.defaultPrevented) {
|
|
186
|
+
return;
|
|
187
|
+
}
|
|
188
|
+
var shouldValidate = (_optionsRef$current$s = optionsRef.current.shouldValidate) !== null && _optionsRef$current$s !== void 0 ? _optionsRef$current$s : globalConfig.shouldValidate;
|
|
189
|
+
var shouldRevalidate = (_ref2 = (_optionsRef$current$s2 = optionsRef.current.shouldRevalidate) !== null && _optionsRef$current$s2 !== void 0 ? _optionsRef$current$s2 : optionsRef.current.shouldValidate) !== null && _ref2 !== void 0 ? _ref2 : globalConfig.shouldRevalidate;
|
|
190
|
+
if (state.isTouched(state$1, event.target.name) ? shouldRevalidate === 'onInput' : shouldValidate === 'onInput') {
|
|
191
|
+
intent.validate(event.target.name);
|
|
192
|
+
}
|
|
193
|
+
},
|
|
194
|
+
handleBlur(event) {
|
|
195
|
+
var _optionsRef$current$o2, _optionsRef$current2, _optionsRef$current$s3, _ref3, _optionsRef$current$s4;
|
|
196
|
+
if (!dom$1.isFieldElement(event.target) || event.target.name === '' || event.target.form === null || event.target.form !== dom.getFormElement(formId)) {
|
|
197
|
+
return;
|
|
198
|
+
}
|
|
199
|
+
(_optionsRef$current$o2 = (_optionsRef$current2 = optionsRef.current).onBlur) === null || _optionsRef$current$o2 === void 0 || _optionsRef$current$o2.call(_optionsRef$current2, _rollupPluginBabelHelpers.objectSpread2(_rollupPluginBabelHelpers.objectSpread2({}, event), {}, {
|
|
200
|
+
target: event.target,
|
|
201
|
+
currentTarget: event.target.form
|
|
202
|
+
}));
|
|
203
|
+
if (event.defaultPrevented) {
|
|
204
|
+
return;
|
|
205
|
+
}
|
|
206
|
+
var shouldValidate = (_optionsRef$current$s3 = optionsRef.current.shouldValidate) !== null && _optionsRef$current$s3 !== void 0 ? _optionsRef$current$s3 : globalConfig.shouldValidate;
|
|
207
|
+
var shouldRevalidate = (_ref3 = (_optionsRef$current$s4 = optionsRef.current.shouldRevalidate) !== null && _optionsRef$current$s4 !== void 0 ? _optionsRef$current$s4 : optionsRef.current.shouldValidate) !== null && _ref3 !== void 0 ? _ref3 : globalConfig.shouldRevalidate;
|
|
208
|
+
if (state.isTouched(state$1, event.target.name) ? shouldRevalidate === 'onBlur' : shouldValidate === 'onBlur') {
|
|
209
|
+
intent.validate(event.target.name);
|
|
210
|
+
}
|
|
211
|
+
}
|
|
212
|
+
}), [formId, state$1, constraint, handleSubmit, intent, optionsRef]);
|
|
213
|
+
var form = react.useMemo(() => state.getFormMetadata(context, {
|
|
214
|
+
serialize: globalConfig.serialize,
|
|
215
|
+
extendFormMetadata: globalConfig.extendFormMetadata,
|
|
216
|
+
extendFieldMetadata: globalConfig.extendFieldMetadata
|
|
217
|
+
}), [context]);
|
|
218
|
+
var fields = react.useMemo(() => state.getFieldset(context, {
|
|
219
|
+
serialize: globalConfig.serialize,
|
|
220
|
+
extendFieldMetadata: globalConfig.extendFieldMetadata
|
|
221
|
+
}), [context]);
|
|
222
|
+
return {
|
|
223
|
+
form,
|
|
224
|
+
fields,
|
|
225
|
+
intent
|
|
226
|
+
};
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
/**
|
|
230
|
+
* A React hook that provides access to form-level metadata and state.
|
|
231
|
+
* Requires `FormProvider` context when used in child components.
|
|
232
|
+
*
|
|
233
|
+
* @see https://conform.guide/api/react/future/useFormMetadata
|
|
234
|
+
* @example
|
|
235
|
+
* ```tsx
|
|
236
|
+
* function ErrorSummary() {
|
|
237
|
+
* const form = useFormMetadata();
|
|
238
|
+
*
|
|
239
|
+
* if (form.valid) return null;
|
|
240
|
+
*
|
|
241
|
+
* return (
|
|
242
|
+
* <div>Please fix {Object.keys(form.fieldErrors).length} errors</div>
|
|
243
|
+
* );
|
|
244
|
+
* }
|
|
245
|
+
* ```
|
|
246
|
+
*/
|
|
247
|
+
function useFormMetadata() {
|
|
248
|
+
var options = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};
|
|
249
|
+
var context = useFormContext(options.formId);
|
|
250
|
+
var formMetadata = react.useMemo(() => state.getFormMetadata(context, {
|
|
251
|
+
serialize: globalConfig.serialize,
|
|
252
|
+
extendFormMetadata: globalConfig.extendFormMetadata,
|
|
253
|
+
extendFieldMetadata: globalConfig.extendFieldMetadata
|
|
254
|
+
}), [context]);
|
|
255
|
+
return formMetadata;
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
/**
|
|
259
|
+
* A React hook that provides access to a specific field's metadata and state.
|
|
260
|
+
* Requires `FormProvider` context when used in child components.
|
|
261
|
+
*
|
|
262
|
+
* @see https://conform.guide/api/react/future/useField
|
|
263
|
+
* @example
|
|
264
|
+
* ```tsx
|
|
265
|
+
* function FormField({ name, label }) {
|
|
266
|
+
* const field = useField(name);
|
|
267
|
+
*
|
|
268
|
+
* return (
|
|
269
|
+
* <div>
|
|
270
|
+
* <label htmlFor={field.id}>{label}</label>
|
|
271
|
+
* <input id={field.id} name={field.name} defaultValue={field.defaultValue} />
|
|
272
|
+
* {field.errors && <div>{field.errors.join(', ')}</div>}
|
|
273
|
+
* </div>
|
|
274
|
+
* );
|
|
275
|
+
* }
|
|
276
|
+
* ```
|
|
277
|
+
*/
|
|
278
|
+
function useField(name) {
|
|
279
|
+
var options = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};
|
|
280
|
+
var context = useFormContext(options.formId);
|
|
281
|
+
var field = react.useMemo(() => state.getField(context, {
|
|
282
|
+
name,
|
|
283
|
+
serialize: globalConfig.serialize,
|
|
284
|
+
extendFieldMetadata: globalConfig.extendFieldMetadata
|
|
285
|
+
}), [context, name]);
|
|
286
|
+
return field;
|
|
287
|
+
}
|
|
288
|
+
|
|
289
|
+
/**
|
|
290
|
+
* A React hook that provides an intent dispatcher for programmatic form actions.
|
|
291
|
+
* Intent dispatchers allow you to trigger form operations like validation, field updates,
|
|
292
|
+
* and array manipulations without manual form submission.
|
|
293
|
+
*
|
|
294
|
+
* @see https://conform.guide/api/react/future/useIntent
|
|
295
|
+
* @example
|
|
296
|
+
* ```tsx
|
|
297
|
+
* function ResetButton() {
|
|
298
|
+
* const buttonRef = useRef<HTMLButtonElement>(null);
|
|
299
|
+
* const intent = useIntent(buttonRef);
|
|
300
|
+
*
|
|
301
|
+
* return (
|
|
302
|
+
* <button type="button" ref={buttonRef} onClick={() => intent.reset()}>
|
|
303
|
+
* Reset Form
|
|
304
|
+
* </button>
|
|
305
|
+
* );
|
|
306
|
+
* }
|
|
307
|
+
* ```
|
|
308
|
+
*/
|
|
309
|
+
function useIntent(formRef) {
|
|
310
|
+
return react.useMemo(() => dom.createIntentDispatcher(() => dom.getFormElement(formRef), globalConfig.intentName), [formRef]);
|
|
311
|
+
}
|
|
312
|
+
return {
|
|
313
|
+
FormProvider,
|
|
314
|
+
useForm,
|
|
315
|
+
useFormMetadata,
|
|
316
|
+
useField,
|
|
317
|
+
useIntent,
|
|
318
|
+
config: globalConfig
|
|
319
|
+
};
|
|
320
|
+
}
|
|
321
|
+
|
|
322
|
+
exports.configureForms = configureForms;
|