@rezi-ui/core 0.1.0-alpha.60 → 0.1.0-alpha.63
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/dist/app/createApp/breadcrumbs.d.ts +38 -0
- package/dist/app/createApp/breadcrumbs.d.ts.map +1 -0
- package/dist/app/createApp/breadcrumbs.js +65 -0
- package/dist/app/createApp/breadcrumbs.js.map +1 -0
- package/dist/app/createApp/config.d.ts +25 -0
- package/dist/app/createApp/config.d.ts.map +1 -0
- package/dist/app/createApp/config.js +130 -0
- package/dist/app/createApp/config.js.map +1 -0
- package/dist/app/createApp/eventLoop.d.ts +95 -0
- package/dist/app/createApp/eventLoop.d.ts.map +1 -0
- package/dist/app/createApp/eventLoop.js +384 -0
- package/dist/app/createApp/eventLoop.js.map +1 -0
- package/dist/app/createApp/guards.d.ts +21 -0
- package/dist/app/createApp/guards.d.ts.map +1 -0
- package/dist/app/createApp/guards.js +54 -0
- package/dist/app/createApp/guards.js.map +1 -0
- package/dist/app/createApp/keybindings.d.ts +28 -0
- package/dist/app/createApp/keybindings.d.ts.map +1 -0
- package/dist/app/createApp/keybindings.js +113 -0
- package/dist/app/createApp/keybindings.js.map +1 -0
- package/dist/app/createApp/renderLoop.d.ts +64 -0
- package/dist/app/createApp/renderLoop.d.ts.map +1 -0
- package/dist/app/createApp/renderLoop.js +305 -0
- package/dist/app/createApp/renderLoop.js.map +1 -0
- package/dist/app/createApp.d.ts +3 -39
- package/dist/app/createApp.d.ts.map +1 -1
- package/dist/app/createApp.js +403 -1205
- package/dist/app/createApp.js.map +1 -1
- package/dist/app/widgetRenderer/constraintState.d.ts +98 -0
- package/dist/app/widgetRenderer/constraintState.d.ts.map +1 -0
- package/dist/app/widgetRenderer/constraintState.js +563 -0
- package/dist/app/widgetRenderer/constraintState.js.map +1 -0
- package/dist/app/widgetRenderer/fileNodeCache.d.ts +2 -0
- package/dist/app/widgetRenderer/fileNodeCache.d.ts.map +1 -1
- package/dist/app/widgetRenderer/fileNodeCache.js +31 -0
- package/dist/app/widgetRenderer/fileNodeCache.js.map +1 -1
- package/dist/app/widgetRenderer/filePickerRouting.d.ts +12 -1
- package/dist/app/widgetRenderer/filePickerRouting.d.ts.map +1 -1
- package/dist/app/widgetRenderer/filePickerRouting.js +63 -14
- package/dist/app/widgetRenderer/filePickerRouting.js.map +1 -1
- package/dist/app/widgetRenderer/focusState.d.ts +46 -0
- package/dist/app/widgetRenderer/focusState.d.ts.map +1 -0
- package/dist/app/widgetRenderer/focusState.js +122 -0
- package/dist/app/widgetRenderer/focusState.js.map +1 -0
- package/dist/app/widgetRenderer/keyboardRouting.d.ts.map +1 -1
- package/dist/app/widgetRenderer/keyboardRouting.js.map +1 -1
- package/dist/app/widgetRenderer/mouseRouting.d.ts +5 -0
- package/dist/app/widgetRenderer/mouseRouting.d.ts.map +1 -1
- package/dist/app/widgetRenderer/mouseRouting.js +78 -8
- package/dist/app/widgetRenderer/mouseRouting.js.map +1 -1
- package/dist/app/widgetRenderer/overlayShortcuts.d.ts.map +1 -1
- package/dist/app/widgetRenderer/overlayShortcuts.js +14 -4
- package/dist/app/widgetRenderer/overlayShortcuts.js.map +1 -1
- package/dist/app/widgetRenderer/overlayState.d.ts +198 -0
- package/dist/app/widgetRenderer/overlayState.d.ts.map +1 -0
- package/dist/app/widgetRenderer/overlayState.js +590 -0
- package/dist/app/widgetRenderer/overlayState.js.map +1 -0
- package/dist/app/widgetRenderer/routeEngineEvent.d.ts +189 -0
- package/dist/app/widgetRenderer/routeEngineEvent.d.ts.map +1 -0
- package/dist/app/widgetRenderer/routeEngineEvent.js +527 -0
- package/dist/app/widgetRenderer/routeEngineEvent.js.map +1 -0
- package/dist/app/widgetRenderer.d.ts +2 -1
- package/dist/app/widgetRenderer.d.ts.map +1 -1
- package/dist/app/widgetRenderer.js +334 -1707
- package/dist/app/widgetRenderer.js.map +1 -1
- package/dist/forms/internal/arrayState.d.ts +35 -0
- package/dist/forms/internal/arrayState.d.ts.map +1 -0
- package/dist/forms/internal/arrayState.js +238 -0
- package/dist/forms/internal/arrayState.js.map +1 -0
- package/dist/forms/internal/bindings.d.ts +46 -0
- package/dist/forms/internal/bindings.d.ts.map +1 -0
- package/dist/forms/internal/bindings.js +161 -0
- package/dist/forms/internal/bindings.js.map +1 -0
- package/dist/forms/internal/dev.d.ts +4 -0
- package/dist/forms/internal/dev.d.ts.map +1 -0
- package/dist/forms/internal/dev.js +21 -0
- package/dist/forms/internal/dev.js.map +1 -0
- package/dist/forms/internal/state.d.ts +52 -0
- package/dist/forms/internal/state.d.ts.map +1 -0
- package/dist/forms/internal/state.js +240 -0
- package/dist/forms/internal/state.js.map +1 -0
- package/dist/forms/internal/submit.d.ts +43 -0
- package/dist/forms/internal/submit.d.ts.map +1 -0
- package/dist/forms/internal/submit.js +165 -0
- package/dist/forms/internal/submit.js.map +1 -0
- package/dist/forms/internal/wizard.d.ts +53 -0
- package/dist/forms/internal/wizard.d.ts.map +1 -0
- package/dist/forms/internal/wizard.js +311 -0
- package/dist/forms/internal/wizard.js.map +1 -0
- package/dist/forms/useForm.d.ts.map +1 -1
- package/dist/forms/useForm.js +90 -1117
- package/dist/forms/useForm.js.map +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -1
- package/dist/index.js.map +1 -1
- package/dist/keybindings/manager.d.ts.map +1 -1
- package/dist/keybindings/manager.js.map +1 -1
- package/dist/keybindings/parser.d.ts.map +1 -1
- package/dist/keybindings/parser.js +10 -5
- package/dist/keybindings/parser.js.map +1 -1
- package/dist/layout/dropdownGeometry.d.ts +8 -0
- package/dist/layout/dropdownGeometry.d.ts.map +1 -1
- package/dist/layout/dropdownGeometry.js +40 -0
- package/dist/layout/dropdownGeometry.js.map +1 -1
- package/dist/layout/engine/layoutEngine.js +1 -1
- package/dist/layout/engine/layoutEngine.js.map +1 -1
- package/dist/layout/kinds/overlays.d.ts.map +1 -1
- package/dist/layout/kinds/stack.d.ts +1 -3
- package/dist/layout/kinds/stack.d.ts.map +1 -1
- package/dist/layout/kinds/stack.js +11 -1523
- package/dist/layout/kinds/stack.js.map +1 -1
- package/dist/layout/kinds/stackParts/axis.d.ts +32 -0
- package/dist/layout/kinds/stackParts/axis.d.ts.map +1 -0
- package/dist/layout/kinds/stackParts/axis.js +61 -0
- package/dist/layout/kinds/stackParts/axis.js.map +1 -0
- package/dist/layout/kinds/stackParts/constraintPlan.d.ts +18 -0
- package/dist/layout/kinds/stackParts/constraintPlan.d.ts.map +1 -0
- package/dist/layout/kinds/stackParts/constraintPlan.js +434 -0
- package/dist/layout/kinds/stackParts/constraintPlan.js.map +1 -0
- package/dist/layout/kinds/stackParts/layout.d.ts +6 -0
- package/dist/layout/kinds/stackParts/layout.d.ts.map +1 -0
- package/dist/layout/kinds/stackParts/layout.js +376 -0
- package/dist/layout/kinds/stackParts/layout.js.map +1 -0
- package/dist/layout/kinds/stackParts/measure.d.ts +6 -0
- package/dist/layout/kinds/stackParts/measure.d.ts.map +1 -0
- package/dist/layout/kinds/stackParts/measure.js +212 -0
- package/dist/layout/kinds/stackParts/measure.js.map +1 -0
- package/dist/layout/kinds/stackParts/shared.d.ts +31 -0
- package/dist/layout/kinds/stackParts/shared.d.ts.map +1 -0
- package/dist/layout/kinds/stackParts/shared.js +94 -0
- package/dist/layout/kinds/stackParts/shared.js.map +1 -0
- package/dist/layout/kinds/stackParts/wrap.d.ts +26 -0
- package/dist/layout/kinds/stackParts/wrap.d.ts.map +1 -0
- package/dist/layout/kinds/stackParts/wrap.js +374 -0
- package/dist/layout/kinds/stackParts/wrap.js.map +1 -0
- package/dist/layout/validate/interactive.d.ts +106 -0
- package/dist/layout/validate/interactive.d.ts.map +1 -0
- package/dist/layout/validate/interactive.js +430 -0
- package/dist/layout/validate/interactive.js.map +1 -0
- package/dist/layout/validate/layoutConstraints.d.ts +51 -0
- package/dist/layout/validate/layoutConstraints.d.ts.map +1 -0
- package/dist/layout/validate/layoutConstraints.js +100 -0
- package/dist/layout/validate/layoutConstraints.js.map +1 -0
- package/dist/layout/validate/primitives.d.ts +31 -0
- package/dist/layout/validate/primitives.d.ts.map +1 -0
- package/dist/layout/validate/primitives.js +299 -0
- package/dist/layout/validate/primitives.js.map +1 -0
- package/dist/layout/validate/shared.d.ts +23 -0
- package/dist/layout/validate/shared.d.ts.map +1 -0
- package/dist/layout/validate/shared.js +32 -0
- package/dist/layout/validate/shared.js.map +1 -0
- package/dist/layout/validate/spacing.d.ts +21 -0
- package/dist/layout/validate/spacing.d.ts.map +1 -0
- package/dist/layout/validate/spacing.js +33 -0
- package/dist/layout/validate/spacing.js.map +1 -0
- package/dist/layout/validateProps.d.ts +5 -159
- package/dist/layout/validateProps.d.ts.map +1 -1
- package/dist/layout/validateProps.js +1 -832
- package/dist/layout/validateProps.js.map +1 -1
- package/dist/pipeline.js +1 -1
- package/dist/pipeline.js.map +1 -1
- package/dist/renderer/renderToDrawlist/renderTree.d.ts +1 -1
- package/dist/renderer/renderToDrawlist/renderTree.d.ts.map +1 -1
- package/dist/renderer/renderToDrawlist/renderTree.js +3 -3
- package/dist/renderer/renderToDrawlist/renderTree.js.map +1 -1
- package/dist/renderer/renderToDrawlist/types.d.ts +2 -0
- package/dist/renderer/renderToDrawlist/types.d.ts.map +1 -1
- package/dist/renderer/renderToDrawlist/widgets/containers.d.ts.map +1 -1
- package/dist/renderer/renderToDrawlist/widgets/containers.js +1 -1
- package/dist/renderer/renderToDrawlist/widgets/containers.js.map +1 -1
- package/dist/renderer/renderToDrawlist/widgets/files.d.ts +2 -1
- package/dist/renderer/renderToDrawlist/widgets/files.d.ts.map +1 -1
- package/dist/renderer/renderToDrawlist/widgets/files.js +30 -3
- package/dist/renderer/renderToDrawlist/widgets/files.js.map +1 -1
- package/dist/renderer/renderToDrawlist/widgets/overlays.d.ts +1 -1
- package/dist/renderer/renderToDrawlist/widgets/overlays.d.ts.map +1 -1
- package/dist/renderer/renderToDrawlist/widgets/overlays.js +26 -6
- package/dist/renderer/renderToDrawlist/widgets/overlays.js.map +1 -1
- package/dist/renderer/renderToDrawlist/widgets/renderFormWidgets.d.ts.map +1 -1
- package/dist/renderer/renderToDrawlist/widgets/renderFormWidgets.js +52 -18
- package/dist/renderer/renderToDrawlist/widgets/renderFormWidgets.js.map +1 -1
- package/dist/renderer/renderToDrawlist.d.ts +0 -8
- package/dist/renderer/renderToDrawlist.d.ts.map +1 -1
- package/dist/renderer/renderToDrawlist.js +1 -9
- package/dist/renderer/renderToDrawlist.js.map +1 -1
- package/dist/runtime/commit/composite.d.ts +11 -0
- package/dist/runtime/commit/composite.d.ts.map +1 -0
- package/dist/runtime/commit/composite.js +238 -0
- package/dist/runtime/commit/composite.js.map +1 -0
- package/dist/runtime/commit/container.d.ts +7 -0
- package/dist/runtime/commit/container.d.ts.map +1 -0
- package/dist/runtime/commit/container.js +350 -0
- package/dist/runtime/commit/container.js.map +1 -0
- package/dist/runtime/commit/equality.d.ts +20 -0
- package/dist/runtime/commit/equality.d.ts.map +1 -0
- package/dist/runtime/commit/equality.js +436 -0
- package/dist/runtime/commit/equality.js.map +1 -0
- package/dist/runtime/commit/errorBoundary.d.ts +7 -0
- package/dist/runtime/commit/errorBoundary.d.ts.map +1 -0
- package/dist/runtime/commit/errorBoundary.js +53 -0
- package/dist/runtime/commit/errorBoundary.js.map +1 -0
- package/dist/runtime/commit/shared.d.ts +138 -0
- package/dist/runtime/commit/shared.d.ts.map +1 -0
- package/dist/runtime/commit/shared.js +11 -0
- package/dist/runtime/commit/shared.js.map +1 -0
- package/dist/runtime/commit/transitions.d.ts +9 -0
- package/dist/runtime/commit/transitions.d.ts.map +1 -0
- package/dist/runtime/commit/transitions.js +93 -0
- package/dist/runtime/commit/transitions.js.map +1 -0
- package/dist/runtime/commit/validation.d.ts +16 -0
- package/dist/runtime/commit/validation.d.ts.map +1 -0
- package/dist/runtime/commit/validation.js +157 -0
- package/dist/runtime/commit/validation.js.map +1 -0
- package/dist/runtime/commit.d.ts +7 -117
- package/dist/runtime/commit.d.ts.map +1 -1
- package/dist/runtime/commit.js +13 -1394
- package/dist/runtime/commit.js.map +1 -1
- package/dist/runtime/localState.d.ts +4 -0
- package/dist/runtime/localState.d.ts.map +1 -1
- package/dist/runtime/localState.js.map +1 -1
- package/dist/runtime/widgetMeta/collector.d.ts +77 -0
- package/dist/runtime/widgetMeta/collector.d.ts.map +1 -0
- package/dist/runtime/widgetMeta/collector.js +293 -0
- package/dist/runtime/widgetMeta/collector.js.map +1 -0
- package/dist/runtime/widgetMeta/focusContainers.d.ts +44 -0
- package/dist/runtime/widgetMeta/focusContainers.d.ts.map +1 -0
- package/dist/runtime/widgetMeta/focusContainers.js +190 -0
- package/dist/runtime/widgetMeta/focusContainers.js.map +1 -0
- package/dist/runtime/widgetMeta/focusInfo.d.ts +19 -0
- package/dist/runtime/widgetMeta/focusInfo.d.ts.map +1 -0
- package/dist/runtime/widgetMeta/focusInfo.js +172 -0
- package/dist/runtime/widgetMeta/focusInfo.js.map +1 -0
- package/dist/runtime/widgetMeta/helpers.d.ts +47 -0
- package/dist/runtime/widgetMeta/helpers.d.ts.map +1 -0
- package/dist/runtime/widgetMeta/helpers.js +182 -0
- package/dist/runtime/widgetMeta/helpers.js.map +1 -0
- package/dist/runtime/widgetMeta.d.ts +12 -175
- package/dist/runtime/widgetMeta.d.ts.map +1 -1
- package/dist/runtime/widgetMeta.js +6 -847
- package/dist/runtime/widgetMeta.js.map +1 -1
- package/dist/ui/capabilities.d.ts.map +1 -1
- package/dist/ui/designTokens.d.ts.map +1 -1
- package/dist/widgets/accordion.d.ts.map +1 -1
- package/dist/widgets/accordion.js +8 -13
- package/dist/widgets/accordion.js.map +1 -1
- package/dist/widgets/factories/advanced.d.ts +20 -0
- package/dist/widgets/factories/advanced.d.ts.map +1 -0
- package/dist/widgets/factories/advanced.js +75 -0
- package/dist/widgets/factories/advanced.js.map +1 -0
- package/dist/widgets/factories/basic.d.ts +14 -0
- package/dist/widgets/factories/basic.d.ts.map +1 -0
- package/dist/widgets/factories/basic.js +44 -0
- package/dist/widgets/factories/basic.js.map +1 -0
- package/dist/widgets/factories/feedback.d.ts +20 -0
- package/dist/widgets/factories/feedback.d.ts.map +1 -0
- package/dist/widgets/factories/feedback.js +102 -0
- package/dist/widgets/factories/feedback.js.map +1 -0
- package/dist/widgets/factories/helpers.d.ts +41 -0
- package/dist/widgets/factories/helpers.d.ts.map +1 -0
- package/dist/widgets/factories/helpers.js +72 -0
- package/dist/widgets/factories/helpers.js.map +1 -0
- package/dist/widgets/factories/interactive.d.ts +15 -0
- package/dist/widgets/factories/interactive.d.ts.map +1 -0
- package/dist/widgets/factories/interactive.js +46 -0
- package/dist/widgets/factories/interactive.js.map +1 -0
- package/dist/widgets/factories/layoutShell.d.ts +22 -0
- package/dist/widgets/factories/layoutShell.d.ts.map +1 -0
- package/dist/widgets/factories/layoutShell.js +190 -0
- package/dist/widgets/factories/layoutShell.js.map +1 -0
- package/dist/widgets/factories/media.d.ts +14 -0
- package/dist/widgets/factories/media.d.ts.map +1 -0
- package/dist/widgets/factories/media.js +25 -0
- package/dist/widgets/factories/media.js.map +1 -0
- package/dist/widgets/factories/navigation.d.ts +10 -0
- package/dist/widgets/factories/navigation.d.ts.map +1 -0
- package/dist/widgets/factories/navigation.js +24 -0
- package/dist/widgets/factories/navigation.js.map +1 -0
- package/dist/widgets/field.d.ts +6 -1
- package/dist/widgets/field.d.ts.map +1 -1
- package/dist/widgets/field.js +8 -2
- package/dist/widgets/field.js.map +1 -1
- package/dist/widgets/filePicker.d.ts +5 -0
- package/dist/widgets/filePicker.d.ts.map +1 -0
- package/dist/widgets/filePicker.js +136 -0
- package/dist/widgets/filePicker.js.map +1 -0
- package/dist/widgets/protocol.d.ts +0 -6
- package/dist/widgets/protocol.d.ts.map +1 -1
- package/dist/widgets/protocol.js +0 -6
- package/dist/widgets/protocol.js.map +1 -1
- package/dist/widgets/select.js +1 -1
- package/dist/widgets/select.js.map +1 -1
- package/dist/widgets/splitPane.d.ts.map +1 -1
- package/dist/widgets/splitPane.js.map +1 -1
- package/dist/widgets/table.d.ts.map +1 -1
- package/dist/widgets/table.js +43 -1
- package/dist/widgets/table.js.map +1 -1
- package/dist/widgets/tree.d.ts.map +1 -1
- package/dist/widgets/types/advanced.d.ts +611 -0
- package/dist/widgets/types/advanced.d.ts.map +1 -0
- package/dist/widgets/types/advanced.js +2 -0
- package/dist/widgets/types/advanced.js.map +1 -0
- package/dist/widgets/types/base.d.ts +933 -0
- package/dist/widgets/types/base.d.ts.map +1 -0
- package/dist/widgets/types/base.js +2 -0
- package/dist/widgets/types/base.js.map +1 -0
- package/dist/widgets/types/forms.d.ts +136 -0
- package/dist/widgets/types/forms.d.ts.map +1 -0
- package/dist/widgets/types/forms.js +2 -0
- package/dist/widgets/types/forms.js.map +1 -0
- package/dist/widgets/types/navigation.d.ts +83 -0
- package/dist/widgets/types/navigation.d.ts.map +1 -0
- package/dist/widgets/types/navigation.js +2 -0
- package/dist/widgets/types/navigation.js.map +1 -0
- package/dist/widgets/types/overlaysShell.d.ts +223 -0
- package/dist/widgets/types/overlaysShell.d.ts.map +1 -0
- package/dist/widgets/types/overlaysShell.js +2 -0
- package/dist/widgets/types/overlaysShell.js.map +1 -0
- package/dist/widgets/types/table.d.ts +104 -0
- package/dist/widgets/types/table.d.ts.map +1 -0
- package/dist/widgets/types/table.js +2 -0
- package/dist/widgets/types/table.js.map +1 -0
- package/dist/widgets/types/tree.d.ts +64 -0
- package/dist/widgets/types/tree.d.ts.map +1 -0
- package/dist/widgets/types/tree.js +2 -0
- package/dist/widgets/types/tree.js.map +1 -0
- package/dist/widgets/types.d.ts +14 -2123
- package/dist/widgets/types.d.ts.map +1 -1
- package/dist/widgets/ui.d.ts +37 -843
- package/dist/widgets/ui.d.ts.map +1 -1
- package/dist/widgets/ui.js +37 -1262
- package/dist/widgets/ui.js.map +1 -1
- package/package.json +2 -2
- package/dist/constraints/aggregation.d.ts +0 -17
- package/dist/constraints/aggregation.d.ts.map +0 -1
- package/dist/constraints/aggregation.js +0 -59
- package/dist/constraints/aggregation.js.map +0 -1
- package/dist/renderer/renderToDrawlist/overflowCulling.d.ts +0 -3
- package/dist/renderer/renderToDrawlist/overflowCulling.d.ts.map +0 -1
- package/dist/renderer/renderToDrawlist/overflowCulling.js +0 -81
- package/dist/renderer/renderToDrawlist/overflowCulling.js.map +0 -1
- package/dist/widgets/tests/protocol.test.d.ts +0 -2
- package/dist/widgets/tests/protocol.test.d.ts.map +0 -1
- package/dist/widgets/tests/protocol.test.js +0 -120
- package/dist/widgets/tests/protocol.test.js.map +0 -1
package/dist/forms/useForm.js
CHANGED
|
@@ -7,312 +7,27 @@
|
|
|
7
7
|
*
|
|
8
8
|
* @see docs/recipes/form-validation.md (GitHub issue #119)
|
|
9
9
|
*/
|
|
10
|
-
import {
|
|
11
|
-
import {
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
if (!DEV_MODE)
|
|
17
|
-
return;
|
|
18
|
-
const c = globalThis.console;
|
|
19
|
-
c?.warn?.(message);
|
|
20
|
-
}
|
|
21
|
-
function formatErrorForDev(error) {
|
|
22
|
-
if (error instanceof Error) {
|
|
23
|
-
return `${error.name}: ${error.message}`;
|
|
24
|
-
}
|
|
25
|
-
try {
|
|
26
|
-
return String(error);
|
|
27
|
-
}
|
|
28
|
-
catch {
|
|
29
|
-
return "[unstringifiable thrown value]";
|
|
30
|
-
}
|
|
31
|
-
}
|
|
32
|
-
function cloneInitialValues(values) {
|
|
33
|
-
return structuredClone(values);
|
|
34
|
-
}
|
|
35
|
-
/**
|
|
36
|
-
* Clamp step index to available wizard range.
|
|
37
|
-
*/
|
|
38
|
-
function clampStepIndex(stepIndex, stepCount) {
|
|
39
|
-
if (stepCount <= 0) {
|
|
40
|
-
return 0;
|
|
41
|
-
}
|
|
42
|
-
if (stepIndex < 0) {
|
|
43
|
-
return 0;
|
|
44
|
-
}
|
|
45
|
-
if (stepIndex >= stepCount) {
|
|
46
|
-
return stepCount - 1;
|
|
47
|
-
}
|
|
48
|
-
return stepIndex;
|
|
49
|
-
}
|
|
50
|
-
/**
|
|
51
|
-
* Create initial form state from options.
|
|
52
|
-
*/
|
|
53
|
-
function createInitialState(options) {
|
|
54
|
-
const stepCount = options.wizard?.steps.length ?? 0;
|
|
55
|
-
const initialStep = clampStepIndex(options.wizard?.initialStep ?? 0, stepCount);
|
|
56
|
-
if (DEV_MODE) {
|
|
57
|
-
const valueKeys = new Set(Object.keys(options.initialValues));
|
|
58
|
-
if (options.fieldDisabled) {
|
|
59
|
-
for (const key of Object.keys(options.fieldDisabled)) {
|
|
60
|
-
if (!valueKeys.has(key)) {
|
|
61
|
-
warnDev(`[rezi] useForm: fieldDisabled key "${key}" does not exist in initialValues`);
|
|
62
|
-
}
|
|
63
|
-
}
|
|
64
|
-
}
|
|
65
|
-
if (options.fieldReadOnly) {
|
|
66
|
-
for (const key of Object.keys(options.fieldReadOnly)) {
|
|
67
|
-
if (!valueKeys.has(key)) {
|
|
68
|
-
warnDev(`[rezi] useForm: fieldReadOnly key "${key}" does not exist in initialValues`);
|
|
69
|
-
}
|
|
70
|
-
}
|
|
71
|
-
}
|
|
72
|
-
}
|
|
73
|
-
return {
|
|
74
|
-
values: cloneInitialValues(options.initialValues),
|
|
75
|
-
errors: {},
|
|
76
|
-
touched: {},
|
|
77
|
-
dirty: {},
|
|
78
|
-
isSubmitting: false,
|
|
79
|
-
submitError: undefined,
|
|
80
|
-
submitCount: 0,
|
|
81
|
-
disabled: options.disabled ?? false,
|
|
82
|
-
readOnly: options.readOnly ?? false,
|
|
83
|
-
fieldDisabled: {
|
|
84
|
-
...(options.fieldDisabled ?? {}),
|
|
85
|
-
},
|
|
86
|
-
fieldReadOnly: {
|
|
87
|
-
...(options.fieldReadOnly ?? {}),
|
|
88
|
-
},
|
|
89
|
-
currentStep: initialStep,
|
|
90
|
-
};
|
|
91
|
-
}
|
|
92
|
-
/**
|
|
93
|
-
* Compute dirty array flags by position.
|
|
94
|
-
*/
|
|
95
|
-
function computeArrayFieldDirty(currentValue, initialValue) {
|
|
96
|
-
if (currentValue.length !== initialValue.length) {
|
|
97
|
-
return true;
|
|
98
|
-
}
|
|
99
|
-
const dirty = [];
|
|
100
|
-
for (let i = 0; i < currentValue.length; i++) {
|
|
101
|
-
dirty.push(!Object.is(currentValue[i], initialValue[i]));
|
|
102
|
-
}
|
|
103
|
-
return dirty;
|
|
104
|
-
}
|
|
105
|
-
/**
|
|
106
|
-
* Compute dirty status for a field by comparing current value to initial.
|
|
107
|
-
*/
|
|
108
|
-
function computeFieldDirty(field, currentValue, initialValues) {
|
|
109
|
-
const initialValue = initialValues[field];
|
|
110
|
-
if (Array.isArray(currentValue) && Array.isArray(initialValue)) {
|
|
111
|
-
return computeArrayFieldDirty(currentValue, initialValue);
|
|
112
|
-
}
|
|
113
|
-
return !Object.is(currentValue, initialValue);
|
|
114
|
-
}
|
|
115
|
-
/**
|
|
116
|
-
* Check if a field-level dirty/touched flag contains any true value.
|
|
117
|
-
*/
|
|
118
|
-
function hasTruthyBooleanValue(value) {
|
|
119
|
-
if (value === true) {
|
|
120
|
-
return true;
|
|
121
|
-
}
|
|
122
|
-
if (!Array.isArray(value)) {
|
|
123
|
-
return false;
|
|
124
|
-
}
|
|
125
|
-
for (const item of value) {
|
|
126
|
-
if (item === true) {
|
|
127
|
-
return true;
|
|
128
|
-
}
|
|
129
|
-
}
|
|
130
|
-
return false;
|
|
131
|
-
}
|
|
132
|
-
function toInputValue(value) {
|
|
133
|
-
if (value === null || value === undefined) {
|
|
134
|
-
return "";
|
|
135
|
-
}
|
|
136
|
-
return String(value);
|
|
137
|
-
}
|
|
138
|
-
function isTextBindableValue(value) {
|
|
139
|
-
return value === null || value === undefined || typeof value === "string";
|
|
140
|
-
}
|
|
141
|
-
function toFieldErrorString(value) {
|
|
142
|
-
if (typeof value === "string") {
|
|
143
|
-
return value.length > 0 ? value : undefined;
|
|
144
|
-
}
|
|
145
|
-
if (!Array.isArray(value)) {
|
|
146
|
-
return undefined;
|
|
147
|
-
}
|
|
148
|
-
for (const item of value) {
|
|
149
|
-
if (item !== undefined && item.length > 0) {
|
|
150
|
-
return item;
|
|
151
|
-
}
|
|
152
|
-
}
|
|
153
|
-
return undefined;
|
|
154
|
-
}
|
|
155
|
-
function isPromiseLike(value) {
|
|
156
|
-
return (typeof value === "object" &&
|
|
157
|
-
value !== null &&
|
|
158
|
-
"then" in value &&
|
|
159
|
-
typeof value.then === "function");
|
|
160
|
-
}
|
|
161
|
-
/**
|
|
162
|
-
* Compute overall dirty status from dirty map.
|
|
163
|
-
*/
|
|
164
|
-
function computeIsDirty(dirty) {
|
|
165
|
-
const keys = Object.keys(dirty);
|
|
166
|
-
for (const key of keys) {
|
|
167
|
-
if (hasTruthyBooleanValue(dirty[key])) {
|
|
168
|
-
return true;
|
|
169
|
-
}
|
|
170
|
-
}
|
|
171
|
-
return false;
|
|
172
|
-
}
|
|
173
|
-
/**
|
|
174
|
-
* Normalize per-field boolean map value to an array.
|
|
175
|
-
*/
|
|
176
|
-
function normalizeBooleanArray(value, size, defaultValue) {
|
|
177
|
-
if (Array.isArray(value)) {
|
|
178
|
-
const next = value.slice(0, size);
|
|
179
|
-
while (next.length < size) {
|
|
180
|
-
next.push(defaultValue);
|
|
181
|
-
}
|
|
182
|
-
return next;
|
|
183
|
-
}
|
|
184
|
-
if (value === true || value === false) {
|
|
185
|
-
return Array.from({ length: size }, () => value);
|
|
186
|
-
}
|
|
187
|
-
return Array.from({ length: size }, () => defaultValue);
|
|
188
|
-
}
|
|
189
|
-
/**
|
|
190
|
-
* Normalize per-field error map value to an array.
|
|
191
|
-
*/
|
|
192
|
-
function normalizeErrorArray(value, size) {
|
|
193
|
-
if (Array.isArray(value)) {
|
|
194
|
-
const next = value.slice(0, size);
|
|
195
|
-
while (next.length < size) {
|
|
196
|
-
next.push(undefined);
|
|
197
|
-
}
|
|
198
|
-
return next;
|
|
199
|
-
}
|
|
200
|
-
return Array.from({ length: size }, () => undefined);
|
|
201
|
-
}
|
|
202
|
-
function removeAtIndex(value, index) {
|
|
203
|
-
const next = [...value];
|
|
204
|
-
next.splice(index, 1);
|
|
205
|
-
return next;
|
|
206
|
-
}
|
|
207
|
-
function moveIndex(value, from, to) {
|
|
208
|
-
const next = [...value];
|
|
209
|
-
const [moved] = next.splice(from, 1);
|
|
210
|
-
if (moved === undefined) {
|
|
211
|
-
return next;
|
|
212
|
-
}
|
|
213
|
-
next.splice(to, 0, moved);
|
|
214
|
-
return next;
|
|
215
|
-
}
|
|
216
|
-
/**
|
|
217
|
-
* Merge per-step errors into global error state.
|
|
218
|
-
*/
|
|
219
|
-
function mergeStepErrors(prevErrors, stepFields, stepErrors) {
|
|
220
|
-
const nextErrors = { ...prevErrors };
|
|
221
|
-
for (const field of stepFields) {
|
|
222
|
-
nextErrors[field] = undefined;
|
|
223
|
-
}
|
|
224
|
-
const stepErrorKeys = Object.keys(stepErrors);
|
|
225
|
-
for (const field of stepErrorKeys) {
|
|
226
|
-
nextErrors[field] = stepErrors[field];
|
|
227
|
-
}
|
|
228
|
-
return nextErrors;
|
|
229
|
-
}
|
|
230
|
-
/**
|
|
231
|
-
* Pick a subset of validation errors by field list.
|
|
232
|
-
*/
|
|
233
|
-
function pickValidationFields(errors, fields) {
|
|
234
|
-
const selected = {};
|
|
235
|
-
for (const field of fields) {
|
|
236
|
-
const value = errors[field];
|
|
237
|
-
if (value !== undefined) {
|
|
238
|
-
selected[field] = value;
|
|
239
|
-
}
|
|
240
|
-
}
|
|
241
|
-
return selected;
|
|
242
|
-
}
|
|
243
|
-
function clearValidationFields(errors, fields) {
|
|
244
|
-
if (fields.length === 0) {
|
|
245
|
-
return errors;
|
|
246
|
-
}
|
|
247
|
-
const nextErrors = { ...errors };
|
|
248
|
-
for (const field of fields) {
|
|
249
|
-
nextErrors[field] = undefined;
|
|
250
|
-
}
|
|
251
|
-
return nextErrors;
|
|
252
|
-
}
|
|
253
|
-
function resolveFieldOverride(field, formFlag, fieldOverrides) {
|
|
254
|
-
const override = fieldOverrides[field];
|
|
255
|
-
if (override === undefined) {
|
|
256
|
-
return formFlag;
|
|
257
|
-
}
|
|
258
|
-
return override;
|
|
259
|
-
}
|
|
260
|
-
function setFieldOverride(fieldOverrides, field, value) {
|
|
261
|
-
const next = { ...fieldOverrides };
|
|
262
|
-
if (value === undefined) {
|
|
263
|
-
delete next[field];
|
|
264
|
-
}
|
|
265
|
-
else {
|
|
266
|
-
next[field] = value;
|
|
267
|
-
}
|
|
268
|
-
return next;
|
|
269
|
-
}
|
|
270
|
-
function markFieldsTouched(prevTouched, values, fields) {
|
|
271
|
-
const nextTouched = {
|
|
272
|
-
...prevTouched,
|
|
273
|
-
};
|
|
274
|
-
for (const field of fields) {
|
|
275
|
-
const value = values[field];
|
|
276
|
-
if (Array.isArray(value)) {
|
|
277
|
-
nextTouched[field] = value.map(() => true);
|
|
278
|
-
}
|
|
279
|
-
else {
|
|
280
|
-
nextTouched[field] = true;
|
|
281
|
-
}
|
|
282
|
-
}
|
|
283
|
-
return nextTouched;
|
|
284
|
-
}
|
|
285
|
-
function getArrayFieldValues(values, field) {
|
|
286
|
-
const raw = values[field];
|
|
287
|
-
if (!Array.isArray(raw)) {
|
|
288
|
-
return [];
|
|
289
|
-
}
|
|
290
|
-
return [...raw];
|
|
291
|
-
}
|
|
292
|
-
function nextFieldArrayKey(field, counterRef) {
|
|
293
|
-
const next = counterRef.current;
|
|
294
|
-
counterRef.current += 1;
|
|
295
|
-
return `${field}_${next}`;
|
|
296
|
-
}
|
|
10
|
+
import { createFieldArrayApi } from "./internal/arrayState.js";
|
|
11
|
+
import { createFieldBindings } from "./internal/bindings.js";
|
|
12
|
+
import { cloneInitialValues, computeIsDirty, createFormFlagActions, createFormStateAccessors, createInitialState, } from "./internal/state.js";
|
|
13
|
+
import { createResetAction, createSubmitAction } from "./internal/submit.js";
|
|
14
|
+
import { clampStepIndex, createWizardActions, runWizardStepValidation } from "./internal/wizard.js";
|
|
15
|
+
import { DEFAULT_ASYNC_DEBOUNCE_MS, createDebouncedAsyncValidator, isValidationClean, mergeValidationErrors, } from "./validation.js";
|
|
297
16
|
/**
|
|
298
17
|
* Form management hook for Rezi widgets.
|
|
299
18
|
*/
|
|
300
19
|
export function useForm(ctx, options) {
|
|
301
|
-
// Store form state using widget's useState hook
|
|
302
20
|
const [state, setState] = ctx.useState(() => createInitialState(options));
|
|
303
21
|
const stateRef = ctx.useRef(state);
|
|
304
22
|
stateRef.current = state;
|
|
305
|
-
// Store initial values in a ref for dirty comparison
|
|
306
23
|
const initialValuesRef = ctx.useRef(cloneInitialValues(options.initialValues));
|
|
307
|
-
// Store async validator reference
|
|
308
24
|
const asyncValidatorRef = ctx.useRef(undefined);
|
|
309
|
-
// Ref to safely pass values from setState callback to async validation
|
|
310
25
|
const pendingAsyncValuesRef = ctx.useRef(null);
|
|
311
26
|
const submittingRef = ctx.useRef(false);
|
|
27
|
+
const submitAttemptRef = ctx.useRef(0);
|
|
312
28
|
const validateRef = ctx.useRef(options.validate);
|
|
313
29
|
validateRef.current = options.validate;
|
|
314
30
|
const nonTextBindingWarningsRef = ctx.useRef(new Set());
|
|
315
|
-
// Stable key tracking for array fields
|
|
316
31
|
const fieldArrayKeysRef = ctx.useRef({});
|
|
317
32
|
const fieldArrayKeyCounterRef = ctx.useRef(0);
|
|
318
33
|
const updateFormState = (nextState) => {
|
|
@@ -330,128 +45,21 @@ export function useForm(ctx, options) {
|
|
|
330
45
|
const currentStep = hasWizard ? clampStepIndex(state.currentStep, stepCount) : 0;
|
|
331
46
|
const isFirstStep = !hasWizard || currentStep === 0;
|
|
332
47
|
const isLastStep = !hasWizard || currentStep === stepCount - 1;
|
|
333
|
-
const isFieldDisabledInternal
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
};
|
|
349
|
-
const runSyncValidationFiltered = (values, source = stateRef.current) => filterDisabledValidationErrors(runSyncValidation(values, validateRef.current), source);
|
|
350
|
-
const runAsyncValidationFiltered = async (values, source = stateRef.current) => filterDisabledValidationErrors(await runAsyncValidation(values, options.validateAsync), source);
|
|
351
|
-
const getStep = (stepIndex) => {
|
|
352
|
-
if (!hasWizard) {
|
|
353
|
-
return undefined;
|
|
354
|
-
}
|
|
355
|
-
const resolvedStep = clampStepIndex(stepIndex, stepCount);
|
|
356
|
-
return wizardSteps[resolvedStep];
|
|
357
|
-
};
|
|
358
|
-
const getStepFields = (step, values) => {
|
|
359
|
-
if (step?.fields && step.fields.length > 0) {
|
|
360
|
-
return [...step.fields];
|
|
361
|
-
}
|
|
362
|
-
return Object.keys(values);
|
|
363
|
-
};
|
|
364
|
-
const getWizardTransitionSteps = (fromStep, toStepExclusive, values) => {
|
|
365
|
-
const steps = [];
|
|
366
|
-
for (let stepIndex = fromStep; stepIndex < toStepExclusive; stepIndex++) {
|
|
367
|
-
steps.push(Object.freeze({
|
|
368
|
-
stepIndex,
|
|
369
|
-
fields: getStepFields(getStep(stepIndex), values),
|
|
370
|
-
}));
|
|
371
|
-
}
|
|
372
|
-
return Object.freeze(steps);
|
|
373
|
-
};
|
|
374
|
-
const runWizardStepValidation = (values, stepIndex, source = stateRef.current) => {
|
|
375
|
-
const step = getStep(stepIndex);
|
|
376
|
-
if (!step) {
|
|
377
|
-
return {};
|
|
378
|
-
}
|
|
379
|
-
const stepFields = getStepFields(step, values);
|
|
380
|
-
const syncStepErrors = pickValidationFields(runSyncValidationFiltered(values, source), stepFields);
|
|
381
|
-
if (!step.validate) {
|
|
382
|
-
return mergeValidationErrors(syncStepErrors, pickValidationFields(filterDisabledValidationErrors(source.errors, source), stepFields));
|
|
383
|
-
}
|
|
384
|
-
const customStepErrors = filterDisabledValidationErrors(pickValidationFields(step.validate(values), stepFields), source);
|
|
385
|
-
return mergeValidationErrors(mergeValidationErrors(syncStepErrors, customStepErrors), pickValidationFields(filterDisabledValidationErrors(source.errors, source), stepFields));
|
|
386
|
-
};
|
|
387
|
-
const resolveWizardTransition = (values, transitionSteps, source, asyncErrors) => {
|
|
388
|
-
let mergedErrors = source.errors;
|
|
389
|
-
for (const transitionStep of transitionSteps) {
|
|
390
|
-
const baseStepErrors = runWizardStepValidation(values, transitionStep.stepIndex, {
|
|
391
|
-
...source,
|
|
392
|
-
errors: mergedErrors,
|
|
393
|
-
});
|
|
394
|
-
const stepErrors = asyncErrors === undefined
|
|
395
|
-
? baseStepErrors
|
|
396
|
-
: mergeValidationErrors(baseStepErrors, pickValidationFields(asyncErrors, transitionStep.fields));
|
|
397
|
-
if (!isValidationClean(stepErrors)) {
|
|
398
|
-
return Object.freeze({
|
|
399
|
-
blockedFields: transitionStep.fields,
|
|
400
|
-
mergedErrors: mergeStepErrors(mergedErrors, transitionStep.fields, stepErrors),
|
|
401
|
-
touched: markFieldsTouched(source.touched, values, transitionStep.fields),
|
|
402
|
-
});
|
|
403
|
-
}
|
|
404
|
-
mergedErrors = clearValidationFields(mergedErrors, transitionStep.fields);
|
|
405
|
-
}
|
|
406
|
-
return null;
|
|
407
|
-
};
|
|
408
|
-
const warnUnsupportedTextBinding = (field) => {
|
|
409
|
-
if (!DEV_MODE)
|
|
410
|
-
return;
|
|
411
|
-
const fieldKey = String(field);
|
|
412
|
-
if (nonTextBindingWarningsRef.current.has(fieldKey))
|
|
413
|
-
return;
|
|
414
|
-
nonTextBindingWarningsRef.current.add(fieldKey);
|
|
415
|
-
warnDev(`[rezi] useForm: bind/field only support string-compatible fields; "${fieldKey}" is not safely bindable to ui.input().`);
|
|
416
|
-
};
|
|
417
|
-
const canBindFieldAsText = (field, values) => isTextBindableValue(values[field]) && isTextBindableValue(initialValuesRef.current[field]);
|
|
418
|
-
const ensureFieldArrayKeys = (field, length) => {
|
|
419
|
-
const existing = [...(fieldArrayKeysRef.current[field] ?? [])];
|
|
420
|
-
if (existing.length > length) {
|
|
421
|
-
existing.length = length;
|
|
422
|
-
}
|
|
423
|
-
while (existing.length < length) {
|
|
424
|
-
existing.push(nextFieldArrayKey(String(field), fieldArrayKeyCounterRef));
|
|
425
|
-
}
|
|
426
|
-
fieldArrayKeysRef.current[field] = existing;
|
|
427
|
-
return existing;
|
|
428
|
-
};
|
|
429
|
-
const appendFieldArrayKey = (field) => {
|
|
430
|
-
const existing = [...(fieldArrayKeysRef.current[field] ?? [])];
|
|
431
|
-
existing.push(nextFieldArrayKey(String(field), fieldArrayKeyCounterRef));
|
|
432
|
-
fieldArrayKeysRef.current[field] = existing;
|
|
433
|
-
};
|
|
434
|
-
const removeFieldArrayKey = (field, index) => {
|
|
435
|
-
const existing = [...(fieldArrayKeysRef.current[field] ?? [])];
|
|
436
|
-
if (index < 0 || index >= existing.length) {
|
|
437
|
-
return;
|
|
438
|
-
}
|
|
439
|
-
existing.splice(index, 1);
|
|
440
|
-
fieldArrayKeysRef.current[field] = existing;
|
|
441
|
-
};
|
|
442
|
-
const moveFieldArrayKey = (field, from, to) => {
|
|
443
|
-
const existing = [...(fieldArrayKeysRef.current[field] ?? [])];
|
|
444
|
-
if (from < 0 || to < 0 || from >= existing.length || to >= existing.length || from === to) {
|
|
445
|
-
return;
|
|
446
|
-
}
|
|
447
|
-
const [moved] = existing.splice(from, 1);
|
|
448
|
-
if (moved === undefined) {
|
|
449
|
-
return;
|
|
450
|
-
}
|
|
451
|
-
existing.splice(to, 0, moved);
|
|
452
|
-
fieldArrayKeysRef.current[field] = existing;
|
|
453
|
-
};
|
|
454
|
-
// Initialize or update async validator when options change
|
|
48
|
+
const { isFieldDisabledInternal, isFieldReadOnlyInternal, isFieldEditableInternal, filterDisabledValidationErrors, runSyncValidationFiltered, runAsyncValidationFiltered, warnUnsupportedTextBinding, canBindFieldAsText, } = createFormStateAccessors({
|
|
49
|
+
stateRef,
|
|
50
|
+
initialValuesRef,
|
|
51
|
+
validateRef,
|
|
52
|
+
validateAsync: options.validateAsync,
|
|
53
|
+
nonTextBindingWarningsRef,
|
|
54
|
+
});
|
|
55
|
+
const runWizardStepValidationForState = (values, stepIndex, source = stateRef.current) => runWizardStepValidation({
|
|
56
|
+
values,
|
|
57
|
+
stepIndex,
|
|
58
|
+
wizardSteps,
|
|
59
|
+
source,
|
|
60
|
+
runSyncValidationFiltered,
|
|
61
|
+
filterDisabledValidationErrors,
|
|
62
|
+
});
|
|
455
63
|
ctx.useEffect(() => {
|
|
456
64
|
if (options.validateAsync) {
|
|
457
65
|
asyncValidatorRef.current = createDebouncedAsyncValidator(options.validateAsync, options.validateAsyncDebounce ?? DEFAULT_ASYNC_DEBOUNCE_MS, (asyncErrors) => {
|
|
@@ -466,709 +74,74 @@ export function useForm(ctx, options) {
|
|
|
466
74
|
}
|
|
467
75
|
return undefined;
|
|
468
76
|
}, [options.validateAsync, options.validateAsyncDebounce]);
|
|
469
|
-
// Compute derived state
|
|
470
77
|
const isValid = isValidationClean(state.errors);
|
|
471
78
|
const isDirty = computeIsDirty(state.dirty);
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
// Trigger async validation if configured
|
|
539
|
-
const asyncValues = pendingAsyncValuesRef.current;
|
|
540
|
-
pendingAsyncValuesRef.current = null; // Clear to avoid stale data
|
|
541
|
-
if (options.validateOnChange && asyncValidatorRef.current && asyncValues) {
|
|
542
|
-
asyncValidatorRef.current.run(asyncValues);
|
|
543
|
-
}
|
|
544
|
-
};
|
|
545
|
-
/**
|
|
546
|
-
* Set a specific field's error.
|
|
547
|
-
*/
|
|
548
|
-
const setFieldError = (field, error) => {
|
|
549
|
-
updateFormState((prev) => ({
|
|
550
|
-
...prev,
|
|
551
|
-
errors: {
|
|
552
|
-
...prev.errors,
|
|
553
|
-
[field]: error,
|
|
554
|
-
},
|
|
555
|
-
}));
|
|
556
|
-
};
|
|
557
|
-
/**
|
|
558
|
-
* Mark a field as touched.
|
|
559
|
-
*/
|
|
560
|
-
const setFieldTouched = (field, touched) => {
|
|
561
|
-
updateFormState((prev) => ({
|
|
562
|
-
...prev,
|
|
563
|
-
touched: {
|
|
564
|
-
...prev.touched,
|
|
565
|
-
[field]: touched,
|
|
566
|
-
},
|
|
567
|
-
}));
|
|
568
|
-
};
|
|
569
|
-
/**
|
|
570
|
-
* Handle change for a specific field.
|
|
571
|
-
*/
|
|
572
|
-
const handleChange = (field) => (value) => {
|
|
573
|
-
setFieldValue(field, value);
|
|
574
|
-
};
|
|
575
|
-
/**
|
|
576
|
-
* Handle blur for a specific field.
|
|
577
|
-
*/
|
|
578
|
-
const handleBlur = (field) => () => {
|
|
579
|
-
const snapshot = stateRef.current;
|
|
580
|
-
if (isFieldDisabledInternal(field, snapshot)) {
|
|
581
|
-
return;
|
|
582
|
-
}
|
|
583
|
-
// Run validation on blur if enabled (default: true)
|
|
584
|
-
const validateOnBlur = options.validateOnBlur ?? true;
|
|
585
|
-
if (!validateOnBlur) {
|
|
586
|
-
setFieldTouched(field, true);
|
|
587
|
-
return;
|
|
588
|
-
}
|
|
589
|
-
const errors = runSyncValidationFiltered(snapshot.values, snapshot);
|
|
590
|
-
updateFormState((prev) => ({
|
|
591
|
-
...prev,
|
|
592
|
-
touched: {
|
|
593
|
-
...prev.touched,
|
|
594
|
-
[field]: true,
|
|
595
|
-
},
|
|
596
|
-
errors,
|
|
597
|
-
}));
|
|
598
|
-
if (asyncValidatorRef.current) {
|
|
599
|
-
asyncValidatorRef.current.run(snapshot.values);
|
|
600
|
-
}
|
|
601
|
-
};
|
|
602
|
-
const bind = (field, options) => {
|
|
603
|
-
const snapshot = stateRef.current;
|
|
604
|
-
const disabled = isFieldDisabledInternal(field, snapshot);
|
|
605
|
-
const readOnly = !disabled && isFieldReadOnlyInternal(field, snapshot);
|
|
606
|
-
const textBindable = canBindFieldAsText(field, snapshot.values);
|
|
607
|
-
if (!textBindable) {
|
|
608
|
-
warnUnsupportedTextBinding(field);
|
|
609
|
-
}
|
|
610
|
-
return {
|
|
611
|
-
id: options?.id ?? ctx.id(String(field)),
|
|
612
|
-
value: toInputValue(snapshot.values[field]),
|
|
613
|
-
disabled,
|
|
614
|
-
readOnly,
|
|
615
|
-
onInput: (value) => {
|
|
616
|
-
if (!textBindable)
|
|
617
|
-
return;
|
|
618
|
-
setFieldValue(field, value);
|
|
619
|
-
},
|
|
620
|
-
onBlur: handleBlur(field),
|
|
621
|
-
};
|
|
622
|
-
};
|
|
623
|
-
const field = (fieldName, options) => {
|
|
624
|
-
const { key, label, required, hint, error, ...inputOverrides } = options ?? {};
|
|
625
|
-
const inputBinding = bind(fieldName, inputOverrides.id === undefined ? undefined : { id: inputOverrides.id });
|
|
626
|
-
const touched = hasTruthyBooleanValue(state.touched[fieldName]);
|
|
627
|
-
const derivedError = touched ? toFieldErrorString(state.errors[fieldName]) : undefined;
|
|
628
|
-
const resolvedError = error ?? derivedError;
|
|
629
|
-
return ui.field({
|
|
630
|
-
...(key !== undefined ? { key } : {}),
|
|
631
|
-
label: label ?? String(fieldName),
|
|
632
|
-
...(required !== undefined ? { required } : {}),
|
|
633
|
-
...(hint !== undefined ? { hint } : {}),
|
|
634
|
-
...(resolvedError !== undefined ? { error: resolvedError } : {}),
|
|
635
|
-
children: ui.input({
|
|
636
|
-
...inputBinding,
|
|
637
|
-
...inputOverrides,
|
|
638
|
-
}),
|
|
639
|
-
});
|
|
640
|
-
};
|
|
641
|
-
/**
|
|
642
|
-
* Set or clear form-level disabled state.
|
|
643
|
-
*/
|
|
644
|
-
const setDisabled = (disabled) => {
|
|
645
|
-
updateFormState((prev) => {
|
|
646
|
-
const nextState = {
|
|
647
|
-
...prev,
|
|
648
|
-
disabled,
|
|
649
|
-
};
|
|
650
|
-
return {
|
|
651
|
-
...nextState,
|
|
652
|
-
errors: filterDisabledValidationErrors(prev.errors, nextState),
|
|
653
|
-
};
|
|
654
|
-
});
|
|
655
|
-
};
|
|
656
|
-
/**
|
|
657
|
-
* Set or clear form-level readOnly state.
|
|
658
|
-
*/
|
|
659
|
-
const setReadOnly = (readOnly) => {
|
|
660
|
-
updateFormState((prev) => ({
|
|
661
|
-
...prev,
|
|
662
|
-
readOnly,
|
|
663
|
-
}));
|
|
664
|
-
};
|
|
665
|
-
/**
|
|
666
|
-
* Set or clear field-level disabled override.
|
|
667
|
-
*/
|
|
668
|
-
const setFieldDisabled = (field, disabled) => {
|
|
669
|
-
updateFormState((prev) => {
|
|
670
|
-
const nextState = {
|
|
671
|
-
...prev,
|
|
672
|
-
fieldDisabled: setFieldOverride(prev.fieldDisabled, field, disabled),
|
|
673
|
-
};
|
|
674
|
-
return {
|
|
675
|
-
...nextState,
|
|
676
|
-
errors: filterDisabledValidationErrors(prev.errors, nextState),
|
|
677
|
-
};
|
|
678
|
-
});
|
|
679
|
-
};
|
|
680
|
-
/**
|
|
681
|
-
* Set or clear field-level readOnly override.
|
|
682
|
-
*/
|
|
683
|
-
const setFieldReadOnly = (field, readOnly) => {
|
|
684
|
-
updateFormState((prev) => ({
|
|
685
|
-
...prev,
|
|
686
|
-
fieldReadOnly: setFieldOverride(prev.fieldReadOnly, field, readOnly),
|
|
687
|
-
}));
|
|
688
|
-
};
|
|
689
|
-
/**
|
|
690
|
-
* Dynamic array helper for array-valued fields.
|
|
691
|
-
*/
|
|
692
|
-
const useFieldArray = (field) => {
|
|
693
|
-
const fieldKey = field;
|
|
694
|
-
const values = getArrayFieldValues(state.values, field);
|
|
695
|
-
const keys = ensureFieldArrayKeys(fieldKey, values.length);
|
|
696
|
-
const append = (item) => {
|
|
697
|
-
pendingAsyncValuesRef.current = null;
|
|
698
|
-
updateFormState((prev) => {
|
|
699
|
-
if (prev.isSubmitting || !isFieldEditableInternal(fieldKey, prev)) {
|
|
700
|
-
return prev;
|
|
701
|
-
}
|
|
702
|
-
const currentValues = getArrayFieldValues(prev.values, field);
|
|
703
|
-
const nextValuesArray = [...currentValues, item];
|
|
704
|
-
const nextValues = {
|
|
705
|
-
...prev.values,
|
|
706
|
-
[field]: nextValuesArray,
|
|
707
|
-
};
|
|
708
|
-
pendingAsyncValuesRef.current = nextValues;
|
|
709
|
-
const nextTouched = [
|
|
710
|
-
...normalizeBooleanArray(prev.touched[fieldKey], currentValues.length, false),
|
|
711
|
-
false,
|
|
712
|
-
];
|
|
713
|
-
const previousDirty = normalizeBooleanArray(prev.dirty[fieldKey], currentValues.length, false);
|
|
714
|
-
const initialArray = Array.isArray(initialValuesRef.current[fieldKey])
|
|
715
|
-
? initialValuesRef.current[fieldKey]
|
|
716
|
-
: [];
|
|
717
|
-
const appendedDirty = !Object.is(item, initialArray[nextValuesArray.length - 1]);
|
|
718
|
-
const nextDirty = [...previousDirty, appendedDirty];
|
|
719
|
-
let nextErrors = prev.errors;
|
|
720
|
-
if (options.validateOnChange) {
|
|
721
|
-
nextErrors = runSyncValidationFiltered(nextValues, prev);
|
|
722
|
-
}
|
|
723
|
-
else {
|
|
724
|
-
const currentErrors = normalizeErrorArray(prev.errors[fieldKey], currentValues.length);
|
|
725
|
-
nextErrors = {
|
|
726
|
-
...prev.errors,
|
|
727
|
-
[fieldKey]: [...currentErrors, undefined],
|
|
728
|
-
};
|
|
729
|
-
}
|
|
730
|
-
appendFieldArrayKey(fieldKey);
|
|
731
|
-
return {
|
|
732
|
-
...prev,
|
|
733
|
-
values: nextValues,
|
|
734
|
-
errors: nextErrors,
|
|
735
|
-
submitError: undefined,
|
|
736
|
-
touched: {
|
|
737
|
-
...prev.touched,
|
|
738
|
-
[fieldKey]: nextTouched,
|
|
739
|
-
},
|
|
740
|
-
dirty: {
|
|
741
|
-
...prev.dirty,
|
|
742
|
-
[fieldKey]: nextDirty,
|
|
743
|
-
},
|
|
744
|
-
};
|
|
745
|
-
});
|
|
746
|
-
const asyncValues = pendingAsyncValuesRef.current;
|
|
747
|
-
pendingAsyncValuesRef.current = null;
|
|
748
|
-
if (options.validateOnChange && asyncValidatorRef.current && asyncValues) {
|
|
749
|
-
asyncValidatorRef.current.run(asyncValues);
|
|
750
|
-
}
|
|
751
|
-
};
|
|
752
|
-
const remove = (index) => {
|
|
753
|
-
pendingAsyncValuesRef.current = null;
|
|
754
|
-
updateFormState((prev) => {
|
|
755
|
-
if (prev.isSubmitting || !isFieldEditableInternal(fieldKey, prev)) {
|
|
756
|
-
return prev;
|
|
757
|
-
}
|
|
758
|
-
const currentValues = getArrayFieldValues(prev.values, field);
|
|
759
|
-
if (index < 0 || index >= currentValues.length) {
|
|
760
|
-
return prev;
|
|
761
|
-
}
|
|
762
|
-
const nextValuesArray = removeAtIndex(currentValues, index);
|
|
763
|
-
const nextValues = {
|
|
764
|
-
...prev.values,
|
|
765
|
-
[field]: nextValuesArray,
|
|
766
|
-
};
|
|
767
|
-
pendingAsyncValuesRef.current = nextValues;
|
|
768
|
-
const nextTouched = removeAtIndex(normalizeBooleanArray(prev.touched[fieldKey], currentValues.length, false), index);
|
|
769
|
-
const nextDirty = removeAtIndex(normalizeBooleanArray(prev.dirty[fieldKey], currentValues.length, false), index);
|
|
770
|
-
let nextErrors = prev.errors;
|
|
771
|
-
if (options.validateOnChange) {
|
|
772
|
-
nextErrors = runSyncValidationFiltered(nextValues, prev);
|
|
773
|
-
}
|
|
774
|
-
else {
|
|
775
|
-
const nextErrorArray = removeAtIndex(normalizeErrorArray(prev.errors[fieldKey], currentValues.length), index);
|
|
776
|
-
nextErrors = {
|
|
777
|
-
...prev.errors,
|
|
778
|
-
[fieldKey]: nextErrorArray,
|
|
779
|
-
};
|
|
780
|
-
}
|
|
781
|
-
removeFieldArrayKey(fieldKey, index);
|
|
782
|
-
return {
|
|
783
|
-
...prev,
|
|
784
|
-
values: nextValues,
|
|
785
|
-
errors: nextErrors,
|
|
786
|
-
submitError: undefined,
|
|
787
|
-
touched: {
|
|
788
|
-
...prev.touched,
|
|
789
|
-
[fieldKey]: nextTouched,
|
|
790
|
-
},
|
|
791
|
-
dirty: {
|
|
792
|
-
...prev.dirty,
|
|
793
|
-
[fieldKey]: nextDirty,
|
|
794
|
-
},
|
|
795
|
-
};
|
|
796
|
-
});
|
|
797
|
-
const asyncValues = pendingAsyncValuesRef.current;
|
|
798
|
-
pendingAsyncValuesRef.current = null;
|
|
799
|
-
if (options.validateOnChange && asyncValidatorRef.current && asyncValues) {
|
|
800
|
-
asyncValidatorRef.current.run(asyncValues);
|
|
801
|
-
}
|
|
802
|
-
};
|
|
803
|
-
const move = (from, to) => {
|
|
804
|
-
pendingAsyncValuesRef.current = null;
|
|
805
|
-
updateFormState((prev) => {
|
|
806
|
-
if (prev.isSubmitting || !isFieldEditableInternal(fieldKey, prev)) {
|
|
807
|
-
return prev;
|
|
808
|
-
}
|
|
809
|
-
const currentValues = getArrayFieldValues(prev.values, field);
|
|
810
|
-
if (from < 0 ||
|
|
811
|
-
to < 0 ||
|
|
812
|
-
from >= currentValues.length ||
|
|
813
|
-
to >= currentValues.length ||
|
|
814
|
-
from === to) {
|
|
815
|
-
return prev;
|
|
816
|
-
}
|
|
817
|
-
const nextValuesArray = moveIndex(currentValues, from, to);
|
|
818
|
-
const nextValues = {
|
|
819
|
-
...prev.values,
|
|
820
|
-
[field]: nextValuesArray,
|
|
821
|
-
};
|
|
822
|
-
pendingAsyncValuesRef.current = nextValues;
|
|
823
|
-
const nextTouched = moveIndex(normalizeBooleanArray(prev.touched[fieldKey], currentValues.length, false), from, to);
|
|
824
|
-
const nextDirty = moveIndex(normalizeBooleanArray(prev.dirty[fieldKey], currentValues.length, false), from, to);
|
|
825
|
-
const nextErrorArray = moveIndex(normalizeErrorArray(prev.errors[fieldKey], currentValues.length), from, to);
|
|
826
|
-
let nextErrors = {
|
|
827
|
-
...prev.errors,
|
|
828
|
-
[fieldKey]: nextErrorArray,
|
|
829
|
-
};
|
|
830
|
-
if (options.validateOnChange) {
|
|
831
|
-
nextErrors = runSyncValidationFiltered(nextValues, prev);
|
|
832
|
-
}
|
|
833
|
-
moveFieldArrayKey(fieldKey, from, to);
|
|
834
|
-
return {
|
|
835
|
-
...prev,
|
|
836
|
-
values: nextValues,
|
|
837
|
-
errors: nextErrors,
|
|
838
|
-
submitError: undefined,
|
|
839
|
-
touched: {
|
|
840
|
-
...prev.touched,
|
|
841
|
-
[fieldKey]: nextTouched,
|
|
842
|
-
},
|
|
843
|
-
dirty: {
|
|
844
|
-
...prev.dirty,
|
|
845
|
-
[fieldKey]: nextDirty,
|
|
846
|
-
},
|
|
847
|
-
};
|
|
848
|
-
});
|
|
849
|
-
const asyncValues = pendingAsyncValuesRef.current;
|
|
850
|
-
pendingAsyncValuesRef.current = null;
|
|
851
|
-
if (options.validateOnChange && asyncValidatorRef.current && asyncValues) {
|
|
852
|
-
asyncValidatorRef.current.run(asyncValues);
|
|
853
|
-
}
|
|
854
|
-
};
|
|
855
|
-
return Object.freeze({
|
|
856
|
-
values,
|
|
857
|
-
keys,
|
|
858
|
-
append,
|
|
859
|
-
remove,
|
|
860
|
-
move,
|
|
861
|
-
});
|
|
862
|
-
};
|
|
863
|
-
/**
|
|
864
|
-
* Advance wizard by one step when current step validates cleanly.
|
|
865
|
-
*/
|
|
866
|
-
const nextStep = () => {
|
|
867
|
-
if (!hasWizard) {
|
|
868
|
-
return true;
|
|
869
|
-
}
|
|
870
|
-
const snapshot = stateRef.current;
|
|
871
|
-
const currentStepIndex = clampStepIndex(snapshot.currentStep, stepCount);
|
|
872
|
-
if (currentStepIndex >= stepCount - 1) {
|
|
873
|
-
return true;
|
|
874
|
-
}
|
|
875
|
-
const targetStep = clampStepIndex(currentStepIndex + 1, stepCount);
|
|
876
|
-
const transitionSteps = getWizardTransitionSteps(currentStepIndex, targetStep, snapshot.values);
|
|
877
|
-
const blocked = resolveWizardTransition(snapshot.values, transitionSteps, snapshot);
|
|
878
|
-
if (blocked) {
|
|
879
|
-
updateFormState((prev) => ({
|
|
880
|
-
...prev,
|
|
881
|
-
touched: blocked.touched,
|
|
882
|
-
errors: blocked.mergedErrors,
|
|
883
|
-
}));
|
|
884
|
-
return false;
|
|
885
|
-
}
|
|
886
|
-
if (!options.validateAsync) {
|
|
887
|
-
const traversedFields = transitionSteps.flatMap((step) => step.fields);
|
|
888
|
-
updateFormState((prev) => ({
|
|
889
|
-
...prev,
|
|
890
|
-
currentStep: targetStep,
|
|
891
|
-
errors: clearValidationFields(prev.errors, traversedFields),
|
|
892
|
-
}));
|
|
893
|
-
return true;
|
|
894
|
-
}
|
|
895
|
-
void (async () => {
|
|
896
|
-
let asyncErrors;
|
|
897
|
-
try {
|
|
898
|
-
asyncErrors = await runAsyncValidationFiltered(snapshot.values, snapshot);
|
|
899
|
-
}
|
|
900
|
-
catch (error) {
|
|
901
|
-
if (stateRef.current.values !== snapshot.values ||
|
|
902
|
-
clampStepIndex(stateRef.current.currentStep, stepCount) !== currentStepIndex) {
|
|
903
|
-
return;
|
|
904
|
-
}
|
|
905
|
-
updateFormState((prev) => ({
|
|
906
|
-
...prev,
|
|
907
|
-
submitError: error,
|
|
908
|
-
}));
|
|
909
|
-
return;
|
|
910
|
-
}
|
|
911
|
-
if (stateRef.current.values !== snapshot.values ||
|
|
912
|
-
clampStepIndex(stateRef.current.currentStep, stepCount) !== currentStepIndex) {
|
|
913
|
-
return;
|
|
914
|
-
}
|
|
915
|
-
const asyncBlocked = resolveWizardTransition(snapshot.values, transitionSteps, stateRef.current, asyncErrors);
|
|
916
|
-
if (asyncBlocked) {
|
|
917
|
-
updateFormState((prev) => ({
|
|
918
|
-
...prev,
|
|
919
|
-
touched: asyncBlocked.touched,
|
|
920
|
-
errors: asyncBlocked.mergedErrors,
|
|
921
|
-
submitError: undefined,
|
|
922
|
-
}));
|
|
923
|
-
return;
|
|
924
|
-
}
|
|
925
|
-
const traversedFields = transitionSteps.flatMap((step) => step.fields);
|
|
926
|
-
updateFormState((prev) => ({
|
|
927
|
-
...prev,
|
|
928
|
-
currentStep: targetStep,
|
|
929
|
-
errors: clearValidationFields(prev.errors, traversedFields),
|
|
930
|
-
submitError: undefined,
|
|
931
|
-
}));
|
|
932
|
-
})();
|
|
933
|
-
return false;
|
|
934
|
-
};
|
|
935
|
-
/**
|
|
936
|
-
* Navigate to previous wizard step without validation.
|
|
937
|
-
*/
|
|
938
|
-
const previousStep = () => {
|
|
939
|
-
if (!hasWizard) {
|
|
940
|
-
return;
|
|
941
|
-
}
|
|
942
|
-
updateFormState((prev) => ({
|
|
943
|
-
...prev,
|
|
944
|
-
currentStep: clampStepIndex(prev.currentStep - 1, stepCount),
|
|
945
|
-
}));
|
|
946
|
-
};
|
|
947
|
-
/**
|
|
948
|
-
* Navigate to a specific wizard step with validation gates on forward moves.
|
|
949
|
-
*/
|
|
950
|
-
const goToStep = (stepIndex) => {
|
|
951
|
-
if (!hasWizard) {
|
|
952
|
-
return false;
|
|
953
|
-
}
|
|
954
|
-
const snapshot = stateRef.current;
|
|
955
|
-
const currentStepIndex = clampStepIndex(snapshot.currentStep, stepCount);
|
|
956
|
-
const targetStep = clampStepIndex(stepIndex, stepCount);
|
|
957
|
-
if (targetStep === currentStepIndex) {
|
|
958
|
-
return true;
|
|
959
|
-
}
|
|
960
|
-
if (targetStep < currentStepIndex) {
|
|
961
|
-
updateFormState((prev) => ({
|
|
962
|
-
...prev,
|
|
963
|
-
currentStep: targetStep,
|
|
964
|
-
}));
|
|
965
|
-
return true;
|
|
966
|
-
}
|
|
967
|
-
const transitionSteps = getWizardTransitionSteps(currentStepIndex, targetStep, snapshot.values);
|
|
968
|
-
const blocked = resolveWizardTransition(snapshot.values, transitionSteps, snapshot);
|
|
969
|
-
if (blocked) {
|
|
970
|
-
updateFormState((prev) => ({
|
|
971
|
-
...prev,
|
|
972
|
-
touched: blocked.touched,
|
|
973
|
-
errors: blocked.mergedErrors,
|
|
974
|
-
}));
|
|
975
|
-
return false;
|
|
976
|
-
}
|
|
977
|
-
if (!options.validateAsync) {
|
|
978
|
-
const traversedFields = transitionSteps.flatMap((step) => step.fields);
|
|
979
|
-
updateFormState((prev) => ({
|
|
980
|
-
...prev,
|
|
981
|
-
currentStep: targetStep,
|
|
982
|
-
errors: clearValidationFields(prev.errors, traversedFields),
|
|
983
|
-
}));
|
|
984
|
-
return true;
|
|
985
|
-
}
|
|
986
|
-
void (async () => {
|
|
987
|
-
let asyncErrors;
|
|
988
|
-
try {
|
|
989
|
-
asyncErrors = await runAsyncValidationFiltered(snapshot.values, snapshot);
|
|
990
|
-
}
|
|
991
|
-
catch (error) {
|
|
992
|
-
if (stateRef.current.values !== snapshot.values ||
|
|
993
|
-
clampStepIndex(stateRef.current.currentStep, stepCount) !== currentStepIndex) {
|
|
994
|
-
return;
|
|
995
|
-
}
|
|
996
|
-
updateFormState((prev) => ({
|
|
997
|
-
...prev,
|
|
998
|
-
submitError: error,
|
|
999
|
-
}));
|
|
1000
|
-
return;
|
|
1001
|
-
}
|
|
1002
|
-
if (stateRef.current.values !== snapshot.values ||
|
|
1003
|
-
clampStepIndex(stateRef.current.currentStep, stepCount) !== currentStepIndex) {
|
|
1004
|
-
return;
|
|
1005
|
-
}
|
|
1006
|
-
const asyncBlocked = resolveWizardTransition(snapshot.values, transitionSteps, stateRef.current, asyncErrors);
|
|
1007
|
-
if (asyncBlocked) {
|
|
1008
|
-
updateFormState((prev) => ({
|
|
1009
|
-
...prev,
|
|
1010
|
-
touched: asyncBlocked.touched,
|
|
1011
|
-
errors: asyncBlocked.mergedErrors,
|
|
1012
|
-
submitError: undefined,
|
|
1013
|
-
}));
|
|
1014
|
-
return;
|
|
1015
|
-
}
|
|
1016
|
-
const traversedFields = transitionSteps.flatMap((step) => step.fields);
|
|
1017
|
-
updateFormState((prev) => ({
|
|
1018
|
-
...prev,
|
|
1019
|
-
currentStep: targetStep,
|
|
1020
|
-
errors: clearValidationFields(prev.errors, traversedFields),
|
|
1021
|
-
submitError: undefined,
|
|
1022
|
-
}));
|
|
1023
|
-
})();
|
|
1024
|
-
return false;
|
|
1025
|
-
};
|
|
1026
|
-
/**
|
|
1027
|
-
* Reset form to initial state.
|
|
1028
|
-
*/
|
|
1029
|
-
const reset = () => {
|
|
1030
|
-
submittingRef.current = false;
|
|
1031
|
-
asyncValidatorRef.current?.cancel();
|
|
1032
|
-
fieldArrayKeysRef.current = {};
|
|
1033
|
-
updateFormState(createInitialState(options));
|
|
1034
|
-
};
|
|
1035
|
-
/**
|
|
1036
|
-
* Handle form submission.
|
|
1037
|
-
*/
|
|
1038
|
-
const handleSubmit = () => {
|
|
1039
|
-
const snapshot = stateRef.current;
|
|
1040
|
-
// Don't submit if disabled or already submitting
|
|
1041
|
-
if (snapshot.disabled || snapshot.isSubmitting || submittingRef.current) {
|
|
1042
|
-
return;
|
|
1043
|
-
}
|
|
1044
|
-
// In wizard mode, submit action advances steps until the last step.
|
|
1045
|
-
const submitStepIndex = hasWizard ? clampStepIndex(snapshot.currentStep, stepCount) : 0;
|
|
1046
|
-
const submitIsLastStep = !hasWizard || submitStepIndex === stepCount - 1;
|
|
1047
|
-
if (hasWizard && !submitIsLastStep) {
|
|
1048
|
-
nextStep();
|
|
1049
|
-
return;
|
|
1050
|
-
}
|
|
1051
|
-
asyncValidatorRef.current?.cancel();
|
|
1052
|
-
// Mark all fields as touched
|
|
1053
|
-
const allTouched = {};
|
|
1054
|
-
const keys = Object.keys(snapshot.values);
|
|
1055
|
-
for (const key of keys) {
|
|
1056
|
-
const value = snapshot.values[key];
|
|
1057
|
-
allTouched[key] = Array.isArray(value) ? value.map(() => true) : true;
|
|
1058
|
-
}
|
|
1059
|
-
// Run sync validation
|
|
1060
|
-
const syncErrors = runSyncValidationFiltered(snapshot.values, snapshot);
|
|
1061
|
-
// Update state with touched and sync errors
|
|
1062
|
-
updateFormState((prev) => ({
|
|
1063
|
-
...prev,
|
|
1064
|
-
touched: allTouched,
|
|
1065
|
-
errors: syncErrors,
|
|
1066
|
-
submitError: undefined,
|
|
1067
|
-
submitCount: prev.submitCount + 1,
|
|
1068
|
-
}));
|
|
1069
|
-
// If sync validation fails, don't submit
|
|
1070
|
-
if (!isValidationClean(syncErrors)) {
|
|
1071
|
-
submittingRef.current = false;
|
|
1072
|
-
return;
|
|
1073
|
-
}
|
|
1074
|
-
const submitValues = cloneInitialValues(snapshot.values);
|
|
1075
|
-
const failSubmit = (error) => {
|
|
1076
|
-
if (typeof options.onSubmitError === "function") {
|
|
1077
|
-
try {
|
|
1078
|
-
options.onSubmitError(error);
|
|
1079
|
-
}
|
|
1080
|
-
catch (callbackError) {
|
|
1081
|
-
warnDev(`[rezi] useForm: onSubmitError callback threw: ${formatErrorForDev(callbackError)}`);
|
|
1082
|
-
}
|
|
1083
|
-
}
|
|
1084
|
-
else {
|
|
1085
|
-
warnDev(`[rezi] useForm: submit failed: ${formatErrorForDev(error)}`);
|
|
1086
|
-
}
|
|
1087
|
-
updateFormState((prev) => ({
|
|
1088
|
-
...prev,
|
|
1089
|
-
isSubmitting: false,
|
|
1090
|
-
submitError: error,
|
|
1091
|
-
}));
|
|
1092
|
-
};
|
|
1093
|
-
const finishSuccessfulSubmit = () => {
|
|
1094
|
-
if (options.resetOnSubmit) {
|
|
1095
|
-
reset();
|
|
1096
|
-
return;
|
|
1097
|
-
}
|
|
1098
|
-
updateFormState((prev) => ({
|
|
1099
|
-
...prev,
|
|
1100
|
-
isSubmitting: false,
|
|
1101
|
-
submitError: undefined,
|
|
1102
|
-
}));
|
|
1103
|
-
};
|
|
1104
|
-
const runSubmitCallback = async () => {
|
|
1105
|
-
let submitResult;
|
|
1106
|
-
try {
|
|
1107
|
-
submitResult = options.onSubmit(submitValues);
|
|
1108
|
-
}
|
|
1109
|
-
catch (error) {
|
|
1110
|
-
submittingRef.current = false;
|
|
1111
|
-
failSubmit(error);
|
|
1112
|
-
return;
|
|
1113
|
-
}
|
|
1114
|
-
if (!isPromiseLike(submitResult)) {
|
|
1115
|
-
submittingRef.current = false;
|
|
1116
|
-
finishSuccessfulSubmit();
|
|
1117
|
-
return;
|
|
1118
|
-
}
|
|
1119
|
-
submittingRef.current = true;
|
|
1120
|
-
if (!stateRef.current.isSubmitting) {
|
|
1121
|
-
updateFormState((prev) => ({
|
|
1122
|
-
...prev,
|
|
1123
|
-
isSubmitting: true,
|
|
1124
|
-
}));
|
|
1125
|
-
}
|
|
1126
|
-
try {
|
|
1127
|
-
await submitResult;
|
|
1128
|
-
}
|
|
1129
|
-
catch (error) {
|
|
1130
|
-
submittingRef.current = false;
|
|
1131
|
-
failSubmit(error);
|
|
1132
|
-
return;
|
|
1133
|
-
}
|
|
1134
|
-
submittingRef.current = false;
|
|
1135
|
-
finishSuccessfulSubmit();
|
|
1136
|
-
};
|
|
1137
|
-
if (!options.validateAsync) {
|
|
1138
|
-
void runSubmitCallback();
|
|
1139
|
-
return;
|
|
1140
|
-
}
|
|
1141
|
-
submittingRef.current = true;
|
|
1142
|
-
updateFormState((prev) => ({
|
|
1143
|
-
...prev,
|
|
1144
|
-
isSubmitting: true,
|
|
1145
|
-
}));
|
|
1146
|
-
void (async () => {
|
|
1147
|
-
try {
|
|
1148
|
-
const asyncErrors = await runAsyncValidationFiltered(submitValues, snapshot);
|
|
1149
|
-
const allErrors = mergeValidationErrors(syncErrors, asyncErrors);
|
|
1150
|
-
if (!isValidationClean(allErrors)) {
|
|
1151
|
-
submittingRef.current = false;
|
|
1152
|
-
updateFormState((prev) => ({
|
|
1153
|
-
...prev,
|
|
1154
|
-
isSubmitting: false,
|
|
1155
|
-
errors: allErrors,
|
|
1156
|
-
submitError: undefined,
|
|
1157
|
-
}));
|
|
1158
|
-
return;
|
|
1159
|
-
}
|
|
1160
|
-
await runSubmitCallback();
|
|
1161
|
-
}
|
|
1162
|
-
catch (error) {
|
|
1163
|
-
submittingRef.current = false;
|
|
1164
|
-
updateFormState((prev) => ({
|
|
1165
|
-
...prev,
|
|
1166
|
-
isSubmitting: false,
|
|
1167
|
-
submitError: error,
|
|
1168
|
-
}));
|
|
1169
|
-
}
|
|
1170
|
-
})();
|
|
1171
|
-
};
|
|
79
|
+
const { validateForm, validateField, setFieldValue, setFieldError, setFieldTouched, handleChange, handleBlur, bind, field, } = createFieldBindings({
|
|
80
|
+
ctx,
|
|
81
|
+
state,
|
|
82
|
+
stateRef,
|
|
83
|
+
initialValuesRef,
|
|
84
|
+
pendingAsyncValuesRef,
|
|
85
|
+
asyncValidatorRef,
|
|
86
|
+
validate: options.validate,
|
|
87
|
+
validateOnBlur: options.validateOnBlur,
|
|
88
|
+
validateOnChange: options.validateOnChange,
|
|
89
|
+
updateFormState,
|
|
90
|
+
isFieldDisabledInternal,
|
|
91
|
+
isFieldReadOnlyInternal,
|
|
92
|
+
isFieldEditableInternal,
|
|
93
|
+
runSyncValidationFiltered,
|
|
94
|
+
canBindFieldAsText,
|
|
95
|
+
warnUnsupportedTextBinding,
|
|
96
|
+
});
|
|
97
|
+
const { setDisabled, setReadOnly, setFieldDisabled, setFieldReadOnly } = createFormFlagActions({
|
|
98
|
+
updateFormState,
|
|
99
|
+
filterDisabledValidationErrors,
|
|
100
|
+
});
|
|
101
|
+
const { useFieldArray } = createFieldArrayApi({
|
|
102
|
+
state,
|
|
103
|
+
validateOnChange: options.validateOnChange,
|
|
104
|
+
initialValuesRef,
|
|
105
|
+
fieldArrayKeysRef,
|
|
106
|
+
fieldArrayKeyCounterRef,
|
|
107
|
+
pendingAsyncValuesRef,
|
|
108
|
+
asyncValidatorRef,
|
|
109
|
+
updateFormState,
|
|
110
|
+
isFieldEditableInternal,
|
|
111
|
+
runSyncValidationFiltered,
|
|
112
|
+
});
|
|
113
|
+
const { nextStep, previousStep, goToStep } = createWizardActions({
|
|
114
|
+
hasWizard,
|
|
115
|
+
stepCount,
|
|
116
|
+
wizardSteps,
|
|
117
|
+
validateAsync: options.validateAsync,
|
|
118
|
+
stateRef,
|
|
119
|
+
updateFormState,
|
|
120
|
+
runAsyncValidationFiltered,
|
|
121
|
+
runWizardStepValidation: runWizardStepValidationForState,
|
|
122
|
+
});
|
|
123
|
+
const reset = createResetAction({
|
|
124
|
+
formOptions: options,
|
|
125
|
+
asyncValidatorRef,
|
|
126
|
+
attemptRef: submitAttemptRef,
|
|
127
|
+
submittingRef,
|
|
128
|
+
fieldArrayKeysRef,
|
|
129
|
+
updateFormState,
|
|
130
|
+
});
|
|
131
|
+
const handleSubmit = createSubmitAction({
|
|
132
|
+
formOptions: options,
|
|
133
|
+
hasWizard,
|
|
134
|
+
stepCount,
|
|
135
|
+
stateRef,
|
|
136
|
+
submittingRef,
|
|
137
|
+
asyncValidatorRef,
|
|
138
|
+
attemptRef: submitAttemptRef,
|
|
139
|
+
updateFormState,
|
|
140
|
+
runSyncValidationFiltered,
|
|
141
|
+
runAsyncValidationFiltered,
|
|
142
|
+
nextStep,
|
|
143
|
+
reset,
|
|
144
|
+
});
|
|
1172
145
|
return Object.freeze({
|
|
1173
146
|
values: state.values,
|
|
1174
147
|
errors: state.errors,
|
|
@@ -1201,8 +174,8 @@ export function useForm(ctx, options) {
|
|
|
1201
174
|
setReadOnly,
|
|
1202
175
|
setFieldDisabled,
|
|
1203
176
|
setFieldReadOnly,
|
|
1204
|
-
isFieldDisabled: (
|
|
1205
|
-
isFieldReadOnly: (
|
|
177
|
+
isFieldDisabled: (fieldName) => isFieldDisabledInternal(fieldName, stateRef.current),
|
|
178
|
+
isFieldReadOnly: (fieldName) => isFieldReadOnlyInternal(fieldName, stateRef.current),
|
|
1206
179
|
useFieldArray,
|
|
1207
180
|
nextStep,
|
|
1208
181
|
previousStep,
|