@conform-to/react 1.13.3 → 1.14.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.d.ts +5 -4
- package/dist/future/dom.js +22 -8
- package/dist/future/dom.mjs +23 -10
- package/dist/future/hooks.d.ts +56 -11
- package/dist/future/hooks.js +120 -68
- package/dist/future/hooks.mjs +121 -69
- package/dist/future/intent.d.ts +1 -1
- package/dist/future/intent.js +10 -14
- package/dist/future/intent.mjs +12 -16
- package/dist/future/state.d.ts +23 -15
- package/dist/future/state.js +53 -20
- package/dist/future/state.mjs +53 -21
- package/dist/future/types.d.ts +63 -46
- package/package.json +2 -2
package/dist/future/hooks.mjs
CHANGED
|
@@ -5,10 +5,12 @@ import { createContext, useContext, useMemo, useId, useRef, useEffect, useSyncEx
|
|
|
5
5
|
import { resolveStandardSchemaResult, resolveValidateResult, appendUniqueItem } from './util.mjs';
|
|
6
6
|
import { isTouched, getFormMetadata, getFieldset, getField, initializeState, updateState } from './state.mjs';
|
|
7
7
|
import { deserializeIntent, actionHandlers, applyIntent } from './intent.mjs';
|
|
8
|
-
import { focusFirstInvalidField, getFormElement, createIntentDispatcher, createDefaultSnapshot, getRadioGroupValue, getCheckboxGroupValue, getInputSnapshot, makeInputFocusable, initializeField, updateFormValue, getSubmitEvent } from './dom.mjs';
|
|
8
|
+
import { focusFirstInvalidField, getFormElement, createIntentDispatcher, createDefaultSnapshot, getRadioGroupValue, getCheckboxGroupValue, getInputSnapshot, makeInputFocusable, initializeField, resetFormValue, updateFormValue, getSubmitEvent } from './dom.mjs';
|
|
9
9
|
import { jsx } from 'react/jsx-runtime';
|
|
10
10
|
|
|
11
11
|
var _excluded = ["children"];
|
|
12
|
+
// Static reset key for consistent hydration during Next.js prerendering
|
|
13
|
+
// See: https://nextjs.org/docs/messages/next-prerender-current-time-client
|
|
12
14
|
var INITIAL_KEY = 'INITIAL_KEY';
|
|
13
15
|
var GlobalFormOptionsContext = /*#__PURE__*/createContext({
|
|
14
16
|
intentName: DEFAULT_INTENT_NAME,
|
|
@@ -62,14 +64,20 @@ function useConform(formRef, options) {
|
|
|
62
64
|
lastResult
|
|
63
65
|
} = options;
|
|
64
66
|
var [state, setState] = useState(() => {
|
|
65
|
-
var state = initializeState(
|
|
67
|
+
var state = initializeState({
|
|
68
|
+
defaultValue: options.defaultValue,
|
|
69
|
+
resetKey: INITIAL_KEY
|
|
70
|
+
});
|
|
66
71
|
if (lastResult) {
|
|
67
72
|
state = updateState(state, _objectSpread2(_objectSpread2({}, lastResult), {}, {
|
|
68
73
|
type: 'initialize',
|
|
69
74
|
intent: lastResult.submission.intent ? deserializeIntent(lastResult.submission.intent) : null,
|
|
70
75
|
ctx: {
|
|
71
76
|
handlers: actionHandlers,
|
|
72
|
-
reset:
|
|
77
|
+
reset: defaultValue => initializeState({
|
|
78
|
+
defaultValue: defaultValue !== null && defaultValue !== void 0 ? defaultValue : options.defaultValue,
|
|
79
|
+
resetKey: INITIAL_KEY
|
|
80
|
+
})
|
|
73
81
|
}
|
|
74
82
|
}));
|
|
75
83
|
}
|
|
@@ -79,22 +87,27 @@ function useConform(formRef, options) {
|
|
|
79
87
|
var resetKeyRef = useRef(state.resetKey);
|
|
80
88
|
var optionsRef = useLatest(options);
|
|
81
89
|
var lastResultRef = useRef(lastResult);
|
|
82
|
-
var
|
|
90
|
+
var pendingValueRef = useRef();
|
|
83
91
|
var lastAsyncResultRef = useRef(null);
|
|
84
92
|
var abortControllerRef = useRef(null);
|
|
85
|
-
var handleSubmission = useCallback((type, result)
|
|
93
|
+
var handleSubmission = useCallback(function (type, result) {
|
|
86
94
|
var _optionsRef$current$o, _optionsRef$current;
|
|
95
|
+
var options = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : optionsRef.current;
|
|
87
96
|
var intent = result.submission.intent ? deserializeIntent(result.submission.intent) : null;
|
|
88
97
|
setState(state => updateState(state, _objectSpread2(_objectSpread2({}, result), {}, {
|
|
89
98
|
type,
|
|
90
99
|
intent,
|
|
91
100
|
ctx: {
|
|
92
101
|
handlers: actionHandlers,
|
|
93
|
-
reset() {
|
|
94
|
-
return initializeState(
|
|
102
|
+
reset(defaultValue) {
|
|
103
|
+
return initializeState({
|
|
104
|
+
defaultValue: defaultValue !== null && defaultValue !== void 0 ? defaultValue : options.defaultValue
|
|
105
|
+
});
|
|
95
106
|
}
|
|
96
107
|
}
|
|
97
108
|
})));
|
|
109
|
+
|
|
110
|
+
// TODO: move on error handler to a new effect
|
|
98
111
|
var formElement = getFormElement(formRef);
|
|
99
112
|
if (!formElement || !result.error) {
|
|
100
113
|
return;
|
|
@@ -105,6 +118,15 @@ function useConform(formRef, options) {
|
|
|
105
118
|
intent
|
|
106
119
|
});
|
|
107
120
|
}, [formRef, optionsRef]);
|
|
121
|
+
if (options.key !== keyRef.current) {
|
|
122
|
+
keyRef.current = options.key;
|
|
123
|
+
setState(initializeState({
|
|
124
|
+
defaultValue: options.defaultValue
|
|
125
|
+
}));
|
|
126
|
+
} else if (lastResult && lastResult !== lastResultRef.current) {
|
|
127
|
+
lastResultRef.current = lastResult;
|
|
128
|
+
handleSubmission('server', lastResult, options);
|
|
129
|
+
}
|
|
108
130
|
useEffect(() => {
|
|
109
131
|
return () => {
|
|
110
132
|
var _abortControllerRef$c;
|
|
@@ -112,42 +134,28 @@ function useConform(formRef, options) {
|
|
|
112
134
|
(_abortControllerRef$c = abortControllerRef.current) === null || _abortControllerRef$c === void 0 || _abortControllerRef$c.abort('The component is unmounted');
|
|
113
135
|
};
|
|
114
136
|
}, []);
|
|
115
|
-
|
|
116
|
-
// To avoid re-applying the same result twice
|
|
117
|
-
if (lastResult && lastResult !== lastResultRef.current) {
|
|
118
|
-
handleSubmission('server', lastResult);
|
|
119
|
-
lastResultRef.current = lastResult;
|
|
120
|
-
}
|
|
121
|
-
}, [lastResult, handleSubmission]);
|
|
122
|
-
useEffect(() => {
|
|
123
|
-
// Reset the form state if the form key changes
|
|
124
|
-
if (options.key !== keyRef.current) {
|
|
125
|
-
keyRef.current = options.key;
|
|
126
|
-
setState(initializeState());
|
|
127
|
-
}
|
|
128
|
-
}, [options.key]);
|
|
129
|
-
useEffect(() => {
|
|
137
|
+
useSafeLayoutEffect(() => {
|
|
130
138
|
var formElement = getFormElement(formRef);
|
|
131
139
|
|
|
132
140
|
// Reset the form values if the reset key changes
|
|
133
141
|
if (formElement && state.resetKey !== resetKeyRef.current) {
|
|
134
142
|
resetKeyRef.current = state.resetKey;
|
|
135
|
-
formElement.
|
|
136
|
-
|
|
137
|
-
}, [formRef, state.resetKey]);
|
|
138
|
-
useEffect(() => {
|
|
139
|
-
if (!state.clientIntendedValue) {
|
|
140
|
-
return;
|
|
143
|
+
resetFormValue(formElement, state.defaultValue, optionsRef.current.serialize);
|
|
144
|
+
pendingValueRef.current = undefined;
|
|
141
145
|
}
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
146
|
+
}, [formRef, state.resetKey, state.defaultValue, optionsRef]);
|
|
147
|
+
useSafeLayoutEffect(() => {
|
|
148
|
+
if (state.targetValue) {
|
|
149
|
+
var formElement = getFormElement(formRef);
|
|
150
|
+
if (!formElement) {
|
|
151
|
+
// eslint-disable-next-line no-console
|
|
152
|
+
console.error('Failed to update form value; No form element found');
|
|
153
|
+
return;
|
|
154
|
+
}
|
|
155
|
+
updateFormValue(formElement, state.targetValue, optionsRef.current.serialize);
|
|
147
156
|
}
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
}, [formRef, state.clientIntendedValue, optionsRef]);
|
|
157
|
+
pendingValueRef.current = undefined;
|
|
158
|
+
}, [formRef, state.targetValue, optionsRef]);
|
|
151
159
|
var handleSubmit = useCallback(event => {
|
|
152
160
|
var _abortControllerRef$c2, _lastAsyncResultRef$c;
|
|
153
161
|
var abortController = new AbortController();
|
|
@@ -180,22 +188,25 @@ function useConform(formRef, options) {
|
|
|
180
188
|
}
|
|
181
189
|
}
|
|
182
190
|
|
|
183
|
-
// Override submission value if the
|
|
184
|
-
if (
|
|
185
|
-
submission.payload =
|
|
191
|
+
// Override submission value if the pending value is not applied yet (i.e. batch updates)
|
|
192
|
+
if (pendingValueRef.current !== undefined) {
|
|
193
|
+
submission.payload = pendingValueRef.current;
|
|
186
194
|
}
|
|
187
|
-
var
|
|
188
|
-
|
|
189
|
-
// Update the last intended value in case there will be another intent dispatched
|
|
190
|
-
lastIntentedValueRef.current = intendedValue === submission.payload ? undefined : intendedValue;
|
|
195
|
+
var value = applyIntent(submission);
|
|
191
196
|
var submissionResult = report(submission, {
|
|
192
197
|
keepFiles: true,
|
|
193
|
-
|
|
198
|
+
value
|
|
194
199
|
});
|
|
200
|
+
|
|
201
|
+
// If there is target value, keep track of it as pending value
|
|
202
|
+
if (submission.payload !== value) {
|
|
203
|
+
var _ref;
|
|
204
|
+
pendingValueRef.current = (_ref = value !== null && value !== void 0 ? value : optionsRef.current.defaultValue) !== null && _ref !== void 0 ? _ref : {};
|
|
205
|
+
}
|
|
195
206
|
var validateResult =
|
|
196
207
|
// Skip validation on form reset
|
|
197
|
-
|
|
198
|
-
payload:
|
|
208
|
+
value !== undefined ? (_optionsRef$current$o2 = (_optionsRef$current2 = optionsRef.current).onValidate) === null || _optionsRef$current$o2 === void 0 ? void 0 : _optionsRef$current$o2.call(_optionsRef$current2, {
|
|
209
|
+
payload: value,
|
|
199
210
|
error: {
|
|
200
211
|
formErrors: [],
|
|
201
212
|
fieldErrors: {}
|
|
@@ -218,11 +229,11 @@ function useConform(formRef, options) {
|
|
|
218
229
|
}
|
|
219
230
|
if (typeof asyncResult !== 'undefined') {
|
|
220
231
|
// Update the form when the validation result is resolved
|
|
221
|
-
asyncResult.then(
|
|
232
|
+
asyncResult.then(_ref2 => {
|
|
222
233
|
var {
|
|
223
234
|
error,
|
|
224
235
|
value
|
|
225
|
-
} =
|
|
236
|
+
} = _ref2;
|
|
226
237
|
// Update the form with the validation result
|
|
227
238
|
// There is no need to flush the update in this case
|
|
228
239
|
if (!abortController.signal.aborted) {
|
|
@@ -286,11 +297,32 @@ function useConform(formRef, options) {
|
|
|
286
297
|
* The main React hook for form management. Handles form state, validation, and submission
|
|
287
298
|
* while providing access to form metadata, field objects, and form actions.
|
|
288
299
|
*
|
|
300
|
+
* It can be called in two ways:
|
|
301
|
+
* - **Schema first**: Pass a schema as the first argument for automatic validation with type inference
|
|
302
|
+
* - **Manual configuration**: Pass options with custom `onValidate` handler for manual validation
|
|
303
|
+
*
|
|
289
304
|
* @see https://conform.guide/api/react/future/useForm
|
|
290
|
-
* @example
|
|
305
|
+
* @example Schema first setup with zod:
|
|
306
|
+
*
|
|
307
|
+
* ```tsx
|
|
308
|
+
* const { form, fields } = useForm(zodSchema, {
|
|
309
|
+
* lastResult,
|
|
310
|
+
* shouldValidate: 'onBlur',
|
|
311
|
+
* });
|
|
312
|
+
*
|
|
313
|
+
* return (
|
|
314
|
+
* <form {...form.props}>
|
|
315
|
+
* <input name={fields.email.name} defaultValue={fields.email.defaultValue} />
|
|
316
|
+
* <div>{fields.email.errors}</div>
|
|
317
|
+
* </form>
|
|
318
|
+
* );
|
|
319
|
+
* ```
|
|
320
|
+
*
|
|
321
|
+
* @example Manual configuration setup with custom validation:
|
|
322
|
+
*
|
|
291
323
|
* ```tsx
|
|
292
324
|
* const { form, fields } = useForm({
|
|
293
|
-
*
|
|
325
|
+
* onValidate({ payload, error }) {
|
|
294
326
|
* if (!payload.email) {
|
|
295
327
|
* error.fieldErrors.email = ['Required'];
|
|
296
328
|
* }
|
|
@@ -306,11 +338,25 @@ function useConform(formRef, options) {
|
|
|
306
338
|
* );
|
|
307
339
|
* ```
|
|
308
340
|
*/
|
|
309
|
-
|
|
310
|
-
|
|
341
|
+
|
|
342
|
+
/**
|
|
343
|
+
* @deprecated Use `useForm(schema, options)` instead for better type inference.
|
|
344
|
+
*/
|
|
345
|
+
|
|
346
|
+
function useForm(schemaOrOptions, maybeOptions) {
|
|
347
|
+
var _options$onError;
|
|
348
|
+
var schema;
|
|
349
|
+
var options;
|
|
350
|
+
if (maybeOptions) {
|
|
351
|
+
schema = schemaOrOptions;
|
|
352
|
+
options = maybeOptions;
|
|
353
|
+
} else {
|
|
354
|
+
var fullOptions = schemaOrOptions;
|
|
355
|
+
options = fullOptions;
|
|
356
|
+
schema = fullOptions.schema;
|
|
357
|
+
}
|
|
311
358
|
var {
|
|
312
359
|
id,
|
|
313
|
-
defaultValue,
|
|
314
360
|
constraint
|
|
315
361
|
} = options;
|
|
316
362
|
var globalOptions = useContext(GlobalFormOptionsContext);
|
|
@@ -321,11 +367,11 @@ function useForm(options) {
|
|
|
321
367
|
var [state, handleSubmit] = useConform(formId, _objectSpread2(_objectSpread2({}, options), {}, {
|
|
322
368
|
serialize: globalOptions.serialize,
|
|
323
369
|
intentName: globalOptions.intentName,
|
|
324
|
-
onError: (
|
|
370
|
+
onError: (_options$onError = options.onError) !== null && _options$onError !== void 0 ? _options$onError : focusFirstInvalidField,
|
|
325
371
|
onValidate(ctx) {
|
|
326
|
-
var _options$onValidate, _options$onValidate2;
|
|
327
|
-
if (
|
|
328
|
-
var standardResult =
|
|
372
|
+
var _options$onValidate, _options$onValidate2, _options;
|
|
373
|
+
if (schema) {
|
|
374
|
+
var standardResult = schema['~standard'].validate(ctx.payload);
|
|
329
375
|
if (standardResult instanceof Promise) {
|
|
330
376
|
return standardResult.then(actualStandardResult => {
|
|
331
377
|
if (typeof options.onValidate === 'function') {
|
|
@@ -358,7 +404,7 @@ function useForm(options) {
|
|
|
358
404
|
}
|
|
359
405
|
return [validateResult.syncResult, validateResult.asyncResult];
|
|
360
406
|
}
|
|
361
|
-
return (_options$onValidate = (_options$onValidate2 = options.onValidate) === null || _options$onValidate2 === void 0 ? void 0 : _options$onValidate2.call(
|
|
407
|
+
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 : {
|
|
362
408
|
// To avoid conform falling back to server validation,
|
|
363
409
|
// if neither schema nor validation handler is provided,
|
|
364
410
|
// we just treat it as a valid client submission
|
|
@@ -370,15 +416,14 @@ function useForm(options) {
|
|
|
370
416
|
var context = useMemo(() => ({
|
|
371
417
|
formId,
|
|
372
418
|
state,
|
|
373
|
-
defaultValue: defaultValue !== null && defaultValue !== void 0 ? defaultValue : null,
|
|
374
419
|
constraint: constraint !== null && constraint !== void 0 ? constraint : null,
|
|
375
|
-
handleSubmit
|
|
420
|
+
handleSubmit,
|
|
376
421
|
handleInput(event) {
|
|
377
|
-
var _optionsRef$current$
|
|
422
|
+
var _optionsRef$current$o4, _optionsRef$current4, _globalOptionsRef$cur;
|
|
378
423
|
if (!isFieldElement(event.target) || event.target.name === '' || event.target.form === null || event.target.form !== getFormElement(formId)) {
|
|
379
424
|
return;
|
|
380
425
|
}
|
|
381
|
-
(_optionsRef$current$
|
|
426
|
+
(_optionsRef$current$o4 = (_optionsRef$current4 = optionsRef.current).onInput) === null || _optionsRef$current$o4 === void 0 || _optionsRef$current$o4.call(_optionsRef$current4, _objectSpread2(_objectSpread2({}, event), {}, {
|
|
382
427
|
target: event.target,
|
|
383
428
|
currentTarget: event.target.form
|
|
384
429
|
}));
|
|
@@ -394,11 +439,11 @@ function useForm(options) {
|
|
|
394
439
|
}
|
|
395
440
|
},
|
|
396
441
|
handleBlur(event) {
|
|
397
|
-
var _optionsRef$current$
|
|
442
|
+
var _optionsRef$current$o5, _optionsRef$current5, _globalOptionsRef$cur2;
|
|
398
443
|
if (!isFieldElement(event.target) || event.target.name === '' || event.target.form === null || event.target.form !== getFormElement(formId)) {
|
|
399
444
|
return;
|
|
400
445
|
}
|
|
401
|
-
(_optionsRef$current$
|
|
446
|
+
(_optionsRef$current$o5 = (_optionsRef$current5 = optionsRef.current).onBlur) === null || _optionsRef$current$o5 === void 0 || _optionsRef$current$o5.call(_optionsRef$current5, _objectSpread2(_objectSpread2({}, event), {}, {
|
|
402
447
|
target: event.target,
|
|
403
448
|
currentTarget: event.target.form
|
|
404
449
|
}));
|
|
@@ -413,7 +458,7 @@ function useForm(options) {
|
|
|
413
458
|
intent.validate(event.target.name);
|
|
414
459
|
}
|
|
415
460
|
}
|
|
416
|
-
}), [formId, state,
|
|
461
|
+
}), [formId, state, constraint, handleSubmit, intent, optionsRef, globalOptionsRef]);
|
|
417
462
|
var form = useMemo(() => getFormMetadata(context, {
|
|
418
463
|
serialize: globalOptions.serialize,
|
|
419
464
|
customize: globalOptions.defineCustomMetadata
|
|
@@ -610,6 +655,13 @@ function useControl(options) {
|
|
|
610
655
|
inputRef.current = null;
|
|
611
656
|
} else if (isFieldElement(element)) {
|
|
612
657
|
inputRef.current = element;
|
|
658
|
+
|
|
659
|
+
// Conform excludes hidden type inputs by default when updating form values
|
|
660
|
+
// Fix that by using the hidden attribute instead
|
|
661
|
+
if (element.type === 'hidden') {
|
|
662
|
+
element.hidden = true;
|
|
663
|
+
element.removeAttribute('type');
|
|
664
|
+
}
|
|
613
665
|
if (shouldHandleFocus) {
|
|
614
666
|
makeInputFocusable(element);
|
|
615
667
|
}
|
|
@@ -718,16 +770,16 @@ function useFormData(formRef, select, options) {
|
|
|
718
770
|
var formElement = getFormElement(formRef);
|
|
719
771
|
if (formElement) {
|
|
720
772
|
var formData = getFormData(formElement);
|
|
721
|
-
formDataRef.current = options !== null && options !== void 0 && options.acceptFiles ? formData : new URLSearchParams(Array.from(formData).map(
|
|
722
|
-
var [key, value] =
|
|
773
|
+
formDataRef.current = options !== null && options !== void 0 && options.acceptFiles ? formData : new URLSearchParams(Array.from(formData).map(_ref3 => {
|
|
774
|
+
var [key, value] = _ref3;
|
|
723
775
|
return [key, value.toString()];
|
|
724
776
|
}));
|
|
725
777
|
}
|
|
726
778
|
var unsubscribe = observer.onFormUpdate(event => {
|
|
727
779
|
if (event.target === getFormElement(formRef)) {
|
|
728
780
|
var _formData = getFormData(event.target, event.submitter);
|
|
729
|
-
formDataRef.current = options !== null && options !== void 0 && options.acceptFiles ? _formData : new URLSearchParams(Array.from(_formData).map(
|
|
730
|
-
var [key, value] =
|
|
781
|
+
formDataRef.current = options !== null && options !== void 0 && options.acceptFiles ? _formData : new URLSearchParams(Array.from(_formData).map(_ref4 => {
|
|
782
|
+
var [key, value] = _ref4;
|
|
731
783
|
return [key, value.toString()];
|
|
732
784
|
}));
|
|
733
785
|
callback();
|
package/dist/future/intent.d.ts
CHANGED
|
@@ -14,7 +14,7 @@ export declare function deserializeIntent(value: string): UnknownIntent;
|
|
|
14
14
|
*/
|
|
15
15
|
export declare function applyIntent(submission: Submission, options?: {
|
|
16
16
|
handlers?: Record<string, ActionHandler>;
|
|
17
|
-
}): Record<string, FormValue> |
|
|
17
|
+
}): Record<string, FormValue> | undefined;
|
|
18
18
|
export declare function insertItem<Item>(list: Array<Item>, item: Item, index: number): void;
|
|
19
19
|
export declare function removeItem(list: Array<unknown>, index: number): void;
|
|
20
20
|
export declare function reorderItems(list: Array<unknown>, fromIndex: number, toIndex: number): void;
|
package/dist/future/intent.js
CHANGED
|
@@ -96,20 +96,17 @@ var actionHandlers = {
|
|
|
96
96
|
return util.isOptional(options, future.isPlainObject) && (util.isUndefined(options === null || options === void 0 ? void 0 : options.defaultValue) || util.isNullable(options === null || options === void 0 ? void 0 : options.defaultValue, future.isPlainObject));
|
|
97
97
|
},
|
|
98
98
|
onApply(_, options) {
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
}
|
|
102
|
-
return
|
|
99
|
+
if ((options === null || options === void 0 ? void 0 : options.defaultValue) === null) {
|
|
100
|
+
return {};
|
|
101
|
+
}
|
|
102
|
+
return options === null || options === void 0 ? void 0 : options.defaultValue;
|
|
103
103
|
},
|
|
104
104
|
onUpdate(_, _ref) {
|
|
105
|
-
var _intent$payload;
|
|
106
105
|
var {
|
|
107
|
-
|
|
106
|
+
targetValue,
|
|
107
|
+
ctx
|
|
108
108
|
} = _ref;
|
|
109
|
-
|
|
110
|
-
return util.merge(state.initializeState(), {
|
|
111
|
-
serverIntendedValue: defaultValue !== null && defaultValue !== void 0 ? defaultValue : defaultValue === null ? {} : null
|
|
112
|
-
});
|
|
109
|
+
return ctx.reset(targetValue);
|
|
113
110
|
}
|
|
114
111
|
},
|
|
115
112
|
validate: {
|
|
@@ -117,13 +114,13 @@ var actionHandlers = {
|
|
|
117
114
|
return util.isOptional(name, util.isString);
|
|
118
115
|
},
|
|
119
116
|
onUpdate(state, _ref2) {
|
|
120
|
-
var _intent$
|
|
117
|
+
var _intent$payload;
|
|
121
118
|
var {
|
|
122
119
|
submission,
|
|
123
120
|
intent,
|
|
124
121
|
error
|
|
125
122
|
} = _ref2;
|
|
126
|
-
var name = (_intent$
|
|
123
|
+
var name = (_intent$payload = intent.payload) !== null && _intent$payload !== void 0 ? _intent$payload : '';
|
|
127
124
|
var basePath = future.getPathSegments(name);
|
|
128
125
|
var allFields = error ?
|
|
129
126
|
// Consider fields / fieldset with errors as touched too
|
|
@@ -147,8 +144,7 @@ var actionHandlers = {
|
|
|
147
144
|
onApply(value, options) {
|
|
148
145
|
var _options$value;
|
|
149
146
|
var name = future.appendPathSegment(options.name, options.index);
|
|
150
|
-
|
|
151
|
-
return util.updateValueAtPath(value, name, newValue);
|
|
147
|
+
return util.updateValueAtPath(value, name, (_options$value = options.value) !== null && _options$value !== void 0 ? _options$value : name === '' ? {} : null);
|
|
152
148
|
},
|
|
153
149
|
onUpdate(state, _ref3) {
|
|
154
150
|
var {
|
package/dist/future/intent.mjs
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { objectSpread2 as _objectSpread2 } from '../_virtual/_rollupPluginBabelHelpers.mjs';
|
|
2
2
|
import { getPathSegments, getRelativePath, isPlainObject, appendPathSegment } from '@conform-to/dom/future';
|
|
3
|
-
import { isOptional, isUndefined, isNullable,
|
|
4
|
-
import {
|
|
3
|
+
import { isOptional, isUndefined, isNullable, appendUniqueItem, merge, updateValueAtPath, isString, getArrayAtPath, createPathIndexUpdater, compactMap, generateUniqueKey, isNumber, transformKeys } from './util.mjs';
|
|
4
|
+
import { getDefaultListKey } from './state.mjs';
|
|
5
5
|
|
|
6
6
|
/**
|
|
7
7
|
* Serializes intent to string format: "type" or "type(payload)".
|
|
@@ -92,20 +92,17 @@ var actionHandlers = {
|
|
|
92
92
|
return isOptional(options, isPlainObject) && (isUndefined(options === null || options === void 0 ? void 0 : options.defaultValue) || isNullable(options === null || options === void 0 ? void 0 : options.defaultValue, isPlainObject));
|
|
93
93
|
},
|
|
94
94
|
onApply(_, options) {
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
}
|
|
98
|
-
return
|
|
95
|
+
if ((options === null || options === void 0 ? void 0 : options.defaultValue) === null) {
|
|
96
|
+
return {};
|
|
97
|
+
}
|
|
98
|
+
return options === null || options === void 0 ? void 0 : options.defaultValue;
|
|
99
99
|
},
|
|
100
100
|
onUpdate(_, _ref) {
|
|
101
|
-
var _intent$payload;
|
|
102
101
|
var {
|
|
103
|
-
|
|
102
|
+
targetValue,
|
|
103
|
+
ctx
|
|
104
104
|
} = _ref;
|
|
105
|
-
|
|
106
|
-
return merge(initializeState(), {
|
|
107
|
-
serverIntendedValue: defaultValue !== null && defaultValue !== void 0 ? defaultValue : defaultValue === null ? {} : null
|
|
108
|
-
});
|
|
105
|
+
return ctx.reset(targetValue);
|
|
109
106
|
}
|
|
110
107
|
},
|
|
111
108
|
validate: {
|
|
@@ -113,13 +110,13 @@ var actionHandlers = {
|
|
|
113
110
|
return isOptional(name, isString);
|
|
114
111
|
},
|
|
115
112
|
onUpdate(state, _ref2) {
|
|
116
|
-
var _intent$
|
|
113
|
+
var _intent$payload;
|
|
117
114
|
var {
|
|
118
115
|
submission,
|
|
119
116
|
intent,
|
|
120
117
|
error
|
|
121
118
|
} = _ref2;
|
|
122
|
-
var name = (_intent$
|
|
119
|
+
var name = (_intent$payload = intent.payload) !== null && _intent$payload !== void 0 ? _intent$payload : '';
|
|
123
120
|
var basePath = getPathSegments(name);
|
|
124
121
|
var allFields = error ?
|
|
125
122
|
// Consider fields / fieldset with errors as touched too
|
|
@@ -143,8 +140,7 @@ var actionHandlers = {
|
|
|
143
140
|
onApply(value, options) {
|
|
144
141
|
var _options$value;
|
|
145
142
|
var name = appendPathSegment(options.name, options.index);
|
|
146
|
-
|
|
147
|
-
return updateValueAtPath(value, name, newValue);
|
|
143
|
+
return updateValueAtPath(value, name, (_options$value = options.value) !== null && _options$value !== void 0 ? _options$value : name === '' ? {} : null);
|
|
148
144
|
},
|
|
149
145
|
onUpdate(state, _ref3) {
|
|
150
146
|
var {
|
package/dist/future/state.d.ts
CHANGED
|
@@ -1,16 +1,24 @@
|
|
|
1
1
|
import { type ValidationAttributes, type Serialize } from '@conform-to/dom/future';
|
|
2
2
|
import type { FieldMetadata, FieldName, Fieldset, FormContext, FormMetadata, FormState, FormAction, UnknownIntent, ActionHandler, CustomMetadataDefinition } from './types';
|
|
3
|
-
export declare function initializeState<ErrorShape>(
|
|
3
|
+
export declare function initializeState<ErrorShape>(options?: {
|
|
4
|
+
defaultValue?: Record<string, unknown> | null | undefined;
|
|
5
|
+
resetKey?: string | undefined;
|
|
6
|
+
}): FormState<ErrorShape>;
|
|
4
7
|
/**
|
|
5
8
|
* Updates form state based on action type:
|
|
6
|
-
* - Client actions: update
|
|
7
|
-
* - Server actions: update server errors and clear client errors
|
|
8
|
-
* - Initialize: set initial
|
|
9
|
+
* - Client actions: update target value and client errors
|
|
10
|
+
* - Server actions: update server errors and clear client errors, with optional target value
|
|
11
|
+
* - Initialize: set initial server value
|
|
9
12
|
*/
|
|
10
13
|
export declare function updateState<ErrorShape>(state: FormState<ErrorShape>, action: FormAction<ErrorShape, UnknownIntent | null, {
|
|
11
14
|
handlers: Record<string, ActionHandler>;
|
|
12
|
-
reset: () => FormState<ErrorShape>;
|
|
15
|
+
reset: (defaultValue?: Record<string, unknown> | null | undefined) => FormState<ErrorShape>;
|
|
13
16
|
}>): FormState<ErrorShape>;
|
|
17
|
+
/**
|
|
18
|
+
* Removes list keys where array length has changed to force regeneration.
|
|
19
|
+
* Minimizes UI state loss by only invalidating keys when necessary.
|
|
20
|
+
*/
|
|
21
|
+
export declare function pruneListKeys(listKeys: Record<string, string[]>, targetValue: Record<string, unknown>): Record<string, string[]>;
|
|
14
22
|
export declare function getDefaultValue(context: FormContext<any>, name: string, serialize?: Serialize): string;
|
|
15
23
|
export declare function getDefaultOptions(context: FormContext<any>, name: string, serialize?: Serialize): string[];
|
|
16
24
|
export declare function isDefaultChecked(context: FormContext<any>, name: string, serialize?: Serialize): boolean;
|
|
@@ -32,30 +40,30 @@ export declare function isValid(state: FormState<any>, name?: string): boolean;
|
|
|
32
40
|
*/
|
|
33
41
|
export declare function getConstraint(context: FormContext<any>, name: string): ValidationAttributes | undefined;
|
|
34
42
|
export declare function getFormMetadata<ErrorShape>(context: FormContext<ErrorShape>, options?: {
|
|
35
|
-
serialize?: Serialize;
|
|
36
|
-
customize?: CustomMetadataDefinition;
|
|
43
|
+
serialize?: Serialize | undefined;
|
|
44
|
+
customize?: CustomMetadataDefinition | undefined;
|
|
37
45
|
}): FormMetadata<ErrorShape>;
|
|
38
46
|
export declare function getField<FieldShape, ErrorShape = string>(context: FormContext<ErrorShape>, options: {
|
|
39
47
|
name: FieldName<FieldShape>;
|
|
40
|
-
serialize?: Serialize;
|
|
41
|
-
customize?: CustomMetadataDefinition;
|
|
42
|
-
key?: string;
|
|
48
|
+
serialize?: Serialize | undefined;
|
|
49
|
+
customize?: CustomMetadataDefinition | undefined;
|
|
50
|
+
key?: string | undefined;
|
|
43
51
|
}): FieldMetadata<FieldShape, ErrorShape>;
|
|
44
52
|
/**
|
|
45
53
|
* Creates a proxy that dynamically generates field objects when properties are accessed.
|
|
46
54
|
*/
|
|
47
55
|
export declare function getFieldset<FieldShape = Record<string, any>, ErrorShape = string>(context: FormContext<ErrorShape>, options: {
|
|
48
|
-
name?: FieldName<FieldShape
|
|
49
|
-
serialize?: Serialize;
|
|
50
|
-
customize?: CustomMetadataDefinition;
|
|
56
|
+
name?: FieldName<FieldShape> | undefined;
|
|
57
|
+
serialize?: Serialize | undefined;
|
|
58
|
+
customize?: CustomMetadataDefinition | undefined;
|
|
51
59
|
}): Fieldset<FieldShape, ErrorShape>;
|
|
52
60
|
/**
|
|
53
61
|
* Creates an array of field objects for list/array inputs
|
|
54
62
|
*/
|
|
55
63
|
export declare function getFieldList<FieldShape = Array<any>, ErrorShape = string>(context: FormContext<ErrorShape>, options: {
|
|
56
64
|
name: FieldName<FieldShape>;
|
|
57
|
-
serialize?: Serialize;
|
|
58
|
-
customize?: CustomMetadataDefinition;
|
|
65
|
+
serialize?: Serialize | undefined;
|
|
66
|
+
customize?: CustomMetadataDefinition | undefined;
|
|
59
67
|
}): FieldMetadata<[
|
|
60
68
|
FieldShape
|
|
61
69
|
] extends [Array<infer ItemShape> | null | undefined] ? ItemShape : unknown, ErrorShape>[];
|