From 631819f16f58cb0bb9d328bce7dd4858b803f44b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julien=20Valverd=C3=A9?= Date: Fri, 13 Mar 2026 21:05:47 +0100 Subject: [PATCH] Fix form example --- .../src/lib/form/TextFieldFormInputView.tsx | 49 ++++----------- .../form/TextFieldOptionalFormInputView.tsx | 60 +++++++++++++++++++ packages/example/src/routes/form.tsx | 5 +- packages/example/src/todo/TodoView.tsx | 5 +- 4 files changed, 79 insertions(+), 40 deletions(-) create mode 100644 packages/example/src/lib/form/TextFieldOptionalFormInputView.tsx diff --git a/packages/example/src/lib/form/TextFieldFormInputView.tsx b/packages/example/src/lib/form/TextFieldFormInputView.tsx index 052348c..53621d3 100644 --- a/packages/example/src/lib/form/TextFieldFormInputView.tsx +++ b/packages/example/src/lib/form/TextFieldFormInputView.tsx @@ -1,33 +1,20 @@ -import { Callout, Flex, Spinner, Switch, TextField } from "@radix-ui/themes" -import { Array, Option, Struct } from "effect" +import { Callout, Flex, Spinner, TextField } from "@radix-ui/themes" +import { Array, Option } from "effect" import { Component, Form, Subscribable } from "effect-fc" -interface Props -extends TextField.RootProps, Form.useInput.Options { - readonly optional?: false - readonly field: Form.FormField +export declare namespace TextFieldFormInputView { + export interface Props + extends TextField.RootProps, Form.useInput.Options { + 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 TextFieldFormInputView extends Component.make("TextFieldFormInputView")(function*(props: TextFieldFormInputProps) { - const input: ( - | { readonly optional: true } & Form.useOptionalInput.Success - | { readonly optional: false } & Form.useInput.Success - ) = 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) } +export class TextFieldFormInputView extends Component.make("TextFieldFormInputView")(function*( + props: TextFieldFormInputView.Props +) { + const input = yield* Form.useInput(props.field, props) const [issues, isValidating, isSubmitting] = yield* Subscribable.useSubscribables([ props.field.issues, props.field.isValidating, @@ -39,19 +26,9 @@ export class TextFieldFormInputView extends Component.make("TextFieldFormInputVi input.setValue(e.target.value)} - disabled={(input.optional && !input.enabled) || isSubmitting} - {...Struct.omit(props, "optional", "defaultValue")} + disabled={isSubmitting} + {...props} > - {input.optional && - - - - } - {isValidating && diff --git a/packages/example/src/lib/form/TextFieldOptionalFormInputView.tsx b/packages/example/src/lib/form/TextFieldOptionalFormInputView.tsx new file mode 100644 index 0000000..fa9e361 --- /dev/null +++ b/packages/example/src/lib/form/TextFieldOptionalFormInputView.tsx @@ -0,0 +1,60 @@ +import { Callout, Flex, Spinner, Switch, TextField } from "@radix-ui/themes" +import { Array, Option, Struct } from "effect" +import { Component, Form, Subscribable } from "effect-fc" + + +export declare namespace TextFieldOptionalFormInputView { + export interface Props + extends Omit, Form.useOptionalInput.Options { + readonly field: Form.FormField> + } +} + + +export class TextFieldOptionalFormInputView extends Component.make("TextFieldOptionalFormInputView")(function*( + props: TextFieldOptionalFormInputView.Props +) { + const input = yield* Form.useOptionalInput(props.field, props) + const [issues, isValidating, isSubmitting] = yield* Subscribable.useSubscribables([ + props.field.issues, + props.field.isValidating, + props.field.isSubmitting, + ]) + + return ( + + input.setValue(e.target.value)} + disabled={!input.enabled || isSubmitting} + {...Struct.omit(props, "defaultValue")} + > + + + + + {isValidating && + + + + } + + {props.children} + + + {Option.match(Array.head(issues), { + onSome: issue => ( + + {issue.message} + + ), + + onNone: () => <>, + })} + + ) +}) {} diff --git a/packages/example/src/routes/form.tsx b/packages/example/src/routes/form.tsx index d8ba6d9..a730c2e 100644 --- a/packages/example/src/routes/form.tsx +++ b/packages/example/src/routes/form.tsx @@ -3,6 +3,7 @@ import { createFileRoute } from "@tanstack/react-router" import { Console, Effect, Match, Option, ParseResult, Schema } from "effect" import { Component, Form, Subscribable } from "effect-fc" import { TextFieldFormInputView } from "@/lib/form/TextFieldFormInputView" +import { TextFieldOptionalFormInputView } from "@/lib/form/TextFieldOptionalFormInputView" import { DateTimeUtcFromZonedInput } from "@/lib/schema" import { runtime } from "@/runtime" @@ -71,6 +72,7 @@ class RegisterFormView extends Component.make("RegisterFormView")(function*() { const runPromise = yield* Component.useRunPromise() const TextFieldFormInput = yield* TextFieldFormInputView.use + const TextFieldOptionalFormInput = yield* TextFieldOptionalFormInputView.use yield* Component.useOnMount(() => Effect.gen(function*() { yield* Effect.addFinalizer(() => Console.log("RegisterFormView unmounted")) @@ -93,8 +95,7 @@ class RegisterFormView extends Component.make("RegisterFormView")(function*() { field={yield* form.field(["password"])} /> - () const TextFieldFormInput = yield* TextFieldFormInputView.use + const TextFieldOptionalFormInput = yield* TextFieldOptionalFormInputView.use return ( @@ -93,8 +95,7 @@ export class TodoView extends Component.make("TodoView")(function*(props: TodoPr -