@conform-to/react 1.19.3 → 1.20.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.
@@ -1,6 +1,6 @@
1
1
  import { type FormValue, type Serialize, type SubmissionResult } from '@conform-to/dom/future';
2
2
  import { useEffect } from 'react';
3
- import type { FormContext, Control, Selector, UseFormDataOptions, ValidateHandler, ErrorHandler, SubmitHandler, FormState, FormRef, BaseControlProps, StandardControlOptions, DefaultControlValue, CheckedControlOptions, CustomControlOptions } from './types';
3
+ import type { FormContext, Control, Selector, UseFormDataOptions, ValidateHandler, ErrorHandler, SubmitHandler, FormState, FormRef, BaseControlProps, StandardControlOptions, DefaultControlValue, CheckedControlOptions, CustomControlOptions, IntentHandler, FormCustomState, CustomStateHandler } from './types';
4
4
  export declare const INITIAL_KEY = "INITIAL_KEY";
5
5
  export declare const GlobalFormsObserverContext: import("react").Context<{
6
6
  onFieldUpdate(callback: (event: {
@@ -17,7 +17,7 @@ export declare const GlobalFormsObserverContext: import("react").Context<{
17
17
  }) => void): () => void;
18
18
  dispose(): void;
19
19
  }>;
20
- export declare const FormContextContext: import("react").Context<FormContext<any>[]>;
20
+ export declare const FormContextContext: import("react").Context<FormContext<any, {}>[]>;
21
21
  /**
22
22
  * Preserves form field values when its contents are unmounted.
23
23
  * Useful for multi-step forms and virtualized lists.
@@ -42,16 +42,21 @@ export declare function useFormContext(formId?: string): FormContext;
42
42
  * Core form hook that manages form state, validation, and submission.
43
43
  * Handles both sync and async validation, intent dispatching, and DOM updates.
44
44
  */
45
- export declare function useConform<FormShape extends Record<string, any>, ErrorShape, Value = undefined, SchemaValue = undefined, SchemaErrorShape = unknown>(formRef: FormRef, options: {
45
+ export declare function useConform<FormShape extends Record<string, any>, ErrorShape, Value = undefined, SchemaValue = undefined, SchemaErrorShape = unknown, CustomStateHandlers extends Record<string, CustomStateHandler<any, any, ErrorShape>> = {}>(formRef: FormRef, options: {
46
46
  key?: string | undefined;
47
47
  defaultValue?: Record<string, FormValue> | null | undefined;
48
48
  serialize: Serialize;
49
49
  intentName: string;
50
+ intentHandlers: Record<string, IntentHandler<any, any>>;
51
+ customStateHandlers?: CustomStateHandlers | undefined;
50
52
  lastResult?: SubmissionResult<NoInfer<ErrorShape>> | null | undefined;
51
53
  onValidate?: ValidateHandler<ErrorShape, Value, SchemaValue, SchemaErrorShape> | undefined;
52
54
  onError?: ErrorHandler<ErrorShape> | undefined;
53
55
  onSubmit?: SubmitHandler<FormShape, NoInfer<ErrorShape>, NoInfer<Value>> | undefined;
54
- }): [FormState<ErrorShape>, (event: React.FormEvent<HTMLFormElement>) => void];
56
+ }): [
57
+ FormState<ErrorShape, FormCustomState<CustomStateHandlers>>,
58
+ (event: React.FormEvent<HTMLFormElement>) => void
59
+ ];
55
60
  /**
56
61
  * A React hook that lets you sync the state of an input and dispatch native form events from it.
57
62
  * This is useful when emulating native input behavior — typically by rendering a hidden base control
@@ -73,25 +73,38 @@ function useConform(formRef, options) {
73
73
  var [state$1, setState] = react.useState(() => {
74
74
  var state$1 = state.initializeState({
75
75
  defaultValue: options.defaultValue,
76
+ customStateHandlers: options.customStateHandlers,
76
77
  resetKey: INITIAL_KEY
77
78
  });
78
79
  if (lastResult) {
79
- var intent$1 = lastResult.submission.intent ? intent.deserializeIntent(lastResult.submission.intent) : null;
80
- var result = intent.applyIntent(lastResult, intent$1, {
81
- handlers: intent.intentHandlers
80
+ var intent$1 = intent.parseIntent(lastResult.submission.intent, {
81
+ handlers: options.intentHandlers
82
82
  });
83
- state$1 = state.updateState(state$1, _rollupPluginBabelHelpers.objectSpread2(_rollupPluginBabelHelpers.objectSpread2({}, result), {}, {
84
- type: 'initialize',
85
- intent: intent$1,
86
- ctx: {
87
- handlers: intent.intentHandlers,
88
- cancelled: result !== lastResult,
89
- reset: defaultValue => state.initializeState({
90
- defaultValue: defaultValue !== null && defaultValue !== void 0 ? defaultValue : options.defaultValue,
91
- resetKey: INITIAL_KEY
92
- })
93
- }
94
- }));
83
+ if (intent$1) {
84
+ var result = intent.applyIntent(lastResult, intent$1, {
85
+ handlers: options.intentHandlers
86
+ });
87
+ state$1 = state.updateState(state$1, {
88
+ type: 'initialize',
89
+ intent: intent$1,
90
+ result,
91
+ ctx: {
92
+ intentHandlers: options.intentHandlers,
93
+ customStateHandlers: options.customStateHandlers,
94
+ status: state.getApplyStatus(lastResult.targetValue, result.targetValue),
95
+ reset() {
96
+ var _result$targetValue;
97
+ return state.initializeState({
98
+ defaultValue: (_result$targetValue = result.targetValue) !== null && _result$targetValue !== void 0 ? _result$targetValue : options.defaultValue,
99
+ resetKey: INITIAL_KEY,
100
+ customStateHandlers: options.customStateHandlers,
101
+ lastCustomState: state$1.customState,
102
+ result: result
103
+ });
104
+ }
105
+ }
106
+ });
107
+ }
95
108
  }
96
109
  return state$1;
97
110
  });
@@ -107,27 +120,38 @@ function useConform(formRef, options) {
107
120
  var normalizedResult = !result.error ? result : _rollupPluginBabelHelpers.objectSpread2(_rollupPluginBabelHelpers.objectSpread2({}, result), {}, {
108
121
  error: future.normalizeFormError(result.error)
109
122
  });
110
- var intent$1 = result.submission.intent ? intent.deserializeIntent(result.submission.intent) : null;
111
- var finalResult = intent.applyIntent(normalizedResult, intent$1, {
112
- handlers: intent.intentHandlers
123
+ var intent$1 = intent.parseIntent(result.submission.intent, {
124
+ handlers: options.intentHandlers
113
125
  });
126
+ if (!intent$1) {
127
+ throw new Error("Unknown intent found in the submission result; Received intent: ".concat(result.submission.intent));
128
+ }
114
129
  var formElement = dom.getFormElement(formRef);
115
- if (formElement && (finalResult.reset || typeof finalResult.targetValue !== 'undefined')) {
130
+ var finalResult = type === 'client' ? intent.applyIntent(normalizedResult, intent$1, {
131
+ handlers: options.intentHandlers
132
+ }) : normalizedResult;
133
+ if (formElement && type === 'client' && (finalResult.reset || typeof finalResult.targetValue !== 'undefined')) {
116
134
  future.dispatchInternalUpdateEvent(formElement);
117
135
  }
118
- setState(state$1 => state.updateState(state$1, _rollupPluginBabelHelpers.objectSpread2(_rollupPluginBabelHelpers.objectSpread2({}, finalResult), {}, {
136
+ setState(current => state.updateState(current, {
119
137
  type,
120
138
  intent: intent$1,
139
+ result: finalResult,
121
140
  ctx: {
122
- handlers: intent.intentHandlers,
123
- cancelled: finalResult !== normalizedResult,
124
- reset(defaultValue) {
141
+ intentHandlers: options.intentHandlers,
142
+ customStateHandlers: options.customStateHandlers,
143
+ status: state.getApplyStatus(normalizedResult.targetValue, finalResult.targetValue),
144
+ reset() {
145
+ var _finalResult$targetVa;
125
146
  return state.initializeState({
126
- defaultValue: defaultValue !== null && defaultValue !== void 0 ? defaultValue : options.defaultValue
147
+ defaultValue: (_finalResult$targetVa = finalResult.targetValue) !== null && _finalResult$targetVa !== void 0 ? _finalResult$targetVa : options.defaultValue,
148
+ customStateHandlers: options.customStateHandlers,
149
+ lastCustomState: current.customState,
150
+ result: finalResult
127
151
  });
128
152
  }
129
153
  }
130
- })));
154
+ }));
131
155
 
132
156
  // TODO: move on error handler to a new effect
133
157
  if (formElement && finalResult.error) {
@@ -146,8 +170,10 @@ function useConform(formRef, options) {
146
170
  if (formElement) {
147
171
  future.dispatchInternalUpdateEvent(formElement);
148
172
  }
149
- setState(state.initializeState({
150
- defaultValue: options.defaultValue
173
+ setState(current => state.initializeState({
174
+ defaultValue: options.defaultValue,
175
+ customStateHandlers: options.customStateHandlers,
176
+ lastCustomState: current.customState
151
177
  }));
152
178
  } else if (lastResult && lastResult !== lastResultRef.current) {
153
179
  lastResultRef.current = lastResult;
@@ -197,6 +223,9 @@ function useConform(formRef, options) {
197
223
  var submission = future.parseSubmission(formData, {
198
224
  intentName: optionsRef.current.intentName
199
225
  });
226
+ var intent$1 = intent.parseIntent(submission.intent, {
227
+ handlers: optionsRef.current.intentHandlers
228
+ });
200
229
 
201
230
  // Patch missing fields in the submission object
202
231
  for (var element of formElement.elements) {
@@ -215,17 +244,20 @@ function useConform(formRef, options) {
215
244
  lastAsyncResultRef.current = null;
216
245
  if (lastAsyncResult &&
217
246
  // Only default submission will be re-submitted after async validation
218
- !submission.intent &&
247
+ (intent$1 === null || intent$1 === void 0 ? void 0 : intent$1.type) === 'submit' &&
219
248
  // Ensure the submission payload is the same as the one being validated
220
249
  future.deepEqual(submission.payload, lastAsyncResult.result.submission.payload)) {
221
250
  result = lastAsyncResult.result;
222
251
  resolvedValue = lastAsyncResult.resolvedValue;
223
252
  } else {
224
253
  var _optionsRef$current$o2, _optionsRef$current2;
225
- var value = intent.resolveIntent(submission);
254
+ var value = intent.resolveIntent(submission, {
255
+ handlers: optionsRef.current.intentHandlers,
256
+ intent: intent$1
257
+ });
226
258
  var submissionResult = future.report(submission, {
227
259
  keepFiles: true,
228
- value
260
+ targetValue: value
229
261
  });
230
262
  var validateResult =
231
263
  // Skip validation on form reset
@@ -235,7 +267,7 @@ function useConform(formRef, options) {
235
267
  formErrors: null,
236
268
  fieldErrors: {}
237
269
  },
238
- intent: submission.intent ? intent.deserializeIntent(submission.intent) : null,
270
+ intent: intent$1,
239
271
  formElement,
240
272
  submitter: submitEvent.submitter,
241
273
  formData,
@@ -262,10 +294,10 @@ function useConform(formRef, options) {
262
294
  // There is no need to flush the update in this case
263
295
  if (!abortController.signal.aborted) {
264
296
  submissionResult.error = error;
265
- handleSubmission('server', submissionResult);
297
+ handleSubmission('client:async', submissionResult);
266
298
 
267
299
  // If the form is meant to be submitted and there is no error
268
- if (submissionResult.error === null && !submission.intent) {
300
+ if (submissionResult.error === null && (intent$1 === null || intent$1 === void 0 ? void 0 : intent$1.type) === 'submit') {
269
301
  // Keep track of the validated payload and resume submission on the next task.
270
302
  // Calling requestSubmit() directly from the async callback, or from a
271
303
  // microtask, can still be ignored before the native submission lifecycle
@@ -293,7 +325,7 @@ function useConform(formRef, options) {
293
325
  // If client validation happens
294
326
  (typeof syncResult !== 'undefined' || typeof asyncResult !== 'undefined') && (
295
327
  // Either the form is not meant to be submitted (i.e. intent is present) or there is an error / pending validation
296
- clientResult.submission.intent || clientResult.error !== null)) {
328
+ (intent$1 === null || intent$1 === void 0 ? void 0 : intent$1.type) !== 'submit' || clientResult.error !== null)) {
297
329
  event.preventDefault();
298
330
  }
299
331
  result = clientResult;
@@ -301,7 +333,7 @@ function useConform(formRef, options) {
301
333
 
302
334
  // We might not prevent form submission if server validation is required
303
335
  // But the `onSubmit` handler should be triggered only if there is no intent
304
- if (!event.isDefaultPrevented() && result.submission.intent === null) {
336
+ if (!event.isDefaultPrevented() && (intent$1 === null || intent$1 === void 0 ? void 0 : intent$1.type) === 'submit') {
305
337
  var _optionsRef$current$o3, _optionsRef$current3;
306
338
  (_optionsRef$current$o3 = (_optionsRef$current3 = optionsRef.current).onSubmit) === null || _optionsRef$current$o3 === void 0 || _optionsRef$current$o3.call(_optionsRef$current3, event, {
307
339
  formData,
@@ -3,8 +3,8 @@ import { objectSpread2 as _objectSpread2, objectWithoutProperties as _objectWith
3
3
  import { createGlobalFormsObserver, isGlobalInstance, deepEqual, isFieldElement, change, focus, blur, getFormData, defaultSerialize, isPlainObject, normalizeFormError, dispatchInternalUpdateEvent, parseSubmission, report, requestSubmit } from '@conform-to/dom/future';
4
4
  import { createContext, useRef, useContext, useMemo, useState, useEffect, useSyncExternalStore, useCallback, useLayoutEffect, forwardRef } from 'react';
5
5
  import { appendUniqueItem, resolveValidateResult } from './util.mjs';
6
- import { initializeState, updateState } from './state.mjs';
7
- import { deserializeIntent, applyIntent, intentHandlers, resolveIntent } from './intent.mjs';
6
+ import { initializeState, updateState, getApplyStatus } from './state.mjs';
7
+ import { parseIntent, applyIntent, resolveIntent } from './intent.mjs';
8
8
  import { cleanupPreservedInputs, preserveInputs, deriveDefaultPayload, resolveControlPayload, initializeField, getFormElement, resetFormValue, updateFormValue, getSubmitEvent } from './dom.mjs';
9
9
  import { flushSync } from 'react-dom';
10
10
  import { jsx } from 'react/jsx-runtime';
@@ -69,25 +69,38 @@ function useConform(formRef, options) {
69
69
  var [state, setState] = useState(() => {
70
70
  var state = initializeState({
71
71
  defaultValue: options.defaultValue,
72
+ customStateHandlers: options.customStateHandlers,
72
73
  resetKey: INITIAL_KEY
73
74
  });
74
75
  if (lastResult) {
75
- var intent = lastResult.submission.intent ? deserializeIntent(lastResult.submission.intent) : null;
76
- var result = applyIntent(lastResult, intent, {
77
- handlers: intentHandlers
76
+ var intent = parseIntent(lastResult.submission.intent, {
77
+ handlers: options.intentHandlers
78
78
  });
79
- state = updateState(state, _objectSpread2(_objectSpread2({}, result), {}, {
80
- type: 'initialize',
81
- intent,
82
- ctx: {
83
- handlers: intentHandlers,
84
- cancelled: result !== lastResult,
85
- reset: defaultValue => initializeState({
86
- defaultValue: defaultValue !== null && defaultValue !== void 0 ? defaultValue : options.defaultValue,
87
- resetKey: INITIAL_KEY
88
- })
89
- }
90
- }));
79
+ if (intent) {
80
+ var result = applyIntent(lastResult, intent, {
81
+ handlers: options.intentHandlers
82
+ });
83
+ state = updateState(state, {
84
+ type: 'initialize',
85
+ intent,
86
+ result,
87
+ ctx: {
88
+ intentHandlers: options.intentHandlers,
89
+ customStateHandlers: options.customStateHandlers,
90
+ status: getApplyStatus(lastResult.targetValue, result.targetValue),
91
+ reset() {
92
+ var _result$targetValue;
93
+ return initializeState({
94
+ defaultValue: (_result$targetValue = result.targetValue) !== null && _result$targetValue !== void 0 ? _result$targetValue : options.defaultValue,
95
+ resetKey: INITIAL_KEY,
96
+ customStateHandlers: options.customStateHandlers,
97
+ lastCustomState: state.customState,
98
+ result: result
99
+ });
100
+ }
101
+ }
102
+ });
103
+ }
91
104
  }
92
105
  return state;
93
106
  });
@@ -103,27 +116,38 @@ function useConform(formRef, options) {
103
116
  var normalizedResult = !result.error ? result : _objectSpread2(_objectSpread2({}, result), {}, {
104
117
  error: normalizeFormError(result.error)
105
118
  });
106
- var intent = result.submission.intent ? deserializeIntent(result.submission.intent) : null;
107
- var finalResult = applyIntent(normalizedResult, intent, {
108
- handlers: intentHandlers
119
+ var intent = parseIntent(result.submission.intent, {
120
+ handlers: options.intentHandlers
109
121
  });
122
+ if (!intent) {
123
+ throw new Error("Unknown intent found in the submission result; Received intent: ".concat(result.submission.intent));
124
+ }
110
125
  var formElement = getFormElement(formRef);
111
- if (formElement && (finalResult.reset || typeof finalResult.targetValue !== 'undefined')) {
126
+ var finalResult = type === 'client' ? applyIntent(normalizedResult, intent, {
127
+ handlers: options.intentHandlers
128
+ }) : normalizedResult;
129
+ if (formElement && type === 'client' && (finalResult.reset || typeof finalResult.targetValue !== 'undefined')) {
112
130
  dispatchInternalUpdateEvent(formElement);
113
131
  }
114
- setState(state => updateState(state, _objectSpread2(_objectSpread2({}, finalResult), {}, {
132
+ setState(current => updateState(current, {
115
133
  type,
116
134
  intent,
135
+ result: finalResult,
117
136
  ctx: {
118
- handlers: intentHandlers,
119
- cancelled: finalResult !== normalizedResult,
120
- reset(defaultValue) {
137
+ intentHandlers: options.intentHandlers,
138
+ customStateHandlers: options.customStateHandlers,
139
+ status: getApplyStatus(normalizedResult.targetValue, finalResult.targetValue),
140
+ reset() {
141
+ var _finalResult$targetVa;
121
142
  return initializeState({
122
- defaultValue: defaultValue !== null && defaultValue !== void 0 ? defaultValue : options.defaultValue
143
+ defaultValue: (_finalResult$targetVa = finalResult.targetValue) !== null && _finalResult$targetVa !== void 0 ? _finalResult$targetVa : options.defaultValue,
144
+ customStateHandlers: options.customStateHandlers,
145
+ lastCustomState: current.customState,
146
+ result: finalResult
123
147
  });
124
148
  }
125
149
  }
126
- })));
150
+ }));
127
151
 
128
152
  // TODO: move on error handler to a new effect
129
153
  if (formElement && finalResult.error) {
@@ -131,7 +155,7 @@ function useConform(formRef, options) {
131
155
  (_optionsRef$current$o = (_optionsRef$current = optionsRef.current).onError) === null || _optionsRef$current$o === void 0 || _optionsRef$current$o.call(_optionsRef$current, {
132
156
  formElement,
133
157
  error: finalResult.error,
134
- intent
158
+ intent: intent
135
159
  });
136
160
  }
137
161
  return finalResult;
@@ -142,8 +166,10 @@ function useConform(formRef, options) {
142
166
  if (formElement) {
143
167
  dispatchInternalUpdateEvent(formElement);
144
168
  }
145
- setState(initializeState({
146
- defaultValue: options.defaultValue
169
+ setState(current => initializeState({
170
+ defaultValue: options.defaultValue,
171
+ customStateHandlers: options.customStateHandlers,
172
+ lastCustomState: current.customState
147
173
  }));
148
174
  } else if (lastResult && lastResult !== lastResultRef.current) {
149
175
  lastResultRef.current = lastResult;
@@ -193,6 +219,9 @@ function useConform(formRef, options) {
193
219
  var submission = parseSubmission(formData, {
194
220
  intentName: optionsRef.current.intentName
195
221
  });
222
+ var intent = parseIntent(submission.intent, {
223
+ handlers: optionsRef.current.intentHandlers
224
+ });
196
225
 
197
226
  // Patch missing fields in the submission object
198
227
  for (var element of formElement.elements) {
@@ -211,17 +240,20 @@ function useConform(formRef, options) {
211
240
  lastAsyncResultRef.current = null;
212
241
  if (lastAsyncResult &&
213
242
  // Only default submission will be re-submitted after async validation
214
- !submission.intent &&
243
+ (intent === null || intent === void 0 ? void 0 : intent.type) === 'submit' &&
215
244
  // Ensure the submission payload is the same as the one being validated
216
245
  deepEqual(submission.payload, lastAsyncResult.result.submission.payload)) {
217
246
  result = lastAsyncResult.result;
218
247
  resolvedValue = lastAsyncResult.resolvedValue;
219
248
  } else {
220
249
  var _optionsRef$current$o2, _optionsRef$current2;
221
- var value = resolveIntent(submission);
250
+ var value = resolveIntent(submission, {
251
+ handlers: optionsRef.current.intentHandlers,
252
+ intent
253
+ });
222
254
  var submissionResult = report(submission, {
223
255
  keepFiles: true,
224
- value
256
+ targetValue: value
225
257
  });
226
258
  var validateResult =
227
259
  // Skip validation on form reset
@@ -231,7 +263,7 @@ function useConform(formRef, options) {
231
263
  formErrors: null,
232
264
  fieldErrors: {}
233
265
  },
234
- intent: submission.intent ? deserializeIntent(submission.intent) : null,
266
+ intent,
235
267
  formElement,
236
268
  submitter: submitEvent.submitter,
237
269
  formData,
@@ -258,10 +290,10 @@ function useConform(formRef, options) {
258
290
  // There is no need to flush the update in this case
259
291
  if (!abortController.signal.aborted) {
260
292
  submissionResult.error = error;
261
- handleSubmission('server', submissionResult);
293
+ handleSubmission('client:async', submissionResult);
262
294
 
263
295
  // If the form is meant to be submitted and there is no error
264
- if (submissionResult.error === null && !submission.intent) {
296
+ if (submissionResult.error === null && (intent === null || intent === void 0 ? void 0 : intent.type) === 'submit') {
265
297
  // Keep track of the validated payload and resume submission on the next task.
266
298
  // Calling requestSubmit() directly from the async callback, or from a
267
299
  // microtask, can still be ignored before the native submission lifecycle
@@ -289,7 +321,7 @@ function useConform(formRef, options) {
289
321
  // If client validation happens
290
322
  (typeof syncResult !== 'undefined' || typeof asyncResult !== 'undefined') && (
291
323
  // Either the form is not meant to be submitted (i.e. intent is present) or there is an error / pending validation
292
- clientResult.submission.intent || clientResult.error !== null)) {
324
+ (intent === null || intent === void 0 ? void 0 : intent.type) !== 'submit' || clientResult.error !== null)) {
293
325
  event.preventDefault();
294
326
  }
295
327
  result = clientResult;
@@ -297,7 +329,7 @@ function useConform(formRef, options) {
297
329
 
298
330
  // We might not prevent form submission if server validation is required
299
331
  // But the `onSubmit` handler should be triggered only if there is no intent
300
- if (!event.isDefaultPrevented() && result.submission.intent === null) {
332
+ if (!event.isDefaultPrevented() && (intent === null || intent === void 0 ? void 0 : intent.type) === 'submit') {
301
333
  var _optionsRef$current$o3, _optionsRef$current3;
302
334
  (_optionsRef$current$o3 = (_optionsRef$current3 = optionsRef.current).onSubmit) === null || _optionsRef$current$o3 === void 0 || _optionsRef$current$o3.call(_optionsRef$current3, event, {
303
335
  formData,
@@ -1,7 +1,9 @@
1
1
  export type { FieldName, FormError, FormValue, Submission, SubmissionResult, } from '@conform-to/dom/future';
2
2
  export { getFieldValue, parseSubmission, report, isDirty, } from '@conform-to/dom/future';
3
- export type { Control, ControlOptions, BaseControlProps, DefaultValue, BaseFieldMetadata, CustomSchemaTypes, FormsConfig, FormContext, FormMetadata, FormOptions, FormRef, FieldMetadata, Fieldset, IntentDispatcher, InferBaseErrorShape, InferCustomFormMetadata, InferCustomFieldMetadata, } from './types';
4
- export { configureForms, FormProvider, useForm, useFormMetadata, useField, useIntent, } from './forms';
3
+ export type { Control, ControlOptions, BaseControlProps, DefaultValue, BaseFieldMetadata, CustomSchemaTypes, CustomStateHandler, FormsConfig, FormContext, FormMetadata, FormOptions, FormRef, FieldMetadata, Fieldset, FormIntent, IntentHandler, IntentDispatcher, InferBaseErrorShape, InferCustomFormMetadata, InferCustomFieldMetadata, InferCustomState, } from './types';
4
+ export { configureForms, FormProvider, useForm, useFormMetadata, useField, useIntent, resolveSubmission, } from './forms';
5
+ export { defineIntent } from './intent';
6
+ export { defineCustomState } from './state';
5
7
  export { BaseControl, PreserveBoundary, useControl, useFormData, } from './hooks';
6
8
  export { shape } from './util';
7
9
  export { memoize } from './memoize';
@@ -4,6 +4,8 @@ Object.defineProperty(exports, '__esModule', { value: true });
4
4
 
5
5
  var future = require('@conform-to/dom/future');
6
6
  var forms = require('./forms.js');
7
+ var intent = require('./intent.js');
8
+ var state = require('./state.js');
7
9
  var hooks = require('./hooks.js');
8
10
  var util = require('./util.js');
9
11
  var memoize = require('./memoize.js');
@@ -28,10 +30,13 @@ Object.defineProperty(exports, 'report', {
28
30
  });
29
31
  exports.FormProvider = forms.FormProvider;
30
32
  exports.configureForms = forms.configureForms;
33
+ exports.resolveSubmission = forms.resolveSubmission;
31
34
  exports.useField = forms.useField;
32
35
  exports.useForm = forms.useForm;
33
36
  exports.useFormMetadata = forms.useFormMetadata;
34
37
  exports.useIntent = forms.useIntent;
38
+ exports.defineIntent = intent.defineIntent;
39
+ exports.defineCustomState = state.defineCustomState;
35
40
  exports.BaseControl = hooks.BaseControl;
36
41
  exports.PreserveBoundary = hooks.PreserveBoundary;
37
42
  exports.useControl = hooks.useControl;
@@ -1,5 +1,7 @@
1
1
  export { getFieldValue, isDirty, parseSubmission, report } from '@conform-to/dom/future';
2
- export { FormProvider, configureForms, useField, useForm, useFormMetadata, useIntent } from './forms.mjs';
2
+ export { FormProvider, configureForms, resolveSubmission, useField, useForm, useFormMetadata, useIntent } from './forms.mjs';
3
+ export { defineIntent } from './intent.mjs';
4
+ export { defineCustomState } from './state.mjs';
3
5
  export { BaseControl, PreserveBoundary, useControl, useFormData } from './hooks.mjs';
4
6
  export { shape } from './util.mjs';
5
7
  export { memoize } from './memoize.mjs';
@@ -1,26 +1,27 @@
1
1
  import type { FormValue, Submission, SubmissionResult } from '@conform-to/dom/future';
2
- import type { IntentHandler, IntentDispatcher, UnknownIntent } from './types';
2
+ import type { FormIntent, IntentHandler, NormalizeIntentType, UnknownIntent, TransportIntent, ResetIntent, SubmitIntent, ValidateIntent, UpdateIntent, InsertIntent, RemoveIntent, ReorderIntent, DefaultIntentHandlers } from './types';
3
+ export declare function defineIntent<Definition = () => void, Payload = never>(definition?: Partial<IntentHandler<NormalizeIntentType<Definition>, Payload>>): IntentHandler<NormalizeIntentType<Definition>, Payload>;
4
+ export declare function mergeIntentHandlers<DefaultHandlers extends Record<string, IntentHandler<any, any>>, CustomHandlers extends Record<string, IntentHandler<any, any>> | undefined>(defaultHandlers: DefaultHandlers, customHandlers: CustomHandlers): DefaultHandlers & CustomHandlers;
3
5
  /**
4
- * Serializes intent to string format: "type" or "type(payload)".
6
+ * Serializes a transport intent to string format.
5
7
  */
6
- export declare function serializeIntent<Intent extends UnknownIntent = UnknownIntent>(intent: Intent): string;
8
+ export declare function serializeIntent(intent: TransportIntent): string;
7
9
  /**
8
- * Parses serialized intent string back to intent object.
10
+ * Parses the serialized intent string into a transport intent.
9
11
  */
10
- export declare function deserializeIntent(value: string): UnknownIntent;
11
- /**
12
- * Applies intent transformation to submission payload.
13
- * Returns modified payload or null for reset intent.
14
- */
15
- export declare function resolveIntent(submission: Submission, options?: {
16
- handlers?: Record<string, IntentHandler>;
12
+ export declare function deserializeIntent(serializedIntent: string): TransportIntent | undefined;
13
+ export declare function parseIntent<Handlers extends Record<string, IntentHandler<any, any>>>(intentValue: string | null, options: {
14
+ handlers: Handlers;
15
+ }): FormIntent<Record<string, any>, Handlers> | {
16
+ type: 'submit';
17
+ payload: undefined;
18
+ } | undefined;
19
+ export declare function resolveIntent(submission: Submission, options: {
20
+ handlers: Record<string, IntentHandler<any, any>>;
21
+ intent: UnknownIntent | undefined;
17
22
  }): Record<string, FormValue> | undefined;
18
- /**
19
- * Resolves an intent after validation by calling the handler's onResolve.
20
- * Mutates the result with updated value/error and returns whether the intent was cancelled.
21
- */
22
- export declare function applyIntent<ErrorShape>(result: SubmissionResult<ErrorShape>, intent: UnknownIntent | null, options?: {
23
- handlers?: Record<string, IntentHandler>;
23
+ export declare function applyIntent<ErrorShape>(result: SubmissionResult<ErrorShape>, intent: UnknownIntent | undefined, options: {
24
+ handlers: Record<string, IntentHandler<any, any>>;
24
25
  }): SubmissionResult<ErrorShape>;
25
26
  export declare function insertItem<Item>(list: Array<Item>, item: Item, index: number): void;
26
27
  export declare function removeItem(list: Array<unknown>, index: number): void;
@@ -29,14 +30,19 @@ export declare function reorderItems(list: Array<unknown>, fromIndex: number, to
29
30
  * Updates list keys by removing child keys and optionally transforming remaining keys.
30
31
  */
31
32
  export declare function updateListKeys(keys: Record<string, string[]> | undefined, keyToBeRemoved: string, updateKey?: (key: string) => string | null): Record<string, string[]>;
33
+ export declare const submit: IntentHandler<SubmitIntent, never>;
34
+ export declare const reset: IntentHandler<ResetIntent, never>;
35
+ export declare const validate: IntentHandler<ValidateIntent, never>;
36
+ export declare const update: IntentHandler<UpdateIntent, never>;
37
+ export declare const insert: IntentHandler<InsertIntent, never>;
38
+ export declare const remove: IntentHandler<RemoveIntent, never>;
39
+ export declare const reorder: IntentHandler<ReorderIntent, never>;
32
40
  /**
33
- * Built-in action handlers for form intents:
41
+ * Default Intent handlers
34
42
  * - reset: clears form data
35
43
  * - validate: marks fields as touched for validation display
36
44
  * - update: updates specific field values
37
45
  * - insert/remove/reorder: manages array field operations
38
46
  */
39
- export declare const intentHandlers: {
40
- [Type in keyof IntentDispatcher]: IntentDispatcher[Type] extends (payload: any) => void ? IntentHandler<IntentDispatcher[Type]> : never;
41
- };
47
+ export declare const defaultIntentHandlers: DefaultIntentHandlers;
42
48
  //# sourceMappingURL=intent.d.ts.map