@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.
- package/README.md +1 -1
- package/dist/context.d.ts +2 -2
- package/dist/future/dom.d.ts +2 -2
- package/dist/future/dom.js +9 -4
- package/dist/future/dom.mjs +9 -4
- package/dist/future/forms.d.ts +62 -21
- package/dist/future/forms.js +59 -11
- package/dist/future/forms.mjs +56 -9
- package/dist/future/hooks.d.ts +9 -4
- package/dist/future/hooks.js +67 -35
- package/dist/future/hooks.mjs +70 -38
- package/dist/future/index.d.ts +4 -2
- package/dist/future/index.js +5 -0
- package/dist/future/index.mjs +3 -1
- package/dist/future/intent.d.ts +27 -21
- package/dist/future/intent.js +369 -371
- package/dist/future/intent.mjs +361 -372
- package/dist/future/state.d.ts +48 -21
- package/dist/future/state.js +344 -43
- package/dist/future/state.mjs +339 -46
- package/dist/future/types.d.ts +158 -149
- package/dist/future/util.d.ts +4 -8
- package/dist/future/util.js +25 -60
- package/dist/future/util.mjs +26 -59
- package/dist/helpers.d.ts +2 -2
- package/dist/helpers.js +2 -2
- package/dist/helpers.mjs +2 -2
- package/dist/hooks.d.ts +1 -1
- package/dist/vitest.config.d.ts +0 -1
- package/package.json +2 -2
package/dist/future/hooks.d.ts
CHANGED
|
@@ -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
|
-
}): [
|
|
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
|
package/dist/future/hooks.js
CHANGED
|
@@ -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 =
|
|
80
|
-
|
|
81
|
-
handlers: intent.intentHandlers
|
|
80
|
+
var intent$1 = intent.parseIntent(lastResult.submission.intent, {
|
|
81
|
+
handlers: options.intentHandlers
|
|
82
82
|
});
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
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 =
|
|
111
|
-
|
|
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
|
-
|
|
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(
|
|
136
|
+
setState(current => state.updateState(current, {
|
|
119
137
|
type,
|
|
120
138
|
intent: intent$1,
|
|
139
|
+
result: finalResult,
|
|
121
140
|
ctx: {
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
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:
|
|
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
|
-
|
|
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:
|
|
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('
|
|
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 &&
|
|
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
|
-
|
|
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() &&
|
|
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,
|
package/dist/future/hooks.mjs
CHANGED
|
@@ -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 {
|
|
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 =
|
|
76
|
-
|
|
77
|
-
handlers: intentHandlers
|
|
76
|
+
var intent = parseIntent(lastResult.submission.intent, {
|
|
77
|
+
handlers: options.intentHandlers
|
|
78
78
|
});
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
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 =
|
|
107
|
-
|
|
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
|
-
|
|
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(
|
|
132
|
+
setState(current => updateState(current, {
|
|
115
133
|
type,
|
|
116
134
|
intent,
|
|
135
|
+
result: finalResult,
|
|
117
136
|
ctx: {
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
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:
|
|
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
|
-
|
|
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
|
|
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('
|
|
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 &&
|
|
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
|
-
|
|
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() &&
|
|
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,
|
package/dist/future/index.d.ts
CHANGED
|
@@ -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';
|
package/dist/future/index.js
CHANGED
|
@@ -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;
|
package/dist/future/index.mjs
CHANGED
|
@@ -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';
|
package/dist/future/intent.d.ts
CHANGED
|
@@ -1,26 +1,27 @@
|
|
|
1
1
|
import type { FormValue, Submission, SubmissionResult } from '@conform-to/dom/future';
|
|
2
|
-
import type { IntentHandler,
|
|
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
|
|
6
|
+
* Serializes a transport intent to string format.
|
|
5
7
|
*/
|
|
6
|
-
export declare function serializeIntent
|
|
8
|
+
export declare function serializeIntent(intent: TransportIntent): string;
|
|
7
9
|
/**
|
|
8
|
-
* Parses serialized intent string
|
|
10
|
+
* Parses the serialized intent string into a transport intent.
|
|
9
11
|
*/
|
|
10
|
-
export declare function deserializeIntent(
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
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
|
-
|
|
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
|
-
*
|
|
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
|
|
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
|