0.1.4 #5
@@ -1,4 +1,4 @@
|
|||||||
import { Array, Duration, Effect, Equivalence, flow, identity, Option, ParseResult, Pipeable, Schema, Scope, Stream, Subscribable, SubscriptionRef } from "effect"
|
import { Array, Duration, Effect, Equal, Equivalence, flow, identity, Option, ParseResult, pipe, Pipeable, Schema, Scope, Stream, Subscribable, SubscriptionRef } from "effect"
|
||||||
import type { NoSuchElementException } from "effect/Cause"
|
import type { NoSuchElementException } from "effect/Cause"
|
||||||
import * as React from "react"
|
import * as React from "react"
|
||||||
import { Hooks } from "./hooks/index.js"
|
import { Hooks } from "./hooks/index.js"
|
||||||
@@ -16,6 +16,8 @@ extends Pipeable.Pipeable {
|
|||||||
readonly encodedValueRef: SubscriptionRef.SubscriptionRef<I>,
|
readonly encodedValueRef: SubscriptionRef.SubscriptionRef<I>,
|
||||||
readonly errorRef: SubscriptionRef.SubscriptionRef<Option.Option<ParseResult.ParseError>>,
|
readonly errorRef: SubscriptionRef.SubscriptionRef<Option.Option<ParseResult.ParseError>>,
|
||||||
|
|
||||||
|
readonly canSubmitSubscribable: Subscribable.Subscribable<boolean>
|
||||||
|
|
||||||
makeFieldIssuesSubscribable<const P extends PropertyPath.Paths<A>>(
|
makeFieldIssuesSubscribable<const P extends PropertyPath.Paths<A>>(
|
||||||
path: P
|
path: P
|
||||||
): Subscribable.Subscribable<readonly ParseResult.ArrayFormatterIssue[]>
|
): Subscribable.Subscribable<readonly ParseResult.ArrayFormatterIssue[]>
|
||||||
@@ -25,6 +27,7 @@ extends Pipeable.Pipeable {
|
|||||||
class FormImpl<in out A, in out I, out R>
|
class FormImpl<in out A, in out I, out R>
|
||||||
extends Pipeable.Class() implements Form<A, I, R> {
|
extends Pipeable.Class() implements Form<A, I, R> {
|
||||||
readonly [TypeId]: TypeId = TypeId
|
readonly [TypeId]: TypeId = TypeId
|
||||||
|
readonly canSubmitSubscribable: Subscribable.Subscribable<boolean>
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
readonly schema: Schema.Schema<A, I, R>,
|
readonly schema: Schema.Schema<A, I, R>,
|
||||||
@@ -33,6 +36,18 @@ extends Pipeable.Class() implements Form<A, I, R> {
|
|||||||
readonly errorRef: SubscriptionRef.SubscriptionRef<Option.Option<ParseResult.ParseError>>,
|
readonly errorRef: SubscriptionRef.SubscriptionRef<Option.Option<ParseResult.ParseError>>,
|
||||||
) {
|
) {
|
||||||
super()
|
super()
|
||||||
|
|
||||||
|
this.canSubmitSubscribable = pipe(
|
||||||
|
<A>([value, error]: readonly [
|
||||||
|
Option.Option<A>,
|
||||||
|
Option.Option<ParseResult.ParseError>,
|
||||||
|
]) => Option.isSome(value) && Option.isNone(error),
|
||||||
|
|
||||||
|
filter => SubscribableInternal.make({
|
||||||
|
get: Effect.map(Effect.all([valueRef, errorRef]), filter),
|
||||||
|
get changes() { return Stream.map(Stream.zipLatestAll(valueRef.changes, errorRef.changes), filter)},
|
||||||
|
}),
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
makeFieldIssuesSubscribable<const P extends PropertyPath.Paths<A>>(path: P) {
|
makeFieldIssuesSubscribable<const P extends PropertyPath.Paths<A>>(path: P) {
|
||||||
@@ -146,8 +161,9 @@ export const useInput: {
|
|||||||
Stream.drop(1),
|
Stream.drop(1),
|
||||||
),
|
),
|
||||||
|
|
||||||
upstreamEncodedValue => internalValueRef.pipe(
|
upstreamEncodedValue => Effect.whenEffect(
|
||||||
|
SubscriptionRef.set(internalValueRef, upstreamEncodedValue),
|
||||||
|
Effect.andThen(internalValueRef, internalValue => !Equal.equals(upstreamEncodedValue, internalValue)),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
|
||||||
|
|||||||
@@ -1,9 +1,9 @@
|
|||||||
import { runtime } from "@/runtime"
|
import { runtime } from "@/runtime"
|
||||||
import { Callout, Container, Flex, TextField } from "@radix-ui/themes"
|
import { Button, Callout, Container, Flex, TextField } from "@radix-ui/themes"
|
||||||
import { createFileRoute } from "@tanstack/react-router"
|
import { createFileRoute } from "@tanstack/react-router"
|
||||||
import { Array, Console, Effect, Option, Schema, Stream } from "effect"
|
import { Array, Effect, Option, Schema } from "effect"
|
||||||
import { Component, Form } from "effect-fc"
|
import { Component, Form } from "effect-fc"
|
||||||
import { useContext, useFork } from "effect-fc/hooks"
|
import { useContext, useSubscribables } from "effect-fc/hooks"
|
||||||
|
|
||||||
|
|
||||||
const email = Schema.pattern<typeof Schema.String>(
|
const email = Schema.pattern<typeof Schema.String>(
|
||||||
@@ -33,8 +33,7 @@ class RegisterPage extends Component.makeUntraced("RegisterPage")(function*() {
|
|||||||
const emailInput = yield* Form.useInput(form, ["email"], { debounce: "200 millis" })
|
const emailInput = yield* Form.useInput(form, ["email"], { debounce: "200 millis" })
|
||||||
const passwordInput = yield* Form.useInput(form, ["password"], { debounce: "200 millis" })
|
const passwordInput = yield* Form.useInput(form, ["password"], { debounce: "200 millis" })
|
||||||
|
|
||||||
yield* useFork(() => Stream.runForEach(form.valueRef.changes, Console.log), [])
|
const [canSubmit] = yield* useSubscribables(form.canSubmitSubscribable)
|
||||||
// yield* useFork(() => Stream.runForEach(form.errorRef.changes, Console.log), [])
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Container>
|
<Container>
|
||||||
@@ -68,6 +67,8 @@ class RegisterPage extends Component.makeUntraced("RegisterPage")(function*() {
|
|||||||
|
|
||||||
onNone: () => <></>,
|
onNone: () => <></>,
|
||||||
})}
|
})}
|
||||||
|
|
||||||
|
<Button disabled={!canSubmit}>Submit</Button>
|
||||||
</Flex>
|
</Flex>
|
||||||
</Container>
|
</Container>
|
||||||
)
|
)
|
||||||
|
|||||||
Reference in New Issue
Block a user