diff --git a/packages/effect-fc/src/Form.ts b/packages/effect-fc/src/Form.ts
index aca094a..eccc270 100644
--- a/packages/effect-fc/src/Form.ts
+++ b/packages/effect-fc/src/Form.ts
@@ -1,5 +1,5 @@
import * as AsyncData from "@typed/async-data"
-import { Array, Duration, Effect, Equal, Equivalence, identity, Option, ParseResult, pipe, Pipeable, Ref, Schema, Scope, Stream, Subscribable, SubscriptionRef } from "effect"
+import { Array, Duration, Effect, Equal, Equivalence, Exit, flow, identity, Option, ParseResult, pipe, Pipeable, Ref, Schema, Scope, Stream, Subscribable, SubscriptionRef } from "effect"
import type { NoSuchElementException } from "effect/Cause"
import * as React from "react"
import { Hooks } from "./hooks/index.js"
@@ -43,51 +43,31 @@ extends Pipeable.Class() implements Form {
super()
this.canSubmitSubscribable = pipe(
- ([value, error, isValidating]: readonly [
+ ([value, error, isValidating, submitState]: readonly [
Option.Option,
Option.Option,
boolean,
- ]) => Option.isSome(value) && Option.isNone(error) && !isValidating,
+ AsyncData.AsyncData,
+ ]) => Option.isSome(value) && Option.isNone(error) && !isValidating && !AsyncData.isLoading(submitState),
filter => SubscribableInternal.make({
- get: Effect.map(Effect.all([valueRef, errorRef, isValidatingRef]), filter),
- get changes() { return Stream.map(Stream.zipLatestAll(valueRef.changes, errorRef.changes, isValidatingRef.changes), filter)},
+ get: Effect.map(Effect.all([valueRef, errorRef, isValidatingRef, submitStateRef]), filter),
+ get changes() {
+ return Stream.map(
+ Stream.zipLatestAll(
+ valueRef.changes,
+ errorRef.changes,
+ isValidatingRef.changes,
+ submitStateRef.changes,
+ ),
+ filter,
+ )
+ },
}),
)
}
}
-
-export const FormFieldTypeId: unique symbol = Symbol.for("effect-fc/FormField")
-export type FormFieldTypeId = typeof FormFieldTypeId
-
-export interface FormField
-extends Pipeable.Pipeable {
- readonly [FormFieldTypeId]: FormFieldTypeId
-
- readonly valueSubscribable: Subscribable.Subscribable, NoSuchElementException>
- readonly encodedValueRef: SubscriptionRef.SubscriptionRef
- readonly issuesSubscribable: Subscribable.Subscribable
- readonly isValidatingSubscribable: Subscribable.Subscribable
- readonly isSubmittingSubscribable: Subscribable.Subscribable
-}
-
-class FormFieldImpl
-extends Pipeable.Class() implements FormField {
- readonly [FormFieldTypeId]: FormFieldTypeId = FormFieldTypeId
-
- constructor(
- readonly valueSubscribable: Subscribable.Subscribable, NoSuchElementException>,
- readonly encodedValueRef: SubscriptionRef.SubscriptionRef,
- readonly issuesSubscribable: Subscribable.Subscribable,
- readonly isValidatingSubscribable: Subscribable.Subscribable,
- readonly isSubmittingSubscribable: Subscribable.Subscribable,
- ) {
- super()
- }
-}
-
-
export namespace make {
export interface Options {
readonly schema: Schema.Schema
@@ -114,6 +94,39 @@ export const make: {
)
})
+export const run = (
+ self: Form
+): Effect.Effect => Stream.runForEach(
+ self.encodedValueRef.changes,
+ encodedValue => SubscriptionRef.set(self.isValidatingRef, true).pipe(
+ Effect.andThen(Schema.decode(self.schema, { errors: "all" })(encodedValue)),
+ Effect.andThen(v => SubscriptionRef.set(self.valueRef, Option.some(v))),
+ Effect.andThen(SubscriptionRef.set(self.errorRef, Option.none())),
+ Effect.catchTag("ParseError", e => SubscriptionRef.set(self.errorRef, Option.some(e))),
+ Effect.andThen(SubscriptionRef.set(self.isValidatingRef, false)),
+ ),
+)
+
+export const submit = (
+ self: Form
+): Effect.Effect>, NoSuchElementException, SR> => Effect.whenEffect(
+ self.valueRef.pipe(
+ Effect.andThen(identity),
+ Effect.tap(Ref.set(self.submitStateRef, AsyncData.loading())),
+ Effect.andThen(flow(
+ self.submit,
+ Effect.exit,
+ Effect.map(Exit.match({
+ onSuccess: a => AsyncData.success(a),
+ onFailure: e => AsyncData.failure(e),
+ })),
+ Effect.tap(v => Ref.set(self.submitStateRef, v))
+ )),
+ ),
+
+ self.canSubmitSubscribable.get,
+)
+
export namespace service {
export interface Options
extends make.Options {}
@@ -126,35 +139,6 @@ export const service = (
form => Effect.forkScoped(run(form)),
)
-export namespace useForm {
- export interface Options
- extends make.Options {}
-}
-
-export const useForm: {
- (
- options: service.Options
- ): Effect.Effect