From 5951a0bf160147fb0f735381f8f6e95380921f28 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julien=20Valverd=C3=A9?= Date: Thu, 2 Oct 2025 16:57:44 +0200 Subject: [PATCH] Form fix --- .../src/lib/form/TextFieldFormInput.tsx | 40 +++++++++++-- .../lib/form/TextFieldFormOptionalInput.tsx | 57 ------------------- packages/example/src/main.tsx | 1 + packages/example/src/routes/form.tsx | 6 +- 4 files changed, 38 insertions(+), 66 deletions(-) delete mode 100644 packages/example/src/lib/form/TextFieldFormOptionalInput.tsx diff --git a/packages/example/src/lib/form/TextFieldFormInput.tsx b/packages/example/src/lib/form/TextFieldFormInput.tsx index d237096..85db7fa 100644 --- a/packages/example/src/lib/form/TextFieldFormInput.tsx +++ b/packages/example/src/lib/form/TextFieldFormInput.tsx @@ -1,16 +1,34 @@ -import { Callout, Flex, Spinner, TextField } from "@radix-ui/themes" +import { Callout, Flex, Spinner, Switch, TextField } from "@radix-ui/themes" import { Array, Option } from "effect" import { Component, Form, Hooks } from "effect-fc" -export interface TextFieldFormInputProps +interface Props extends TextField.RootProps, Form.useInput.Options { + readonly optional?: false readonly field: Form.FormField } +interface OptionalProps +extends Omit, Form.useOptionalInput.Options { + readonly optional: true + readonly field: Form.FormField> +} + +export type TextFieldFormInputProps = Props | OptionalProps + + export class TextFieldFormInput extends Component.makeUntraced("TextFieldFormInput")( function*(props: TextFieldFormInputProps) { - const { value, setValue } = yield* Form.useInput(props.field, props) + const input: ( + | { readonly optional: true } & Form.useOptionalInput.Result + | { readonly optional: false } & Form.useInput.Result + ) = props.optional + // biome-ignore lint/correctness/useHookAtTopLevel: "optional" reactivity not supported + ? { optional: true, ...yield* Form.useOptionalInput(props.field, props) } + // biome-ignore lint/correctness/useHookAtTopLevel: "optional" reactivity not supported + : { optional: false, ...yield* Form.useInput(props.field, props) } + const [issues, isValidating, isSubmitting] = yield* Hooks.useSubscribables( props.field.issuesSubscribable, props.field.isValidatingSubscribable, @@ -20,11 +38,21 @@ export class TextFieldFormInput extends Component.makeUntraced("TextFieldFormInp return ( setValue(e.target.value)} - disabled={isSubmitting} + value={input.value} + onChange={e => input.setValue(e.target.value)} + disabled={(input.optional && !input.enabled) || isSubmitting} {...props} > + {input.optional && + + + + } + {isValidating && diff --git a/packages/example/src/lib/form/TextFieldFormOptionalInput.tsx b/packages/example/src/lib/form/TextFieldFormOptionalInput.tsx deleted file mode 100644 index 4344f42..0000000 --- a/packages/example/src/lib/form/TextFieldFormOptionalInput.tsx +++ /dev/null @@ -1,57 +0,0 @@ -import { Callout, Flex, Spinner, Switch, TextField } from "@radix-ui/themes" -import { Array, Option } from "effect" -import { Component, Form, Hooks } from "effect-fc" - - -export interface TextFieldFormOptionalInputProps -extends Omit, Form.useOptionalInput.Options { - readonly field: Form.FormField> -} - -export class TextFieldFormOptionalInput extends Component.makeUntraced("TextFieldFormOptionalInput")( - function*(props: TextFieldFormOptionalInputProps) { - const { value, setValue, enabled, setEnabled } = yield* Form.useOptionalInput(props.field, props) - const [issues, isValidating, isSubmitting] = yield* Hooks.useSubscribables( - props.field.issuesSubscribable, - props.field.isValidatingSubscribable, - props.field.isSubmittingSubscribable, - ) - - return ( - - setValue(e.target.value)} - disabled={!enabled || isSubmitting} - {...props} - > - - - - - {isValidating && - - - - } - - {props.children} - - - {Option.match(Array.head(issues), { - onSome: issue => ( - - {issue.message} - - ), - - onNone: () => <>, - })} - - ) - } -) {} diff --git a/packages/example/src/main.tsx b/packages/example/src/main.tsx index bcf65ab..7bdc0dd 100644 --- a/packages/example/src/main.tsx +++ b/packages/example/src/main.tsx @@ -14,6 +14,7 @@ declare module "@tanstack/react-router" { } } +// biome-ignore lint/style/noNonNullAssertion: React entrypoint createRoot(document.getElementById("root")!).render( diff --git a/packages/example/src/routes/form.tsx b/packages/example/src/routes/form.tsx index 803c7ec..84ae904 100644 --- a/packages/example/src/routes/form.tsx +++ b/packages/example/src/routes/form.tsx @@ -3,7 +3,6 @@ import { createFileRoute } from "@tanstack/react-router" import { Console, Effect, Option, ParseResult, Schema } from "effect" import { Component, Form, Hooks } from "effect-fc" import { TextFieldFormInput } from "@/lib/form/TextFieldFormInput" -import { TextFieldFormOptionalInput } from "@/lib/form/TextFieldFormOptionalInput" import { DateTimeUtcFromZonedInput } from "@/lib/schema" import { runtime } from "@/runtime" @@ -53,7 +52,7 @@ class RegisterPage extends Component.makeUntraced("RegisterPage")(function*() { const [canSubmit] = yield* Hooks.useSubscribables(form.canSubmitSubscribable) const TextFieldFormInputFC = yield* TextFieldFormInput - const TextFieldFormOptionalInputFC = yield* TextFieldFormOptionalInput + return ( @@ -72,7 +71,8 @@ class RegisterPage extends Component.makeUntraced("RegisterPage")(function*() { debounce="200 millis" /> -