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