@lucas-barake/effect-form-react 0.5.0 → 0.7.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/dist/cjs/FormReact.js +51 -172
- package/dist/cjs/FormReact.js.map +1 -1
- package/dist/dts/FormReact.d.ts +34 -58
- package/dist/dts/FormReact.d.ts.map +1 -1
- package/dist/esm/FormReact.js +52 -173
- package/dist/esm/FormReact.js.map +1 -1
- package/package.json +2 -2
- package/src/FormReact.tsx +73 -266
package/src/FormReact.tsx
CHANGED
|
@@ -2,13 +2,12 @@
|
|
|
2
2
|
* @since 1.0.0
|
|
3
3
|
*/
|
|
4
4
|
import { RegistryContext, useAtom, useAtomSet, useAtomSubscribe, useAtomValue } from "@effect-atom/atom-react"
|
|
5
|
-
import * as Atom from "@effect-atom/atom/Atom"
|
|
6
|
-
import type * as Result from "@effect-atom/atom/Result"
|
|
5
|
+
import type * as Atom from "@effect-atom/atom/Atom"
|
|
7
6
|
import { Field, FormAtoms, Mode, Validation } from "@lucas-barake/effect-form"
|
|
8
7
|
import type * as FormBuilder from "@lucas-barake/effect-form/FormBuilder"
|
|
9
|
-
import { getNestedValue, isPathOrParentDirty,
|
|
8
|
+
import { getNestedValue, isPathOrParentDirty, isPathUnderRoot } from "@lucas-barake/effect-form/internal/path"
|
|
10
9
|
import * as Cause from "effect/Cause"
|
|
11
|
-
import * as Effect from "effect/Effect"
|
|
10
|
+
import type * as Effect from "effect/Effect"
|
|
12
11
|
import * as Option from "effect/Option"
|
|
13
12
|
import * as ParseResult from "effect/ParseResult"
|
|
14
13
|
import type * as Schema from "effect/Schema"
|
|
@@ -105,25 +104,6 @@ export interface ArrayFieldOperations<TItem> {
|
|
|
105
104
|
readonly move: (from: number, to: number) => void
|
|
106
105
|
}
|
|
107
106
|
|
|
108
|
-
/**
|
|
109
|
-
* State exposed to form.Subscribe render prop.
|
|
110
|
-
*
|
|
111
|
-
* @since 1.0.0
|
|
112
|
-
* @category Models
|
|
113
|
-
*/
|
|
114
|
-
export interface SubscribeState<TFields extends Field.FieldsRecord> {
|
|
115
|
-
readonly values: Field.EncodedFromFields<TFields>
|
|
116
|
-
readonly isDirty: boolean
|
|
117
|
-
readonly hasChangedSinceSubmit: boolean
|
|
118
|
-
readonly lastSubmittedValues: Option.Option<Field.EncodedFromFields<TFields>>
|
|
119
|
-
readonly submitResult: Result.Result<unknown, unknown>
|
|
120
|
-
readonly submit: () => void
|
|
121
|
-
readonly reset: () => void
|
|
122
|
-
readonly revertToLastSubmit: () => void
|
|
123
|
-
readonly setValue: <S>(field: FormBuilder.FieldRef<S>, update: S | ((prev: S) => S)) => void
|
|
124
|
-
readonly setValues: (values: Field.EncodedFromFields<TFields>) => void
|
|
125
|
-
}
|
|
126
|
-
|
|
127
107
|
/**
|
|
128
108
|
* The result of building a form, containing all components and utilities needed
|
|
129
109
|
* for form rendering and submission.
|
|
@@ -134,45 +114,29 @@ export interface SubscribeState<TFields extends Field.FieldsRecord> {
|
|
|
134
114
|
export type BuiltForm<
|
|
135
115
|
TFields extends Field.FieldsRecord,
|
|
136
116
|
R,
|
|
117
|
+
A = void,
|
|
118
|
+
E = never,
|
|
137
119
|
CM extends FieldComponentMap<TFields> = FieldComponentMap<TFields>,
|
|
138
120
|
> = {
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
121
|
+
// Atoms for fine-grained subscriptions (use with useAtomValue)
|
|
122
|
+
readonly isDirty: Atom.Atom<boolean>
|
|
123
|
+
readonly hasChangedSinceSubmit: Atom.Atom<boolean>
|
|
124
|
+
readonly lastSubmittedValues: Atom.Atom<Option.Option<FormBuilder.SubmittedValues<TFields>>>
|
|
125
|
+
readonly submitCount: Atom.Atom<number>
|
|
126
|
+
|
|
143
127
|
readonly schema: Schema.Schema<Field.DecodedFromFields<TFields>, Field.EncodedFromFields<TFields>, R>
|
|
144
128
|
readonly fields: FieldRefs<TFields>
|
|
145
129
|
|
|
146
|
-
readonly
|
|
130
|
+
readonly Initialize: React.FC<{
|
|
147
131
|
readonly defaultValues: Field.EncodedFromFields<TFields>
|
|
148
|
-
readonly onSubmit: Atom.AtomResultFn<Field.DecodedFromFields<TFields>, unknown, unknown>
|
|
149
132
|
readonly children: React.ReactNode
|
|
150
133
|
}>
|
|
151
134
|
|
|
152
|
-
readonly
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
readonly
|
|
157
|
-
readonly submit: () => void
|
|
158
|
-
readonly reset: () => void
|
|
159
|
-
readonly revertToLastSubmit: () => void
|
|
160
|
-
readonly isDirty: boolean
|
|
161
|
-
readonly hasChangedSinceSubmit: boolean
|
|
162
|
-
readonly lastSubmittedValues: Option.Option<Field.EncodedFromFields<TFields>>
|
|
163
|
-
readonly submitResult: Result.Result<unknown, unknown>
|
|
164
|
-
readonly values: Field.EncodedFromFields<TFields>
|
|
165
|
-
readonly setValue: <S>(field: FormBuilder.FieldRef<S>, update: S | ((prev: S) => S)) => void
|
|
166
|
-
readonly setValues: (values: Field.EncodedFromFields<TFields>) => void
|
|
167
|
-
}
|
|
168
|
-
|
|
169
|
-
readonly submit: <A>(
|
|
170
|
-
fn: (values: Field.DecodedFromFields<TFields>, get: Atom.FnContext) => A,
|
|
171
|
-
) => Atom.AtomResultFn<
|
|
172
|
-
Field.DecodedFromFields<TFields>,
|
|
173
|
-
A extends Effect.Effect<infer T, any, any> ? T : A,
|
|
174
|
-
A extends Effect.Effect<any, infer E, any> ? E : never
|
|
175
|
-
>
|
|
135
|
+
readonly submit: Atom.AtomResultFn<void, A, E | ParseResult.ParseError>
|
|
136
|
+
readonly reset: Atom.Writable<void, void>
|
|
137
|
+
readonly revertToLastSubmit: Atom.Writable<void, void>
|
|
138
|
+
readonly setValues: Atom.Writable<void, Field.EncodedFromFields<TFields>>
|
|
139
|
+
readonly setValue: <S>(field: FormBuilder.FieldRef<S>) => Atom.Writable<void, S | ((prev: S) => S)>
|
|
176
140
|
} & FieldComponents<TFields, CM>
|
|
177
141
|
|
|
178
142
|
type FieldComponents<TFields extends Field.FieldsRecord, CM extends FieldComponentMap<TFields>> = {
|
|
@@ -285,12 +249,13 @@ const makeFieldComponent = <S extends Schema.Schema.Any, P extends Record<string
|
|
|
285
249
|
(newValue: Schema.Schema.Encoded<S>) => {
|
|
286
250
|
setValue(newValue)
|
|
287
251
|
setCrossFieldErrors((prev) => {
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
252
|
+
const next = new Map<string, string>()
|
|
253
|
+
for (const [errorPath, message] of prev) {
|
|
254
|
+
if (!isPathUnderRoot(errorPath, fieldPath)) {
|
|
255
|
+
next.set(errorPath, message)
|
|
256
|
+
}
|
|
292
257
|
}
|
|
293
|
-
return prev
|
|
258
|
+
return next.size !== prev.size ? next : prev
|
|
294
259
|
})
|
|
295
260
|
if (parsedMode.validation === "onChange") {
|
|
296
261
|
validate(newValue)
|
|
@@ -457,6 +422,7 @@ const makeArrayFieldComponent = <S extends Schema.Schema.Any>(
|
|
|
457
422
|
...itemFieldComponents,
|
|
458
423
|
}
|
|
459
424
|
|
|
425
|
+
// Proxy enables <Form.items.Item> and <Form.items.name> syntax
|
|
460
426
|
return new Proxy(ArrayWrapper, {
|
|
461
427
|
get(target, prop) {
|
|
462
428
|
if (prop in properties) {
|
|
@@ -530,11 +496,11 @@ const makeFieldComponents = <
|
|
|
530
496
|
*
|
|
531
497
|
* @example
|
|
532
498
|
* ```tsx
|
|
533
|
-
* import {
|
|
499
|
+
* import { FormBuilder } from "@lucas-barake/effect-form"
|
|
534
500
|
* import { FormReact } from "@lucas-barake/effect-form-react"
|
|
501
|
+
* import { useAtomValue, useAtomSet } from "@effect-atom/atom-react"
|
|
535
502
|
* import * as Atom from "@effect-atom/atom/Atom"
|
|
536
503
|
* import * as Schema from "effect/Schema"
|
|
537
|
-
* import * as Effect from "effect/Effect"
|
|
538
504
|
* import * as Layer from "effect/Layer"
|
|
539
505
|
*
|
|
540
506
|
* const runtime = Atom.runtime(Layer.empty)
|
|
@@ -546,26 +512,28 @@ const makeFieldComponents = <
|
|
|
546
512
|
* const form = FormReact.build(loginForm, {
|
|
547
513
|
* runtime,
|
|
548
514
|
* fields: { email: TextInput, password: PasswordInput },
|
|
515
|
+
* onSubmit: (values) => Effect.log(`Login: ${values.email}`),
|
|
549
516
|
* })
|
|
550
517
|
*
|
|
551
|
-
*
|
|
552
|
-
*
|
|
553
|
-
*
|
|
554
|
-
*
|
|
555
|
-
*
|
|
556
|
-
*
|
|
518
|
+
* // Subscribe to atoms anywhere in the tree
|
|
519
|
+
* function SubmitButton() {
|
|
520
|
+
* const isDirty = useAtomValue(form.isDirty)
|
|
521
|
+
* const submit = useAtomValue(form.submit)
|
|
522
|
+
* const callSubmit = useAtomSet(form.submit)
|
|
523
|
+
* return (
|
|
524
|
+
* <button onClick={() => callSubmit()} disabled={!isDirty || submit.waiting}>
|
|
525
|
+
* {submit.waiting ? "Validating..." : "Login"}
|
|
526
|
+
* </button>
|
|
557
527
|
* )
|
|
528
|
+
* }
|
|
558
529
|
*
|
|
530
|
+
* function LoginDialog({ onClose }) {
|
|
559
531
|
* return (
|
|
560
|
-
* <form.
|
|
532
|
+
* <form.Initialize defaultValues={{ email: "", password: "" }}>
|
|
561
533
|
* <form.email />
|
|
562
534
|
* <form.password />
|
|
563
|
-
* <
|
|
564
|
-
*
|
|
565
|
-
* <button onClick={submit} disabled={!isDirty}>Login</button>
|
|
566
|
-
* )}
|
|
567
|
-
* </form.Subscribe>
|
|
568
|
-
* </form.Form>
|
|
535
|
+
* <SubmitButton />
|
|
536
|
+
* </form.Initialize>
|
|
569
537
|
* )
|
|
570
538
|
* }
|
|
571
539
|
* ```
|
|
@@ -576,6 +544,8 @@ const makeFieldComponents = <
|
|
|
576
544
|
export const build = <
|
|
577
545
|
TFields extends Field.FieldsRecord,
|
|
578
546
|
R,
|
|
547
|
+
A,
|
|
548
|
+
E,
|
|
579
549
|
ER = never,
|
|
580
550
|
CM extends FieldComponentMap<TFields> = FieldComponentMap<TFields>,
|
|
581
551
|
>(
|
|
@@ -584,21 +554,22 @@ export const build = <
|
|
|
584
554
|
readonly runtime: Atom.AtomRuntime<R, ER>
|
|
585
555
|
readonly fields: CM
|
|
586
556
|
readonly mode?: Mode.FormMode
|
|
557
|
+
readonly onSubmit: (decoded: Field.DecodedFromFields<TFields>, get: Atom.FnContext) => A | Effect.Effect<A, E, R>
|
|
587
558
|
},
|
|
588
|
-
): BuiltForm<TFields, R, CM> => {
|
|
589
|
-
const { fields: components, mode, runtime } = options
|
|
559
|
+
): BuiltForm<TFields, R, A, E, CM> => {
|
|
560
|
+
const { fields: components, mode, onSubmit, runtime } = options
|
|
590
561
|
const parsedMode = Mode.parse(mode)
|
|
591
562
|
const { fields } = self
|
|
592
563
|
|
|
593
|
-
const formAtoms: FormAtoms.FormAtoms<TFields, R> = FormAtoms.make({
|
|
564
|
+
const formAtoms: FormAtoms.FormAtoms<TFields, R, A, E> = FormAtoms.make({
|
|
594
565
|
formBuilder: self,
|
|
595
566
|
runtime,
|
|
567
|
+
onSubmit,
|
|
596
568
|
})
|
|
597
569
|
|
|
598
570
|
const {
|
|
599
571
|
combinedSchema,
|
|
600
572
|
crossFieldErrorsAtom,
|
|
601
|
-
decodeAndSubmit,
|
|
602
573
|
dirtyFieldsAtom,
|
|
603
574
|
fieldRefs,
|
|
604
575
|
getOrCreateFieldAtoms,
|
|
@@ -606,42 +577,42 @@ export const build = <
|
|
|
606
577
|
hasChangedSinceSubmitAtom,
|
|
607
578
|
isDirtyAtom,
|
|
608
579
|
lastSubmittedValuesAtom,
|
|
609
|
-
onSubmitAtom,
|
|
610
580
|
operations,
|
|
611
|
-
|
|
581
|
+
resetAtom,
|
|
582
|
+
revertToLastSubmitAtom,
|
|
583
|
+
setValue,
|
|
584
|
+
setValuesAtom,
|
|
612
585
|
stateAtom,
|
|
586
|
+
submitAtom,
|
|
613
587
|
submitCountAtom,
|
|
614
588
|
} = formAtoms
|
|
615
589
|
|
|
616
|
-
const
|
|
590
|
+
const InitializeComponent: React.FC<{
|
|
617
591
|
readonly defaultValues: Field.EncodedFromFields<TFields>
|
|
618
|
-
readonly onSubmit: Atom.AtomResultFn<Field.DecodedFromFields<TFields>, unknown, unknown>
|
|
619
592
|
readonly children: React.ReactNode
|
|
620
|
-
}> = ({ children, defaultValues
|
|
593
|
+
}> = ({ children, defaultValues }) => {
|
|
621
594
|
const registry = React.useContext(RegistryContext)
|
|
622
595
|
const state = useAtomValue(stateAtom)
|
|
623
596
|
const setFormState = useAtomSet(stateAtom)
|
|
624
|
-
const
|
|
625
|
-
const
|
|
626
|
-
|
|
627
|
-
React.useEffect(() => {
|
|
628
|
-
setOnSubmit(onSubmit)
|
|
629
|
-
}, [onSubmit, setOnSubmit])
|
|
597
|
+
const callSubmit = useAtomSet(submitAtom)
|
|
598
|
+
const isInitializedRef = React.useRef(false)
|
|
630
599
|
|
|
631
600
|
React.useEffect(() => {
|
|
632
601
|
setFormState(Option.some(operations.createInitialState(defaultValues)))
|
|
602
|
+
isInitializedRef.current = true
|
|
633
603
|
// eslint-disable-next-line react-hooks/exhaustive-deps -- mount-only
|
|
634
604
|
}, [])
|
|
635
605
|
|
|
636
606
|
const debouncedAutoSubmit = useDebounced(() => {
|
|
637
607
|
const stateOption = registry.get(stateAtom)
|
|
638
608
|
if (Option.isNone(stateOption)) return
|
|
639
|
-
|
|
609
|
+
callSubmit()
|
|
640
610
|
}, parsedMode.autoSubmit && parsedMode.validation === "onChange" ? parsedMode.debounce : null)
|
|
641
611
|
|
|
642
612
|
useAtomSubscribe(
|
|
643
613
|
stateAtom,
|
|
644
614
|
React.useCallback(() => {
|
|
615
|
+
if (!isInitializedRef.current) return
|
|
645
616
|
if (parsedMode.autoSubmit && parsedMode.validation === "onChange") {
|
|
646
617
|
debouncedAutoSubmit()
|
|
647
618
|
}
|
|
@@ -653,9 +624,9 @@ export const build = <
|
|
|
653
624
|
if (parsedMode.autoSubmit && parsedMode.validation === "onBlur") {
|
|
654
625
|
const stateOption = registry.get(stateAtom)
|
|
655
626
|
if (Option.isNone(stateOption)) return
|
|
656
|
-
|
|
627
|
+
callSubmit()
|
|
657
628
|
}
|
|
658
|
-
}, [registry,
|
|
629
|
+
}, [registry, callSubmit])
|
|
659
630
|
|
|
660
631
|
if (Option.isNone(state)) return null
|
|
661
632
|
|
|
@@ -673,175 +644,6 @@ export const build = <
|
|
|
673
644
|
)
|
|
674
645
|
}
|
|
675
646
|
|
|
676
|
-
const useFormHook = () => {
|
|
677
|
-
const registry = React.useContext(RegistryContext)
|
|
678
|
-
const formValues = Option.getOrThrow(useAtomValue(stateAtom)).values
|
|
679
|
-
const setFormState = useAtomSet(stateAtom)
|
|
680
|
-
const setCrossFieldErrors = useAtomSet(crossFieldErrorsAtom)
|
|
681
|
-
const [decodeAndSubmitResult, callDecodeAndSubmit] = useAtom(decodeAndSubmit)
|
|
682
|
-
const isDirty = useAtomValue(isDirtyAtom)
|
|
683
|
-
const hasChangedSinceSubmit = useAtomValue(hasChangedSinceSubmitAtom)
|
|
684
|
-
const lastSubmittedValues = useAtomValue(lastSubmittedValuesAtom)
|
|
685
|
-
|
|
686
|
-
React.useEffect(() => {
|
|
687
|
-
if (decodeAndSubmitResult._tag === "Failure") {
|
|
688
|
-
const parseError = Cause.failureOption(decodeAndSubmitResult.cause)
|
|
689
|
-
if (Option.isSome(parseError) && ParseResult.isParseError(parseError.value)) {
|
|
690
|
-
const issues = ParseResult.ArrayFormatter.formatErrorSync(parseError.value)
|
|
691
|
-
|
|
692
|
-
const fieldErrors = new Map<string, string>()
|
|
693
|
-
for (const issue of issues) {
|
|
694
|
-
if (issue.path.length > 0) {
|
|
695
|
-
const fieldPath = schemaPathToFieldPath(issue.path)
|
|
696
|
-
if (!fieldErrors.has(fieldPath)) {
|
|
697
|
-
fieldErrors.set(fieldPath, issue.message)
|
|
698
|
-
}
|
|
699
|
-
}
|
|
700
|
-
}
|
|
701
|
-
|
|
702
|
-
if (fieldErrors.size > 0) {
|
|
703
|
-
setCrossFieldErrors(fieldErrors)
|
|
704
|
-
}
|
|
705
|
-
}
|
|
706
|
-
}
|
|
707
|
-
}, [decodeAndSubmitResult, setCrossFieldErrors])
|
|
708
|
-
|
|
709
|
-
const submit = React.useCallback(() => {
|
|
710
|
-
const stateOption = registry.get(stateAtom)
|
|
711
|
-
if (Option.isNone(stateOption)) return
|
|
712
|
-
|
|
713
|
-
setCrossFieldErrors(new Map())
|
|
714
|
-
|
|
715
|
-
setFormState((prev) => {
|
|
716
|
-
if (Option.isNone(prev)) return prev
|
|
717
|
-
return Option.some(operations.createSubmitState(prev.value))
|
|
718
|
-
})
|
|
719
|
-
|
|
720
|
-
callDecodeAndSubmit(stateOption.value.values)
|
|
721
|
-
}, [setFormState, callDecodeAndSubmit, setCrossFieldErrors, registry])
|
|
722
|
-
|
|
723
|
-
const reset = React.useCallback(() => {
|
|
724
|
-
setFormState((prev) => {
|
|
725
|
-
if (Option.isNone(prev)) return prev
|
|
726
|
-
return Option.some(operations.createResetState(prev.value))
|
|
727
|
-
})
|
|
728
|
-
setCrossFieldErrors(new Map())
|
|
729
|
-
resetValidationAtoms(registry)
|
|
730
|
-
callDecodeAndSubmit(Atom.Reset)
|
|
731
|
-
}, [setFormState, setCrossFieldErrors, callDecodeAndSubmit, registry])
|
|
732
|
-
|
|
733
|
-
const revertToLastSubmit = React.useCallback(() => {
|
|
734
|
-
setFormState((prev) => {
|
|
735
|
-
if (Option.isNone(prev)) return prev
|
|
736
|
-
return Option.some(operations.revertToLastSubmit(prev.value))
|
|
737
|
-
})
|
|
738
|
-
setCrossFieldErrors(new Map())
|
|
739
|
-
}, [setFormState, setCrossFieldErrors])
|
|
740
|
-
|
|
741
|
-
const setValue = React.useCallback(<S,>(
|
|
742
|
-
field: FormBuilder.FieldRef<S>,
|
|
743
|
-
update: S | ((prev: S) => S),
|
|
744
|
-
) => {
|
|
745
|
-
const path = field.key
|
|
746
|
-
|
|
747
|
-
setFormState((prev) => {
|
|
748
|
-
if (Option.isNone(prev)) return prev
|
|
749
|
-
const state = prev.value
|
|
750
|
-
|
|
751
|
-
const currentValue = getNestedValue(state.values, path) as S
|
|
752
|
-
const newValue = typeof update === "function"
|
|
753
|
-
? (update as (prev: S) => S)(currentValue)
|
|
754
|
-
: update
|
|
755
|
-
|
|
756
|
-
return Option.some(operations.setFieldValue(state, path, newValue))
|
|
757
|
-
})
|
|
758
|
-
|
|
759
|
-
setCrossFieldErrors((prev) => {
|
|
760
|
-
let changed = false
|
|
761
|
-
const next = new Map(prev)
|
|
762
|
-
for (const errorPath of prev.keys()) {
|
|
763
|
-
if (errorPath === path || errorPath.startsWith(path + ".") || errorPath.startsWith(path + "[")) {
|
|
764
|
-
next.delete(errorPath)
|
|
765
|
-
changed = true
|
|
766
|
-
}
|
|
767
|
-
}
|
|
768
|
-
return changed ? next : prev
|
|
769
|
-
})
|
|
770
|
-
}, [setFormState, setCrossFieldErrors])
|
|
771
|
-
|
|
772
|
-
const setValues = React.useCallback((values: Field.EncodedFromFields<TFields>) => {
|
|
773
|
-
setFormState((prev) => {
|
|
774
|
-
if (Option.isNone(prev)) return prev
|
|
775
|
-
return Option.some(operations.setFormValues(prev.value, values))
|
|
776
|
-
})
|
|
777
|
-
|
|
778
|
-
setCrossFieldErrors(new Map())
|
|
779
|
-
}, [setFormState, setCrossFieldErrors])
|
|
780
|
-
|
|
781
|
-
return {
|
|
782
|
-
submit,
|
|
783
|
-
reset,
|
|
784
|
-
revertToLastSubmit,
|
|
785
|
-
isDirty,
|
|
786
|
-
hasChangedSinceSubmit,
|
|
787
|
-
lastSubmittedValues,
|
|
788
|
-
submitResult: decodeAndSubmitResult,
|
|
789
|
-
values: formValues,
|
|
790
|
-
setValue,
|
|
791
|
-
setValues,
|
|
792
|
-
}
|
|
793
|
-
}
|
|
794
|
-
|
|
795
|
-
const SubscribeComponent: React.FC<{
|
|
796
|
-
readonly children: (state: SubscribeState<TFields>) => React.ReactNode
|
|
797
|
-
}> = ({ children }) => {
|
|
798
|
-
const {
|
|
799
|
-
hasChangedSinceSubmit,
|
|
800
|
-
isDirty,
|
|
801
|
-
lastSubmittedValues,
|
|
802
|
-
reset,
|
|
803
|
-
revertToLastSubmit,
|
|
804
|
-
setValue,
|
|
805
|
-
setValues,
|
|
806
|
-
submit,
|
|
807
|
-
submitResult,
|
|
808
|
-
values,
|
|
809
|
-
} = useFormHook()
|
|
810
|
-
|
|
811
|
-
return (
|
|
812
|
-
<>
|
|
813
|
-
{children({
|
|
814
|
-
hasChangedSinceSubmit,
|
|
815
|
-
isDirty,
|
|
816
|
-
lastSubmittedValues,
|
|
817
|
-
reset,
|
|
818
|
-
revertToLastSubmit,
|
|
819
|
-
setValue,
|
|
820
|
-
setValues,
|
|
821
|
-
submit,
|
|
822
|
-
submitResult,
|
|
823
|
-
values,
|
|
824
|
-
})}
|
|
825
|
-
</>
|
|
826
|
-
)
|
|
827
|
-
}
|
|
828
|
-
|
|
829
|
-
const submitHelper = <A,>(
|
|
830
|
-
fn: (values: Field.DecodedFromFields<TFields>, get: Atom.FnContext) => A,
|
|
831
|
-
) =>
|
|
832
|
-
runtime.fn<Field.DecodedFromFields<TFields>>()((values, get) => {
|
|
833
|
-
const result = fn(values, get)
|
|
834
|
-
return (Effect.isEffect(result) ? result : Effect.succeed(result)) as Effect.Effect<
|
|
835
|
-
A extends Effect.Effect<infer T, any, any> ? T : A,
|
|
836
|
-
A extends Effect.Effect<any, infer E, any> ? E : never,
|
|
837
|
-
R
|
|
838
|
-
>
|
|
839
|
-
}) as Atom.AtomResultFn<
|
|
840
|
-
Field.DecodedFromFields<TFields>,
|
|
841
|
-
A extends Effect.Effect<infer T, any, any> ? T : A,
|
|
842
|
-
A extends Effect.Effect<any, infer E, any> ? E : never
|
|
843
|
-
>
|
|
844
|
-
|
|
845
647
|
const fieldComponents = makeFieldComponents(
|
|
846
648
|
fields,
|
|
847
649
|
stateAtom,
|
|
@@ -856,15 +658,20 @@ export const build = <
|
|
|
856
658
|
)
|
|
857
659
|
|
|
858
660
|
return {
|
|
859
|
-
|
|
661
|
+
isDirty: isDirtyAtom,
|
|
662
|
+
hasChangedSinceSubmit: hasChangedSinceSubmitAtom,
|
|
663
|
+
lastSubmittedValues: lastSubmittedValuesAtom,
|
|
664
|
+
submitCount: submitCountAtom,
|
|
860
665
|
schema: combinedSchema,
|
|
861
666
|
fields: fieldRefs,
|
|
862
|
-
|
|
863
|
-
|
|
864
|
-
|
|
865
|
-
|
|
667
|
+
Initialize: InitializeComponent,
|
|
668
|
+
submit: submitAtom,
|
|
669
|
+
reset: resetAtom,
|
|
670
|
+
revertToLastSubmit: revertToLastSubmitAtom,
|
|
671
|
+
setValues: setValuesAtom,
|
|
672
|
+
setValue,
|
|
866
673
|
...fieldComponents,
|
|
867
|
-
} as BuiltForm<TFields, R, CM>
|
|
674
|
+
} as BuiltForm<TFields, R, A, E, CM>
|
|
868
675
|
}
|
|
869
676
|
|
|
870
677
|
/**
|