diff --git a/packages/effect-fc/src/Form.ts b/packages/effect-fc/src/Form.ts index 57ed027..3df3bc0 100644 --- a/packages/effect-fc/src/Form.ts +++ b/packages/effect-fc/src/Form.ts @@ -336,8 +336,8 @@ export const useInput: { }) export namespace useOptionalInput { - export interface Options extends useInput.Options { - readonly defaultValue: I + export interface Options extends useInput.Options { + readonly defaultValue: T } export interface Result extends useInput.Result { diff --git a/packages/example/src/lib/form/TextFieldFormInput.tsx b/packages/example/src/lib/form/TextFieldFormInput.tsx index 040a3dc..d237096 100644 --- a/packages/example/src/lib/form/TextFieldFormInput.tsx +++ b/packages/example/src/lib/form/TextFieldFormInput.tsx @@ -1,7 +1,6 @@ import { Callout, Flex, Spinner, TextField } from "@radix-ui/themes" import { Array, Option } from "effect" -import { Component, Form } from "effect-fc" -import { useSubscribables } from "effect-fc/Hooks" +import { Component, Form, Hooks } from "effect-fc" export interface TextFieldFormInputProps @@ -9,38 +8,42 @@ extends TextField.RootProps, Form.useInput.Options { readonly field: Form.FormField } -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, - ) +export class TextFieldFormInput extends Component.makeUntraced("TextFieldFormInput")( + function*(props: TextFieldFormInputProps) { + const { value, setValue } = yield* Form.useInput(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={isSubmitting} - {...props} - > - {isValidating && - - - - } - + return ( + + setValue(e.target.value)} + disabled={isSubmitting} + {...props} + > + {isValidating && + + + + } - {Option.match(Array.head(issues), { - onSome: issue => ( - - {issue.message} - - ), + {props.children} + - onNone: () => <>, - })} - - ) -}) {} + {Option.match(Array.head(issues), { + onSome: issue => ( + + {issue.message} + + ), + + onNone: () => <>, + })} + + ) + } +) {} diff --git a/packages/example/src/lib/form/TextFieldFormOptionalInput.tsx b/packages/example/src/lib/form/TextFieldFormOptionalInput.tsx new file mode 100644 index 0000000..4344f42 --- /dev/null +++ b/packages/example/src/lib/form/TextFieldFormOptionalInput.tsx @@ -0,0 +1,57 @@ +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/lib/input/TextFieldInput.tsx b/packages/example/src/lib/input/TextFieldInput.tsx index e0bf339..b06d1ff 100644 --- a/packages/example/src/lib/input/TextFieldInput.tsx +++ b/packages/example/src/lib/input/TextFieldInput.tsx @@ -29,12 +29,10 @@ export const TextFieldInput = (options: { ) = options.optional ? { optional: true, - // eslint-disable-next-line react-hooks/rules-of-hooks ...yield* useOptionalInput({ ...options, ...props as TextFieldOptionalInputProps }), } : { optional: false, - // eslint-disable-next-line react-hooks/rules-of-hooks ...yield* useInput({ ...options, ...props as TextFieldInputProps }), } diff --git a/packages/example/src/routes/form.tsx b/packages/example/src/routes/form.tsx index 83c74ee..cef0a5a 100644 --- a/packages/example/src/routes/form.tsx +++ b/packages/example/src/routes/form.tsx @@ -1,9 +1,9 @@ import { Button, Container, Flex } from "@radix-ui/themes" import { createFileRoute } from "@tanstack/react-router" -import { Effect, ParseResult, Schema } from "effect" +import { Console, Effect, Option, ParseResult, Schema } from "effect" import { Component, Form, Hooks } from "effect-fc" -import { useSubscribables } from "effect-fc/Hooks" import { TextFieldFormInput } from "@/lib/form/TextFieldFormInput" +import { TextFieldFormOptionalInput } from "@/lib/form/TextFieldFormOptionalInput" import { runtime } from "@/runtime" @@ -20,22 +20,28 @@ const email = Schema.pattern( const RegisterFormSchema = Schema.Struct({ email: Schema.String.pipe(email), password: Schema.String.pipe(Schema.minLength(3)), + iq: Schema.OptionFromSelf(Schema.NumberFromString), }) class RegisterForm extends Effect.Service()("RegisterForm", { scoped: Form.service({ - schema: Schema.transformOrFail( - Schema.encodedSchema(RegisterFormSchema), - Schema.typeSchema(RegisterFormSchema), - { - decode: v => Effect.andThen(Effect.sleep("500 millis"), ParseResult.succeed(v)), - encode: ParseResult.succeed, - }, + schema: RegisterFormSchema.pipe( + Schema.compose( + Schema.transformOrFail( + Schema.typeSchema(RegisterFormSchema), + Schema.typeSchema(RegisterFormSchema), + { + decode: v => Effect.andThen(Effect.sleep("500 millis"), ParseResult.succeed(v)), + encode: ParseResult.succeed, + }, + ), + ), ), - initialEncodedValue: { email: "", password: "" }, - submit: () => Effect.andThen( - Effect.sleep("500 millis"), - Effect.sync(() => alert("Done!")), + + initialEncodedValue: { email: "", password: "", iq: Option.none() }, + submit: v => Effect.sleep("500 millis").pipe( + Effect.andThen(Console.log(v)), + Effect.andThen(Effect.sync(() => alert("Done!"))), ), }) }) {} @@ -43,9 +49,10 @@ class RegisterForm extends Effect.Service()("RegisterForm", { class RegisterPage extends Component.makeUntraced("RegisterPage")(function*() { const form = yield* RegisterForm const submit = yield* Form.useSubmit(form) - const [canSubmit] = yield* useSubscribables(form.canSubmitSubscribable) + const [canSubmit] = yield* Hooks.useSubscribables(form.canSubmitSubscribable) const TextFieldFormInputFC = yield* TextFieldFormInput + const TextFieldFormOptionalInputFC = yield* TextFieldFormOptionalInput return ( @@ -64,6 +71,11 @@ class RegisterPage extends Component.makeUntraced("RegisterPage")(function*() { debounce="200 millis" /> + +