This commit is contained in:
@@ -260,7 +260,6 @@ export namespace useInput {
|
||||
export interface Result<T> {
|
||||
readonly value: T
|
||||
readonly setValue: React.Dispatch<React.SetStateAction<T>>
|
||||
readonly issues: readonly ParseResult.ArrayFormatterIssue[]
|
||||
}
|
||||
}
|
||||
|
||||
@@ -275,7 +274,6 @@ export const useInput: {
|
||||
) {
|
||||
const internalValueRef = yield* Hooks.useMemo(() => Effect.andThen(field.encodedValueRef, SubscriptionRef.make), [field])
|
||||
const [value, setValue] = yield* Hooks.useRefState(internalValueRef)
|
||||
const [issues] = yield* Hooks.useSubscribables(field.issuesSubscribable)
|
||||
|
||||
yield* Hooks.useFork(() => Effect.all([
|
||||
Stream.runForEach(
|
||||
@@ -296,7 +294,7 @@ export const useInput: {
|
||||
),
|
||||
], { concurrency: "unbounded" }), [field, internalValueRef, options?.debounce])
|
||||
|
||||
return { value, setValue, issues }
|
||||
return { value, setValue }
|
||||
})
|
||||
|
||||
export namespace useOptionalInput {
|
||||
@@ -329,7 +327,6 @@ export const useOptionalInput: {
|
||||
|
||||
const [enabled, setEnabled] = yield* Hooks.useRefState(enabledRef)
|
||||
const [value, setValue] = yield* Hooks.useRefState(internalValueRef)
|
||||
const [issues] = yield* Hooks.useSubscribables(field.issuesSubscribable)
|
||||
|
||||
yield* Hooks.useFork(() => Effect.all([
|
||||
Stream.runForEach(
|
||||
@@ -365,5 +362,5 @@ export const useOptionalInput: {
|
||||
),
|
||||
], { concurrency: "unbounded" }), [field, enabledRef, internalValueRef, options.debounce])
|
||||
|
||||
return { enabled, setEnabled, value, setValue, issues }
|
||||
return { enabled, setEnabled, value, setValue }
|
||||
})
|
||||
|
||||
40
packages/example/src/lib/form/TextFieldFormInput.tsx
Normal file
40
packages/example/src/lib/form/TextFieldFormInput.tsx
Normal file
@@ -0,0 +1,40 @@
|
||||
import { Callout, Flex, TextField } from "@radix-ui/themes"
|
||||
import { Array, Option } from "effect"
|
||||
import { Component, Form } from "effect-fc"
|
||||
import { useSubscribables } from "effect-fc/hooks"
|
||||
|
||||
|
||||
export interface TextFieldFormInputProps
|
||||
extends TextField.RootProps, Form.useInput.Options {
|
||||
readonly field: Form.FormField<any, string>
|
||||
}
|
||||
|
||||
export class TextFieldFormInput extends Component.makeUntraced("TextFieldFormInput")(function*(props: TextFieldFormInputProps) {
|
||||
const { value, setValue } = yield* Form.useInput(props.field, props)
|
||||
const [issues, isValidating, isSubmitting] = yield* useSubscribables(
|
||||
props.field.issuesSubscribable,
|
||||
props.field.isValidatingSubscribable,
|
||||
props.field.isSubmittingSubscribable,
|
||||
)
|
||||
|
||||
return (
|
||||
<Flex direction="column" gap="1">
|
||||
<TextField.Root
|
||||
value={value}
|
||||
onChange={e => setValue(e.target.value)}
|
||||
disabled={isSubmitting}
|
||||
{...props}
|
||||
/>
|
||||
|
||||
{Option.match(Array.head(issues), {
|
||||
onSome: issue => (
|
||||
<Callout.Root>
|
||||
<Callout.Text>{issue.message}</Callout.Text>
|
||||
</Callout.Root>
|
||||
),
|
||||
|
||||
onNone: () => <></>,
|
||||
})}
|
||||
</Flex>
|
||||
)
|
||||
}) {}
|
||||
@@ -1,7 +1,8 @@
|
||||
import { TextFieldFormInput } from "@/lib/form/TextFieldFormInput"
|
||||
import { runtime } from "@/runtime"
|
||||
import { Button, Callout, Container, Flex, TextField } from "@radix-ui/themes"
|
||||
import { Button, Container, Flex } from "@radix-ui/themes"
|
||||
import { createFileRoute } from "@tanstack/react-router"
|
||||
import { Array, Effect, Option, Schema } from "effect"
|
||||
import { Effect, Schema } from "effect"
|
||||
import { Component, Form } from "effect-fc"
|
||||
import { useContext, useSubscribables } from "effect-fc/hooks"
|
||||
|
||||
@@ -34,14 +35,11 @@ class RegisterForm extends Effect.Service<RegisterForm>()("RegisterForm", {
|
||||
|
||||
class RegisterPage extends Component.makeUntraced("RegisterPage")(function*() {
|
||||
const form = yield* RegisterForm
|
||||
const emailField = Form.useField(form, ["email"])
|
||||
const passwordField = Form.useField(form, ["password"])
|
||||
const emailInput = yield* Form.useInput(emailField, { debounce: "200 millis" })
|
||||
const passwordInput = yield* Form.useInput(passwordField, { debounce: "200 millis" })
|
||||
|
||||
const submit = yield* Form.useSubmit(form)
|
||||
const [canSubmit] = yield* useSubscribables(form.canSubmitSubscribable)
|
||||
|
||||
const TextFieldFormInputFC = yield* TextFieldFormInput
|
||||
|
||||
return (
|
||||
<Container>
|
||||
<form onSubmit={e => {
|
||||
@@ -49,36 +47,16 @@ class RegisterPage extends Component.makeUntraced("RegisterPage")(function*() {
|
||||
void submit()
|
||||
}}>
|
||||
<Flex direction="column" gap="2">
|
||||
<TextField.Root
|
||||
value={emailInput.value}
|
||||
onChange={e => emailInput.setValue(e.target.value)}
|
||||
<TextFieldFormInputFC
|
||||
field={Form.useField(form, ["email"])}
|
||||
debounce="500 millis"
|
||||
/>
|
||||
|
||||
{Option.match(Array.head(emailInput.issues), {
|
||||
onSome: issue => (
|
||||
<Callout.Root>
|
||||
<Callout.Text>{issue.message}</Callout.Text>
|
||||
</Callout.Root>
|
||||
),
|
||||
|
||||
onNone: () => <></>,
|
||||
})}
|
||||
|
||||
<TextField.Root
|
||||
value={passwordInput.value}
|
||||
onChange={e => passwordInput.setValue(e.target.value)}
|
||||
<TextFieldFormInputFC
|
||||
field={Form.useField(form, ["password"])}
|
||||
debounce="500 millis"
|
||||
/>
|
||||
|
||||
{Option.match(Array.head(passwordInput.issues), {
|
||||
onSome: issue => (
|
||||
<Callout.Root>
|
||||
<Callout.Text>{issue.message}</Callout.Text>
|
||||
</Callout.Root>
|
||||
),
|
||||
|
||||
onNone: () => <></>,
|
||||
})}
|
||||
|
||||
<Button disabled={!canSubmit}>Submit</Button>
|
||||
</Flex>
|
||||
</form>
|
||||
|
||||
Reference in New Issue
Block a user