@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.js
CHANGED
|
@@ -13,41 +13,19 @@ var dom = require('./dom.js');
|
|
|
13
13
|
var reactDom = require('react-dom');
|
|
14
14
|
var jsxRuntime = require('react/jsx-runtime');
|
|
15
15
|
|
|
16
|
-
var _excluded = ["
|
|
17
|
-
_excluded2 = ["
|
|
18
|
-
_excluded3 = ["defaultValue", "
|
|
19
|
-
_excluded4 = ["defaultValue", "hidden"],
|
|
20
|
-
_excluded5 = ["defaultValue", "
|
|
21
|
-
_excluded6 = ["defaultValue", "hidden"];
|
|
16
|
+
var _excluded = ["name", "form", "defaultValue", "hidden"],
|
|
17
|
+
_excluded2 = ["defaultValue", "multiple", "hidden"],
|
|
18
|
+
_excluded3 = ["defaultValue", "hidden"],
|
|
19
|
+
_excluded4 = ["defaultValue", "value", "hidden"],
|
|
20
|
+
_excluded5 = ["defaultValue", "hidden"];
|
|
22
21
|
var INITIAL_KEY = 'INITIAL_KEY';
|
|
23
|
-
var
|
|
24
|
-
intentName: future.DEFAULT_INTENT_NAME,
|
|
25
|
-
observer: future.createGlobalFormsObserver(),
|
|
26
|
-
serialize: future.serialize,
|
|
27
|
-
shouldValidate: 'onSubmit'
|
|
28
|
-
});
|
|
29
|
-
var FormContextContext = /*#__PURE__*/react.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 = react.useContext(FormContextContext);
|
|
37
|
-
var value = react.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__*/jsxRuntime.jsx(FormContextContext.Provider, {
|
|
41
|
-
value: value,
|
|
42
|
-
children: props.children
|
|
43
|
-
});
|
|
44
|
-
}
|
|
22
|
+
var GlobalFormsObserverContext = /*#__PURE__*/react.createContext(future.createGlobalFormsObserver());
|
|
45
23
|
|
|
46
24
|
/**
|
|
47
25
|
* Preserves form field values when its contents are unmounted.
|
|
48
26
|
* Useful for multi-step forms and virtualized lists.
|
|
49
27
|
*
|
|
50
|
-
*
|
|
28
|
+
* See https://conform.guide/api/react/future/PreserveBoundary
|
|
51
29
|
*/
|
|
52
30
|
function PreserveBoundary(props) {
|
|
53
31
|
// name is used as key so React properly unmounts/remounts when switching
|
|
@@ -84,30 +62,6 @@ function PreserveBoundaryImpl(props) {
|
|
|
84
62
|
});
|
|
85
63
|
}
|
|
86
64
|
|
|
87
|
-
/**
|
|
88
|
-
* @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`.
|
|
89
|
-
*/
|
|
90
|
-
function FormOptionsProvider(props) {
|
|
91
|
-
var {
|
|
92
|
-
children
|
|
93
|
-
} = props,
|
|
94
|
-
providedOptions = _rollupPluginBabelHelpers.objectWithoutProperties(props, _excluded);
|
|
95
|
-
var defaultOptions = react.useContext(GlobalFormOptionsContext);
|
|
96
|
-
var options = react.useMemo(() => _rollupPluginBabelHelpers.objectSpread2(_rollupPluginBabelHelpers.objectSpread2({}, defaultOptions), providedOptions), [defaultOptions, providedOptions]);
|
|
97
|
-
return /*#__PURE__*/jsxRuntime.jsx(GlobalFormOptionsContext.Provider, {
|
|
98
|
-
value: options,
|
|
99
|
-
children: children
|
|
100
|
-
});
|
|
101
|
-
}
|
|
102
|
-
function useFormContext(formId) {
|
|
103
|
-
var contexts = react.useContext(FormContextContext);
|
|
104
|
-
var context = formId ? contexts.find(context => formId === context.formId) : contexts[0];
|
|
105
|
-
if (!context) {
|
|
106
|
-
throw new Error('No form context found; Have you render a <FormProvider /> with the corresponding form context?');
|
|
107
|
-
}
|
|
108
|
-
return context;
|
|
109
|
-
}
|
|
110
|
-
|
|
111
65
|
/**
|
|
112
66
|
* Core form hook that manages form state, validation, and submission.
|
|
113
67
|
* Handles both sync and async validation, intent dispatching, and DOM updates.
|
|
@@ -150,8 +104,11 @@ function useConform(formRef, options) {
|
|
|
150
104
|
var abortControllerRef = react.useRef(null);
|
|
151
105
|
var handleSubmission = react.useCallback(function (type, result) {
|
|
152
106
|
var options = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : optionsRef.current;
|
|
107
|
+
var normalizedResult = !result.error ? result : _rollupPluginBabelHelpers.objectSpread2(_rollupPluginBabelHelpers.objectSpread2({}, result), {}, {
|
|
108
|
+
error: future.normalizeFormError(result.error)
|
|
109
|
+
});
|
|
153
110
|
var intent$1 = result.submission.intent ? intent.deserializeIntent(result.submission.intent) : null;
|
|
154
|
-
var finalResult = intent.applyIntent(
|
|
111
|
+
var finalResult = intent.applyIntent(normalizedResult, intent$1, {
|
|
155
112
|
handlers: intent.intentHandlers
|
|
156
113
|
});
|
|
157
114
|
var formElement = dom.getFormElement(formRef);
|
|
@@ -163,7 +120,7 @@ function useConform(formRef, options) {
|
|
|
163
120
|
intent: intent$1,
|
|
164
121
|
ctx: {
|
|
165
122
|
handlers: intent.intentHandlers,
|
|
166
|
-
cancelled: finalResult !==
|
|
123
|
+
cancelled: finalResult !== normalizedResult,
|
|
167
124
|
reset(defaultValue) {
|
|
168
125
|
return state.initializeState({
|
|
169
126
|
defaultValue: defaultValue !== null && defaultValue !== void 0 ? defaultValue : options.defaultValue
|
|
@@ -173,11 +130,11 @@ function useConform(formRef, options) {
|
|
|
173
130
|
})));
|
|
174
131
|
|
|
175
132
|
// TODO: move on error handler to a new effect
|
|
176
|
-
if (formElement &&
|
|
133
|
+
if (formElement && finalResult.error) {
|
|
177
134
|
var _optionsRef$current$o, _optionsRef$current;
|
|
178
135
|
(_optionsRef$current$o = (_optionsRef$current = optionsRef.current).onError) === null || _optionsRef$current$o === void 0 || _optionsRef$current$o.call(_optionsRef$current, {
|
|
179
136
|
formElement,
|
|
180
|
-
error:
|
|
137
|
+
error: finalResult.error,
|
|
181
138
|
intent: intent$1
|
|
182
139
|
});
|
|
183
140
|
}
|
|
@@ -275,7 +232,7 @@ function useConform(formRef, options) {
|
|
|
275
232
|
value !== undefined ? (_optionsRef$current$o2 = (_optionsRef$current2 = optionsRef.current).onValidate) === null || _optionsRef$current$o2 === void 0 ? void 0 : _optionsRef$current$o2.call(_optionsRef$current2, {
|
|
276
233
|
payload: value,
|
|
277
234
|
error: {
|
|
278
|
-
formErrors:
|
|
235
|
+
formErrors: null,
|
|
279
236
|
fieldErrors: {}
|
|
280
237
|
},
|
|
281
238
|
intent: submission.intent ? intent.deserializeIntent(submission.intent) : null,
|
|
@@ -368,279 +325,12 @@ function useConform(formRef, options) {
|
|
|
368
325
|
return [state$1, handleSubmit];
|
|
369
326
|
}
|
|
370
327
|
|
|
371
|
-
/**
|
|
372
|
-
* The main React hook for form management. Handles form state, validation, and submission
|
|
373
|
-
* while providing access to form metadata, field objects, and form actions.
|
|
374
|
-
*
|
|
375
|
-
* It can be called in two ways:
|
|
376
|
-
* - **Schema first**: Pass a schema as the first argument for automatic validation with type inference
|
|
377
|
-
* - **Manual configuration**: Pass options with custom `onValidate` handler for manual validation
|
|
378
|
-
*
|
|
379
|
-
* @see https://conform.guide/api/react/future/useForm
|
|
380
|
-
* @example Schema first setup with zod:
|
|
381
|
-
*
|
|
382
|
-
* ```tsx
|
|
383
|
-
* const { form, fields } = useForm(zodSchema, {
|
|
384
|
-
* lastResult,
|
|
385
|
-
* shouldValidate: 'onBlur',
|
|
386
|
-
* });
|
|
387
|
-
*
|
|
388
|
-
* return (
|
|
389
|
-
* <form {...form.props}>
|
|
390
|
-
* <input name={fields.email.name} defaultValue={fields.email.defaultValue} />
|
|
391
|
-
* <div>{fields.email.errors}</div>
|
|
392
|
-
* </form>
|
|
393
|
-
* );
|
|
394
|
-
* ```
|
|
395
|
-
*
|
|
396
|
-
* @example Manual configuration setup with custom validation:
|
|
397
|
-
*
|
|
398
|
-
* ```tsx
|
|
399
|
-
* const { form, fields } = useForm({
|
|
400
|
-
* onValidate({ payload, error }) {
|
|
401
|
-
* if (!payload.email) {
|
|
402
|
-
* error.fieldErrors.email = ['Required'];
|
|
403
|
-
* }
|
|
404
|
-
* return error;
|
|
405
|
-
* }
|
|
406
|
-
* });
|
|
407
|
-
*
|
|
408
|
-
* return (
|
|
409
|
-
* <form {...form.props}>
|
|
410
|
-
* <input name={fields.email.name} defaultValue={fields.email.defaultValue} />
|
|
411
|
-
* <div>{fields.email.errors}</div>
|
|
412
|
-
* </form>
|
|
413
|
-
* );
|
|
414
|
-
* ```
|
|
415
|
-
*/
|
|
416
|
-
|
|
417
|
-
/**
|
|
418
|
-
* @deprecated Use `useForm(schema, options)` instead for better type inference.
|
|
419
|
-
*/
|
|
420
|
-
|
|
421
|
-
function useForm(schemaOrOptions, maybeOptions) {
|
|
422
|
-
var _options$onError;
|
|
423
|
-
var schema;
|
|
424
|
-
var options;
|
|
425
|
-
if (maybeOptions) {
|
|
426
|
-
schema = schemaOrOptions;
|
|
427
|
-
options = maybeOptions;
|
|
428
|
-
} else {
|
|
429
|
-
var fullOptions = schemaOrOptions;
|
|
430
|
-
options = fullOptions;
|
|
431
|
-
schema = fullOptions.schema;
|
|
432
|
-
}
|
|
433
|
-
var {
|
|
434
|
-
id,
|
|
435
|
-
constraint
|
|
436
|
-
} = options;
|
|
437
|
-
var globalOptions = react.useContext(GlobalFormOptionsContext);
|
|
438
|
-
var optionsRef = useLatest(options);
|
|
439
|
-
var globalOptionsRef = useLatest(globalOptions);
|
|
440
|
-
var fallbackId = react.useId();
|
|
441
|
-
var formId = id !== null && id !== void 0 ? id : "form-".concat(fallbackId);
|
|
442
|
-
var [state$1, handleSubmit] = useConform(formId, _rollupPluginBabelHelpers.objectSpread2(_rollupPluginBabelHelpers.objectSpread2({}, options), {}, {
|
|
443
|
-
serialize: globalOptions.serialize,
|
|
444
|
-
intentName: globalOptions.intentName,
|
|
445
|
-
onError: (_options$onError = options.onError) !== null && _options$onError !== void 0 ? _options$onError : dom.focusFirstInvalidField,
|
|
446
|
-
onValidate(ctx) {
|
|
447
|
-
var _options$onValidate, _options$onValidate2, _options;
|
|
448
|
-
if (schema) {
|
|
449
|
-
var standardResult = schema['~standard'].validate(ctx.payload);
|
|
450
|
-
if (standardResult instanceof Promise) {
|
|
451
|
-
return standardResult.then(actualStandardResult => {
|
|
452
|
-
if (typeof options.onValidate === 'function') {
|
|
453
|
-
throw new Error('The "onValidate" handler is not supported when used with asynchronous schema validation.');
|
|
454
|
-
}
|
|
455
|
-
return util.resolveStandardSchemaResult(actualStandardResult);
|
|
456
|
-
});
|
|
457
|
-
}
|
|
458
|
-
var resolvedResult = util.resolveStandardSchemaResult(standardResult);
|
|
459
|
-
if (!options.onValidate) {
|
|
460
|
-
return resolvedResult;
|
|
461
|
-
}
|
|
462
|
-
|
|
463
|
-
// Update the schema error in the context
|
|
464
|
-
if (resolvedResult.error) {
|
|
465
|
-
ctx.error = resolvedResult.error;
|
|
466
|
-
}
|
|
467
|
-
ctx.schemaValue = resolvedResult.value;
|
|
468
|
-
var validateResult = util.resolveValidateResult(options.onValidate(ctx));
|
|
469
|
-
if (validateResult.syncResult) {
|
|
470
|
-
var _validateResult$syncR, _validateResult$syncR2;
|
|
471
|
-
(_validateResult$syncR2 = (_validateResult$syncR = validateResult.syncResult).value) !== null && _validateResult$syncR2 !== void 0 ? _validateResult$syncR2 : _validateResult$syncR.value = resolvedResult.value;
|
|
472
|
-
}
|
|
473
|
-
if (validateResult.asyncResult) {
|
|
474
|
-
validateResult.asyncResult = validateResult.asyncResult.then(result => {
|
|
475
|
-
var _result$value;
|
|
476
|
-
(_result$value = result.value) !== null && _result$value !== void 0 ? _result$value : result.value = resolvedResult.value;
|
|
477
|
-
return result;
|
|
478
|
-
});
|
|
479
|
-
}
|
|
480
|
-
return [validateResult.syncResult, validateResult.asyncResult];
|
|
481
|
-
}
|
|
482
|
-
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 : {
|
|
483
|
-
// To avoid conform falling back to server validation,
|
|
484
|
-
// if neither schema nor validation handler is provided,
|
|
485
|
-
// we just treat it as a valid client submission
|
|
486
|
-
error: null
|
|
487
|
-
};
|
|
488
|
-
}
|
|
489
|
-
}));
|
|
490
|
-
var intent = useIntent(formId);
|
|
491
|
-
var context = react.useMemo(() => ({
|
|
492
|
-
formId,
|
|
493
|
-
state: state$1,
|
|
494
|
-
constraint: constraint !== null && constraint !== void 0 ? constraint : null,
|
|
495
|
-
handleSubmit,
|
|
496
|
-
handleInput(event) {
|
|
497
|
-
var _optionsRef$current$o4, _optionsRef$current4, _globalOptionsRef$cur;
|
|
498
|
-
if (!future.isFieldElement(event.target) || event.target.name === '' || event.target.form === null || event.target.form !== dom.getFormElement(formId)) {
|
|
499
|
-
return;
|
|
500
|
-
}
|
|
501
|
-
(_optionsRef$current$o4 = (_optionsRef$current4 = optionsRef.current).onInput) === null || _optionsRef$current$o4 === void 0 || _optionsRef$current$o4.call(_optionsRef$current4, _rollupPluginBabelHelpers.objectSpread2(_rollupPluginBabelHelpers.objectSpread2({}, event), {}, {
|
|
502
|
-
target: event.target,
|
|
503
|
-
currentTarget: event.target.form
|
|
504
|
-
}));
|
|
505
|
-
if (event.defaultPrevented) {
|
|
506
|
-
return;
|
|
507
|
-
}
|
|
508
|
-
var {
|
|
509
|
-
shouldValidate = globalOptionsRef.current.shouldValidate,
|
|
510
|
-
shouldRevalidate = (_globalOptionsRef$cur = globalOptionsRef.current.shouldRevalidate) !== null && _globalOptionsRef$cur !== void 0 ? _globalOptionsRef$cur : shouldValidate
|
|
511
|
-
} = optionsRef.current;
|
|
512
|
-
if (state.isTouched(state$1, event.target.name) ? shouldRevalidate === 'onInput' : shouldValidate === 'onInput') {
|
|
513
|
-
intent.validate(event.target.name);
|
|
514
|
-
}
|
|
515
|
-
},
|
|
516
|
-
handleBlur(event) {
|
|
517
|
-
var _optionsRef$current$o5, _optionsRef$current5, _globalOptionsRef$cur2;
|
|
518
|
-
if (!future.isFieldElement(event.target) || event.target.name === '' || event.target.form === null || event.target.form !== dom.getFormElement(formId)) {
|
|
519
|
-
return;
|
|
520
|
-
}
|
|
521
|
-
(_optionsRef$current$o5 = (_optionsRef$current5 = optionsRef.current).onBlur) === null || _optionsRef$current$o5 === void 0 || _optionsRef$current$o5.call(_optionsRef$current5, _rollupPluginBabelHelpers.objectSpread2(_rollupPluginBabelHelpers.objectSpread2({}, event), {}, {
|
|
522
|
-
target: event.target,
|
|
523
|
-
currentTarget: event.target.form
|
|
524
|
-
}));
|
|
525
|
-
if (event.defaultPrevented) {
|
|
526
|
-
return;
|
|
527
|
-
}
|
|
528
|
-
var {
|
|
529
|
-
shouldValidate = globalOptionsRef.current.shouldValidate,
|
|
530
|
-
shouldRevalidate = (_globalOptionsRef$cur2 = globalOptionsRef.current.shouldRevalidate) !== null && _globalOptionsRef$cur2 !== void 0 ? _globalOptionsRef$cur2 : shouldValidate
|
|
531
|
-
} = optionsRef.current;
|
|
532
|
-
if (state.isTouched(state$1, event.target.name) ? shouldRevalidate === 'onBlur' : shouldValidate === 'onBlur') {
|
|
533
|
-
intent.validate(event.target.name);
|
|
534
|
-
}
|
|
535
|
-
}
|
|
536
|
-
}), [formId, state$1, constraint, handleSubmit, intent, optionsRef, globalOptionsRef]);
|
|
537
|
-
var form = react.useMemo(() => state.getFormMetadata(context, {
|
|
538
|
-
serialize: globalOptions.serialize,
|
|
539
|
-
extendFieldMetadata: globalOptions.defineCustomMetadata
|
|
540
|
-
}), [context, globalOptions.serialize, globalOptions.defineCustomMetadata]);
|
|
541
|
-
var fields = react.useMemo(() => state.getFieldset(context, {
|
|
542
|
-
serialize: globalOptions.serialize,
|
|
543
|
-
extendFieldMetadata: globalOptions.defineCustomMetadata
|
|
544
|
-
}), [context, globalOptions.serialize, globalOptions.defineCustomMetadata]);
|
|
545
|
-
return {
|
|
546
|
-
form,
|
|
547
|
-
fields,
|
|
548
|
-
intent
|
|
549
|
-
};
|
|
550
|
-
}
|
|
551
|
-
|
|
552
|
-
/**
|
|
553
|
-
* A React hook that provides access to form-level metadata and state.
|
|
554
|
-
* Requires `FormProvider` context when used in child components.
|
|
555
|
-
*
|
|
556
|
-
* @see https://conform.guide/api/react/future/useFormMetadata
|
|
557
|
-
* @example
|
|
558
|
-
* ```tsx
|
|
559
|
-
* function ErrorSummary() {
|
|
560
|
-
* const form = useFormMetadata();
|
|
561
|
-
*
|
|
562
|
-
* if (form.valid) return null;
|
|
563
|
-
*
|
|
564
|
-
* return (
|
|
565
|
-
* <div>Please fix {Object.keys(form.fieldErrors).length} errors</div>
|
|
566
|
-
* );
|
|
567
|
-
* }
|
|
568
|
-
* ```
|
|
569
|
-
*/
|
|
570
|
-
function useFormMetadata() {
|
|
571
|
-
var options = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};
|
|
572
|
-
var globalOptions = react.useContext(GlobalFormOptionsContext);
|
|
573
|
-
var context = useFormContext(options.formId);
|
|
574
|
-
var formMetadata = react.useMemo(() => state.getFormMetadata(context, {
|
|
575
|
-
serialize: globalOptions.serialize,
|
|
576
|
-
extendFieldMetadata: globalOptions.defineCustomMetadata
|
|
577
|
-
}), [context, globalOptions.serialize, globalOptions.defineCustomMetadata]);
|
|
578
|
-
return formMetadata;
|
|
579
|
-
}
|
|
580
|
-
|
|
581
|
-
/**
|
|
582
|
-
* A React hook that provides access to a specific field's metadata and state.
|
|
583
|
-
* Requires `FormProvider` context when used in child components.
|
|
584
|
-
*
|
|
585
|
-
* @see https://conform.guide/api/react/future/useField
|
|
586
|
-
* @example
|
|
587
|
-
* ```tsx
|
|
588
|
-
* function FormField({ name, label }) {
|
|
589
|
-
* const field = useField(name);
|
|
590
|
-
*
|
|
591
|
-
* return (
|
|
592
|
-
* <div>
|
|
593
|
-
* <label htmlFor={field.id}>{label}</label>
|
|
594
|
-
* <input id={field.id} name={field.name} defaultValue={field.defaultValue} />
|
|
595
|
-
* {field.errors && <div>{field.errors.join(', ')}</div>}
|
|
596
|
-
* </div>
|
|
597
|
-
* );
|
|
598
|
-
* }
|
|
599
|
-
* ```
|
|
600
|
-
*/
|
|
601
|
-
function useField(name) {
|
|
602
|
-
var options = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};
|
|
603
|
-
var globalOptions = react.useContext(GlobalFormOptionsContext);
|
|
604
|
-
var context = useFormContext(options.formId);
|
|
605
|
-
var field = react.useMemo(() => state.getField(context, {
|
|
606
|
-
name,
|
|
607
|
-
serialize: globalOptions.serialize,
|
|
608
|
-
extendFieldMetadata: globalOptions.defineCustomMetadata
|
|
609
|
-
}), [context, name, globalOptions.serialize, globalOptions.defineCustomMetadata]);
|
|
610
|
-
return field;
|
|
611
|
-
}
|
|
612
|
-
|
|
613
|
-
/**
|
|
614
|
-
* A React hook that provides an intent dispatcher for programmatic form actions.
|
|
615
|
-
* Intent dispatchers allow you to trigger form operations like validation, field updates,
|
|
616
|
-
* and array manipulations without manual form submission.
|
|
617
|
-
*
|
|
618
|
-
* @see https://conform.guide/api/react/future/useIntent
|
|
619
|
-
* @example
|
|
620
|
-
* ```tsx
|
|
621
|
-
* function ResetButton() {
|
|
622
|
-
* const buttonRef = useRef<HTMLButtonElement>(null);
|
|
623
|
-
* const intent = useIntent(buttonRef);
|
|
624
|
-
*
|
|
625
|
-
* return (
|
|
626
|
-
* <button type="button" ref={buttonRef} onClick={() => intent.reset()}>
|
|
627
|
-
* Reset Form
|
|
628
|
-
* </button>
|
|
629
|
-
* );
|
|
630
|
-
* }
|
|
631
|
-
* ```
|
|
632
|
-
*/
|
|
633
|
-
function useIntent(formRef) {
|
|
634
|
-
var globalOptions = react.useContext(GlobalFormOptionsContext);
|
|
635
|
-
return react.useMemo(() => dom.createIntentDispatcher(() => dom.getFormElement(formRef), globalOptions.intentName), [formRef, globalOptions.intentName]);
|
|
636
|
-
}
|
|
637
|
-
|
|
638
328
|
/**
|
|
639
329
|
* A React hook that lets you sync the state of an input and dispatch native form events from it.
|
|
640
330
|
* This is useful when emulating native input behavior — typically by rendering a hidden base control
|
|
641
331
|
* and syncing it with a custom input.
|
|
642
332
|
*
|
|
643
|
-
*
|
|
333
|
+
* **Example:**
|
|
644
334
|
* ```ts
|
|
645
335
|
* const control = useControl(options);
|
|
646
336
|
* ```
|
|
@@ -648,9 +338,7 @@ function useIntent(formRef) {
|
|
|
648
338
|
|
|
649
339
|
function useControl() {
|
|
650
340
|
var options = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};
|
|
651
|
-
var
|
|
652
|
-
observer
|
|
653
|
-
} = react.useContext(GlobalFormOptionsContext);
|
|
341
|
+
var observer = react.useContext(GlobalFormsObserverContext);
|
|
654
342
|
var inputRef = react.useRef(null);
|
|
655
343
|
var formRef = react.useMemo(() => ({
|
|
656
344
|
get current() {
|
|
@@ -703,27 +391,25 @@ function useControl() {
|
|
|
703
391
|
return next;
|
|
704
392
|
}, () => snapshotRef.current);
|
|
705
393
|
react.useEffect(() => {
|
|
706
|
-
var createEventListener = listener => {
|
|
707
|
-
|
|
708
|
-
|
|
709
|
-
|
|
710
|
-
|
|
711
|
-
|
|
712
|
-
clearTimeout(timer);
|
|
713
|
-
}
|
|
714
|
-
eventDispatched.current[listener] = window.setTimeout(() => {
|
|
715
|
-
eventDispatched.current[listener] = undefined;
|
|
716
|
-
});
|
|
717
|
-
if (listener === 'focus') {
|
|
718
|
-
var _optionsRef$current6, _optionsRef$current6$;
|
|
719
|
-
(_optionsRef$current6 = optionsRef.current) === null || _optionsRef$current6 === void 0 || (_optionsRef$current6$ = _optionsRef$current6.onFocus) === null || _optionsRef$current6$ === void 0 || _optionsRef$current6$.call(_optionsRef$current6);
|
|
720
|
-
}
|
|
394
|
+
var createEventListener = (listener, event) => {
|
|
395
|
+
var _inputRef$current2;
|
|
396
|
+
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))) {
|
|
397
|
+
var timer = eventDispatched.current[listener];
|
|
398
|
+
if (timer) {
|
|
399
|
+
clearTimeout(timer);
|
|
721
400
|
}
|
|
722
|
-
|
|
401
|
+
eventDispatched.current[listener] = window.setTimeout(() => {
|
|
402
|
+
eventDispatched.current[listener] = undefined;
|
|
403
|
+
});
|
|
404
|
+
if (listener === 'focus') {
|
|
405
|
+
var _optionsRef$current4, _optionsRef$current4$;
|
|
406
|
+
(_optionsRef$current4 = optionsRef.current) === null || _optionsRef$current4 === void 0 || (_optionsRef$current4$ = _optionsRef$current4.onFocus) === null || _optionsRef$current4$ === void 0 || _optionsRef$current4$.call(_optionsRef$current4);
|
|
407
|
+
}
|
|
408
|
+
}
|
|
723
409
|
};
|
|
724
|
-
var inputHandler = createEventListener('change');
|
|
725
|
-
var focusHandler = createEventListener('focus');
|
|
726
|
-
var blurHandler = createEventListener('blur');
|
|
410
|
+
var inputHandler = createEventListener.bind(null, 'change');
|
|
411
|
+
var focusHandler = createEventListener.bind(null, 'focus');
|
|
412
|
+
var blurHandler = createEventListener.bind(null, 'blur');
|
|
727
413
|
document.addEventListener('input', inputHandler, true);
|
|
728
414
|
document.addEventListener('focusin', focusHandler, true);
|
|
729
415
|
document.addEventListener('focusout', blurHandler, true);
|
|
@@ -806,7 +492,7 @@ function useControl() {
|
|
|
806
492
|
if (element.type === 'checkbox' || element.type === 'radio') {
|
|
807
493
|
// React set the value as empty string incorrectly when the value is undefined
|
|
808
494
|
// This make sure the checkbox value falls back to the default value "on" properly
|
|
809
|
-
//
|
|
495
|
+
// See https://github.com/facebook/react/issues/17590
|
|
810
496
|
var value = 'value' in optionsRef.current && optionsRef.current.value ? optionsRef.current.value : 'on';
|
|
811
497
|
element.value = value;
|
|
812
498
|
}
|
|
@@ -824,10 +510,10 @@ function useControl() {
|
|
|
824
510
|
inputRef.current = inputs;
|
|
825
511
|
if ('defaultValue' in optionsRef.current) {
|
|
826
512
|
for (var input of inputs) {
|
|
827
|
-
var _optionsRef$
|
|
513
|
+
var _optionsRef$current5;
|
|
828
514
|
dom.initializeField(input, {
|
|
829
515
|
// We will not be uitlizing defaultChecked / value on checkbox / radio group
|
|
830
|
-
defaultValue: (_optionsRef$
|
|
516
|
+
defaultValue: (_optionsRef$current5 = optionsRef.current) === null || _optionsRef$current5 === void 0 ? void 0 : _optionsRef$current5.defaultValue
|
|
831
517
|
});
|
|
832
518
|
}
|
|
833
519
|
}
|
|
@@ -893,8 +579,9 @@ function useControl() {
|
|
|
893
579
|
* Returns `undefined` when the form element is not available (e.g., on SSR or initial client render),
|
|
894
580
|
* unless a `fallback` is provided.
|
|
895
581
|
*
|
|
896
|
-
*
|
|
897
|
-
*
|
|
582
|
+
* See https://conform.guide/api/react/future/useFormData
|
|
583
|
+
*
|
|
584
|
+
* **Example:**
|
|
898
585
|
* ```ts
|
|
899
586
|
* const value = useFormData(
|
|
900
587
|
* formRef,
|
|
@@ -904,9 +591,7 @@ function useControl() {
|
|
|
904
591
|
*/
|
|
905
592
|
|
|
906
593
|
function useFormData(formRef, select, options) {
|
|
907
|
-
var
|
|
908
|
-
observer
|
|
909
|
-
} = react.useContext(GlobalFormOptionsContext);
|
|
594
|
+
var observer = react.useContext(GlobalFormsObserverContext);
|
|
910
595
|
var valueRef = react.useRef();
|
|
911
596
|
var formDataRef = react.useRef();
|
|
912
597
|
var value = react.useSyncExternalStore(react.useCallback(callback => {
|
|
@@ -966,7 +651,7 @@ function useLatest(value) {
|
|
|
966
651
|
* A component that renders hidden base control(s) based on the shape of defaultValue.
|
|
967
652
|
* Used with useControl to sync complex values with form data.
|
|
968
653
|
*
|
|
969
|
-
*
|
|
654
|
+
* **Example:**
|
|
970
655
|
* ```tsx
|
|
971
656
|
* const control = useControl<{ street: string; city: string }>({
|
|
972
657
|
* defaultValue: { street: '123 Main St', city: 'Anytown' },
|
|
@@ -996,7 +681,7 @@ function useLatest(value) {
|
|
|
996
681
|
*/
|
|
997
682
|
var BaseControl = /*#__PURE__*/react.forwardRef(function BaseControl(props, ref) {
|
|
998
683
|
function formatValue(value) {
|
|
999
|
-
var serialized = future.
|
|
684
|
+
var serialized = future.defaultSerialize(value);
|
|
1000
685
|
if (typeof serialized === 'string') {
|
|
1001
686
|
return serialized;
|
|
1002
687
|
}
|
|
@@ -1027,7 +712,7 @@ var BaseControl = /*#__PURE__*/react.forwardRef(function BaseControl(props, ref)
|
|
|
1027
712
|
defaultValue: _defaultValue,
|
|
1028
713
|
hidden: _hidden = true
|
|
1029
714
|
} = props,
|
|
1030
|
-
fieldsetProps = _rollupPluginBabelHelpers.objectWithoutProperties(props,
|
|
715
|
+
fieldsetProps = _rollupPluginBabelHelpers.objectWithoutProperties(props, _excluded);
|
|
1031
716
|
return /*#__PURE__*/jsxRuntime.jsx("fieldset", _rollupPluginBabelHelpers.objectSpread2(_rollupPluginBabelHelpers.objectSpread2({}, fieldsetProps), {}, {
|
|
1032
717
|
ref: ref,
|
|
1033
718
|
name: name,
|
|
@@ -1042,7 +727,7 @@ var BaseControl = /*#__PURE__*/react.forwardRef(function BaseControl(props, ref)
|
|
|
1042
727
|
multiple = Array.isArray(_defaultValue2),
|
|
1043
728
|
hidden: _hidden2 = true
|
|
1044
729
|
} = props,
|
|
1045
|
-
selectProps = _rollupPluginBabelHelpers.objectWithoutProperties(props,
|
|
730
|
+
selectProps = _rollupPluginBabelHelpers.objectWithoutProperties(props, _excluded2);
|
|
1046
731
|
if (multiple) {
|
|
1047
732
|
var defaultOptions = Array.isArray(_defaultValue2) ? _defaultValue2.map(formatValue) : [formatValue(_defaultValue2)];
|
|
1048
733
|
return /*#__PURE__*/jsxRuntime.jsx("select", _rollupPluginBabelHelpers.objectSpread2(_rollupPluginBabelHelpers.objectSpread2({}, selectProps), {}, {
|
|
@@ -1072,7 +757,7 @@ var BaseControl = /*#__PURE__*/react.forwardRef(function BaseControl(props, ref)
|
|
|
1072
757
|
defaultValue: _defaultValue3,
|
|
1073
758
|
hidden: _hidden3 = true
|
|
1074
759
|
} = props,
|
|
1075
|
-
textareaProps = _rollupPluginBabelHelpers.objectWithoutProperties(props,
|
|
760
|
+
textareaProps = _rollupPluginBabelHelpers.objectWithoutProperties(props, _excluded3);
|
|
1076
761
|
return /*#__PURE__*/jsxRuntime.jsx("textarea", _rollupPluginBabelHelpers.objectSpread2(_rollupPluginBabelHelpers.objectSpread2({}, textareaProps), {}, {
|
|
1077
762
|
defaultValue: formatValue(_defaultValue3),
|
|
1078
763
|
ref: ref,
|
|
@@ -1085,7 +770,7 @@ var BaseControl = /*#__PURE__*/react.forwardRef(function BaseControl(props, ref)
|
|
|
1085
770
|
value = _defaultValue4,
|
|
1086
771
|
hidden: _hidden4 = true
|
|
1087
772
|
} = props,
|
|
1088
|
-
_inputProps = _rollupPluginBabelHelpers.objectWithoutProperties(props,
|
|
773
|
+
_inputProps = _rollupPluginBabelHelpers.objectWithoutProperties(props, _excluded4);
|
|
1089
774
|
return /*#__PURE__*/jsxRuntime.jsx("input", _rollupPluginBabelHelpers.objectSpread2(_rollupPluginBabelHelpers.objectSpread2({}, _inputProps), {}, {
|
|
1090
775
|
ref: ref,
|
|
1091
776
|
value: value,
|
|
@@ -1096,7 +781,7 @@ var BaseControl = /*#__PURE__*/react.forwardRef(function BaseControl(props, ref)
|
|
|
1096
781
|
defaultValue,
|
|
1097
782
|
hidden = true
|
|
1098
783
|
} = props,
|
|
1099
|
-
inputProps = _rollupPluginBabelHelpers.objectWithoutProperties(props,
|
|
784
|
+
inputProps = _rollupPluginBabelHelpers.objectWithoutProperties(props, _excluded5);
|
|
1100
785
|
return /*#__PURE__*/jsxRuntime.jsx("input", _rollupPluginBabelHelpers.objectSpread2(_rollupPluginBabelHelpers.objectSpread2({}, inputProps), {}, {
|
|
1101
786
|
ref: ref,
|
|
1102
787
|
defaultValue: defaultValue !== undefined ? formatValue(defaultValue) : undefined,
|
|
@@ -1105,19 +790,11 @@ var BaseControl = /*#__PURE__*/react.forwardRef(function BaseControl(props, ref)
|
|
|
1105
790
|
});
|
|
1106
791
|
|
|
1107
792
|
exports.BaseControl = BaseControl;
|
|
1108
|
-
exports.
|
|
1109
|
-
exports.FormOptionsProvider = FormOptionsProvider;
|
|
1110
|
-
exports.FormProvider = FormProvider;
|
|
1111
|
-
exports.GlobalFormOptionsContext = GlobalFormOptionsContext;
|
|
793
|
+
exports.GlobalFormsObserverContext = GlobalFormsObserverContext;
|
|
1112
794
|
exports.INITIAL_KEY = INITIAL_KEY;
|
|
1113
795
|
exports.PreserveBoundary = PreserveBoundary;
|
|
1114
796
|
exports.useConform = useConform;
|
|
1115
797
|
exports.useControl = useControl;
|
|
1116
|
-
exports.useField = useField;
|
|
1117
|
-
exports.useForm = useForm;
|
|
1118
|
-
exports.useFormContext = useFormContext;
|
|
1119
798
|
exports.useFormData = useFormData;
|
|
1120
|
-
exports.useFormMetadata = useFormMetadata;
|
|
1121
|
-
exports.useIntent = useIntent;
|
|
1122
799
|
exports.useLatest = useLatest;
|
|
1123
800
|
exports.useSafeLayoutEffect = useSafeLayoutEffect;
|