@conform-to/react 1.18.0 → 1.19.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 +6 -2
- package/dist/future/dom.mjs +6 -2
- package/dist/future/forms.d.ts +1157 -26
- package/dist/future/forms.js +152 -23
- package/dist/future/forms.mjs +150 -26
- package/dist/future/hooks.d.ts +25 -164
- package/dist/future/hooks.js +34 -355
- package/dist/future/hooks.mjs +39 -352
- package/dist/future/index.d.ts +3 -3
- package/dist/future/index.js +5 -6
- package/dist/future/index.mjs +2 -2
- package/dist/future/intent.js +24 -12
- package/dist/future/intent.mjs +24 -12
- package/dist/future/memoize.d.ts +1 -1
- package/dist/future/memoize.js +1 -1
- package/dist/future/memoize.mjs +1 -1
- package/dist/future/state.d.ts +8 -12
- package/dist/future/state.js +24 -27
- package/dist/future/state.mjs +25 -28
- package/dist/future/types.d.ts +66 -111
- package/dist/future/util.d.ts +6 -10
- package/dist/future/util.js +17 -20
- package/dist/future/util.mjs +16 -19
- package/dist/helpers.d.ts +14 -10
- package/dist/helpers.js +14 -10
- package/dist/helpers.mjs +14 -10
- package/package.json +2 -2
package/dist/future/hooks.mjs
CHANGED
|
@@ -1,49 +1,27 @@
|
|
|
1
1
|
'use client';
|
|
2
2
|
import { objectSpread2 as _objectSpread2, objectWithoutProperties as _objectWithoutProperties } from '../_virtual/_rollupPluginBabelHelpers.mjs';
|
|
3
|
-
import {
|
|
4
|
-
import { createContext, useContext, useMemo,
|
|
5
|
-
import {
|
|
6
|
-
import {
|
|
3
|
+
import { createGlobalFormsObserver, isGlobalInstance, deepEqual, isFieldElement, change, focus, blur, getFormData, defaultSerialize, isPlainObject, normalizeFormError, dispatchInternalUpdateEvent, parseSubmission, report, requestSubmit } from '@conform-to/dom/future';
|
|
4
|
+
import { createContext, useRef, useContext, useMemo, useState, useEffect, useSyncExternalStore, useCallback, useLayoutEffect, forwardRef } from 'react';
|
|
5
|
+
import { appendUniqueItem, resolveValidateResult } from './util.mjs';
|
|
6
|
+
import { initializeState, updateState } from './state.mjs';
|
|
7
7
|
import { deserializeIntent, applyIntent, intentHandlers, resolveIntent } from './intent.mjs';
|
|
8
|
-
import { cleanupPreservedInputs, preserveInputs,
|
|
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';
|
|
11
11
|
|
|
12
|
-
var _excluded = ["
|
|
13
|
-
_excluded2 = ["
|
|
14
|
-
_excluded3 = ["defaultValue", "
|
|
15
|
-
_excluded4 = ["defaultValue", "hidden"],
|
|
16
|
-
_excluded5 = ["defaultValue", "
|
|
17
|
-
_excluded6 = ["defaultValue", "hidden"];
|
|
12
|
+
var _excluded = ["name", "form", "defaultValue", "hidden"],
|
|
13
|
+
_excluded2 = ["defaultValue", "multiple", "hidden"],
|
|
14
|
+
_excluded3 = ["defaultValue", "hidden"],
|
|
15
|
+
_excluded4 = ["defaultValue", "value", "hidden"],
|
|
16
|
+
_excluded5 = ["defaultValue", "hidden"];
|
|
18
17
|
var INITIAL_KEY = 'INITIAL_KEY';
|
|
19
|
-
var
|
|
20
|
-
intentName: DEFAULT_INTENT_NAME,
|
|
21
|
-
observer: createGlobalFormsObserver(),
|
|
22
|
-
serialize,
|
|
23
|
-
shouldValidate: 'onSubmit'
|
|
24
|
-
});
|
|
25
|
-
var FormContextContext = /*#__PURE__*/createContext([]);
|
|
26
|
-
|
|
27
|
-
/**
|
|
28
|
-
* Provides form context to child components.
|
|
29
|
-
* Stacks contexts to support nested forms, with latest context taking priority.
|
|
30
|
-
*/
|
|
31
|
-
function FormProvider(props) {
|
|
32
|
-
var stack = useContext(FormContextContext);
|
|
33
|
-
var value = useMemo(
|
|
34
|
-
// Put the latest form context first to ensure that to be the first one found
|
|
35
|
-
() => [props.context].concat(stack), [stack, props.context]);
|
|
36
|
-
return /*#__PURE__*/jsx(FormContextContext.Provider, {
|
|
37
|
-
value: value,
|
|
38
|
-
children: props.children
|
|
39
|
-
});
|
|
40
|
-
}
|
|
18
|
+
var GlobalFormsObserverContext = /*#__PURE__*/createContext(createGlobalFormsObserver());
|
|
41
19
|
|
|
42
20
|
/**
|
|
43
21
|
* Preserves form field values when its contents are unmounted.
|
|
44
22
|
* Useful for multi-step forms and virtualized lists.
|
|
45
23
|
*
|
|
46
|
-
*
|
|
24
|
+
* See https://conform.guide/api/react/future/PreserveBoundary
|
|
47
25
|
*/
|
|
48
26
|
function PreserveBoundary(props) {
|
|
49
27
|
// name is used as key so React properly unmounts/remounts when switching
|
|
@@ -80,30 +58,6 @@ function PreserveBoundaryImpl(props) {
|
|
|
80
58
|
});
|
|
81
59
|
}
|
|
82
60
|
|
|
83
|
-
/**
|
|
84
|
-
* @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`.
|
|
85
|
-
*/
|
|
86
|
-
function FormOptionsProvider(props) {
|
|
87
|
-
var {
|
|
88
|
-
children
|
|
89
|
-
} = props,
|
|
90
|
-
providedOptions = _objectWithoutProperties(props, _excluded);
|
|
91
|
-
var defaultOptions = useContext(GlobalFormOptionsContext);
|
|
92
|
-
var options = useMemo(() => _objectSpread2(_objectSpread2({}, defaultOptions), providedOptions), [defaultOptions, providedOptions]);
|
|
93
|
-
return /*#__PURE__*/jsx(GlobalFormOptionsContext.Provider, {
|
|
94
|
-
value: options,
|
|
95
|
-
children: children
|
|
96
|
-
});
|
|
97
|
-
}
|
|
98
|
-
function useFormContext(formId) {
|
|
99
|
-
var contexts = useContext(FormContextContext);
|
|
100
|
-
var context = formId ? contexts.find(context => formId === context.formId) : contexts[0];
|
|
101
|
-
if (!context) {
|
|
102
|
-
throw new Error('No form context found; Have you render a <FormProvider /> with the corresponding form context?');
|
|
103
|
-
}
|
|
104
|
-
return context;
|
|
105
|
-
}
|
|
106
|
-
|
|
107
61
|
/**
|
|
108
62
|
* Core form hook that manages form state, validation, and submission.
|
|
109
63
|
* Handles both sync and async validation, intent dispatching, and DOM updates.
|
|
@@ -146,8 +100,11 @@ function useConform(formRef, options) {
|
|
|
146
100
|
var abortControllerRef = useRef(null);
|
|
147
101
|
var handleSubmission = useCallback(function (type, result) {
|
|
148
102
|
var options = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : optionsRef.current;
|
|
103
|
+
var normalizedResult = !result.error ? result : _objectSpread2(_objectSpread2({}, result), {}, {
|
|
104
|
+
error: normalizeFormError(result.error)
|
|
105
|
+
});
|
|
149
106
|
var intent = result.submission.intent ? deserializeIntent(result.submission.intent) : null;
|
|
150
|
-
var finalResult = applyIntent(
|
|
107
|
+
var finalResult = applyIntent(normalizedResult, intent, {
|
|
151
108
|
handlers: intentHandlers
|
|
152
109
|
});
|
|
153
110
|
var formElement = getFormElement(formRef);
|
|
@@ -159,7 +116,7 @@ function useConform(formRef, options) {
|
|
|
159
116
|
intent,
|
|
160
117
|
ctx: {
|
|
161
118
|
handlers: intentHandlers,
|
|
162
|
-
cancelled: finalResult !==
|
|
119
|
+
cancelled: finalResult !== normalizedResult,
|
|
163
120
|
reset(defaultValue) {
|
|
164
121
|
return initializeState({
|
|
165
122
|
defaultValue: defaultValue !== null && defaultValue !== void 0 ? defaultValue : options.defaultValue
|
|
@@ -169,11 +126,11 @@ function useConform(formRef, options) {
|
|
|
169
126
|
})));
|
|
170
127
|
|
|
171
128
|
// TODO: move on error handler to a new effect
|
|
172
|
-
if (formElement &&
|
|
129
|
+
if (formElement && finalResult.error) {
|
|
173
130
|
var _optionsRef$current$o, _optionsRef$current;
|
|
174
131
|
(_optionsRef$current$o = (_optionsRef$current = optionsRef.current).onError) === null || _optionsRef$current$o === void 0 || _optionsRef$current$o.call(_optionsRef$current, {
|
|
175
132
|
formElement,
|
|
176
|
-
error:
|
|
133
|
+
error: finalResult.error,
|
|
177
134
|
intent
|
|
178
135
|
});
|
|
179
136
|
}
|
|
@@ -271,7 +228,7 @@ function useConform(formRef, options) {
|
|
|
271
228
|
value !== undefined ? (_optionsRef$current$o2 = (_optionsRef$current2 = optionsRef.current).onValidate) === null || _optionsRef$current$o2 === void 0 ? void 0 : _optionsRef$current$o2.call(_optionsRef$current2, {
|
|
272
229
|
payload: value,
|
|
273
230
|
error: {
|
|
274
|
-
formErrors:
|
|
231
|
+
formErrors: null,
|
|
275
232
|
fieldErrors: {}
|
|
276
233
|
},
|
|
277
234
|
intent: submission.intent ? deserializeIntent(submission.intent) : null,
|
|
@@ -364,279 +321,12 @@ function useConform(formRef, options) {
|
|
|
364
321
|
return [state, handleSubmit];
|
|
365
322
|
}
|
|
366
323
|
|
|
367
|
-
/**
|
|
368
|
-
* The main React hook for form management. Handles form state, validation, and submission
|
|
369
|
-
* while providing access to form metadata, field objects, and form actions.
|
|
370
|
-
*
|
|
371
|
-
* It can be called in two ways:
|
|
372
|
-
* - **Schema first**: Pass a schema as the first argument for automatic validation with type inference
|
|
373
|
-
* - **Manual configuration**: Pass options with custom `onValidate` handler for manual validation
|
|
374
|
-
*
|
|
375
|
-
* @see https://conform.guide/api/react/future/useForm
|
|
376
|
-
* @example Schema first setup with zod:
|
|
377
|
-
*
|
|
378
|
-
* ```tsx
|
|
379
|
-
* const { form, fields } = useForm(zodSchema, {
|
|
380
|
-
* lastResult,
|
|
381
|
-
* shouldValidate: 'onBlur',
|
|
382
|
-
* });
|
|
383
|
-
*
|
|
384
|
-
* return (
|
|
385
|
-
* <form {...form.props}>
|
|
386
|
-
* <input name={fields.email.name} defaultValue={fields.email.defaultValue} />
|
|
387
|
-
* <div>{fields.email.errors}</div>
|
|
388
|
-
* </form>
|
|
389
|
-
* );
|
|
390
|
-
* ```
|
|
391
|
-
*
|
|
392
|
-
* @example Manual configuration setup with custom validation:
|
|
393
|
-
*
|
|
394
|
-
* ```tsx
|
|
395
|
-
* const { form, fields } = useForm({
|
|
396
|
-
* onValidate({ payload, error }) {
|
|
397
|
-
* if (!payload.email) {
|
|
398
|
-
* error.fieldErrors.email = ['Required'];
|
|
399
|
-
* }
|
|
400
|
-
* return error;
|
|
401
|
-
* }
|
|
402
|
-
* });
|
|
403
|
-
*
|
|
404
|
-
* return (
|
|
405
|
-
* <form {...form.props}>
|
|
406
|
-
* <input name={fields.email.name} defaultValue={fields.email.defaultValue} />
|
|
407
|
-
* <div>{fields.email.errors}</div>
|
|
408
|
-
* </form>
|
|
409
|
-
* );
|
|
410
|
-
* ```
|
|
411
|
-
*/
|
|
412
|
-
|
|
413
|
-
/**
|
|
414
|
-
* @deprecated Use `useForm(schema, options)` instead for better type inference.
|
|
415
|
-
*/
|
|
416
|
-
|
|
417
|
-
function useForm(schemaOrOptions, maybeOptions) {
|
|
418
|
-
var _options$onError;
|
|
419
|
-
var schema;
|
|
420
|
-
var options;
|
|
421
|
-
if (maybeOptions) {
|
|
422
|
-
schema = schemaOrOptions;
|
|
423
|
-
options = maybeOptions;
|
|
424
|
-
} else {
|
|
425
|
-
var fullOptions = schemaOrOptions;
|
|
426
|
-
options = fullOptions;
|
|
427
|
-
schema = fullOptions.schema;
|
|
428
|
-
}
|
|
429
|
-
var {
|
|
430
|
-
id,
|
|
431
|
-
constraint
|
|
432
|
-
} = options;
|
|
433
|
-
var globalOptions = useContext(GlobalFormOptionsContext);
|
|
434
|
-
var optionsRef = useLatest(options);
|
|
435
|
-
var globalOptionsRef = useLatest(globalOptions);
|
|
436
|
-
var fallbackId = useId();
|
|
437
|
-
var formId = id !== null && id !== void 0 ? id : "form-".concat(fallbackId);
|
|
438
|
-
var [state, handleSubmit] = useConform(formId, _objectSpread2(_objectSpread2({}, options), {}, {
|
|
439
|
-
serialize: globalOptions.serialize,
|
|
440
|
-
intentName: globalOptions.intentName,
|
|
441
|
-
onError: (_options$onError = options.onError) !== null && _options$onError !== void 0 ? _options$onError : focusFirstInvalidField,
|
|
442
|
-
onValidate(ctx) {
|
|
443
|
-
var _options$onValidate, _options$onValidate2, _options;
|
|
444
|
-
if (schema) {
|
|
445
|
-
var standardResult = schema['~standard'].validate(ctx.payload);
|
|
446
|
-
if (standardResult instanceof Promise) {
|
|
447
|
-
return standardResult.then(actualStandardResult => {
|
|
448
|
-
if (typeof options.onValidate === 'function') {
|
|
449
|
-
throw new Error('The "onValidate" handler is not supported when used with asynchronous schema validation.');
|
|
450
|
-
}
|
|
451
|
-
return resolveStandardSchemaResult(actualStandardResult);
|
|
452
|
-
});
|
|
453
|
-
}
|
|
454
|
-
var resolvedResult = resolveStandardSchemaResult(standardResult);
|
|
455
|
-
if (!options.onValidate) {
|
|
456
|
-
return resolvedResult;
|
|
457
|
-
}
|
|
458
|
-
|
|
459
|
-
// Update the schema error in the context
|
|
460
|
-
if (resolvedResult.error) {
|
|
461
|
-
ctx.error = resolvedResult.error;
|
|
462
|
-
}
|
|
463
|
-
ctx.schemaValue = resolvedResult.value;
|
|
464
|
-
var validateResult = resolveValidateResult(options.onValidate(ctx));
|
|
465
|
-
if (validateResult.syncResult) {
|
|
466
|
-
var _validateResult$syncR, _validateResult$syncR2;
|
|
467
|
-
(_validateResult$syncR2 = (_validateResult$syncR = validateResult.syncResult).value) !== null && _validateResult$syncR2 !== void 0 ? _validateResult$syncR2 : _validateResult$syncR.value = resolvedResult.value;
|
|
468
|
-
}
|
|
469
|
-
if (validateResult.asyncResult) {
|
|
470
|
-
validateResult.asyncResult = validateResult.asyncResult.then(result => {
|
|
471
|
-
var _result$value;
|
|
472
|
-
(_result$value = result.value) !== null && _result$value !== void 0 ? _result$value : result.value = resolvedResult.value;
|
|
473
|
-
return result;
|
|
474
|
-
});
|
|
475
|
-
}
|
|
476
|
-
return [validateResult.syncResult, validateResult.asyncResult];
|
|
477
|
-
}
|
|
478
|
-
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 : {
|
|
479
|
-
// To avoid conform falling back to server validation,
|
|
480
|
-
// if neither schema nor validation handler is provided,
|
|
481
|
-
// we just treat it as a valid client submission
|
|
482
|
-
error: null
|
|
483
|
-
};
|
|
484
|
-
}
|
|
485
|
-
}));
|
|
486
|
-
var intent = useIntent(formId);
|
|
487
|
-
var context = useMemo(() => ({
|
|
488
|
-
formId,
|
|
489
|
-
state,
|
|
490
|
-
constraint: constraint !== null && constraint !== void 0 ? constraint : null,
|
|
491
|
-
handleSubmit,
|
|
492
|
-
handleInput(event) {
|
|
493
|
-
var _optionsRef$current$o4, _optionsRef$current4, _globalOptionsRef$cur;
|
|
494
|
-
if (!isFieldElement(event.target) || event.target.name === '' || event.target.form === null || event.target.form !== getFormElement(formId)) {
|
|
495
|
-
return;
|
|
496
|
-
}
|
|
497
|
-
(_optionsRef$current$o4 = (_optionsRef$current4 = optionsRef.current).onInput) === null || _optionsRef$current$o4 === void 0 || _optionsRef$current$o4.call(_optionsRef$current4, _objectSpread2(_objectSpread2({}, event), {}, {
|
|
498
|
-
target: event.target,
|
|
499
|
-
currentTarget: event.target.form
|
|
500
|
-
}));
|
|
501
|
-
if (event.defaultPrevented) {
|
|
502
|
-
return;
|
|
503
|
-
}
|
|
504
|
-
var {
|
|
505
|
-
shouldValidate = globalOptionsRef.current.shouldValidate,
|
|
506
|
-
shouldRevalidate = (_globalOptionsRef$cur = globalOptionsRef.current.shouldRevalidate) !== null && _globalOptionsRef$cur !== void 0 ? _globalOptionsRef$cur : shouldValidate
|
|
507
|
-
} = optionsRef.current;
|
|
508
|
-
if (isTouched(state, event.target.name) ? shouldRevalidate === 'onInput' : shouldValidate === 'onInput') {
|
|
509
|
-
intent.validate(event.target.name);
|
|
510
|
-
}
|
|
511
|
-
},
|
|
512
|
-
handleBlur(event) {
|
|
513
|
-
var _optionsRef$current$o5, _optionsRef$current5, _globalOptionsRef$cur2;
|
|
514
|
-
if (!isFieldElement(event.target) || event.target.name === '' || event.target.form === null || event.target.form !== getFormElement(formId)) {
|
|
515
|
-
return;
|
|
516
|
-
}
|
|
517
|
-
(_optionsRef$current$o5 = (_optionsRef$current5 = optionsRef.current).onBlur) === null || _optionsRef$current$o5 === void 0 || _optionsRef$current$o5.call(_optionsRef$current5, _objectSpread2(_objectSpread2({}, event), {}, {
|
|
518
|
-
target: event.target,
|
|
519
|
-
currentTarget: event.target.form
|
|
520
|
-
}));
|
|
521
|
-
if (event.defaultPrevented) {
|
|
522
|
-
return;
|
|
523
|
-
}
|
|
524
|
-
var {
|
|
525
|
-
shouldValidate = globalOptionsRef.current.shouldValidate,
|
|
526
|
-
shouldRevalidate = (_globalOptionsRef$cur2 = globalOptionsRef.current.shouldRevalidate) !== null && _globalOptionsRef$cur2 !== void 0 ? _globalOptionsRef$cur2 : shouldValidate
|
|
527
|
-
} = optionsRef.current;
|
|
528
|
-
if (isTouched(state, event.target.name) ? shouldRevalidate === 'onBlur' : shouldValidate === 'onBlur') {
|
|
529
|
-
intent.validate(event.target.name);
|
|
530
|
-
}
|
|
531
|
-
}
|
|
532
|
-
}), [formId, state, constraint, handleSubmit, intent, optionsRef, globalOptionsRef]);
|
|
533
|
-
var form = useMemo(() => getFormMetadata(context, {
|
|
534
|
-
serialize: globalOptions.serialize,
|
|
535
|
-
extendFieldMetadata: globalOptions.defineCustomMetadata
|
|
536
|
-
}), [context, globalOptions.serialize, globalOptions.defineCustomMetadata]);
|
|
537
|
-
var fields = useMemo(() => getFieldset(context, {
|
|
538
|
-
serialize: globalOptions.serialize,
|
|
539
|
-
extendFieldMetadata: globalOptions.defineCustomMetadata
|
|
540
|
-
}), [context, globalOptions.serialize, globalOptions.defineCustomMetadata]);
|
|
541
|
-
return {
|
|
542
|
-
form,
|
|
543
|
-
fields,
|
|
544
|
-
intent
|
|
545
|
-
};
|
|
546
|
-
}
|
|
547
|
-
|
|
548
|
-
/**
|
|
549
|
-
* A React hook that provides access to form-level metadata and state.
|
|
550
|
-
* Requires `FormProvider` context when used in child components.
|
|
551
|
-
*
|
|
552
|
-
* @see https://conform.guide/api/react/future/useFormMetadata
|
|
553
|
-
* @example
|
|
554
|
-
* ```tsx
|
|
555
|
-
* function ErrorSummary() {
|
|
556
|
-
* const form = useFormMetadata();
|
|
557
|
-
*
|
|
558
|
-
* if (form.valid) return null;
|
|
559
|
-
*
|
|
560
|
-
* return (
|
|
561
|
-
* <div>Please fix {Object.keys(form.fieldErrors).length} errors</div>
|
|
562
|
-
* );
|
|
563
|
-
* }
|
|
564
|
-
* ```
|
|
565
|
-
*/
|
|
566
|
-
function useFormMetadata() {
|
|
567
|
-
var options = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};
|
|
568
|
-
var globalOptions = useContext(GlobalFormOptionsContext);
|
|
569
|
-
var context = useFormContext(options.formId);
|
|
570
|
-
var formMetadata = useMemo(() => getFormMetadata(context, {
|
|
571
|
-
serialize: globalOptions.serialize,
|
|
572
|
-
extendFieldMetadata: globalOptions.defineCustomMetadata
|
|
573
|
-
}), [context, globalOptions.serialize, globalOptions.defineCustomMetadata]);
|
|
574
|
-
return formMetadata;
|
|
575
|
-
}
|
|
576
|
-
|
|
577
|
-
/**
|
|
578
|
-
* A React hook that provides access to a specific field's metadata and state.
|
|
579
|
-
* Requires `FormProvider` context when used in child components.
|
|
580
|
-
*
|
|
581
|
-
* @see https://conform.guide/api/react/future/useField
|
|
582
|
-
* @example
|
|
583
|
-
* ```tsx
|
|
584
|
-
* function FormField({ name, label }) {
|
|
585
|
-
* const field = useField(name);
|
|
586
|
-
*
|
|
587
|
-
* return (
|
|
588
|
-
* <div>
|
|
589
|
-
* <label htmlFor={field.id}>{label}</label>
|
|
590
|
-
* <input id={field.id} name={field.name} defaultValue={field.defaultValue} />
|
|
591
|
-
* {field.errors && <div>{field.errors.join(', ')}</div>}
|
|
592
|
-
* </div>
|
|
593
|
-
* );
|
|
594
|
-
* }
|
|
595
|
-
* ```
|
|
596
|
-
*/
|
|
597
|
-
function useField(name) {
|
|
598
|
-
var options = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};
|
|
599
|
-
var globalOptions = useContext(GlobalFormOptionsContext);
|
|
600
|
-
var context = useFormContext(options.formId);
|
|
601
|
-
var field = useMemo(() => getField(context, {
|
|
602
|
-
name,
|
|
603
|
-
serialize: globalOptions.serialize,
|
|
604
|
-
extendFieldMetadata: globalOptions.defineCustomMetadata
|
|
605
|
-
}), [context, name, globalOptions.serialize, globalOptions.defineCustomMetadata]);
|
|
606
|
-
return field;
|
|
607
|
-
}
|
|
608
|
-
|
|
609
|
-
/**
|
|
610
|
-
* A React hook that provides an intent dispatcher for programmatic form actions.
|
|
611
|
-
* Intent dispatchers allow you to trigger form operations like validation, field updates,
|
|
612
|
-
* and array manipulations without manual form submission.
|
|
613
|
-
*
|
|
614
|
-
* @see https://conform.guide/api/react/future/useIntent
|
|
615
|
-
* @example
|
|
616
|
-
* ```tsx
|
|
617
|
-
* function ResetButton() {
|
|
618
|
-
* const buttonRef = useRef<HTMLButtonElement>(null);
|
|
619
|
-
* const intent = useIntent(buttonRef);
|
|
620
|
-
*
|
|
621
|
-
* return (
|
|
622
|
-
* <button type="button" ref={buttonRef} onClick={() => intent.reset()}>
|
|
623
|
-
* Reset Form
|
|
624
|
-
* </button>
|
|
625
|
-
* );
|
|
626
|
-
* }
|
|
627
|
-
* ```
|
|
628
|
-
*/
|
|
629
|
-
function useIntent(formRef) {
|
|
630
|
-
var globalOptions = useContext(GlobalFormOptionsContext);
|
|
631
|
-
return useMemo(() => createIntentDispatcher(() => getFormElement(formRef), globalOptions.intentName), [formRef, globalOptions.intentName]);
|
|
632
|
-
}
|
|
633
|
-
|
|
634
324
|
/**
|
|
635
325
|
* A React hook that lets you sync the state of an input and dispatch native form events from it.
|
|
636
326
|
* This is useful when emulating native input behavior — typically by rendering a hidden base control
|
|
637
327
|
* and syncing it with a custom input.
|
|
638
328
|
*
|
|
639
|
-
*
|
|
329
|
+
* **Example:**
|
|
640
330
|
* ```ts
|
|
641
331
|
* const control = useControl(options);
|
|
642
332
|
* ```
|
|
@@ -644,9 +334,7 @@ function useIntent(formRef) {
|
|
|
644
334
|
|
|
645
335
|
function useControl() {
|
|
646
336
|
var options = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};
|
|
647
|
-
var
|
|
648
|
-
observer
|
|
649
|
-
} = useContext(GlobalFormOptionsContext);
|
|
337
|
+
var observer = useContext(GlobalFormsObserverContext);
|
|
650
338
|
var inputRef = useRef(null);
|
|
651
339
|
var formRef = useMemo(() => ({
|
|
652
340
|
get current() {
|
|
@@ -711,8 +399,8 @@ function useControl() {
|
|
|
711
399
|
eventDispatched.current[listener] = undefined;
|
|
712
400
|
});
|
|
713
401
|
if (listener === 'focus') {
|
|
714
|
-
var _optionsRef$
|
|
715
|
-
(_optionsRef$
|
|
402
|
+
var _optionsRef$current4, _optionsRef$current4$;
|
|
403
|
+
(_optionsRef$current4 = optionsRef.current) === null || _optionsRef$current4 === void 0 || (_optionsRef$current4$ = _optionsRef$current4.onFocus) === null || _optionsRef$current4$ === void 0 || _optionsRef$current4$.call(_optionsRef$current4);
|
|
716
404
|
}
|
|
717
405
|
}
|
|
718
406
|
};
|
|
@@ -802,7 +490,7 @@ function useControl() {
|
|
|
802
490
|
if (element.type === 'checkbox' || element.type === 'radio') {
|
|
803
491
|
// React set the value as empty string incorrectly when the value is undefined
|
|
804
492
|
// This make sure the checkbox value falls back to the default value "on" properly
|
|
805
|
-
//
|
|
493
|
+
// See https://github.com/facebook/react/issues/17590
|
|
806
494
|
var value = 'value' in optionsRef.current && optionsRef.current.value ? optionsRef.current.value : 'on';
|
|
807
495
|
element.value = value;
|
|
808
496
|
}
|
|
@@ -820,10 +508,10 @@ function useControl() {
|
|
|
820
508
|
inputRef.current = inputs;
|
|
821
509
|
if ('defaultValue' in optionsRef.current) {
|
|
822
510
|
for (var input of inputs) {
|
|
823
|
-
var _optionsRef$
|
|
511
|
+
var _optionsRef$current5;
|
|
824
512
|
initializeField(input, {
|
|
825
513
|
// We will not be uitlizing defaultChecked / value on checkbox / radio group
|
|
826
|
-
defaultValue: (_optionsRef$
|
|
514
|
+
defaultValue: (_optionsRef$current5 = optionsRef.current) === null || _optionsRef$current5 === void 0 ? void 0 : _optionsRef$current5.defaultValue
|
|
827
515
|
});
|
|
828
516
|
}
|
|
829
517
|
}
|
|
@@ -889,8 +577,9 @@ function useControl() {
|
|
|
889
577
|
* Returns `undefined` when the form element is not available (e.g., on SSR or initial client render),
|
|
890
578
|
* unless a `fallback` is provided.
|
|
891
579
|
*
|
|
892
|
-
*
|
|
893
|
-
*
|
|
580
|
+
* See https://conform.guide/api/react/future/useFormData
|
|
581
|
+
*
|
|
582
|
+
* **Example:**
|
|
894
583
|
* ```ts
|
|
895
584
|
* const value = useFormData(
|
|
896
585
|
* formRef,
|
|
@@ -900,9 +589,7 @@ function useControl() {
|
|
|
900
589
|
*/
|
|
901
590
|
|
|
902
591
|
function useFormData(formRef, select, options) {
|
|
903
|
-
var
|
|
904
|
-
observer
|
|
905
|
-
} = useContext(GlobalFormOptionsContext);
|
|
592
|
+
var observer = useContext(GlobalFormsObserverContext);
|
|
906
593
|
var valueRef = useRef();
|
|
907
594
|
var formDataRef = useRef();
|
|
908
595
|
var value = useSyncExternalStore(useCallback(callback => {
|
|
@@ -962,7 +649,7 @@ function useLatest(value) {
|
|
|
962
649
|
* A component that renders hidden base control(s) based on the shape of defaultValue.
|
|
963
650
|
* Used with useControl to sync complex values with form data.
|
|
964
651
|
*
|
|
965
|
-
*
|
|
652
|
+
* **Example:**
|
|
966
653
|
* ```tsx
|
|
967
654
|
* const control = useControl<{ street: string; city: string }>({
|
|
968
655
|
* defaultValue: { street: '123 Main St', city: 'Anytown' },
|
|
@@ -992,7 +679,7 @@ function useLatest(value) {
|
|
|
992
679
|
*/
|
|
993
680
|
var BaseControl = /*#__PURE__*/forwardRef(function BaseControl(props, ref) {
|
|
994
681
|
function formatValue(value) {
|
|
995
|
-
var serialized =
|
|
682
|
+
var serialized = defaultSerialize(value);
|
|
996
683
|
if (typeof serialized === 'string') {
|
|
997
684
|
return serialized;
|
|
998
685
|
}
|
|
@@ -1023,7 +710,7 @@ var BaseControl = /*#__PURE__*/forwardRef(function BaseControl(props, ref) {
|
|
|
1023
710
|
defaultValue: _defaultValue,
|
|
1024
711
|
hidden: _hidden = true
|
|
1025
712
|
} = props,
|
|
1026
|
-
fieldsetProps = _objectWithoutProperties(props,
|
|
713
|
+
fieldsetProps = _objectWithoutProperties(props, _excluded);
|
|
1027
714
|
return /*#__PURE__*/jsx("fieldset", _objectSpread2(_objectSpread2({}, fieldsetProps), {}, {
|
|
1028
715
|
ref: ref,
|
|
1029
716
|
name: name,
|
|
@@ -1038,7 +725,7 @@ var BaseControl = /*#__PURE__*/forwardRef(function BaseControl(props, ref) {
|
|
|
1038
725
|
multiple = Array.isArray(_defaultValue2),
|
|
1039
726
|
hidden: _hidden2 = true
|
|
1040
727
|
} = props,
|
|
1041
|
-
selectProps = _objectWithoutProperties(props,
|
|
728
|
+
selectProps = _objectWithoutProperties(props, _excluded2);
|
|
1042
729
|
if (multiple) {
|
|
1043
730
|
var defaultOptions = Array.isArray(_defaultValue2) ? _defaultValue2.map(formatValue) : [formatValue(_defaultValue2)];
|
|
1044
731
|
return /*#__PURE__*/jsx("select", _objectSpread2(_objectSpread2({}, selectProps), {}, {
|
|
@@ -1068,7 +755,7 @@ var BaseControl = /*#__PURE__*/forwardRef(function BaseControl(props, ref) {
|
|
|
1068
755
|
defaultValue: _defaultValue3,
|
|
1069
756
|
hidden: _hidden3 = true
|
|
1070
757
|
} = props,
|
|
1071
|
-
textareaProps = _objectWithoutProperties(props,
|
|
758
|
+
textareaProps = _objectWithoutProperties(props, _excluded3);
|
|
1072
759
|
return /*#__PURE__*/jsx("textarea", _objectSpread2(_objectSpread2({}, textareaProps), {}, {
|
|
1073
760
|
defaultValue: formatValue(_defaultValue3),
|
|
1074
761
|
ref: ref,
|
|
@@ -1081,7 +768,7 @@ var BaseControl = /*#__PURE__*/forwardRef(function BaseControl(props, ref) {
|
|
|
1081
768
|
value = _defaultValue4,
|
|
1082
769
|
hidden: _hidden4 = true
|
|
1083
770
|
} = props,
|
|
1084
|
-
_inputProps = _objectWithoutProperties(props,
|
|
771
|
+
_inputProps = _objectWithoutProperties(props, _excluded4);
|
|
1085
772
|
return /*#__PURE__*/jsx("input", _objectSpread2(_objectSpread2({}, _inputProps), {}, {
|
|
1086
773
|
ref: ref,
|
|
1087
774
|
value: value,
|
|
@@ -1092,7 +779,7 @@ var BaseControl = /*#__PURE__*/forwardRef(function BaseControl(props, ref) {
|
|
|
1092
779
|
defaultValue,
|
|
1093
780
|
hidden = true
|
|
1094
781
|
} = props,
|
|
1095
|
-
inputProps = _objectWithoutProperties(props,
|
|
782
|
+
inputProps = _objectWithoutProperties(props, _excluded5);
|
|
1096
783
|
return /*#__PURE__*/jsx("input", _objectSpread2(_objectSpread2({}, inputProps), {}, {
|
|
1097
784
|
ref: ref,
|
|
1098
785
|
defaultValue: defaultValue !== undefined ? formatValue(defaultValue) : undefined,
|
|
@@ -1100,4 +787,4 @@ var BaseControl = /*#__PURE__*/forwardRef(function BaseControl(props, ref) {
|
|
|
1100
787
|
}));
|
|
1101
788
|
});
|
|
1102
789
|
|
|
1103
|
-
export { BaseControl,
|
|
790
|
+
export { BaseControl, GlobalFormsObserverContext, INITIAL_KEY, PreserveBoundary, useConform, useControl, useFormData, useLatest, useSafeLayoutEffect };
|
package/dist/future/index.d.ts
CHANGED
|
@@ -1,8 +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, ControlOptions, BaseControlProps, DefaultValue,
|
|
4
|
-
export { configureForms } from './forms';
|
|
5
|
-
export { BaseControl, PreserveBoundary,
|
|
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';
|
|
5
|
+
export { BaseControl, PreserveBoundary, useControl, useFormData, } from './hooks';
|
|
6
6
|
export { shape } from './util';
|
|
7
7
|
export { memoize } from './memoize';
|
|
8
8
|
//# sourceMappingURL=index.d.ts.map
|
package/dist/future/index.js
CHANGED
|
@@ -26,16 +26,15 @@ Object.defineProperty(exports, 'report', {
|
|
|
26
26
|
enumerable: true,
|
|
27
27
|
get: function () { return future.report; }
|
|
28
28
|
});
|
|
29
|
+
exports.FormProvider = forms.FormProvider;
|
|
29
30
|
exports.configureForms = forms.configureForms;
|
|
31
|
+
exports.useField = forms.useField;
|
|
32
|
+
exports.useForm = forms.useForm;
|
|
33
|
+
exports.useFormMetadata = forms.useFormMetadata;
|
|
34
|
+
exports.useIntent = forms.useIntent;
|
|
30
35
|
exports.BaseControl = hooks.BaseControl;
|
|
31
|
-
exports.FormOptionsProvider = hooks.FormOptionsProvider;
|
|
32
|
-
exports.FormProvider = hooks.FormProvider;
|
|
33
36
|
exports.PreserveBoundary = hooks.PreserveBoundary;
|
|
34
37
|
exports.useControl = hooks.useControl;
|
|
35
|
-
exports.useField = hooks.useField;
|
|
36
|
-
exports.useForm = hooks.useForm;
|
|
37
38
|
exports.useFormData = hooks.useFormData;
|
|
38
|
-
exports.useFormMetadata = hooks.useFormMetadata;
|
|
39
|
-
exports.useIntent = hooks.useIntent;
|
|
40
39
|
exports.shape = util.shape;
|
|
41
40
|
exports.memoize = memoize.memoize;
|
package/dist/future/index.mjs
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
export { getFieldValue, isDirty, parseSubmission, report } from '@conform-to/dom/future';
|
|
2
|
-
export { configureForms } from './forms.mjs';
|
|
3
|
-
export { BaseControl,
|
|
2
|
+
export { FormProvider, configureForms, useField, useForm, useFormMetadata, useIntent } from './forms.mjs';
|
|
3
|
+
export { BaseControl, PreserveBoundary, useControl, useFormData } from './hooks.mjs';
|
|
4
4
|
export { shape } from './util.mjs';
|
|
5
5
|
export { memoize } from './memoize.mjs';
|
package/dist/future/intent.js
CHANGED
|
@@ -215,27 +215,39 @@ var intentHandlers = {
|
|
|
215
215
|
console.warn('intent.insert() with `onInvalid` or `from` requires the validation result to be available synchronously. ' + 'These options are ignored because the error is not yet known.');
|
|
216
216
|
return result;
|
|
217
217
|
}
|
|
218
|
-
var
|
|
219
|
-
if (options.onInvalid === 'revert' &&
|
|
218
|
+
var listError = (_result$error = result.error) === null || _result$error === void 0 ? void 0 : _result$error.fieldErrors[options.name];
|
|
219
|
+
if (options.onInvalid === 'revert' && listError != null) {
|
|
220
220
|
return _rollupPluginBabelHelpers.objectSpread2(_rollupPluginBabelHelpers.objectSpread2({}, result), {}, {
|
|
221
221
|
targetValue: undefined
|
|
222
222
|
});
|
|
223
223
|
}
|
|
224
224
|
if (options.from !== undefined) {
|
|
225
|
-
var _options$index2, _result$error2;
|
|
225
|
+
var _options$index2, _result$error2, _result$error3;
|
|
226
226
|
var index = (_options$index2 = options.index) !== null && _options$index2 !== void 0 ? _options$index2 : util.getPathArray(result.submission.payload, options.name).length;
|
|
227
227
|
var insertedItemPath = future.appendPath(options.name, index);
|
|
228
|
-
var
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
var
|
|
228
|
+
var insertedItemError = (_result$error2 = result.error) === null || _result$error2 === void 0 ? void 0 : _result$error2.fieldErrors[insertedItemPath];
|
|
229
|
+
var fromFieldError = (_result$error3 = result.error) === null || _result$error3 === void 0 ? void 0 : _result$error3.fieldErrors[options.from];
|
|
230
|
+
if (fromFieldError != null) {
|
|
231
|
+
var _result$error$formErr, _result$error4, _result$error5;
|
|
232
232
|
return _rollupPluginBabelHelpers.objectSpread2(_rollupPluginBabelHelpers.objectSpread2({}, result), {}, {
|
|
233
233
|
targetValue: undefined,
|
|
234
234
|
error: {
|
|
235
|
-
formErrors: (_result$error$formErr = (_result$error4 = result.error) === null || _result$error4 === void 0 ? void 0 : _result$error4.formErrors) !== null && _result$error$formErr !== void 0 ? _result$error$formErr :
|
|
235
|
+
formErrors: (_result$error$formErr = (_result$error4 = result.error) === null || _result$error4 === void 0 ? void 0 : _result$error4.formErrors) !== null && _result$error$formErr !== void 0 ? _result$error$formErr : null,
|
|
236
236
|
fieldErrors: _rollupPluginBabelHelpers.objectSpread2(_rollupPluginBabelHelpers.objectSpread2({}, (_result$error5 = result.error) === null || _result$error5 === void 0 ? void 0 : _result$error5.fieldErrors), {}, {
|
|
237
|
-
[
|
|
238
|
-
|
|
237
|
+
[insertedItemPath]: null
|
|
238
|
+
})
|
|
239
|
+
}
|
|
240
|
+
});
|
|
241
|
+
}
|
|
242
|
+
if (insertedItemError != null) {
|
|
243
|
+
var _result$error$formErr2, _result$error6, _result$error7;
|
|
244
|
+
return _rollupPluginBabelHelpers.objectSpread2(_rollupPluginBabelHelpers.objectSpread2({}, result), {}, {
|
|
245
|
+
targetValue: undefined,
|
|
246
|
+
error: {
|
|
247
|
+
formErrors: (_result$error$formErr2 = (_result$error6 = result.error) === null || _result$error6 === void 0 ? void 0 : _result$error6.formErrors) !== null && _result$error$formErr2 !== void 0 ? _result$error$formErr2 : null,
|
|
248
|
+
fieldErrors: _rollupPluginBabelHelpers.objectSpread2(_rollupPluginBabelHelpers.objectSpread2({}, (_result$error7 = result.error) === null || _result$error7 === void 0 ? void 0 : _result$error7.fieldErrors), {}, {
|
|
249
|
+
[options.from]: insertedItemError,
|
|
250
|
+
[insertedItemPath]: null
|
|
239
251
|
})
|
|
240
252
|
}
|
|
241
253
|
});
|
|
@@ -293,7 +305,7 @@ var intentHandlers = {
|
|
|
293
305
|
return util.updatePathValue(value, options.name, list);
|
|
294
306
|
},
|
|
295
307
|
apply(result, options) {
|
|
296
|
-
var _result$
|
|
308
|
+
var _result$error8;
|
|
297
309
|
// Warn if validation result is not yet available
|
|
298
310
|
if (typeof result.error === 'undefined' && options.onInvalid) {
|
|
299
311
|
if (process.env.NODE_ENV !== 'production') {
|
|
@@ -302,7 +314,7 @@ var intentHandlers = {
|
|
|
302
314
|
}
|
|
303
315
|
return result;
|
|
304
316
|
}
|
|
305
|
-
if (result.targetValue && (_result$
|
|
317
|
+
if (result.targetValue && (_result$error8 = result.error) !== null && _result$error8 !== void 0 && _result$error8.fieldErrors[options.name]) {
|
|
306
318
|
switch (options.onInvalid) {
|
|
307
319
|
case 'revert':
|
|
308
320
|
return _rollupPluginBabelHelpers.objectSpread2(_rollupPluginBabelHelpers.objectSpread2({}, result), {}, {
|