@conform-to/react 1.11.0 → 1.12.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/hooks.d.ts +14 -24
- package/dist/future/hooks.js +59 -47
- package/dist/future/hooks.mjs +58 -47
- package/dist/future/index.d.ts +2 -2
- package/dist/future/index.js +1 -0
- package/dist/future/index.mjs +1 -1
- package/dist/future/intent.js +16 -10
- package/dist/future/intent.mjs +16 -10
- package/dist/future/state.d.ts +10 -6
- package/dist/future/state.js +97 -49
- package/dist/future/state.mjs +97 -49
- package/dist/future/types.d.ts +124 -63
- package/package.json +2 -2
package/README.md
CHANGED
|
@@ -7,7 +7,7 @@
|
|
|
7
7
|
╚══════╝ ╚═════╝ ╚═╝ ╚══╝ ╚═╝ ╚═════╝ ╚═╝ ╚═╝ ╚═╝ ╚═╝
|
|
8
8
|
```
|
|
9
9
|
|
|
10
|
-
Version 1.
|
|
10
|
+
Version 1.12.1 / License MIT / Copyright (c) 2025 Edmund Hung
|
|
11
11
|
|
|
12
12
|
Progressively enhance HTML forms with React. Build resilient, type-safe forms with no hassle using web standards.
|
|
13
13
|
|
package/dist/future/hooks.d.ts
CHANGED
|
@@ -1,24 +1,11 @@
|
|
|
1
|
-
import { type Serialize, type SubmissionResult,
|
|
1
|
+
import { type Serialize, type SubmissionResult, createGlobalFormsObserver } from '@conform-to/dom/future';
|
|
2
2
|
import { useEffect } from 'react';
|
|
3
|
-
import type { FormContext, IntentDispatcher, FormMetadata, Fieldset, FormOptions, FieldName, FieldMetadata, Control, Selector, UseFormDataOptions, ValidateHandler, ErrorHandler, SubmitHandler, FormState, FormRef } from './types';
|
|
3
|
+
import type { FormContext, IntentDispatcher, FormMetadata, Fieldset, GlobalFormOptions, FormOptions, FieldName, FieldMetadata, Control, Selector, UseFormDataOptions, ValidateHandler, ErrorHandler, SubmitHandler, FormState, FormRef, BaseErrorShape, DefaultErrorShape } from './types';
|
|
4
4
|
export declare const INITIAL_KEY = "INITIAL_KEY";
|
|
5
|
-
export declare const
|
|
6
|
-
|
|
7
|
-
observer: {
|
|
8
|
-
onFieldUpdate(callback: (event: {
|
|
9
|
-
type: "input" | "reset" | "mutation";
|
|
10
|
-
target: HTMLInputElement | HTMLSelectElement | HTMLTextAreaElement;
|
|
11
|
-
}) => void): () => void;
|
|
12
|
-
onFormUpdate(callback: (event: {
|
|
13
|
-
type: "submit" | "input" | "reset" | "mutation";
|
|
14
|
-
target: HTMLFormElement;
|
|
15
|
-
submitter?: HTMLInputElement | HTMLButtonElement | null;
|
|
16
|
-
}) => void): () => void;
|
|
17
|
-
dispose(): void;
|
|
18
|
-
};
|
|
19
|
-
serialize: typeof serialize;
|
|
5
|
+
export declare const GlobalFormOptionsContext: import("react").Context<GlobalFormOptions & {
|
|
6
|
+
observer: ReturnType<typeof createGlobalFormsObserver>;
|
|
20
7
|
}>;
|
|
21
|
-
export declare const
|
|
8
|
+
export declare const FormContextContext: import("react").Context<FormContext<string>[]>;
|
|
22
9
|
/**
|
|
23
10
|
* Provides form context to child components.
|
|
24
11
|
* Stacks contexts to support nested forms, with latest context taking priority.
|
|
@@ -27,7 +14,10 @@ export declare function FormProvider(props: {
|
|
|
27
14
|
context: FormContext;
|
|
28
15
|
children: React.ReactNode;
|
|
29
16
|
}): React.ReactElement;
|
|
30
|
-
export declare function
|
|
17
|
+
export declare function FormOptionsProvider(props: Partial<GlobalFormOptions> & {
|
|
18
|
+
children: React.ReactNode;
|
|
19
|
+
}): React.ReactElement;
|
|
20
|
+
export declare function useFormContext(formId?: string): FormContext;
|
|
31
21
|
/**
|
|
32
22
|
* Core form hook that manages form state, validation, and submission.
|
|
33
23
|
* Handles both sync and async validation, intent dispatching, and DOM updates.
|
|
@@ -65,7 +55,7 @@ export declare function useConform<ErrorShape, Value = undefined>(formRef: FormR
|
|
|
65
55
|
* );
|
|
66
56
|
* ```
|
|
67
57
|
*/
|
|
68
|
-
export declare function useForm<FormShape extends Record<string, any> = Record<string, any>, ErrorShape =
|
|
58
|
+
export declare function useForm<FormShape extends Record<string, any> = Record<string, any>, ErrorShape extends BaseErrorShape = DefaultErrorShape, Value = undefined>(options: FormOptions<FormShape, ErrorShape, Value>): {
|
|
69
59
|
form: FormMetadata<ErrorShape>;
|
|
70
60
|
fields: Fieldset<FormShape, ErrorShape>;
|
|
71
61
|
intent: IntentDispatcher;
|
|
@@ -88,9 +78,9 @@ export declare function useForm<FormShape extends Record<string, any> = Record<s
|
|
|
88
78
|
* }
|
|
89
79
|
* ```
|
|
90
80
|
*/
|
|
91
|
-
export declare function useFormMetadata
|
|
81
|
+
export declare function useFormMetadata(options?: {
|
|
92
82
|
formId?: string;
|
|
93
|
-
}): FormMetadata
|
|
83
|
+
}): FormMetadata;
|
|
94
84
|
/**
|
|
95
85
|
* A React hook that provides access to a specific field's metadata and state.
|
|
96
86
|
* Requires `FormProvider` context when used in child components.
|
|
@@ -111,9 +101,9 @@ export declare function useFormMetadata<ErrorShape = string[]>(options?: {
|
|
|
111
101
|
* }
|
|
112
102
|
* ```
|
|
113
103
|
*/
|
|
114
|
-
export declare function useField<FieldShape = any
|
|
104
|
+
export declare function useField<FieldShape = any>(name: FieldName<FieldShape>, options?: {
|
|
115
105
|
formId?: string;
|
|
116
|
-
}): FieldMetadata<FieldShape
|
|
106
|
+
}): FieldMetadata<FieldShape>;
|
|
117
107
|
/**
|
|
118
108
|
* A React hook that provides an intent dispatcher for programmatic form actions.
|
|
119
109
|
* Intent dispatchers allow you to trigger form operations like validation, field updates,
|
package/dist/future/hooks.js
CHANGED
|
@@ -12,30 +12,44 @@ var intent = require('./intent.js');
|
|
|
12
12
|
var dom = require('./dom.js');
|
|
13
13
|
var jsxRuntime = require('react/jsx-runtime');
|
|
14
14
|
|
|
15
|
+
var _excluded = ["children"];
|
|
15
16
|
var INITIAL_KEY = 'INITIAL_KEY';
|
|
16
|
-
var
|
|
17
|
+
var GlobalFormOptionsContext = /*#__PURE__*/react.createContext({
|
|
17
18
|
intentName: future.DEFAULT_INTENT_NAME,
|
|
18
19
|
observer: future.createGlobalFormsObserver(),
|
|
19
|
-
serialize: future.serialize
|
|
20
|
+
serialize: future.serialize,
|
|
21
|
+
shouldValidate: 'onSubmit'
|
|
20
22
|
});
|
|
21
|
-
var
|
|
23
|
+
var FormContextContext = /*#__PURE__*/react.createContext([]);
|
|
22
24
|
|
|
23
25
|
/**
|
|
24
26
|
* Provides form context to child components.
|
|
25
27
|
* Stacks contexts to support nested forms, with latest context taking priority.
|
|
26
28
|
*/
|
|
27
29
|
function FormProvider(props) {
|
|
28
|
-
var stack = react.useContext(
|
|
30
|
+
var stack = react.useContext(FormContextContext);
|
|
29
31
|
var value = react.useMemo(
|
|
30
32
|
// Put the latest form context first to ensure that to be the first one found
|
|
31
33
|
() => [props.context].concat(stack), [stack, props.context]);
|
|
32
|
-
return /*#__PURE__*/jsxRuntime.jsx(
|
|
34
|
+
return /*#__PURE__*/jsxRuntime.jsx(FormContextContext.Provider, {
|
|
33
35
|
value: value,
|
|
34
36
|
children: props.children
|
|
35
37
|
});
|
|
36
38
|
}
|
|
39
|
+
function FormOptionsProvider(props) {
|
|
40
|
+
var {
|
|
41
|
+
children
|
|
42
|
+
} = props,
|
|
43
|
+
providedOptions = _rollupPluginBabelHelpers.objectWithoutProperties(props, _excluded);
|
|
44
|
+
var defaultOptions = react.useContext(GlobalFormOptionsContext);
|
|
45
|
+
var options = react.useMemo(() => _rollupPluginBabelHelpers.objectSpread2(_rollupPluginBabelHelpers.objectSpread2({}, defaultOptions), providedOptions), [defaultOptions, providedOptions]);
|
|
46
|
+
return /*#__PURE__*/jsxRuntime.jsx(GlobalFormOptionsContext.Provider, {
|
|
47
|
+
value: options,
|
|
48
|
+
children: children
|
|
49
|
+
});
|
|
50
|
+
}
|
|
37
51
|
function useFormContext(formId) {
|
|
38
|
-
var contexts = react.useContext(
|
|
52
|
+
var contexts = react.useContext(FormContextContext);
|
|
39
53
|
var context = formId ? contexts.find(context => formId === context.formId) : contexts[0];
|
|
40
54
|
if (!context) {
|
|
41
55
|
throw new Error('No form context found; Have you render a <FormProvider /> with the corresponding form context?');
|
|
@@ -72,11 +86,11 @@ function useConform(formRef, options) {
|
|
|
72
86
|
var lastIntentedValueRef = react.useRef();
|
|
73
87
|
var lastAsyncResultRef = react.useRef(null);
|
|
74
88
|
var abortControllerRef = react.useRef(null);
|
|
75
|
-
var handleSubmission = react.useCallback((
|
|
89
|
+
var handleSubmission = react.useCallback((type, result) => {
|
|
76
90
|
var _optionsRef$current$o, _optionsRef$current;
|
|
77
91
|
var intent$1 = result.submission.intent ? intent.deserializeIntent(result.submission.intent) : null;
|
|
78
92
|
setState(state$1 => state.updateState(state$1, _rollupPluginBabelHelpers.objectSpread2(_rollupPluginBabelHelpers.objectSpread2({}, result), {}, {
|
|
79
|
-
type
|
|
93
|
+
type,
|
|
80
94
|
intent: intent$1,
|
|
81
95
|
ctx: {
|
|
82
96
|
handlers: intent.actionHandlers,
|
|
@@ -105,9 +119,7 @@ function useConform(formRef, options) {
|
|
|
105
119
|
react.useEffect(() => {
|
|
106
120
|
// To avoid re-applying the same result twice
|
|
107
121
|
if (lastResult && lastResult !== lastResultRef.current) {
|
|
108
|
-
handleSubmission(
|
|
109
|
-
type: 'server'
|
|
110
|
-
});
|
|
122
|
+
handleSubmission('server', lastResult);
|
|
111
123
|
lastResultRef.current = lastResult;
|
|
112
124
|
}
|
|
113
125
|
}, [lastResult, handleSubmission]);
|
|
@@ -168,7 +180,7 @@ function useConform(formRef, options) {
|
|
|
168
180
|
// Patch missing fields in the submission object
|
|
169
181
|
for (var element of formElement.elements) {
|
|
170
182
|
if (future.isFieldElement(element) && element.name) {
|
|
171
|
-
util.appendUniqueItem(submission.fields, element.name);
|
|
183
|
+
submission.fields = util.appendUniqueItem(submission.fields, element.name);
|
|
172
184
|
}
|
|
173
185
|
}
|
|
174
186
|
|
|
@@ -219,9 +231,7 @@ function useConform(formRef, options) {
|
|
|
219
231
|
// There is no need to flush the update in this case
|
|
220
232
|
if (!abortController.signal.aborted) {
|
|
221
233
|
submissionResult.error = error;
|
|
222
|
-
handleSubmission(
|
|
223
|
-
type: 'server'
|
|
224
|
-
});
|
|
234
|
+
handleSubmission('server', submissionResult);
|
|
225
235
|
|
|
226
236
|
// If the form is meant to be submitted and there is no error
|
|
227
237
|
if (error === null && !submission.intent) {
|
|
@@ -239,9 +249,7 @@ function useConform(formRef, options) {
|
|
|
239
249
|
}
|
|
240
250
|
});
|
|
241
251
|
}
|
|
242
|
-
handleSubmission(
|
|
243
|
-
type: 'client'
|
|
244
|
-
});
|
|
252
|
+
handleSubmission('client', submissionResult);
|
|
245
253
|
if (
|
|
246
254
|
// If client validation happens
|
|
247
255
|
(typeof syncResult !== 'undefined' || typeof asyncResult !== 'undefined') && (
|
|
@@ -269,9 +277,7 @@ function useConform(formRef, options) {
|
|
|
269
277
|
var _submissionResult = future.report(result.submission, _rollupPluginBabelHelpers.objectSpread2(_rollupPluginBabelHelpers.objectSpread2({}, options), {}, {
|
|
270
278
|
keepFiles: true
|
|
271
279
|
}));
|
|
272
|
-
handleSubmission(
|
|
273
|
-
type: 'server'
|
|
274
|
-
});
|
|
280
|
+
handleSubmission('server', _submissionResult);
|
|
275
281
|
}
|
|
276
282
|
}
|
|
277
283
|
});
|
|
@@ -311,13 +317,14 @@ function useForm(options) {
|
|
|
311
317
|
defaultValue,
|
|
312
318
|
constraint
|
|
313
319
|
} = options;
|
|
314
|
-
var
|
|
320
|
+
var globalOptions = react.useContext(GlobalFormOptionsContext);
|
|
315
321
|
var optionsRef = useLatest(options);
|
|
322
|
+
var globalOptionsRef = useLatest(globalOptions);
|
|
316
323
|
var fallbackId = react.useId();
|
|
317
324
|
var formId = id !== null && id !== void 0 ? id : "form-".concat(fallbackId);
|
|
318
325
|
var [state$1, handleSubmit] = useConform(formId, _rollupPluginBabelHelpers.objectSpread2(_rollupPluginBabelHelpers.objectSpread2({}, options), {}, {
|
|
319
|
-
serialize:
|
|
320
|
-
intentName:
|
|
326
|
+
serialize: globalOptions.serialize,
|
|
327
|
+
intentName: globalOptions.intentName,
|
|
321
328
|
onError: (_optionsRef$current$o4 = optionsRef.current.onError) !== null && _optionsRef$current$o4 !== void 0 ? _optionsRef$current$o4 : dom.focusFirstInvalidField,
|
|
322
329
|
onValidate(ctx) {
|
|
323
330
|
var _options$onValidate, _options$onValidate2;
|
|
@@ -371,7 +378,7 @@ function useForm(options) {
|
|
|
371
378
|
constraint: constraint !== null && constraint !== void 0 ? constraint : null,
|
|
372
379
|
handleSubmit: handleSubmit,
|
|
373
380
|
handleInput(event) {
|
|
374
|
-
var _optionsRef$current$o5, _optionsRef$current4;
|
|
381
|
+
var _optionsRef$current$o5, _optionsRef$current4, _globalOptionsRef$cur;
|
|
375
382
|
if (!future.isFieldElement(event.target) || event.target.name === '' || event.target.form === null || event.target.form !== dom.getFormElement(formId)) {
|
|
376
383
|
return;
|
|
377
384
|
}
|
|
@@ -383,15 +390,15 @@ function useForm(options) {
|
|
|
383
390
|
return;
|
|
384
391
|
}
|
|
385
392
|
var {
|
|
386
|
-
shouldValidate =
|
|
387
|
-
shouldRevalidate = shouldValidate
|
|
393
|
+
shouldValidate = globalOptionsRef.current.shouldValidate,
|
|
394
|
+
shouldRevalidate = (_globalOptionsRef$cur = globalOptionsRef.current.shouldRevalidate) !== null && _globalOptionsRef$cur !== void 0 ? _globalOptionsRef$cur : shouldValidate
|
|
388
395
|
} = optionsRef.current;
|
|
389
396
|
if (state.isTouched(state$1, event.target.name) ? shouldRevalidate === 'onInput' : shouldValidate === 'onInput') {
|
|
390
397
|
intent.validate(event.target.name);
|
|
391
398
|
}
|
|
392
399
|
},
|
|
393
400
|
handleBlur(event) {
|
|
394
|
-
var _optionsRef$current$o6, _optionsRef$current5;
|
|
401
|
+
var _optionsRef$current$o6, _optionsRef$current5, _globalOptionsRef$cur2;
|
|
395
402
|
if (!future.isFieldElement(event.target) || event.target.name === '' || event.target.form === null || event.target.form !== dom.getFormElement(formId)) {
|
|
396
403
|
return;
|
|
397
404
|
}
|
|
@@ -403,20 +410,22 @@ function useForm(options) {
|
|
|
403
410
|
return;
|
|
404
411
|
}
|
|
405
412
|
var {
|
|
406
|
-
shouldValidate =
|
|
407
|
-
shouldRevalidate = shouldValidate
|
|
413
|
+
shouldValidate = globalOptionsRef.current.shouldValidate,
|
|
414
|
+
shouldRevalidate = (_globalOptionsRef$cur2 = globalOptionsRef.current.shouldRevalidate) !== null && _globalOptionsRef$cur2 !== void 0 ? _globalOptionsRef$cur2 : shouldValidate
|
|
408
415
|
} = optionsRef.current;
|
|
409
416
|
if (state.isTouched(state$1, event.target.name) ? shouldRevalidate === 'onBlur' : shouldValidate === 'onBlur') {
|
|
410
417
|
intent.validate(event.target.name);
|
|
411
418
|
}
|
|
412
419
|
}
|
|
413
|
-
}), [formId, state$1, defaultValue, constraint, handleSubmit, intent, optionsRef]);
|
|
420
|
+
}), [formId, state$1, defaultValue, constraint, handleSubmit, intent, optionsRef, globalOptionsRef]);
|
|
414
421
|
var form = react.useMemo(() => state.getFormMetadata(context, {
|
|
415
|
-
serialize:
|
|
416
|
-
|
|
422
|
+
serialize: globalOptions.serialize,
|
|
423
|
+
customize: globalOptions.defineCustomMetadata
|
|
424
|
+
}), [context, globalOptions.serialize, globalOptions.defineCustomMetadata]);
|
|
417
425
|
var fields = react.useMemo(() => state.getFieldset(context, {
|
|
418
|
-
serialize:
|
|
419
|
-
|
|
426
|
+
serialize: globalOptions.serialize,
|
|
427
|
+
customize: globalOptions.defineCustomMetadata
|
|
428
|
+
}), [context, globalOptions.serialize, globalOptions.defineCustomMetadata]);
|
|
420
429
|
return {
|
|
421
430
|
form,
|
|
422
431
|
fields,
|
|
@@ -444,11 +453,12 @@ function useForm(options) {
|
|
|
444
453
|
*/
|
|
445
454
|
function useFormMetadata() {
|
|
446
455
|
var options = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};
|
|
447
|
-
var
|
|
456
|
+
var globalOptions = react.useContext(GlobalFormOptionsContext);
|
|
448
457
|
var context = useFormContext(options.formId);
|
|
449
458
|
var formMetadata = react.useMemo(() => state.getFormMetadata(context, {
|
|
450
|
-
serialize:
|
|
451
|
-
|
|
459
|
+
serialize: globalOptions.serialize,
|
|
460
|
+
customize: globalOptions.defineCustomMetadata
|
|
461
|
+
}), [context, globalOptions.serialize, globalOptions.defineCustomMetadata]);
|
|
452
462
|
return formMetadata;
|
|
453
463
|
}
|
|
454
464
|
|
|
@@ -474,12 +484,13 @@ function useFormMetadata() {
|
|
|
474
484
|
*/
|
|
475
485
|
function useField(name) {
|
|
476
486
|
var options = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};
|
|
477
|
-
var
|
|
487
|
+
var globalOptions = react.useContext(GlobalFormOptionsContext);
|
|
478
488
|
var context = useFormContext(options.formId);
|
|
479
489
|
var field = react.useMemo(() => state.getField(context, {
|
|
480
490
|
name,
|
|
481
|
-
serialize:
|
|
482
|
-
|
|
491
|
+
serialize: globalOptions.serialize,
|
|
492
|
+
customize: globalOptions.defineCustomMetadata
|
|
493
|
+
}), [context, name, globalOptions.serialize, globalOptions.defineCustomMetadata]);
|
|
483
494
|
return field;
|
|
484
495
|
}
|
|
485
496
|
|
|
@@ -504,8 +515,8 @@ function useField(name) {
|
|
|
504
515
|
* ```
|
|
505
516
|
*/
|
|
506
517
|
function useIntent(formRef) {
|
|
507
|
-
var
|
|
508
|
-
return react.useMemo(() => dom.createIntentDispatcher(() => dom.getFormElement(formRef),
|
|
518
|
+
var globalOptions = react.useContext(GlobalFormOptionsContext);
|
|
519
|
+
return react.useMemo(() => dom.createIntentDispatcher(() => dom.getFormElement(formRef), globalOptions.intentName), [formRef, globalOptions.intentName]);
|
|
509
520
|
}
|
|
510
521
|
|
|
511
522
|
/**
|
|
@@ -521,7 +532,7 @@ function useIntent(formRef) {
|
|
|
521
532
|
function useControl(options) {
|
|
522
533
|
var {
|
|
523
534
|
observer
|
|
524
|
-
} = react.useContext(
|
|
535
|
+
} = react.useContext(GlobalFormOptionsContext);
|
|
525
536
|
var inputRef = react.useRef(null);
|
|
526
537
|
var eventDispatched = react.useRef({});
|
|
527
538
|
var defaultSnapshot = dom.createDefaultSnapshot(options === null || options === void 0 ? void 0 : options.defaultValue, options === null || options === void 0 ? void 0 : options.defaultChecked, options === null || options === void 0 ? void 0 : options.value);
|
|
@@ -693,7 +704,7 @@ function useControl(options) {
|
|
|
693
704
|
function useFormData(formRef, select, options) {
|
|
694
705
|
var {
|
|
695
706
|
observer
|
|
696
|
-
} = react.useContext(
|
|
707
|
+
} = react.useContext(GlobalFormOptionsContext);
|
|
697
708
|
var valueRef = react.useRef();
|
|
698
709
|
var formDataRef = react.useRef(null);
|
|
699
710
|
var value = react.useSyncExternalStore(react.useCallback(callback => {
|
|
@@ -746,9 +757,10 @@ function useLatest(value) {
|
|
|
746
757
|
return ref;
|
|
747
758
|
}
|
|
748
759
|
|
|
749
|
-
exports.
|
|
750
|
-
exports.
|
|
760
|
+
exports.FormContextContext = FormContextContext;
|
|
761
|
+
exports.FormOptionsProvider = FormOptionsProvider;
|
|
751
762
|
exports.FormProvider = FormProvider;
|
|
763
|
+
exports.GlobalFormOptionsContext = GlobalFormOptionsContext;
|
|
752
764
|
exports.INITIAL_KEY = INITIAL_KEY;
|
|
753
765
|
exports.useConform = useConform;
|
|
754
766
|
exports.useControl = useControl;
|
package/dist/future/hooks.mjs
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
'use client';
|
|
2
|
-
import { objectSpread2 as _objectSpread2 } from '../_virtual/_rollupPluginBabelHelpers.mjs';
|
|
2
|
+
import { objectWithoutProperties as _objectWithoutProperties, objectSpread2 as _objectSpread2 } from '../_virtual/_rollupPluginBabelHelpers.mjs';
|
|
3
3
|
import { DEFAULT_INTENT_NAME, createGlobalFormsObserver, serialize, isFieldElement, deepEqual, change, focus, blur, getFormData, parseSubmission, report, createSubmitEvent } from '@conform-to/dom/future';
|
|
4
4
|
import { createContext, useContext, useMemo, useId, useRef, useEffect, useSyncExternalStore, useCallback, useState, useLayoutEffect } from 'react';
|
|
5
5
|
import { resolveStandardSchemaResult, resolveValidateResult, appendUniqueItem } from './util.mjs';
|
|
@@ -8,30 +8,44 @@ import { deserializeIntent, actionHandlers, applyIntent } from './intent.mjs';
|
|
|
8
8
|
import { focusFirstInvalidField, getFormElement, createIntentDispatcher, createDefaultSnapshot, getRadioGroupValue, getCheckboxGroupValue, getInputSnapshot, makeInputFocusable, initializeField, updateFormValue, getSubmitEvent } from './dom.mjs';
|
|
9
9
|
import { jsx } from 'react/jsx-runtime';
|
|
10
10
|
|
|
11
|
+
var _excluded = ["children"];
|
|
11
12
|
var INITIAL_KEY = 'INITIAL_KEY';
|
|
12
|
-
var
|
|
13
|
+
var GlobalFormOptionsContext = /*#__PURE__*/createContext({
|
|
13
14
|
intentName: DEFAULT_INTENT_NAME,
|
|
14
15
|
observer: createGlobalFormsObserver(),
|
|
15
|
-
serialize
|
|
16
|
+
serialize,
|
|
17
|
+
shouldValidate: 'onSubmit'
|
|
16
18
|
});
|
|
17
|
-
var
|
|
19
|
+
var FormContextContext = /*#__PURE__*/createContext([]);
|
|
18
20
|
|
|
19
21
|
/**
|
|
20
22
|
* Provides form context to child components.
|
|
21
23
|
* Stacks contexts to support nested forms, with latest context taking priority.
|
|
22
24
|
*/
|
|
23
25
|
function FormProvider(props) {
|
|
24
|
-
var stack = useContext(
|
|
26
|
+
var stack = useContext(FormContextContext);
|
|
25
27
|
var value = useMemo(
|
|
26
28
|
// Put the latest form context first to ensure that to be the first one found
|
|
27
29
|
() => [props.context].concat(stack), [stack, props.context]);
|
|
28
|
-
return /*#__PURE__*/jsx(
|
|
30
|
+
return /*#__PURE__*/jsx(FormContextContext.Provider, {
|
|
29
31
|
value: value,
|
|
30
32
|
children: props.children
|
|
31
33
|
});
|
|
32
34
|
}
|
|
35
|
+
function FormOptionsProvider(props) {
|
|
36
|
+
var {
|
|
37
|
+
children
|
|
38
|
+
} = props,
|
|
39
|
+
providedOptions = _objectWithoutProperties(props, _excluded);
|
|
40
|
+
var defaultOptions = useContext(GlobalFormOptionsContext);
|
|
41
|
+
var options = useMemo(() => _objectSpread2(_objectSpread2({}, defaultOptions), providedOptions), [defaultOptions, providedOptions]);
|
|
42
|
+
return /*#__PURE__*/jsx(GlobalFormOptionsContext.Provider, {
|
|
43
|
+
value: options,
|
|
44
|
+
children: children
|
|
45
|
+
});
|
|
46
|
+
}
|
|
33
47
|
function useFormContext(formId) {
|
|
34
|
-
var contexts = useContext(
|
|
48
|
+
var contexts = useContext(FormContextContext);
|
|
35
49
|
var context = formId ? contexts.find(context => formId === context.formId) : contexts[0];
|
|
36
50
|
if (!context) {
|
|
37
51
|
throw new Error('No form context found; Have you render a <FormProvider /> with the corresponding form context?');
|
|
@@ -68,11 +82,11 @@ function useConform(formRef, options) {
|
|
|
68
82
|
var lastIntentedValueRef = useRef();
|
|
69
83
|
var lastAsyncResultRef = useRef(null);
|
|
70
84
|
var abortControllerRef = useRef(null);
|
|
71
|
-
var handleSubmission = useCallback((
|
|
85
|
+
var handleSubmission = useCallback((type, result) => {
|
|
72
86
|
var _optionsRef$current$o, _optionsRef$current;
|
|
73
87
|
var intent = result.submission.intent ? deserializeIntent(result.submission.intent) : null;
|
|
74
88
|
setState(state => updateState(state, _objectSpread2(_objectSpread2({}, result), {}, {
|
|
75
|
-
type
|
|
89
|
+
type,
|
|
76
90
|
intent,
|
|
77
91
|
ctx: {
|
|
78
92
|
handlers: actionHandlers,
|
|
@@ -101,9 +115,7 @@ function useConform(formRef, options) {
|
|
|
101
115
|
useEffect(() => {
|
|
102
116
|
// To avoid re-applying the same result twice
|
|
103
117
|
if (lastResult && lastResult !== lastResultRef.current) {
|
|
104
|
-
handleSubmission(
|
|
105
|
-
type: 'server'
|
|
106
|
-
});
|
|
118
|
+
handleSubmission('server', lastResult);
|
|
107
119
|
lastResultRef.current = lastResult;
|
|
108
120
|
}
|
|
109
121
|
}, [lastResult, handleSubmission]);
|
|
@@ -164,7 +176,7 @@ function useConform(formRef, options) {
|
|
|
164
176
|
// Patch missing fields in the submission object
|
|
165
177
|
for (var element of formElement.elements) {
|
|
166
178
|
if (isFieldElement(element) && element.name) {
|
|
167
|
-
appendUniqueItem(submission.fields, element.name);
|
|
179
|
+
submission.fields = appendUniqueItem(submission.fields, element.name);
|
|
168
180
|
}
|
|
169
181
|
}
|
|
170
182
|
|
|
@@ -215,9 +227,7 @@ function useConform(formRef, options) {
|
|
|
215
227
|
// There is no need to flush the update in this case
|
|
216
228
|
if (!abortController.signal.aborted) {
|
|
217
229
|
submissionResult.error = error;
|
|
218
|
-
handleSubmission(
|
|
219
|
-
type: 'server'
|
|
220
|
-
});
|
|
230
|
+
handleSubmission('server', submissionResult);
|
|
221
231
|
|
|
222
232
|
// If the form is meant to be submitted and there is no error
|
|
223
233
|
if (error === null && !submission.intent) {
|
|
@@ -235,9 +245,7 @@ function useConform(formRef, options) {
|
|
|
235
245
|
}
|
|
236
246
|
});
|
|
237
247
|
}
|
|
238
|
-
handleSubmission(
|
|
239
|
-
type: 'client'
|
|
240
|
-
});
|
|
248
|
+
handleSubmission('client', submissionResult);
|
|
241
249
|
if (
|
|
242
250
|
// If client validation happens
|
|
243
251
|
(typeof syncResult !== 'undefined' || typeof asyncResult !== 'undefined') && (
|
|
@@ -265,9 +273,7 @@ function useConform(formRef, options) {
|
|
|
265
273
|
var _submissionResult = report(result.submission, _objectSpread2(_objectSpread2({}, options), {}, {
|
|
266
274
|
keepFiles: true
|
|
267
275
|
}));
|
|
268
|
-
handleSubmission(
|
|
269
|
-
type: 'server'
|
|
270
|
-
});
|
|
276
|
+
handleSubmission('server', _submissionResult);
|
|
271
277
|
}
|
|
272
278
|
}
|
|
273
279
|
});
|
|
@@ -307,13 +313,14 @@ function useForm(options) {
|
|
|
307
313
|
defaultValue,
|
|
308
314
|
constraint
|
|
309
315
|
} = options;
|
|
310
|
-
var
|
|
316
|
+
var globalOptions = useContext(GlobalFormOptionsContext);
|
|
311
317
|
var optionsRef = useLatest(options);
|
|
318
|
+
var globalOptionsRef = useLatest(globalOptions);
|
|
312
319
|
var fallbackId = useId();
|
|
313
320
|
var formId = id !== null && id !== void 0 ? id : "form-".concat(fallbackId);
|
|
314
321
|
var [state, handleSubmit] = useConform(formId, _objectSpread2(_objectSpread2({}, options), {}, {
|
|
315
|
-
serialize:
|
|
316
|
-
intentName:
|
|
322
|
+
serialize: globalOptions.serialize,
|
|
323
|
+
intentName: globalOptions.intentName,
|
|
317
324
|
onError: (_optionsRef$current$o4 = optionsRef.current.onError) !== null && _optionsRef$current$o4 !== void 0 ? _optionsRef$current$o4 : focusFirstInvalidField,
|
|
318
325
|
onValidate(ctx) {
|
|
319
326
|
var _options$onValidate, _options$onValidate2;
|
|
@@ -367,7 +374,7 @@ function useForm(options) {
|
|
|
367
374
|
constraint: constraint !== null && constraint !== void 0 ? constraint : null,
|
|
368
375
|
handleSubmit: handleSubmit,
|
|
369
376
|
handleInput(event) {
|
|
370
|
-
var _optionsRef$current$o5, _optionsRef$current4;
|
|
377
|
+
var _optionsRef$current$o5, _optionsRef$current4, _globalOptionsRef$cur;
|
|
371
378
|
if (!isFieldElement(event.target) || event.target.name === '' || event.target.form === null || event.target.form !== getFormElement(formId)) {
|
|
372
379
|
return;
|
|
373
380
|
}
|
|
@@ -379,15 +386,15 @@ function useForm(options) {
|
|
|
379
386
|
return;
|
|
380
387
|
}
|
|
381
388
|
var {
|
|
382
|
-
shouldValidate =
|
|
383
|
-
shouldRevalidate = shouldValidate
|
|
389
|
+
shouldValidate = globalOptionsRef.current.shouldValidate,
|
|
390
|
+
shouldRevalidate = (_globalOptionsRef$cur = globalOptionsRef.current.shouldRevalidate) !== null && _globalOptionsRef$cur !== void 0 ? _globalOptionsRef$cur : shouldValidate
|
|
384
391
|
} = optionsRef.current;
|
|
385
392
|
if (isTouched(state, event.target.name) ? shouldRevalidate === 'onInput' : shouldValidate === 'onInput') {
|
|
386
393
|
intent.validate(event.target.name);
|
|
387
394
|
}
|
|
388
395
|
},
|
|
389
396
|
handleBlur(event) {
|
|
390
|
-
var _optionsRef$current$o6, _optionsRef$current5;
|
|
397
|
+
var _optionsRef$current$o6, _optionsRef$current5, _globalOptionsRef$cur2;
|
|
391
398
|
if (!isFieldElement(event.target) || event.target.name === '' || event.target.form === null || event.target.form !== getFormElement(formId)) {
|
|
392
399
|
return;
|
|
393
400
|
}
|
|
@@ -399,20 +406,22 @@ function useForm(options) {
|
|
|
399
406
|
return;
|
|
400
407
|
}
|
|
401
408
|
var {
|
|
402
|
-
shouldValidate =
|
|
403
|
-
shouldRevalidate = shouldValidate
|
|
409
|
+
shouldValidate = globalOptionsRef.current.shouldValidate,
|
|
410
|
+
shouldRevalidate = (_globalOptionsRef$cur2 = globalOptionsRef.current.shouldRevalidate) !== null && _globalOptionsRef$cur2 !== void 0 ? _globalOptionsRef$cur2 : shouldValidate
|
|
404
411
|
} = optionsRef.current;
|
|
405
412
|
if (isTouched(state, event.target.name) ? shouldRevalidate === 'onBlur' : shouldValidate === 'onBlur') {
|
|
406
413
|
intent.validate(event.target.name);
|
|
407
414
|
}
|
|
408
415
|
}
|
|
409
|
-
}), [formId, state, defaultValue, constraint, handleSubmit, intent, optionsRef]);
|
|
416
|
+
}), [formId, state, defaultValue, constraint, handleSubmit, intent, optionsRef, globalOptionsRef]);
|
|
410
417
|
var form = useMemo(() => getFormMetadata(context, {
|
|
411
|
-
serialize:
|
|
412
|
-
|
|
418
|
+
serialize: globalOptions.serialize,
|
|
419
|
+
customize: globalOptions.defineCustomMetadata
|
|
420
|
+
}), [context, globalOptions.serialize, globalOptions.defineCustomMetadata]);
|
|
413
421
|
var fields = useMemo(() => getFieldset(context, {
|
|
414
|
-
serialize:
|
|
415
|
-
|
|
422
|
+
serialize: globalOptions.serialize,
|
|
423
|
+
customize: globalOptions.defineCustomMetadata
|
|
424
|
+
}), [context, globalOptions.serialize, globalOptions.defineCustomMetadata]);
|
|
416
425
|
return {
|
|
417
426
|
form,
|
|
418
427
|
fields,
|
|
@@ -440,11 +449,12 @@ function useForm(options) {
|
|
|
440
449
|
*/
|
|
441
450
|
function useFormMetadata() {
|
|
442
451
|
var options = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};
|
|
443
|
-
var
|
|
452
|
+
var globalOptions = useContext(GlobalFormOptionsContext);
|
|
444
453
|
var context = useFormContext(options.formId);
|
|
445
454
|
var formMetadata = useMemo(() => getFormMetadata(context, {
|
|
446
|
-
serialize:
|
|
447
|
-
|
|
455
|
+
serialize: globalOptions.serialize,
|
|
456
|
+
customize: globalOptions.defineCustomMetadata
|
|
457
|
+
}), [context, globalOptions.serialize, globalOptions.defineCustomMetadata]);
|
|
448
458
|
return formMetadata;
|
|
449
459
|
}
|
|
450
460
|
|
|
@@ -470,12 +480,13 @@ function useFormMetadata() {
|
|
|
470
480
|
*/
|
|
471
481
|
function useField(name) {
|
|
472
482
|
var options = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};
|
|
473
|
-
var
|
|
483
|
+
var globalOptions = useContext(GlobalFormOptionsContext);
|
|
474
484
|
var context = useFormContext(options.formId);
|
|
475
485
|
var field = useMemo(() => getField(context, {
|
|
476
486
|
name,
|
|
477
|
-
serialize:
|
|
478
|
-
|
|
487
|
+
serialize: globalOptions.serialize,
|
|
488
|
+
customize: globalOptions.defineCustomMetadata
|
|
489
|
+
}), [context, name, globalOptions.serialize, globalOptions.defineCustomMetadata]);
|
|
479
490
|
return field;
|
|
480
491
|
}
|
|
481
492
|
|
|
@@ -500,8 +511,8 @@ function useField(name) {
|
|
|
500
511
|
* ```
|
|
501
512
|
*/
|
|
502
513
|
function useIntent(formRef) {
|
|
503
|
-
var
|
|
504
|
-
return useMemo(() => createIntentDispatcher(() => getFormElement(formRef),
|
|
514
|
+
var globalOptions = useContext(GlobalFormOptionsContext);
|
|
515
|
+
return useMemo(() => createIntentDispatcher(() => getFormElement(formRef), globalOptions.intentName), [formRef, globalOptions.intentName]);
|
|
505
516
|
}
|
|
506
517
|
|
|
507
518
|
/**
|
|
@@ -517,7 +528,7 @@ function useIntent(formRef) {
|
|
|
517
528
|
function useControl(options) {
|
|
518
529
|
var {
|
|
519
530
|
observer
|
|
520
|
-
} = useContext(
|
|
531
|
+
} = useContext(GlobalFormOptionsContext);
|
|
521
532
|
var inputRef = useRef(null);
|
|
522
533
|
var eventDispatched = useRef({});
|
|
523
534
|
var defaultSnapshot = createDefaultSnapshot(options === null || options === void 0 ? void 0 : options.defaultValue, options === null || options === void 0 ? void 0 : options.defaultChecked, options === null || options === void 0 ? void 0 : options.value);
|
|
@@ -689,7 +700,7 @@ function useControl(options) {
|
|
|
689
700
|
function useFormData(formRef, select, options) {
|
|
690
701
|
var {
|
|
691
702
|
observer
|
|
692
|
-
} = useContext(
|
|
703
|
+
} = useContext(GlobalFormOptionsContext);
|
|
693
704
|
var valueRef = useRef();
|
|
694
705
|
var formDataRef = useRef(null);
|
|
695
706
|
var value = useSyncExternalStore(useCallback(callback => {
|
|
@@ -742,4 +753,4 @@ function useLatest(value) {
|
|
|
742
753
|
return ref;
|
|
743
754
|
}
|
|
744
755
|
|
|
745
|
-
export {
|
|
756
|
+
export { FormContextContext, FormOptionsProvider, FormProvider, GlobalFormOptionsContext, INITIAL_KEY, useConform, useControl, useField, useForm, useFormContext, useFormData, useFormMetadata, useIntent, useLatest, useSafeLayoutEffect };
|
package/dist/future/index.d.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
export type { FormError, FormValue, Submission, SubmissionResult, } from '@conform-to/dom/future';
|
|
2
2
|
export { parseSubmission, report, isDirty } from '@conform-to/dom/future';
|
|
3
|
-
export type { Control, DefaultValue, FormContext, FormMetadata, FormOptions, FormRef, FieldMetadata, FieldName, Fieldset, IntentDispatcher, } from './types';
|
|
4
|
-
export { FormProvider, useControl, useField, useForm, useFormData, useFormMetadata, useIntent, } from './hooks';
|
|
3
|
+
export type { Control, DefaultValue, BaseMetadata, CustomMetadata, CustomMetadataDefinition, BaseErrorShape, CustomTypes, FormContext, FormMetadata, FormOptions, FormRef, FieldMetadata, FieldName, Fieldset, IntentDispatcher, } from './types';
|
|
4
|
+
export { FormProvider, FormOptionsProvider, useControl, useField, useForm, useFormData, useFormMetadata, useIntent, } from './hooks';
|
|
5
5
|
export { memoize } from './memoize';
|
|
6
6
|
//# sourceMappingURL=index.d.ts.map
|
package/dist/future/index.js
CHANGED
|
@@ -20,6 +20,7 @@ Object.defineProperty(exports, 'report', {
|
|
|
20
20
|
enumerable: true,
|
|
21
21
|
get: function () { return future.report; }
|
|
22
22
|
});
|
|
23
|
+
exports.FormOptionsProvider = hooks.FormOptionsProvider;
|
|
23
24
|
exports.FormProvider = hooks.FormProvider;
|
|
24
25
|
exports.useControl = hooks.useControl;
|
|
25
26
|
exports.useField = hooks.useField;
|
package/dist/future/index.mjs
CHANGED
|
@@ -1,3 +1,3 @@
|
|
|
1
1
|
export { isDirty, parseSubmission, report } from '@conform-to/dom/future';
|
|
2
|
-
export { FormProvider, useControl, useField, useForm, useFormData, useFormMetadata, useIntent } from './hooks.mjs';
|
|
2
|
+
export { FormOptionsProvider, FormProvider, useControl, useField, useForm, useFormData, useFormMetadata, useIntent } from './hooks.mjs';
|
|
3
3
|
export { memoize } from './memoize.mjs';
|