@conform-to/react 1.16.0 → 1.17.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +1 -1
- package/dist/context.js +1 -0
- package/dist/context.mjs +1 -0
- package/dist/future/dom.d.ts +10 -0
- package/dist/future/dom.js +126 -0
- package/dist/future/dom.mjs +125 -1
- package/dist/future/forms.js +1 -0
- package/dist/future/forms.mjs +1 -0
- package/dist/future/hooks.d.ts +19 -0
- package/dist/future/hooks.js +75 -26
- package/dist/future/hooks.mjs +79 -31
- package/dist/future/index.d.ts +1 -1
- package/dist/future/index.js +1 -0
- package/dist/future/index.mjs +1 -1
- package/dist/future/intent.d.ts +13 -6
- package/dist/future/intent.js +175 -68
- package/dist/future/intent.mjs +175 -69
- package/dist/future/state.d.ts +3 -2
- package/dist/future/state.js +7 -5
- package/dist/future/state.mjs +7 -5
- package/dist/future/types.d.ts +35 -8
- package/dist/helpers.d.ts +1 -0
- package/dist/helpers.js +2 -1
- package/dist/helpers.mjs +2 -1
- 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.17.0 / 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/context.js
CHANGED
|
@@ -189,6 +189,7 @@ function getFieldMetadata(context, subjectRef, stateSnapshot) {
|
|
|
189
189
|
case 'pattern':
|
|
190
190
|
case 'step':
|
|
191
191
|
case 'multiple':
|
|
192
|
+
case 'accept':
|
|
192
193
|
return (_state$constraint$nam = state.constraint[name]) === null || _state$constraint$nam === void 0 ? void 0 : _state$constraint$nam[key];
|
|
193
194
|
case 'getFieldList':
|
|
194
195
|
{
|
package/dist/context.mjs
CHANGED
|
@@ -185,6 +185,7 @@ function getFieldMetadata(context, subjectRef, stateSnapshot) {
|
|
|
185
185
|
case 'pattern':
|
|
186
186
|
case 'step':
|
|
187
187
|
case 'multiple':
|
|
188
|
+
case 'accept':
|
|
188
189
|
return (_state$constraint$nam = state.constraint[name]) === null || _state$constraint$nam === void 0 ? void 0 : _state$constraint$nam[key];
|
|
189
190
|
case 'getFieldList':
|
|
190
191
|
{
|
package/dist/future/dom.d.ts
CHANGED
|
@@ -34,4 +34,14 @@ export declare function resetFormValue(form: HTMLFormElement, defaultValue: Reco
|
|
|
34
34
|
* Each property access returns a function that submits the intent to the form.
|
|
35
35
|
*/
|
|
36
36
|
export declare function createIntentDispatcher<FormShape extends Record<string, any>>(formElement: HTMLFormElement | (() => HTMLFormElement | null), intentName: string): IntentDispatcher<FormShape>;
|
|
37
|
+
/**
|
|
38
|
+
* Restores values from preserved inputs and removes them.
|
|
39
|
+
* Called when PreserveBoundary mounts.
|
|
40
|
+
*/
|
|
41
|
+
export declare function cleanupPreservedInputs(boundary: HTMLElement, form: HTMLFormElement, name?: string): void;
|
|
42
|
+
/**
|
|
43
|
+
* Clones inputs as hidden elements to preserve their values.
|
|
44
|
+
* Called when PreserveBoundary unmounts.
|
|
45
|
+
*/
|
|
46
|
+
export declare function preserveInputs(inputs: Iterable<HTMLInputElement | HTMLSelectElement | HTMLTextAreaElement>, form: HTMLFormElement, name?: string): void;
|
|
37
47
|
//# sourceMappingURL=dom.d.ts.map
|
package/dist/future/dom.js
CHANGED
|
@@ -237,7 +237,132 @@ function createIntentDispatcher(formElement, intentName) {
|
|
|
237
237
|
}
|
|
238
238
|
});
|
|
239
239
|
}
|
|
240
|
+
var PERSIST_ATTR = 'data-conform-persist';
|
|
241
|
+
var containerCache = new WeakMap();
|
|
240
242
|
|
|
243
|
+
/**
|
|
244
|
+
* Gets or creates a hidden container for persisted inputs.
|
|
245
|
+
* Using a container div instead of appending directly to <form> provides ~10x
|
|
246
|
+
* better performance (form.elements bookkeeping is expensive at scale).
|
|
247
|
+
*/
|
|
248
|
+
function getPersistContainer(form) {
|
|
249
|
+
var container = containerCache.get(form);
|
|
250
|
+
|
|
251
|
+
// Verify container is still attached to the form
|
|
252
|
+
if (container && container.parentNode !== form) {
|
|
253
|
+
container = undefined;
|
|
254
|
+
}
|
|
255
|
+
if (!container) {
|
|
256
|
+
container = form.ownerDocument.createElement('div');
|
|
257
|
+
container.setAttribute(PERSIST_ATTR, '');
|
|
258
|
+
container.hidden = true;
|
|
259
|
+
form.appendChild(container);
|
|
260
|
+
containerCache.set(form, container);
|
|
261
|
+
}
|
|
262
|
+
return container;
|
|
263
|
+
}
|
|
264
|
+
|
|
265
|
+
/**
|
|
266
|
+
* Restores values from preserved inputs and removes them.
|
|
267
|
+
* Called when PreserveBoundary mounts.
|
|
268
|
+
*/
|
|
269
|
+
function cleanupPreservedInputs(boundary, form, name) {
|
|
270
|
+
var inputs = boundary.querySelectorAll('input,select,textarea');
|
|
271
|
+
var container = getPersistContainer(form);
|
|
272
|
+
for (var input of inputs) {
|
|
273
|
+
if (!future.isFieldElement(input) || !input.name) {
|
|
274
|
+
continue;
|
|
275
|
+
}
|
|
276
|
+
|
|
277
|
+
// For checkbox/radio, match by field name + value (+ boundary name if provided)
|
|
278
|
+
// For other inputs, match by field name only (+ boundary name if provided)
|
|
279
|
+
var isCheckboxOrRadio = input.type === 'checkbox' || input.type === 'radio';
|
|
280
|
+
|
|
281
|
+
// Query the persist container, not the whole form
|
|
282
|
+
var boundarySelector = name ? "[".concat(PERSIST_ATTR, "=\"").concat(name, "\"]") : '';
|
|
283
|
+
var selector = isCheckboxOrRadio ? "".concat(boundarySelector, "[name=\"").concat(input.name, "\"][value=\"").concat(input.value, "\"]") : "".concat(boundarySelector, "[name=\"").concat(input.name, "\"]");
|
|
284
|
+
var persisted = container.querySelector(selector);
|
|
285
|
+
if (persisted) {
|
|
286
|
+
if (input instanceof HTMLInputElement && persisted instanceof HTMLInputElement) {
|
|
287
|
+
if (isCheckboxOrRadio) {
|
|
288
|
+
input.checked = persisted.checked;
|
|
289
|
+
} else if (input.type === 'file') {
|
|
290
|
+
// Restore files from the persisted input (may be empty)
|
|
291
|
+
input.files = persisted.files;
|
|
292
|
+
} else {
|
|
293
|
+
input.value = persisted.value;
|
|
294
|
+
}
|
|
295
|
+
} else if (input instanceof HTMLSelectElement && persisted instanceof HTMLSelectElement) {
|
|
296
|
+
var _loop = function _loop(option) {
|
|
297
|
+
var _persistedOption$sele;
|
|
298
|
+
var persistedOption = Array.from(persisted.options).find(o => o.value === option.value);
|
|
299
|
+
option.selected = (_persistedOption$sele = persistedOption === null || persistedOption === void 0 ? void 0 : persistedOption.selected) !== null && _persistedOption$sele !== void 0 ? _persistedOption$sele : false;
|
|
300
|
+
};
|
|
301
|
+
for (var option of input.options) {
|
|
302
|
+
_loop(option);
|
|
303
|
+
}
|
|
304
|
+
} else if (input instanceof HTMLTextAreaElement && persisted instanceof HTMLTextAreaElement) {
|
|
305
|
+
input.value = persisted.value;
|
|
306
|
+
}
|
|
307
|
+
persisted.remove();
|
|
308
|
+
}
|
|
309
|
+
}
|
|
310
|
+
|
|
311
|
+
// If name is provided, remove any remaining persisted inputs with this name
|
|
312
|
+
// (handles the case where inputs were removed from the boundary)
|
|
313
|
+
if (name) {
|
|
314
|
+
var remainingPersisted = container.querySelectorAll("[".concat(PERSIST_ATTR, "=\"").concat(name, "\"]"));
|
|
315
|
+
remainingPersisted.forEach(el => el.remove());
|
|
316
|
+
}
|
|
317
|
+
}
|
|
318
|
+
|
|
319
|
+
/**
|
|
320
|
+
* Clones inputs as hidden elements to preserve their values.
|
|
321
|
+
* Called when PreserveBoundary unmounts.
|
|
322
|
+
*/
|
|
323
|
+
function preserveInputs(inputs, form, name) {
|
|
324
|
+
// Get the persist container once, outside the loop
|
|
325
|
+
var container = getPersistContainer(form);
|
|
326
|
+
for (var input of inputs) {
|
|
327
|
+
if (!future.isFieldElement(input) || !input.name) {
|
|
328
|
+
continue;
|
|
329
|
+
}
|
|
330
|
+
|
|
331
|
+
// Skip unchecked checkbox/radio (they don't contribute to FormData)
|
|
332
|
+
if (input instanceof HTMLInputElement && (input.type === 'checkbox' || input.type === 'radio') && !input.checked) {
|
|
333
|
+
continue;
|
|
334
|
+
}
|
|
335
|
+
|
|
336
|
+
// Clone the input element
|
|
337
|
+
var clone = input.cloneNode(true);
|
|
338
|
+
|
|
339
|
+
// Mark with name if provided, and hide it
|
|
340
|
+
if (name) {
|
|
341
|
+
clone.setAttribute(PERSIST_ATTR, name);
|
|
342
|
+
}
|
|
343
|
+
clone.hidden = true;
|
|
344
|
+
|
|
345
|
+
// Copy dynamic state that cloneNode doesn't preserve
|
|
346
|
+
if (input instanceof HTMLSelectElement) {
|
|
347
|
+
// cloneNode doesn't copy selected state for options
|
|
348
|
+
for (var i = 0; i < input.options.length; i++) {
|
|
349
|
+
var inputOption = input.options[i];
|
|
350
|
+
var cloneOption = clone.options[i];
|
|
351
|
+
if (inputOption && cloneOption) {
|
|
352
|
+
cloneOption.selected = inputOption.selected;
|
|
353
|
+
}
|
|
354
|
+
}
|
|
355
|
+
} else if (input instanceof HTMLInputElement && input.type === 'file') {
|
|
356
|
+
// cloneNode doesn't copy files
|
|
357
|
+
clone.files = input.files;
|
|
358
|
+
}
|
|
359
|
+
|
|
360
|
+
// Append to persist container (faster than appending directly to form)
|
|
361
|
+
container.appendChild(clone);
|
|
362
|
+
}
|
|
363
|
+
}
|
|
364
|
+
|
|
365
|
+
exports.cleanupPreservedInputs = cleanupPreservedInputs;
|
|
241
366
|
exports.createDefaultSnapshot = createDefaultSnapshot;
|
|
242
367
|
exports.createIntentDispatcher = createIntentDispatcher;
|
|
243
368
|
exports.focusFirstInvalidField = focusFirstInvalidField;
|
|
@@ -248,5 +373,6 @@ exports.getRadioGroupValue = getRadioGroupValue;
|
|
|
248
373
|
exports.getSubmitEvent = getSubmitEvent;
|
|
249
374
|
exports.initializeField = initializeField;
|
|
250
375
|
exports.makeInputFocusable = makeInputFocusable;
|
|
376
|
+
exports.preserveInputs = preserveInputs;
|
|
251
377
|
exports.resetFormValue = resetFormValue;
|
|
252
378
|
exports.updateFormValue = updateFormValue;
|
package/dist/future/dom.mjs
CHANGED
|
@@ -233,5 +233,129 @@ function createIntentDispatcher(formElement, intentName) {
|
|
|
233
233
|
}
|
|
234
234
|
});
|
|
235
235
|
}
|
|
236
|
+
var PERSIST_ATTR = 'data-conform-persist';
|
|
237
|
+
var containerCache = new WeakMap();
|
|
236
238
|
|
|
237
|
-
|
|
239
|
+
/**
|
|
240
|
+
* Gets or creates a hidden container for persisted inputs.
|
|
241
|
+
* Using a container div instead of appending directly to <form> provides ~10x
|
|
242
|
+
* better performance (form.elements bookkeeping is expensive at scale).
|
|
243
|
+
*/
|
|
244
|
+
function getPersistContainer(form) {
|
|
245
|
+
var container = containerCache.get(form);
|
|
246
|
+
|
|
247
|
+
// Verify container is still attached to the form
|
|
248
|
+
if (container && container.parentNode !== form) {
|
|
249
|
+
container = undefined;
|
|
250
|
+
}
|
|
251
|
+
if (!container) {
|
|
252
|
+
container = form.ownerDocument.createElement('div');
|
|
253
|
+
container.setAttribute(PERSIST_ATTR, '');
|
|
254
|
+
container.hidden = true;
|
|
255
|
+
form.appendChild(container);
|
|
256
|
+
containerCache.set(form, container);
|
|
257
|
+
}
|
|
258
|
+
return container;
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
/**
|
|
262
|
+
* Restores values from preserved inputs and removes them.
|
|
263
|
+
* Called when PreserveBoundary mounts.
|
|
264
|
+
*/
|
|
265
|
+
function cleanupPreservedInputs(boundary, form, name) {
|
|
266
|
+
var inputs = boundary.querySelectorAll('input,select,textarea');
|
|
267
|
+
var container = getPersistContainer(form);
|
|
268
|
+
for (var input of inputs) {
|
|
269
|
+
if (!isFieldElement(input) || !input.name) {
|
|
270
|
+
continue;
|
|
271
|
+
}
|
|
272
|
+
|
|
273
|
+
// For checkbox/radio, match by field name + value (+ boundary name if provided)
|
|
274
|
+
// For other inputs, match by field name only (+ boundary name if provided)
|
|
275
|
+
var isCheckboxOrRadio = input.type === 'checkbox' || input.type === 'radio';
|
|
276
|
+
|
|
277
|
+
// Query the persist container, not the whole form
|
|
278
|
+
var boundarySelector = name ? "[".concat(PERSIST_ATTR, "=\"").concat(name, "\"]") : '';
|
|
279
|
+
var selector = isCheckboxOrRadio ? "".concat(boundarySelector, "[name=\"").concat(input.name, "\"][value=\"").concat(input.value, "\"]") : "".concat(boundarySelector, "[name=\"").concat(input.name, "\"]");
|
|
280
|
+
var persisted = container.querySelector(selector);
|
|
281
|
+
if (persisted) {
|
|
282
|
+
if (input instanceof HTMLInputElement && persisted instanceof HTMLInputElement) {
|
|
283
|
+
if (isCheckboxOrRadio) {
|
|
284
|
+
input.checked = persisted.checked;
|
|
285
|
+
} else if (input.type === 'file') {
|
|
286
|
+
// Restore files from the persisted input (may be empty)
|
|
287
|
+
input.files = persisted.files;
|
|
288
|
+
} else {
|
|
289
|
+
input.value = persisted.value;
|
|
290
|
+
}
|
|
291
|
+
} else if (input instanceof HTMLSelectElement && persisted instanceof HTMLSelectElement) {
|
|
292
|
+
var _loop = function _loop(option) {
|
|
293
|
+
var _persistedOption$sele;
|
|
294
|
+
var persistedOption = Array.from(persisted.options).find(o => o.value === option.value);
|
|
295
|
+
option.selected = (_persistedOption$sele = persistedOption === null || persistedOption === void 0 ? void 0 : persistedOption.selected) !== null && _persistedOption$sele !== void 0 ? _persistedOption$sele : false;
|
|
296
|
+
};
|
|
297
|
+
for (var option of input.options) {
|
|
298
|
+
_loop(option);
|
|
299
|
+
}
|
|
300
|
+
} else if (input instanceof HTMLTextAreaElement && persisted instanceof HTMLTextAreaElement) {
|
|
301
|
+
input.value = persisted.value;
|
|
302
|
+
}
|
|
303
|
+
persisted.remove();
|
|
304
|
+
}
|
|
305
|
+
}
|
|
306
|
+
|
|
307
|
+
// If name is provided, remove any remaining persisted inputs with this name
|
|
308
|
+
// (handles the case where inputs were removed from the boundary)
|
|
309
|
+
if (name) {
|
|
310
|
+
var remainingPersisted = container.querySelectorAll("[".concat(PERSIST_ATTR, "=\"").concat(name, "\"]"));
|
|
311
|
+
remainingPersisted.forEach(el => el.remove());
|
|
312
|
+
}
|
|
313
|
+
}
|
|
314
|
+
|
|
315
|
+
/**
|
|
316
|
+
* Clones inputs as hidden elements to preserve their values.
|
|
317
|
+
* Called when PreserveBoundary unmounts.
|
|
318
|
+
*/
|
|
319
|
+
function preserveInputs(inputs, form, name) {
|
|
320
|
+
// Get the persist container once, outside the loop
|
|
321
|
+
var container = getPersistContainer(form);
|
|
322
|
+
for (var input of inputs) {
|
|
323
|
+
if (!isFieldElement(input) || !input.name) {
|
|
324
|
+
continue;
|
|
325
|
+
}
|
|
326
|
+
|
|
327
|
+
// Skip unchecked checkbox/radio (they don't contribute to FormData)
|
|
328
|
+
if (input instanceof HTMLInputElement && (input.type === 'checkbox' || input.type === 'radio') && !input.checked) {
|
|
329
|
+
continue;
|
|
330
|
+
}
|
|
331
|
+
|
|
332
|
+
// Clone the input element
|
|
333
|
+
var clone = input.cloneNode(true);
|
|
334
|
+
|
|
335
|
+
// Mark with name if provided, and hide it
|
|
336
|
+
if (name) {
|
|
337
|
+
clone.setAttribute(PERSIST_ATTR, name);
|
|
338
|
+
}
|
|
339
|
+
clone.hidden = true;
|
|
340
|
+
|
|
341
|
+
// Copy dynamic state that cloneNode doesn't preserve
|
|
342
|
+
if (input instanceof HTMLSelectElement) {
|
|
343
|
+
// cloneNode doesn't copy selected state for options
|
|
344
|
+
for (var i = 0; i < input.options.length; i++) {
|
|
345
|
+
var inputOption = input.options[i];
|
|
346
|
+
var cloneOption = clone.options[i];
|
|
347
|
+
if (inputOption && cloneOption) {
|
|
348
|
+
cloneOption.selected = inputOption.selected;
|
|
349
|
+
}
|
|
350
|
+
}
|
|
351
|
+
} else if (input instanceof HTMLInputElement && input.type === 'file') {
|
|
352
|
+
// cloneNode doesn't copy files
|
|
353
|
+
clone.files = input.files;
|
|
354
|
+
}
|
|
355
|
+
|
|
356
|
+
// Append to persist container (faster than appending directly to form)
|
|
357
|
+
container.appendChild(clone);
|
|
358
|
+
}
|
|
359
|
+
}
|
|
360
|
+
|
|
361
|
+
export { cleanupPreservedInputs, createDefaultSnapshot, createIntentDispatcher, focusFirstInvalidField, getCheckboxGroupValue, getFormElement, getInputSnapshot, getRadioGroupValue, getSubmitEvent, initializeField, makeInputFocusable, preserveInputs, resetFormValue, updateFormValue };
|
package/dist/future/forms.js
CHANGED
package/dist/future/forms.mjs
CHANGED
package/dist/future/hooks.d.ts
CHANGED
|
@@ -15,6 +15,25 @@ export declare function FormProvider(props: {
|
|
|
15
15
|
context: FormContext;
|
|
16
16
|
children: React.ReactNode;
|
|
17
17
|
}): React.ReactElement;
|
|
18
|
+
/**
|
|
19
|
+
* Preserves form field values when its contents are unmounted.
|
|
20
|
+
* Useful for multi-step forms and virtualized lists.
|
|
21
|
+
*
|
|
22
|
+
* @see https://conform.guide/api/react/future/PreserveBoundary
|
|
23
|
+
*/
|
|
24
|
+
export declare function PreserveBoundary(props: {
|
|
25
|
+
/**
|
|
26
|
+
* A unique name for the boundary within the form. Used to ensure proper
|
|
27
|
+
* unmount/remount behavior and to isolate preserved inputs between boundaries.
|
|
28
|
+
*/
|
|
29
|
+
name: string;
|
|
30
|
+
/**
|
|
31
|
+
* The id of the form to associate with. Only needed when the boundary
|
|
32
|
+
* is rendered outside the form element.
|
|
33
|
+
*/
|
|
34
|
+
form?: string;
|
|
35
|
+
children: React.ReactNode;
|
|
36
|
+
}): React.ReactElement;
|
|
18
37
|
/**
|
|
19
38
|
* @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`.
|
|
20
39
|
*/
|
package/dist/future/hooks.js
CHANGED
|
@@ -39,6 +39,47 @@ function FormProvider(props) {
|
|
|
39
39
|
});
|
|
40
40
|
}
|
|
41
41
|
|
|
42
|
+
/**
|
|
43
|
+
* Preserves form field values when its contents are unmounted.
|
|
44
|
+
* Useful for multi-step forms and virtualized lists.
|
|
45
|
+
*
|
|
46
|
+
* @see https://conform.guide/api/react/future/PreserveBoundary
|
|
47
|
+
*/
|
|
48
|
+
function PreserveBoundary(props) {
|
|
49
|
+
// name is used as key so React properly unmounts/remounts when switching
|
|
50
|
+
// between boundaries. Without it, both sides of a ternary share
|
|
51
|
+
// key={undefined} and React reuses the instance (useId and key prop
|
|
52
|
+
// can't help here). This is why name is required.
|
|
53
|
+
return /*#__PURE__*/jsxRuntime.jsx(PreserveBoundaryImpl, _rollupPluginBabelHelpers.objectSpread2({}, props), props.name);
|
|
54
|
+
}
|
|
55
|
+
function PreserveBoundaryImpl(props) {
|
|
56
|
+
var fieldsetRef = react.useRef(null);
|
|
57
|
+
|
|
58
|
+
// useLayoutEffect to restore values before paint, avoiding flash of default values
|
|
59
|
+
useSafeLayoutEffect(() => {
|
|
60
|
+
var fieldset = fieldsetRef.current;
|
|
61
|
+
if (!fieldset || !fieldset.form) {
|
|
62
|
+
return;
|
|
63
|
+
}
|
|
64
|
+
var form = fieldset.form;
|
|
65
|
+
|
|
66
|
+
// On mount: restore values from preserved inputs
|
|
67
|
+
dom.cleanupPreservedInputs(fieldset, form, props.name);
|
|
68
|
+
return () => {
|
|
69
|
+
// On unmount: preserve input values
|
|
70
|
+
dom.preserveInputs(fieldset.querySelectorAll('input,select,textarea'), form, props.name);
|
|
71
|
+
};
|
|
72
|
+
}, [props.name]);
|
|
73
|
+
return /*#__PURE__*/jsxRuntime.jsx("fieldset", {
|
|
74
|
+
ref: fieldsetRef,
|
|
75
|
+
form: props.form,
|
|
76
|
+
style: {
|
|
77
|
+
display: 'contents'
|
|
78
|
+
},
|
|
79
|
+
children: props.children
|
|
80
|
+
});
|
|
81
|
+
}
|
|
82
|
+
|
|
42
83
|
/**
|
|
43
84
|
* @deprecated Replaced by the `configureForms` factory API. This will be removed in the next minor version. If you are not ready to migrate, please pin to `v1.16.0`.
|
|
44
85
|
*/
|
|
@@ -77,11 +118,16 @@ function useConform(formRef, options) {
|
|
|
77
118
|
resetKey: INITIAL_KEY
|
|
78
119
|
});
|
|
79
120
|
if (lastResult) {
|
|
80
|
-
|
|
121
|
+
var intent$1 = lastResult.submission.intent ? intent.deserializeIntent(lastResult.submission.intent) : null;
|
|
122
|
+
var result = intent.applyIntent(lastResult, intent$1, {
|
|
123
|
+
handlers: intent.intentHandlers
|
|
124
|
+
});
|
|
125
|
+
state$1 = state.updateState(state$1, _rollupPluginBabelHelpers.objectSpread2(_rollupPluginBabelHelpers.objectSpread2({}, result), {}, {
|
|
81
126
|
type: 'initialize',
|
|
82
|
-
intent:
|
|
127
|
+
intent: intent$1,
|
|
83
128
|
ctx: {
|
|
84
|
-
handlers: intent.
|
|
129
|
+
handlers: intent.intentHandlers,
|
|
130
|
+
cancelled: result !== lastResult,
|
|
85
131
|
reset: defaultValue => state.initializeState({
|
|
86
132
|
defaultValue: defaultValue !== null && defaultValue !== void 0 ? defaultValue : options.defaultValue,
|
|
87
133
|
resetKey: INITIAL_KEY
|
|
@@ -99,14 +145,17 @@ function useConform(formRef, options) {
|
|
|
99
145
|
var lastAsyncResultRef = react.useRef(null);
|
|
100
146
|
var abortControllerRef = react.useRef(null);
|
|
101
147
|
var handleSubmission = react.useCallback(function (type, result) {
|
|
102
|
-
var _optionsRef$current$o, _optionsRef$current;
|
|
103
148
|
var options = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : optionsRef.current;
|
|
104
149
|
var intent$1 = result.submission.intent ? intent.deserializeIntent(result.submission.intent) : null;
|
|
105
|
-
|
|
150
|
+
var finalResult = intent.applyIntent(result, intent$1, {
|
|
151
|
+
handlers: intent.intentHandlers
|
|
152
|
+
});
|
|
153
|
+
setState(state$1 => state.updateState(state$1, _rollupPluginBabelHelpers.objectSpread2(_rollupPluginBabelHelpers.objectSpread2({}, finalResult), {}, {
|
|
106
154
|
type,
|
|
107
155
|
intent: intent$1,
|
|
108
156
|
ctx: {
|
|
109
|
-
handlers: intent.
|
|
157
|
+
handlers: intent.intentHandlers,
|
|
158
|
+
cancelled: finalResult !== result,
|
|
110
159
|
reset(defaultValue) {
|
|
111
160
|
return state.initializeState({
|
|
112
161
|
defaultValue: defaultValue !== null && defaultValue !== void 0 ? defaultValue : options.defaultValue
|
|
@@ -117,14 +166,15 @@ function useConform(formRef, options) {
|
|
|
117
166
|
|
|
118
167
|
// TODO: move on error handler to a new effect
|
|
119
168
|
var formElement = dom.getFormElement(formRef);
|
|
120
|
-
if (
|
|
121
|
-
|
|
169
|
+
if (formElement && result.error) {
|
|
170
|
+
var _optionsRef$current$o, _optionsRef$current;
|
|
171
|
+
(_optionsRef$current$o = (_optionsRef$current = optionsRef.current).onError) === null || _optionsRef$current$o === void 0 || _optionsRef$current$o.call(_optionsRef$current, {
|
|
172
|
+
formElement,
|
|
173
|
+
error: result.error,
|
|
174
|
+
intent: intent$1
|
|
175
|
+
});
|
|
122
176
|
}
|
|
123
|
-
|
|
124
|
-
formElement,
|
|
125
|
-
error: result.error,
|
|
126
|
-
intent: intent$1
|
|
127
|
-
});
|
|
177
|
+
return finalResult;
|
|
128
178
|
}, [formRef, optionsRef]);
|
|
129
179
|
if (options.key !== keyRef.current) {
|
|
130
180
|
keyRef.current = options.key;
|
|
@@ -200,17 +250,11 @@ function useConform(formRef, options) {
|
|
|
200
250
|
if (pendingValueRef.current !== undefined) {
|
|
201
251
|
submission.payload = pendingValueRef.current;
|
|
202
252
|
}
|
|
203
|
-
var value = intent.
|
|
253
|
+
var value = intent.resolveIntent(submission);
|
|
204
254
|
var submissionResult = future.report(submission, {
|
|
205
255
|
keepFiles: true,
|
|
206
256
|
value
|
|
207
257
|
});
|
|
208
|
-
|
|
209
|
-
// If there is target value, keep track of it as pending value
|
|
210
|
-
if (submission.payload !== value) {
|
|
211
|
-
var _ref;
|
|
212
|
-
pendingValueRef.current = (_ref = value !== null && value !== void 0 ? value : optionsRef.current.defaultValue) !== null && _ref !== void 0 ? _ref : {};
|
|
213
|
-
}
|
|
214
258
|
var validateResult =
|
|
215
259
|
// Skip validation on form reset
|
|
216
260
|
value !== undefined ? (_optionsRef$current$o2 = (_optionsRef$current2 = optionsRef.current).onValidate) === null || _optionsRef$current$o2 === void 0 ? void 0 : _optionsRef$current$o2.call(_optionsRef$current2, {
|
|
@@ -237,11 +281,11 @@ function useConform(formRef, options) {
|
|
|
237
281
|
}
|
|
238
282
|
if (typeof asyncResult !== 'undefined') {
|
|
239
283
|
// Update the form when the validation result is resolved
|
|
240
|
-
asyncResult.then(
|
|
284
|
+
asyncResult.then(_ref => {
|
|
241
285
|
var {
|
|
242
286
|
error,
|
|
243
287
|
value
|
|
244
|
-
} =
|
|
288
|
+
} = _ref;
|
|
245
289
|
// Update the form with the validation result
|
|
246
290
|
// There is no need to flush the update in this case
|
|
247
291
|
if (!abortController.signal.aborted) {
|
|
@@ -249,7 +293,7 @@ function useConform(formRef, options) {
|
|
|
249
293
|
handleSubmission('server', submissionResult);
|
|
250
294
|
|
|
251
295
|
// If the form is meant to be submitted and there is no error
|
|
252
|
-
if (error === null && !submission.intent) {
|
|
296
|
+
if (submissionResult.error === null && !submission.intent) {
|
|
253
297
|
var _event = future.createSubmitEvent(submitEvent.submitter);
|
|
254
298
|
|
|
255
299
|
// Keep track of the submit event so we can skip validation on the next submit
|
|
@@ -264,15 +308,19 @@ function useConform(formRef, options) {
|
|
|
264
308
|
}
|
|
265
309
|
});
|
|
266
310
|
}
|
|
267
|
-
handleSubmission('client', submissionResult);
|
|
311
|
+
var clientResult = handleSubmission('client', submissionResult);
|
|
312
|
+
if (clientResult.reset || clientResult.targetValue !== undefined) {
|
|
313
|
+
var _ref2, _clientResult$targetV;
|
|
314
|
+
pendingValueRef.current = (_ref2 = (_clientResult$targetV = clientResult.targetValue) !== null && _clientResult$targetV !== void 0 ? _clientResult$targetV : optionsRef.current.defaultValue) !== null && _ref2 !== void 0 ? _ref2 : {};
|
|
315
|
+
}
|
|
268
316
|
if (
|
|
269
317
|
// If client validation happens
|
|
270
318
|
(typeof syncResult !== 'undefined' || typeof asyncResult !== 'undefined') && (
|
|
271
319
|
// Either the form is not meant to be submitted (i.e. intent is present) or there is an error / pending validation
|
|
272
|
-
|
|
320
|
+
clientResult.submission.intent || clientResult.error !== null)) {
|
|
273
321
|
event.preventDefault();
|
|
274
322
|
}
|
|
275
|
-
result =
|
|
323
|
+
result = clientResult;
|
|
276
324
|
}
|
|
277
325
|
|
|
278
326
|
// We might not prevent form submission if server validation is required
|
|
@@ -838,6 +886,7 @@ exports.FormOptionsProvider = FormOptionsProvider;
|
|
|
838
886
|
exports.FormProvider = FormProvider;
|
|
839
887
|
exports.GlobalFormOptionsContext = GlobalFormOptionsContext;
|
|
840
888
|
exports.INITIAL_KEY = INITIAL_KEY;
|
|
889
|
+
exports.PreserveBoundary = PreserveBoundary;
|
|
841
890
|
exports.useConform = useConform;
|
|
842
891
|
exports.useControl = useControl;
|
|
843
892
|
exports.useField = useField;
|