@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
|
@@ -0,0 +1,318 @@
|
|
|
1
|
+
import { objectSpread2 as _objectSpread2 } from '../_virtual/_rollupPluginBabelHelpers.mjs';
|
|
2
|
+
import { isFieldElement } from '@conform-to/dom';
|
|
3
|
+
import { DEFAULT_INTENT_NAME, serialize } from '@conform-to/dom/future';
|
|
4
|
+
import { useContext, useMemo, useId, createContext } from 'react';
|
|
5
|
+
import { focusFirstInvalidField, getFormElement, createIntentDispatcher } from './dom.mjs';
|
|
6
|
+
import { useLatest, useConform } from './hooks.mjs';
|
|
7
|
+
import { isTouched, getFormMetadata, getFieldset, getField } from './state.mjs';
|
|
8
|
+
import { isStandardSchemaV1, validateStandardSchemaV1, resolveValidateResult } from './util.mjs';
|
|
9
|
+
import { jsx } from 'react/jsx-runtime';
|
|
10
|
+
|
|
11
|
+
function configureForms() {
|
|
12
|
+
var _config$intentName, _config$serialize, _config$shouldValidat, _ref, _config$shouldRevalid, _config$isSchema, _config$validateSchem;
|
|
13
|
+
var config = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};
|
|
14
|
+
/**
|
|
15
|
+
* Global configuration with defaults applied
|
|
16
|
+
*/
|
|
17
|
+
var globalConfig = _objectSpread2(_objectSpread2({}, config), {}, {
|
|
18
|
+
intentName: (_config$intentName = config.intentName) !== null && _config$intentName !== void 0 ? _config$intentName : DEFAULT_INTENT_NAME,
|
|
19
|
+
serialize: (_config$serialize = config.serialize) !== null && _config$serialize !== void 0 ? _config$serialize : serialize,
|
|
20
|
+
shouldValidate: (_config$shouldValidat = config.shouldValidate) !== null && _config$shouldValidat !== void 0 ? _config$shouldValidat : 'onSubmit',
|
|
21
|
+
shouldRevalidate: (_ref = (_config$shouldRevalid = config.shouldRevalidate) !== null && _config$shouldRevalid !== void 0 ? _config$shouldRevalid : config.shouldValidate) !== null && _ref !== void 0 ? _ref : 'onSubmit',
|
|
22
|
+
isSchema: (_config$isSchema = config.isSchema) !== null && _config$isSchema !== void 0 ? _config$isSchema : isStandardSchemaV1,
|
|
23
|
+
validateSchema: (_config$validateSchem = config.validateSchema) !== null && _config$validateSchem !== void 0 ? _config$validateSchem : validateStandardSchemaV1
|
|
24
|
+
});
|
|
25
|
+
|
|
26
|
+
/**
|
|
27
|
+
* React context
|
|
28
|
+
*/
|
|
29
|
+
var ReactFormContext = /*#__PURE__*/createContext([]);
|
|
30
|
+
|
|
31
|
+
/**
|
|
32
|
+
* Provides form context to child components.
|
|
33
|
+
* Stacks contexts to support nested forms, with latest context taking priority.
|
|
34
|
+
*/
|
|
35
|
+
function FormProvider(props) {
|
|
36
|
+
var stack = useContext(ReactFormContext);
|
|
37
|
+
var value = useMemo(
|
|
38
|
+
// Put the latest form context first to ensure that to be the first one found
|
|
39
|
+
() => [props.context].concat(stack), [stack, props.context]);
|
|
40
|
+
return /*#__PURE__*/jsx(ReactFormContext.Provider, {
|
|
41
|
+
value: value,
|
|
42
|
+
children: props.children
|
|
43
|
+
});
|
|
44
|
+
}
|
|
45
|
+
function useFormContext(formId) {
|
|
46
|
+
var contexts = useContext(ReactFormContext);
|
|
47
|
+
var context = formId ? contexts.find(context => formId === context.formId) : contexts[0];
|
|
48
|
+
if (!context) {
|
|
49
|
+
throw new Error('No form context found. ' + 'Wrap your component with <FormProvider context={form.context}> ' + 'where `form` is returned from useForm().');
|
|
50
|
+
}
|
|
51
|
+
return context;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
/**
|
|
55
|
+
* The main React hook for form management. Handles form state, validation, and submission
|
|
56
|
+
* while providing access to form metadata, field objects, and form actions.
|
|
57
|
+
*
|
|
58
|
+
* It can be called in two ways:
|
|
59
|
+
* - **Schema first**: Pass a schema as the first argument for automatic validation with type inference
|
|
60
|
+
* - **Manual configuration**: Pass options with custom `onValidate` handler for manual validation
|
|
61
|
+
*
|
|
62
|
+
* @see https://conform.guide/api/react/future/useForm
|
|
63
|
+
* @example Schema first setup with zod:
|
|
64
|
+
*
|
|
65
|
+
* ```tsx
|
|
66
|
+
* const { form, fields } = useForm(zodSchema, {
|
|
67
|
+
* lastResult,
|
|
68
|
+
* shouldValidate: 'onBlur',
|
|
69
|
+
* });
|
|
70
|
+
*
|
|
71
|
+
* return (
|
|
72
|
+
* <form {...form.props}>
|
|
73
|
+
* <input name={fields.email.name} defaultValue={fields.email.defaultValue} />
|
|
74
|
+
* <div>{fields.email.errors}</div>
|
|
75
|
+
* </form>
|
|
76
|
+
* );
|
|
77
|
+
* ```
|
|
78
|
+
*
|
|
79
|
+
* @example Manual configuration setup with custom validation:
|
|
80
|
+
*
|
|
81
|
+
* ```tsx
|
|
82
|
+
* const { form, fields } = useForm({
|
|
83
|
+
* onValidate({ payload, error }) {
|
|
84
|
+
* if (!payload.email) {
|
|
85
|
+
* error.fieldErrors.email = ['Required'];
|
|
86
|
+
* }
|
|
87
|
+
* return error;
|
|
88
|
+
* }
|
|
89
|
+
* });
|
|
90
|
+
*
|
|
91
|
+
* return (
|
|
92
|
+
* <form {...form.props}>
|
|
93
|
+
* <input name={fields.email.name} defaultValue={fields.email.defaultValue} />
|
|
94
|
+
* <div>{fields.email.errors}</div>
|
|
95
|
+
* </form>
|
|
96
|
+
* );
|
|
97
|
+
* ```
|
|
98
|
+
*/
|
|
99
|
+
|
|
100
|
+
/**
|
|
101
|
+
* @deprecated Use `useForm(schema, options)` instead for better type inference.
|
|
102
|
+
*/
|
|
103
|
+
|
|
104
|
+
function useForm(schemaOrOptions, maybeOptions) {
|
|
105
|
+
var _options$constraint, _globalConfig$getCons, _options$id, _options$onError;
|
|
106
|
+
var schema;
|
|
107
|
+
var options;
|
|
108
|
+
if (globalConfig.isSchema(schemaOrOptions)) {
|
|
109
|
+
schema = schemaOrOptions;
|
|
110
|
+
options = maybeOptions !== null && maybeOptions !== void 0 ? maybeOptions : {};
|
|
111
|
+
} else {
|
|
112
|
+
options = schemaOrOptions;
|
|
113
|
+
}
|
|
114
|
+
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;
|
|
115
|
+
var optionsRef = useLatest(options);
|
|
116
|
+
var fallbackId = useId();
|
|
117
|
+
var formId = (_options$id = options.id) !== null && _options$id !== void 0 ? _options$id : "form-".concat(fallbackId);
|
|
118
|
+
var [state, handleSubmit] = useConform(formId, _objectSpread2(_objectSpread2({}, options), {}, {
|
|
119
|
+
serialize: globalConfig.serialize,
|
|
120
|
+
intentName: globalConfig.intentName,
|
|
121
|
+
onError: (_options$onError = options.onError) !== null && _options$onError !== void 0 ? _options$onError : focusFirstInvalidField,
|
|
122
|
+
onValidate(ctx) {
|
|
123
|
+
var _options$onValidate, _options$onValidate2, _options;
|
|
124
|
+
if (schema) {
|
|
125
|
+
var schemaResult = globalConfig.validateSchema(schema, ctx.payload, options.schemaOptions);
|
|
126
|
+
if (schemaResult instanceof Promise) {
|
|
127
|
+
return schemaResult.then(resolvedResult => {
|
|
128
|
+
if (typeof options.onValidate === 'function') {
|
|
129
|
+
throw new Error('The "onValidate" handler is not supported when used with asynchronous schema validation.');
|
|
130
|
+
}
|
|
131
|
+
return resolvedResult;
|
|
132
|
+
});
|
|
133
|
+
}
|
|
134
|
+
if (!options.onValidate) {
|
|
135
|
+
return schemaResult;
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
// Update the schema error in the context
|
|
139
|
+
if (schemaResult.error) {
|
|
140
|
+
ctx.error = schemaResult.error;
|
|
141
|
+
}
|
|
142
|
+
var schemaValue = schemaResult.value;
|
|
143
|
+
ctx.schemaValue = schemaValue;
|
|
144
|
+
var validateResult = resolveValidateResult(options.onValidate(ctx));
|
|
145
|
+
if (validateResult.syncResult) {
|
|
146
|
+
var _validateResult$syncR, _validateResult$syncR2;
|
|
147
|
+
(_validateResult$syncR2 = (_validateResult$syncR = validateResult.syncResult).value) !== null && _validateResult$syncR2 !== void 0 ? _validateResult$syncR2 : _validateResult$syncR.value = schemaValue;
|
|
148
|
+
}
|
|
149
|
+
if (validateResult.asyncResult) {
|
|
150
|
+
validateResult.asyncResult = validateResult.asyncResult.then(result => {
|
|
151
|
+
var _result$value;
|
|
152
|
+
(_result$value = result.value) !== null && _result$value !== void 0 ? _result$value : result.value = schemaValue;
|
|
153
|
+
return result;
|
|
154
|
+
});
|
|
155
|
+
}
|
|
156
|
+
return [validateResult.syncResult, validateResult.asyncResult];
|
|
157
|
+
}
|
|
158
|
+
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 : {
|
|
159
|
+
// To avoid conform falling back to server validation,
|
|
160
|
+
// if neither schema nor validation handler is provided,
|
|
161
|
+
// we just treat it as a valid client submission
|
|
162
|
+
error: null
|
|
163
|
+
};
|
|
164
|
+
}
|
|
165
|
+
}));
|
|
166
|
+
var intent = useIntent(formId);
|
|
167
|
+
var context = useMemo(() => ({
|
|
168
|
+
formId,
|
|
169
|
+
state,
|
|
170
|
+
constraint: constraint !== null && constraint !== void 0 ? constraint : null,
|
|
171
|
+
handleSubmit,
|
|
172
|
+
handleInput(event) {
|
|
173
|
+
var _optionsRef$current$o, _optionsRef$current, _optionsRef$current$s, _ref2, _optionsRef$current$s2;
|
|
174
|
+
if (!isFieldElement(event.target) || event.target.name === '' || event.target.form === null || event.target.form !== getFormElement(formId)) {
|
|
175
|
+
return;
|
|
176
|
+
}
|
|
177
|
+
(_optionsRef$current$o = (_optionsRef$current = optionsRef.current).onInput) === null || _optionsRef$current$o === void 0 || _optionsRef$current$o.call(_optionsRef$current, _objectSpread2(_objectSpread2({}, event), {}, {
|
|
178
|
+
target: event.target,
|
|
179
|
+
currentTarget: event.target.form
|
|
180
|
+
}));
|
|
181
|
+
if (event.defaultPrevented) {
|
|
182
|
+
return;
|
|
183
|
+
}
|
|
184
|
+
var shouldValidate = (_optionsRef$current$s = optionsRef.current.shouldValidate) !== null && _optionsRef$current$s !== void 0 ? _optionsRef$current$s : globalConfig.shouldValidate;
|
|
185
|
+
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;
|
|
186
|
+
if (isTouched(state, event.target.name) ? shouldRevalidate === 'onInput' : shouldValidate === 'onInput') {
|
|
187
|
+
intent.validate(event.target.name);
|
|
188
|
+
}
|
|
189
|
+
},
|
|
190
|
+
handleBlur(event) {
|
|
191
|
+
var _optionsRef$current$o2, _optionsRef$current2, _optionsRef$current$s3, _ref3, _optionsRef$current$s4;
|
|
192
|
+
if (!isFieldElement(event.target) || event.target.name === '' || event.target.form === null || event.target.form !== getFormElement(formId)) {
|
|
193
|
+
return;
|
|
194
|
+
}
|
|
195
|
+
(_optionsRef$current$o2 = (_optionsRef$current2 = optionsRef.current).onBlur) === null || _optionsRef$current$o2 === void 0 || _optionsRef$current$o2.call(_optionsRef$current2, _objectSpread2(_objectSpread2({}, event), {}, {
|
|
196
|
+
target: event.target,
|
|
197
|
+
currentTarget: event.target.form
|
|
198
|
+
}));
|
|
199
|
+
if (event.defaultPrevented) {
|
|
200
|
+
return;
|
|
201
|
+
}
|
|
202
|
+
var shouldValidate = (_optionsRef$current$s3 = optionsRef.current.shouldValidate) !== null && _optionsRef$current$s3 !== void 0 ? _optionsRef$current$s3 : globalConfig.shouldValidate;
|
|
203
|
+
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;
|
|
204
|
+
if (isTouched(state, event.target.name) ? shouldRevalidate === 'onBlur' : shouldValidate === 'onBlur') {
|
|
205
|
+
intent.validate(event.target.name);
|
|
206
|
+
}
|
|
207
|
+
}
|
|
208
|
+
}), [formId, state, constraint, handleSubmit, intent, optionsRef]);
|
|
209
|
+
var form = useMemo(() => getFormMetadata(context, {
|
|
210
|
+
serialize: globalConfig.serialize,
|
|
211
|
+
extendFormMetadata: globalConfig.extendFormMetadata,
|
|
212
|
+
extendFieldMetadata: globalConfig.extendFieldMetadata
|
|
213
|
+
}), [context]);
|
|
214
|
+
var fields = useMemo(() => getFieldset(context, {
|
|
215
|
+
serialize: globalConfig.serialize,
|
|
216
|
+
extendFieldMetadata: globalConfig.extendFieldMetadata
|
|
217
|
+
}), [context]);
|
|
218
|
+
return {
|
|
219
|
+
form,
|
|
220
|
+
fields,
|
|
221
|
+
intent
|
|
222
|
+
};
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
/**
|
|
226
|
+
* A React hook that provides access to form-level metadata and state.
|
|
227
|
+
* Requires `FormProvider` context when used in child components.
|
|
228
|
+
*
|
|
229
|
+
* @see https://conform.guide/api/react/future/useFormMetadata
|
|
230
|
+
* @example
|
|
231
|
+
* ```tsx
|
|
232
|
+
* function ErrorSummary() {
|
|
233
|
+
* const form = useFormMetadata();
|
|
234
|
+
*
|
|
235
|
+
* if (form.valid) return null;
|
|
236
|
+
*
|
|
237
|
+
* return (
|
|
238
|
+
* <div>Please fix {Object.keys(form.fieldErrors).length} errors</div>
|
|
239
|
+
* );
|
|
240
|
+
* }
|
|
241
|
+
* ```
|
|
242
|
+
*/
|
|
243
|
+
function useFormMetadata() {
|
|
244
|
+
var options = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};
|
|
245
|
+
var context = useFormContext(options.formId);
|
|
246
|
+
var formMetadata = useMemo(() => getFormMetadata(context, {
|
|
247
|
+
serialize: globalConfig.serialize,
|
|
248
|
+
extendFormMetadata: globalConfig.extendFormMetadata,
|
|
249
|
+
extendFieldMetadata: globalConfig.extendFieldMetadata
|
|
250
|
+
}), [context]);
|
|
251
|
+
return formMetadata;
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
/**
|
|
255
|
+
* A React hook that provides access to a specific field's metadata and state.
|
|
256
|
+
* Requires `FormProvider` context when used in child components.
|
|
257
|
+
*
|
|
258
|
+
* @see https://conform.guide/api/react/future/useField
|
|
259
|
+
* @example
|
|
260
|
+
* ```tsx
|
|
261
|
+
* function FormField({ name, label }) {
|
|
262
|
+
* const field = useField(name);
|
|
263
|
+
*
|
|
264
|
+
* return (
|
|
265
|
+
* <div>
|
|
266
|
+
* <label htmlFor={field.id}>{label}</label>
|
|
267
|
+
* <input id={field.id} name={field.name} defaultValue={field.defaultValue} />
|
|
268
|
+
* {field.errors && <div>{field.errors.join(', ')}</div>}
|
|
269
|
+
* </div>
|
|
270
|
+
* );
|
|
271
|
+
* }
|
|
272
|
+
* ```
|
|
273
|
+
*/
|
|
274
|
+
function useField(name) {
|
|
275
|
+
var options = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};
|
|
276
|
+
var context = useFormContext(options.formId);
|
|
277
|
+
var field = useMemo(() => getField(context, {
|
|
278
|
+
name,
|
|
279
|
+
serialize: globalConfig.serialize,
|
|
280
|
+
extendFieldMetadata: globalConfig.extendFieldMetadata
|
|
281
|
+
}), [context, name]);
|
|
282
|
+
return field;
|
|
283
|
+
}
|
|
284
|
+
|
|
285
|
+
/**
|
|
286
|
+
* A React hook that provides an intent dispatcher for programmatic form actions.
|
|
287
|
+
* Intent dispatchers allow you to trigger form operations like validation, field updates,
|
|
288
|
+
* and array manipulations without manual form submission.
|
|
289
|
+
*
|
|
290
|
+
* @see https://conform.guide/api/react/future/useIntent
|
|
291
|
+
* @example
|
|
292
|
+
* ```tsx
|
|
293
|
+
* function ResetButton() {
|
|
294
|
+
* const buttonRef = useRef<HTMLButtonElement>(null);
|
|
295
|
+
* const intent = useIntent(buttonRef);
|
|
296
|
+
*
|
|
297
|
+
* return (
|
|
298
|
+
* <button type="button" ref={buttonRef} onClick={() => intent.reset()}>
|
|
299
|
+
* Reset Form
|
|
300
|
+
* </button>
|
|
301
|
+
* );
|
|
302
|
+
* }
|
|
303
|
+
* ```
|
|
304
|
+
*/
|
|
305
|
+
function useIntent(formRef) {
|
|
306
|
+
return useMemo(() => createIntentDispatcher(() => getFormElement(formRef), globalConfig.intentName), [formRef]);
|
|
307
|
+
}
|
|
308
|
+
return {
|
|
309
|
+
FormProvider,
|
|
310
|
+
useForm,
|
|
311
|
+
useFormMetadata,
|
|
312
|
+
useField,
|
|
313
|
+
useIntent,
|
|
314
|
+
config: globalConfig
|
|
315
|
+
};
|
|
316
|
+
}
|
|
317
|
+
|
|
318
|
+
export { configureForms };
|
package/dist/future/hooks.d.ts
CHANGED
|
@@ -15,6 +15,9 @@ export declare function FormProvider(props: {
|
|
|
15
15
|
context: FormContext;
|
|
16
16
|
children: React.ReactNode;
|
|
17
17
|
}): React.ReactElement;
|
|
18
|
+
/**
|
|
19
|
+
* @deprecated Replaced by the `configureForms` factory API. This will be removed in the next minor version. If you are not ready to migrate, please pin to `v1.16.0`.
|
|
20
|
+
*/
|
|
18
21
|
export declare function FormOptionsProvider(props: Partial<GlobalFormOptions> & {
|
|
19
22
|
children: React.ReactNode;
|
|
20
23
|
}): React.ReactElement;
|
|
@@ -206,18 +209,36 @@ export declare function useControl(options?: {
|
|
|
206
209
|
* A React hook that lets you subscribe to the current `FormData` of a form and derive a custom value from it.
|
|
207
210
|
* The selector runs whenever the form's structure or data changes, and the hook re-renders only when the result is deeply different.
|
|
208
211
|
*
|
|
212
|
+
* Returns `undefined` when the form element is not available (e.g., on SSR or initial client render),
|
|
213
|
+
* unless a `fallback` is provided.
|
|
214
|
+
*
|
|
209
215
|
* @see https://conform.guide/api/react/future/useFormData
|
|
210
216
|
* @example
|
|
211
217
|
* ```ts
|
|
212
|
-
* const value = useFormData(
|
|
218
|
+
* const value = useFormData(
|
|
219
|
+
* formRef,
|
|
220
|
+
* formData => formData.get('fieldName') ?? '',
|
|
221
|
+
* );
|
|
213
222
|
* ```
|
|
214
223
|
*/
|
|
215
|
-
export declare function useFormData<Value
|
|
224
|
+
export declare function useFormData<Value>(formRef: FormRef, select: Selector<FormData, Value>, options: UseFormDataOptions<Value> & {
|
|
216
225
|
acceptFiles: true;
|
|
226
|
+
fallback: Value;
|
|
217
227
|
}): Value;
|
|
218
|
-
export declare function useFormData<Value
|
|
219
|
-
acceptFiles
|
|
228
|
+
export declare function useFormData<Value>(formRef: FormRef, select: Selector<FormData, Value>, options: UseFormDataOptions & {
|
|
229
|
+
acceptFiles: true;
|
|
230
|
+
}): Value | undefined;
|
|
231
|
+
export declare function useFormData<Value>(formRef: FormRef, select: Selector<URLSearchParams, Value>, options: UseFormDataOptions<Value> & {
|
|
232
|
+
acceptFiles?: false;
|
|
233
|
+
fallback: Value;
|
|
220
234
|
}): Value;
|
|
235
|
+
export declare function useFormData<Value>(formRef: FormRef, select: Selector<URLSearchParams, Value>, options?: UseFormDataOptions & {
|
|
236
|
+
acceptFiles?: false;
|
|
237
|
+
}): Value | undefined;
|
|
238
|
+
export declare function useFormData<Value>(formRef: FormRef, select: Selector<FormData, Value> | Selector<URLSearchParams, Value>, options?: UseFormDataOptions & {
|
|
239
|
+
acceptFiles?: boolean;
|
|
240
|
+
fallback?: Value;
|
|
241
|
+
}): Value | undefined;
|
|
221
242
|
/**
|
|
222
243
|
* useLayoutEffect is client-only.
|
|
223
244
|
* This basically makes it a no-op on server
|
package/dist/future/hooks.js
CHANGED
|
@@ -38,6 +38,10 @@ function FormProvider(props) {
|
|
|
38
38
|
children: props.children
|
|
39
39
|
});
|
|
40
40
|
}
|
|
41
|
+
|
|
42
|
+
/**
|
|
43
|
+
* @deprecated Replaced by the `configureForms` factory API. This will be removed in the next minor version. If you are not ready to migrate, please pin to `v1.16.0`.
|
|
44
|
+
*/
|
|
41
45
|
function FormOptionsProvider(props) {
|
|
42
46
|
var {
|
|
43
47
|
children
|
|
@@ -465,11 +469,11 @@ function useForm(schemaOrOptions, maybeOptions) {
|
|
|
465
469
|
}), [formId, state$1, constraint, handleSubmit, intent, optionsRef, globalOptionsRef]);
|
|
466
470
|
var form = react.useMemo(() => state.getFormMetadata(context, {
|
|
467
471
|
serialize: globalOptions.serialize,
|
|
468
|
-
|
|
472
|
+
extendFieldMetadata: globalOptions.defineCustomMetadata
|
|
469
473
|
}), [context, globalOptions.serialize, globalOptions.defineCustomMetadata]);
|
|
470
474
|
var fields = react.useMemo(() => state.getFieldset(context, {
|
|
471
475
|
serialize: globalOptions.serialize,
|
|
472
|
-
|
|
476
|
+
extendFieldMetadata: globalOptions.defineCustomMetadata
|
|
473
477
|
}), [context, globalOptions.serialize, globalOptions.defineCustomMetadata]);
|
|
474
478
|
return {
|
|
475
479
|
form,
|
|
@@ -502,7 +506,7 @@ function useFormMetadata() {
|
|
|
502
506
|
var context = useFormContext(options.formId);
|
|
503
507
|
var formMetadata = react.useMemo(() => state.getFormMetadata(context, {
|
|
504
508
|
serialize: globalOptions.serialize,
|
|
505
|
-
|
|
509
|
+
extendFieldMetadata: globalOptions.defineCustomMetadata
|
|
506
510
|
}), [context, globalOptions.serialize, globalOptions.defineCustomMetadata]);
|
|
507
511
|
return formMetadata;
|
|
508
512
|
}
|
|
@@ -534,7 +538,7 @@ function useField(name) {
|
|
|
534
538
|
var field = react.useMemo(() => state.getField(context, {
|
|
535
539
|
name,
|
|
536
540
|
serialize: globalOptions.serialize,
|
|
537
|
-
|
|
541
|
+
extendFieldMetadata: globalOptions.defineCustomMetadata
|
|
538
542
|
}), [context, name, globalOptions.serialize, globalOptions.defineCustomMetadata]);
|
|
539
543
|
return field;
|
|
540
544
|
}
|
|
@@ -757,10 +761,16 @@ function useControl(options) {
|
|
|
757
761
|
* A React hook that lets you subscribe to the current `FormData` of a form and derive a custom value from it.
|
|
758
762
|
* The selector runs whenever the form's structure or data changes, and the hook re-renders only when the result is deeply different.
|
|
759
763
|
*
|
|
764
|
+
* Returns `undefined` when the form element is not available (e.g., on SSR or initial client render),
|
|
765
|
+
* unless a `fallback` is provided.
|
|
766
|
+
*
|
|
760
767
|
* @see https://conform.guide/api/react/future/useFormData
|
|
761
768
|
* @example
|
|
762
769
|
* ```ts
|
|
763
|
-
* const value = useFormData(
|
|
770
|
+
* const value = useFormData(
|
|
771
|
+
* formRef,
|
|
772
|
+
* formData => formData.get('fieldName') ?? '',
|
|
773
|
+
* );
|
|
764
774
|
* ```
|
|
765
775
|
*/
|
|
766
776
|
|
|
@@ -769,7 +779,7 @@ function useFormData(formRef, select, options) {
|
|
|
769
779
|
observer
|
|
770
780
|
} = react.useContext(GlobalFormOptionsContext);
|
|
771
781
|
var valueRef = react.useRef();
|
|
772
|
-
var formDataRef = react.useRef(
|
|
782
|
+
var formDataRef = react.useRef();
|
|
773
783
|
var value = react.useSyncExternalStore(react.useCallback(callback => {
|
|
774
784
|
var formElement = dom.getFormElement(formRef);
|
|
775
785
|
if (formElement) {
|
|
@@ -791,14 +801,17 @@ function useFormData(formRef, select, options) {
|
|
|
791
801
|
});
|
|
792
802
|
return unsubscribe;
|
|
793
803
|
}, [observer, formRef, options === null || options === void 0 ? void 0 : options.acceptFiles]), () => {
|
|
794
|
-
//
|
|
804
|
+
// Return fallback if form is not available
|
|
805
|
+
if (formDataRef.current === undefined) {
|
|
806
|
+
return options === null || options === void 0 ? void 0 : options.fallback;
|
|
807
|
+
}
|
|
795
808
|
var result = select(formDataRef.current, valueRef.current);
|
|
796
809
|
if (typeof valueRef.current !== 'undefined' && future.deepEqual(result, valueRef.current)) {
|
|
797
810
|
return valueRef.current;
|
|
798
811
|
}
|
|
799
812
|
valueRef.current = result;
|
|
800
813
|
return result;
|
|
801
|
-
}, () =>
|
|
814
|
+
}, () => options === null || options === void 0 ? void 0 : options.fallback);
|
|
802
815
|
return value;
|
|
803
816
|
}
|
|
804
817
|
|
package/dist/future/hooks.mjs
CHANGED
|
@@ -34,6 +34,10 @@ function FormProvider(props) {
|
|
|
34
34
|
children: props.children
|
|
35
35
|
});
|
|
36
36
|
}
|
|
37
|
+
|
|
38
|
+
/**
|
|
39
|
+
* @deprecated Replaced by the `configureForms` factory API. This will be removed in the next minor version. If you are not ready to migrate, please pin to `v1.16.0`.
|
|
40
|
+
*/
|
|
37
41
|
function FormOptionsProvider(props) {
|
|
38
42
|
var {
|
|
39
43
|
children
|
|
@@ -461,11 +465,11 @@ function useForm(schemaOrOptions, maybeOptions) {
|
|
|
461
465
|
}), [formId, state, constraint, handleSubmit, intent, optionsRef, globalOptionsRef]);
|
|
462
466
|
var form = useMemo(() => getFormMetadata(context, {
|
|
463
467
|
serialize: globalOptions.serialize,
|
|
464
|
-
|
|
468
|
+
extendFieldMetadata: globalOptions.defineCustomMetadata
|
|
465
469
|
}), [context, globalOptions.serialize, globalOptions.defineCustomMetadata]);
|
|
466
470
|
var fields = useMemo(() => getFieldset(context, {
|
|
467
471
|
serialize: globalOptions.serialize,
|
|
468
|
-
|
|
472
|
+
extendFieldMetadata: globalOptions.defineCustomMetadata
|
|
469
473
|
}), [context, globalOptions.serialize, globalOptions.defineCustomMetadata]);
|
|
470
474
|
return {
|
|
471
475
|
form,
|
|
@@ -498,7 +502,7 @@ function useFormMetadata() {
|
|
|
498
502
|
var context = useFormContext(options.formId);
|
|
499
503
|
var formMetadata = useMemo(() => getFormMetadata(context, {
|
|
500
504
|
serialize: globalOptions.serialize,
|
|
501
|
-
|
|
505
|
+
extendFieldMetadata: globalOptions.defineCustomMetadata
|
|
502
506
|
}), [context, globalOptions.serialize, globalOptions.defineCustomMetadata]);
|
|
503
507
|
return formMetadata;
|
|
504
508
|
}
|
|
@@ -530,7 +534,7 @@ function useField(name) {
|
|
|
530
534
|
var field = useMemo(() => getField(context, {
|
|
531
535
|
name,
|
|
532
536
|
serialize: globalOptions.serialize,
|
|
533
|
-
|
|
537
|
+
extendFieldMetadata: globalOptions.defineCustomMetadata
|
|
534
538
|
}), [context, name, globalOptions.serialize, globalOptions.defineCustomMetadata]);
|
|
535
539
|
return field;
|
|
536
540
|
}
|
|
@@ -753,10 +757,16 @@ function useControl(options) {
|
|
|
753
757
|
* A React hook that lets you subscribe to the current `FormData` of a form and derive a custom value from it.
|
|
754
758
|
* The selector runs whenever the form's structure or data changes, and the hook re-renders only when the result is deeply different.
|
|
755
759
|
*
|
|
760
|
+
* Returns `undefined` when the form element is not available (e.g., on SSR or initial client render),
|
|
761
|
+
* unless a `fallback` is provided.
|
|
762
|
+
*
|
|
756
763
|
* @see https://conform.guide/api/react/future/useFormData
|
|
757
764
|
* @example
|
|
758
765
|
* ```ts
|
|
759
|
-
* const value = useFormData(
|
|
766
|
+
* const value = useFormData(
|
|
767
|
+
* formRef,
|
|
768
|
+
* formData => formData.get('fieldName') ?? '',
|
|
769
|
+
* );
|
|
760
770
|
* ```
|
|
761
771
|
*/
|
|
762
772
|
|
|
@@ -765,7 +775,7 @@ function useFormData(formRef, select, options) {
|
|
|
765
775
|
observer
|
|
766
776
|
} = useContext(GlobalFormOptionsContext);
|
|
767
777
|
var valueRef = useRef();
|
|
768
|
-
var formDataRef = useRef(
|
|
778
|
+
var formDataRef = useRef();
|
|
769
779
|
var value = useSyncExternalStore(useCallback(callback => {
|
|
770
780
|
var formElement = getFormElement(formRef);
|
|
771
781
|
if (formElement) {
|
|
@@ -787,14 +797,17 @@ function useFormData(formRef, select, options) {
|
|
|
787
797
|
});
|
|
788
798
|
return unsubscribe;
|
|
789
799
|
}, [observer, formRef, options === null || options === void 0 ? void 0 : options.acceptFiles]), () => {
|
|
790
|
-
//
|
|
800
|
+
// Return fallback if form is not available
|
|
801
|
+
if (formDataRef.current === undefined) {
|
|
802
|
+
return options === null || options === void 0 ? void 0 : options.fallback;
|
|
803
|
+
}
|
|
791
804
|
var result = select(formDataRef.current, valueRef.current);
|
|
792
805
|
if (typeof valueRef.current !== 'undefined' && deepEqual(result, valueRef.current)) {
|
|
793
806
|
return valueRef.current;
|
|
794
807
|
}
|
|
795
808
|
valueRef.current = result;
|
|
796
809
|
return result;
|
|
797
|
-
}, () =>
|
|
810
|
+
}, () => options === null || options === void 0 ? void 0 : options.fallback);
|
|
798
811
|
return value;
|
|
799
812
|
}
|
|
800
813
|
|
package/dist/future/index.d.ts
CHANGED
|
@@ -1,6 +1,8 @@
|
|
|
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, DefaultValue, BaseMetadata, CustomMetadata, CustomMetadataDefinition, BaseErrorShape, CustomTypes, FormContext, FormMetadata, FormOptions, FormRef, FieldMetadata, Fieldset, IntentDispatcher, } from './types';
|
|
3
|
+
export type { Control, DefaultValue, BaseMetadata, BaseFieldMetadata, CustomMetadata, CustomMetadataDefinition, BaseErrorShape, CustomTypes, CustomSchemaTypes, FormsConfig, FormContext, FormMetadata, FormOptions, FormRef, FieldMetadata, Fieldset, IntentDispatcher, InferBaseErrorShape, InferCustomFormMetadata, InferCustomFieldMetadata, } from './types';
|
|
4
|
+
export { configureForms } from './forms';
|
|
4
5
|
export { FormProvider, FormOptionsProvider, useControl, useField, useForm, useFormData, useFormMetadata, useIntent, } from './hooks';
|
|
6
|
+
export { shape } from './util';
|
|
5
7
|
export { memoize } from './memoize';
|
|
6
8
|
//# sourceMappingURL=index.d.ts.map
|
package/dist/future/index.js
CHANGED
|
@@ -3,7 +3,9 @@
|
|
|
3
3
|
Object.defineProperty(exports, '__esModule', { value: true });
|
|
4
4
|
|
|
5
5
|
var future = require('@conform-to/dom/future');
|
|
6
|
+
var forms = require('./forms.js');
|
|
6
7
|
var hooks = require('./hooks.js');
|
|
8
|
+
var util = require('./util.js');
|
|
7
9
|
var memoize = require('./memoize.js');
|
|
8
10
|
|
|
9
11
|
|
|
@@ -24,6 +26,7 @@ Object.defineProperty(exports, 'report', {
|
|
|
24
26
|
enumerable: true,
|
|
25
27
|
get: function () { return future.report; }
|
|
26
28
|
});
|
|
29
|
+
exports.configureForms = forms.configureForms;
|
|
27
30
|
exports.FormOptionsProvider = hooks.FormOptionsProvider;
|
|
28
31
|
exports.FormProvider = hooks.FormProvider;
|
|
29
32
|
exports.useControl = hooks.useControl;
|
|
@@ -32,4 +35,5 @@ exports.useForm = hooks.useForm;
|
|
|
32
35
|
exports.useFormData = hooks.useFormData;
|
|
33
36
|
exports.useFormMetadata = hooks.useFormMetadata;
|
|
34
37
|
exports.useIntent = hooks.useIntent;
|
|
38
|
+
exports.shape = util.shape;
|
|
35
39
|
exports.memoize = memoize.memoize;
|
package/dist/future/index.mjs
CHANGED
|
@@ -1,3 +1,5 @@
|
|
|
1
1
|
export { getFieldValue, isDirty, parseSubmission, report } from '@conform-to/dom/future';
|
|
2
|
+
export { configureForms } from './forms.mjs';
|
|
2
3
|
export { FormOptionsProvider, FormProvider, useControl, useField, useForm, useFormData, useFormMetadata, useIntent } from './hooks.mjs';
|
|
4
|
+
export { shape } from './util.mjs';
|
|
3
5
|
export { memoize } from './memoize.mjs';
|
package/dist/future/state.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { type FieldName, type ValidationAttributes, type Serialize } from '@conform-to/dom/future';
|
|
2
|
-
import type { FieldMetadata, Fieldset, FormContext, FormMetadata, FormState, FormAction, UnknownIntent, ActionHandler,
|
|
2
|
+
import type { FieldMetadata, Fieldset, FormContext, FormMetadata, FormState, FormAction, UnknownIntent, ActionHandler, BaseFieldMetadata, BaseFormMetadata, DefineConditionalField } from './types';
|
|
3
3
|
export declare function initializeState<ErrorShape>(options?: {
|
|
4
4
|
defaultValue?: Record<string, unknown> | null | undefined;
|
|
5
5
|
resetKey?: string | undefined;
|
|
@@ -39,32 +39,47 @@ export declare function isValid(state: FormState<any>, name?: string): boolean;
|
|
|
39
39
|
* e.g. "array[0].key" falls back to "array[].key" if specific constraint not found.
|
|
40
40
|
*/
|
|
41
41
|
export declare function getConstraint(context: FormContext<any>, name: string): ValidationAttributes | undefined;
|
|
42
|
-
export declare function getFormMetadata<ErrorShape>(context: FormContext<ErrorShape>, options?: {
|
|
42
|
+
export declare function getFormMetadata<ErrorShape, CustomFormMetadata extends Record<string, unknown> = {}, CustomFieldMetadata extends Record<string, unknown> = {}>(context: FormContext<ErrorShape>, options?: {
|
|
43
43
|
serialize?: Serialize | undefined;
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
44
|
+
extendFormMetadata?: ((metadata: BaseFormMetadata<ErrorShape>) => CustomFormMetadata) | undefined;
|
|
45
|
+
extendFieldMetadata?: (<FieldShape>(metadata: BaseFieldMetadata<FieldShape, ErrorShape>, ctx: {
|
|
46
|
+
form: BaseFormMetadata<ErrorShape>;
|
|
47
|
+
when: DefineConditionalField;
|
|
48
|
+
}) => CustomFieldMetadata) | undefined;
|
|
49
|
+
}): FormMetadata<ErrorShape, CustomFormMetadata, CustomFieldMetadata>;
|
|
50
|
+
export declare function getField<FieldShape, ErrorShape = string, CustomFieldMetadata extends Record<string, unknown> = {}>(context: FormContext<ErrorShape>, options: {
|
|
47
51
|
name: FieldName<FieldShape>;
|
|
48
52
|
serialize?: Serialize | undefined;
|
|
49
|
-
|
|
53
|
+
extendFieldMetadata?: (<F>(metadata: BaseFieldMetadata<F, ErrorShape>, ctx: {
|
|
54
|
+
form: BaseFormMetadata<ErrorShape>;
|
|
55
|
+
when: DefineConditionalField;
|
|
56
|
+
}) => CustomFieldMetadata) | undefined;
|
|
57
|
+
form?: BaseFormMetadata<ErrorShape, CustomFieldMetadata> | undefined;
|
|
50
58
|
key?: string | undefined;
|
|
51
|
-
}): FieldMetadata<FieldShape, ErrorShape>;
|
|
59
|
+
}): FieldMetadata<FieldShape, ErrorShape, CustomFieldMetadata>;
|
|
52
60
|
/**
|
|
53
61
|
* Creates a proxy that dynamically generates field objects when properties are accessed.
|
|
54
62
|
*/
|
|
55
|
-
export declare function getFieldset<FieldShape = Record<string, any>, ErrorShape = string>(context: FormContext<ErrorShape>, options: {
|
|
63
|
+
export declare function getFieldset<FieldShape = Record<string, any>, ErrorShape = string, CustomFieldMetadata extends Record<string, unknown> = {}>(context: FormContext<ErrorShape>, options: {
|
|
56
64
|
name?: FieldName<FieldShape> | undefined;
|
|
57
65
|
serialize?: Serialize | undefined;
|
|
58
|
-
|
|
59
|
-
|
|
66
|
+
extendFieldMetadata?: (<F>(metadata: BaseFieldMetadata<F, ErrorShape>, ctx: {
|
|
67
|
+
form: BaseFormMetadata<ErrorShape>;
|
|
68
|
+
when: DefineConditionalField;
|
|
69
|
+
}) => CustomFieldMetadata) | undefined;
|
|
70
|
+
form?: BaseFormMetadata<ErrorShape, CustomFieldMetadata> | undefined;
|
|
71
|
+
}): Fieldset<FieldShape, ErrorShape, CustomFieldMetadata>;
|
|
60
72
|
/**
|
|
61
73
|
* Creates an array of field objects for list/array inputs
|
|
62
74
|
*/
|
|
63
|
-
export declare function getFieldList<FieldShape = Array<any>, ErrorShape = string>(context: FormContext<ErrorShape>, options: {
|
|
75
|
+
export declare function getFieldList<FieldShape = Array<any>, ErrorShape = string, CustomFieldMetadata extends Record<string, unknown> = {}>(context: FormContext<ErrorShape>, options: {
|
|
64
76
|
name: FieldName<FieldShape>;
|
|
65
77
|
serialize?: Serialize | undefined;
|
|
66
|
-
|
|
78
|
+
extendFieldMetadata?: (<F>(metadata: BaseFieldMetadata<F, ErrorShape>, ctx: {
|
|
79
|
+
form: BaseFormMetadata<ErrorShape>;
|
|
80
|
+
when: DefineConditionalField;
|
|
81
|
+
}) => CustomFieldMetadata) | undefined;
|
|
67
82
|
}): FieldMetadata<[
|
|
68
83
|
FieldShape
|
|
69
|
-
] extends [Array<infer ItemShape> | null | undefined] ? ItemShape : unknown, ErrorShape>[];
|
|
84
|
+
] extends [Array<infer ItemShape> | null | undefined] ? ItemShape : unknown, ErrorShape, CustomFieldMetadata>[];
|
|
70
85
|
//# sourceMappingURL=state.d.ts.map
|