@conform-to/react 1.18.0 → 1.19.1
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 +49 -372
- package/dist/future/hooks.mjs +54 -369
- 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/dist/integrations.js +8 -10
- package/dist/integrations.mjs +8 -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() {
|
|
@@ -699,27 +387,25 @@ function useControl() {
|
|
|
699
387
|
return next;
|
|
700
388
|
}, () => snapshotRef.current);
|
|
701
389
|
useEffect(() => {
|
|
702
|
-
var createEventListener = listener => {
|
|
703
|
-
|
|
704
|
-
|
|
705
|
-
|
|
706
|
-
|
|
707
|
-
|
|
708
|
-
clearTimeout(timer);
|
|
709
|
-
}
|
|
710
|
-
eventDispatched.current[listener] = window.setTimeout(() => {
|
|
711
|
-
eventDispatched.current[listener] = undefined;
|
|
712
|
-
});
|
|
713
|
-
if (listener === 'focus') {
|
|
714
|
-
var _optionsRef$current6, _optionsRef$current6$;
|
|
715
|
-
(_optionsRef$current6 = optionsRef.current) === null || _optionsRef$current6 === void 0 || (_optionsRef$current6$ = _optionsRef$current6.onFocus) === null || _optionsRef$current6$ === void 0 || _optionsRef$current6$.call(_optionsRef$current6);
|
|
716
|
-
}
|
|
390
|
+
var createEventListener = (listener, event) => {
|
|
391
|
+
var _inputRef$current2;
|
|
392
|
+
if (Array.isArray(inputRef.current) ? inputRef.current.some(item => item === event.target) : event.target instanceof Node && ((_inputRef$current2 = inputRef.current) === null || _inputRef$current2 === void 0 ? void 0 : _inputRef$current2.contains(event.target))) {
|
|
393
|
+
var timer = eventDispatched.current[listener];
|
|
394
|
+
if (timer) {
|
|
395
|
+
clearTimeout(timer);
|
|
717
396
|
}
|
|
718
|
-
|
|
397
|
+
eventDispatched.current[listener] = window.setTimeout(() => {
|
|
398
|
+
eventDispatched.current[listener] = undefined;
|
|
399
|
+
});
|
|
400
|
+
if (listener === 'focus') {
|
|
401
|
+
var _optionsRef$current4, _optionsRef$current4$;
|
|
402
|
+
(_optionsRef$current4 = optionsRef.current) === null || _optionsRef$current4 === void 0 || (_optionsRef$current4$ = _optionsRef$current4.onFocus) === null || _optionsRef$current4$ === void 0 || _optionsRef$current4$.call(_optionsRef$current4);
|
|
403
|
+
}
|
|
404
|
+
}
|
|
719
405
|
};
|
|
720
|
-
var inputHandler = createEventListener('change');
|
|
721
|
-
var focusHandler = createEventListener('focus');
|
|
722
|
-
var blurHandler = createEventListener('blur');
|
|
406
|
+
var inputHandler = createEventListener.bind(null, 'change');
|
|
407
|
+
var focusHandler = createEventListener.bind(null, 'focus');
|
|
408
|
+
var blurHandler = createEventListener.bind(null, 'blur');
|
|
723
409
|
document.addEventListener('input', inputHandler, true);
|
|
724
410
|
document.addEventListener('focusin', focusHandler, true);
|
|
725
411
|
document.addEventListener('focusout', blurHandler, true);
|
|
@@ -802,7 +488,7 @@ function useControl() {
|
|
|
802
488
|
if (element.type === 'checkbox' || element.type === 'radio') {
|
|
803
489
|
// React set the value as empty string incorrectly when the value is undefined
|
|
804
490
|
// This make sure the checkbox value falls back to the default value "on" properly
|
|
805
|
-
//
|
|
491
|
+
// See https://github.com/facebook/react/issues/17590
|
|
806
492
|
var value = 'value' in optionsRef.current && optionsRef.current.value ? optionsRef.current.value : 'on';
|
|
807
493
|
element.value = value;
|
|
808
494
|
}
|
|
@@ -820,10 +506,10 @@ function useControl() {
|
|
|
820
506
|
inputRef.current = inputs;
|
|
821
507
|
if ('defaultValue' in optionsRef.current) {
|
|
822
508
|
for (var input of inputs) {
|
|
823
|
-
var _optionsRef$
|
|
509
|
+
var _optionsRef$current5;
|
|
824
510
|
initializeField(input, {
|
|
825
511
|
// We will not be uitlizing defaultChecked / value on checkbox / radio group
|
|
826
|
-
defaultValue: (_optionsRef$
|
|
512
|
+
defaultValue: (_optionsRef$current5 = optionsRef.current) === null || _optionsRef$current5 === void 0 ? void 0 : _optionsRef$current5.defaultValue
|
|
827
513
|
});
|
|
828
514
|
}
|
|
829
515
|
}
|
|
@@ -889,8 +575,9 @@ function useControl() {
|
|
|
889
575
|
* Returns `undefined` when the form element is not available (e.g., on SSR or initial client render),
|
|
890
576
|
* unless a `fallback` is provided.
|
|
891
577
|
*
|
|
892
|
-
*
|
|
893
|
-
*
|
|
578
|
+
* See https://conform.guide/api/react/future/useFormData
|
|
579
|
+
*
|
|
580
|
+
* **Example:**
|
|
894
581
|
* ```ts
|
|
895
582
|
* const value = useFormData(
|
|
896
583
|
* formRef,
|
|
@@ -900,9 +587,7 @@ function useControl() {
|
|
|
900
587
|
*/
|
|
901
588
|
|
|
902
589
|
function useFormData(formRef, select, options) {
|
|
903
|
-
var
|
|
904
|
-
observer
|
|
905
|
-
} = useContext(GlobalFormOptionsContext);
|
|
590
|
+
var observer = useContext(GlobalFormsObserverContext);
|
|
906
591
|
var valueRef = useRef();
|
|
907
592
|
var formDataRef = useRef();
|
|
908
593
|
var value = useSyncExternalStore(useCallback(callback => {
|
|
@@ -962,7 +647,7 @@ function useLatest(value) {
|
|
|
962
647
|
* A component that renders hidden base control(s) based on the shape of defaultValue.
|
|
963
648
|
* Used with useControl to sync complex values with form data.
|
|
964
649
|
*
|
|
965
|
-
*
|
|
650
|
+
* **Example:**
|
|
966
651
|
* ```tsx
|
|
967
652
|
* const control = useControl<{ street: string; city: string }>({
|
|
968
653
|
* defaultValue: { street: '123 Main St', city: 'Anytown' },
|
|
@@ -992,7 +677,7 @@ function useLatest(value) {
|
|
|
992
677
|
*/
|
|
993
678
|
var BaseControl = /*#__PURE__*/forwardRef(function BaseControl(props, ref) {
|
|
994
679
|
function formatValue(value) {
|
|
995
|
-
var serialized =
|
|
680
|
+
var serialized = defaultSerialize(value);
|
|
996
681
|
if (typeof serialized === 'string') {
|
|
997
682
|
return serialized;
|
|
998
683
|
}
|
|
@@ -1023,7 +708,7 @@ var BaseControl = /*#__PURE__*/forwardRef(function BaseControl(props, ref) {
|
|
|
1023
708
|
defaultValue: _defaultValue,
|
|
1024
709
|
hidden: _hidden = true
|
|
1025
710
|
} = props,
|
|
1026
|
-
fieldsetProps = _objectWithoutProperties(props,
|
|
711
|
+
fieldsetProps = _objectWithoutProperties(props, _excluded);
|
|
1027
712
|
return /*#__PURE__*/jsx("fieldset", _objectSpread2(_objectSpread2({}, fieldsetProps), {}, {
|
|
1028
713
|
ref: ref,
|
|
1029
714
|
name: name,
|
|
@@ -1038,7 +723,7 @@ var BaseControl = /*#__PURE__*/forwardRef(function BaseControl(props, ref) {
|
|
|
1038
723
|
multiple = Array.isArray(_defaultValue2),
|
|
1039
724
|
hidden: _hidden2 = true
|
|
1040
725
|
} = props,
|
|
1041
|
-
selectProps = _objectWithoutProperties(props,
|
|
726
|
+
selectProps = _objectWithoutProperties(props, _excluded2);
|
|
1042
727
|
if (multiple) {
|
|
1043
728
|
var defaultOptions = Array.isArray(_defaultValue2) ? _defaultValue2.map(formatValue) : [formatValue(_defaultValue2)];
|
|
1044
729
|
return /*#__PURE__*/jsx("select", _objectSpread2(_objectSpread2({}, selectProps), {}, {
|
|
@@ -1068,7 +753,7 @@ var BaseControl = /*#__PURE__*/forwardRef(function BaseControl(props, ref) {
|
|
|
1068
753
|
defaultValue: _defaultValue3,
|
|
1069
754
|
hidden: _hidden3 = true
|
|
1070
755
|
} = props,
|
|
1071
|
-
textareaProps = _objectWithoutProperties(props,
|
|
756
|
+
textareaProps = _objectWithoutProperties(props, _excluded3);
|
|
1072
757
|
return /*#__PURE__*/jsx("textarea", _objectSpread2(_objectSpread2({}, textareaProps), {}, {
|
|
1073
758
|
defaultValue: formatValue(_defaultValue3),
|
|
1074
759
|
ref: ref,
|
|
@@ -1081,7 +766,7 @@ var BaseControl = /*#__PURE__*/forwardRef(function BaseControl(props, ref) {
|
|
|
1081
766
|
value = _defaultValue4,
|
|
1082
767
|
hidden: _hidden4 = true
|
|
1083
768
|
} = props,
|
|
1084
|
-
_inputProps = _objectWithoutProperties(props,
|
|
769
|
+
_inputProps = _objectWithoutProperties(props, _excluded4);
|
|
1085
770
|
return /*#__PURE__*/jsx("input", _objectSpread2(_objectSpread2({}, _inputProps), {}, {
|
|
1086
771
|
ref: ref,
|
|
1087
772
|
value: value,
|
|
@@ -1092,7 +777,7 @@ var BaseControl = /*#__PURE__*/forwardRef(function BaseControl(props, ref) {
|
|
|
1092
777
|
defaultValue,
|
|
1093
778
|
hidden = true
|
|
1094
779
|
} = props,
|
|
1095
|
-
inputProps = _objectWithoutProperties(props,
|
|
780
|
+
inputProps = _objectWithoutProperties(props, _excluded5);
|
|
1096
781
|
return /*#__PURE__*/jsx("input", _objectSpread2(_objectSpread2({}, inputProps), {}, {
|
|
1097
782
|
ref: ref,
|
|
1098
783
|
defaultValue: defaultValue !== undefined ? formatValue(defaultValue) : undefined,
|
|
@@ -1100,4 +785,4 @@ var BaseControl = /*#__PURE__*/forwardRef(function BaseControl(props, ref) {
|
|
|
1100
785
|
}));
|
|
1101
786
|
});
|
|
1102
787
|
|
|
1103
|
-
export { BaseControl,
|
|
788
|
+
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';
|