87 lines
2.8 KiB
TypeScript
87 lines
2.8 KiB
TypeScript
import { Button, Container, Flex } from "@radix-ui/themes"
|
|
import { createFileRoute } from "@tanstack/react-router"
|
|
import { Effect, ParseResult, Schema } from "effect"
|
|
import { Component, Form, Hooks } from "effect-fc"
|
|
import { useSubscribables } from "effect-fc/Hooks"
|
|
import { TextFieldFormInput } from "@/lib/form/TextFieldFormInput"
|
|
import { runtime } from "@/runtime"
|
|
|
|
|
|
const email = Schema.pattern<typeof Schema.String>(
|
|
/^(?!\.)(?!.*\.\.)([A-Z0-9_+-.]*)[A-Z0-9_+-]@([A-Z0-9][A-Z0-9-]*\.)+[A-Z]{2,}$/i,
|
|
|
|
{
|
|
identifier: "email",
|
|
title: "email",
|
|
message: () => "Not an email address",
|
|
},
|
|
)
|
|
|
|
const RegisterFormSchema = Schema.Struct({
|
|
email: Schema.String.pipe(email),
|
|
password: Schema.String.pipe(Schema.minLength(3)),
|
|
})
|
|
|
|
class RegisterForm extends Effect.Service<RegisterForm>()("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,
|
|
},
|
|
),
|
|
initialEncodedValue: { email: "", password: "" },
|
|
submit: () => Effect.andThen(
|
|
Effect.sleep("500 millis"),
|
|
Effect.sync(() => alert("Done!")),
|
|
),
|
|
})
|
|
}) {}
|
|
|
|
class RegisterPage extends Component.makeUntraced("RegisterPage")(function*() {
|
|
const form = yield* RegisterForm
|
|
const submit = yield* Form.useSubmit(form)
|
|
const [canSubmit] = yield* useSubscribables(form.canSubmitSubscribable)
|
|
|
|
const TextFieldFormInputFC = yield* TextFieldFormInput
|
|
|
|
return (
|
|
<Container width="300">
|
|
<form onSubmit={e => {
|
|
e.preventDefault()
|
|
void submit()
|
|
}}>
|
|
<Flex direction="column" gap="2">
|
|
<TextFieldFormInputFC
|
|
field={Form.useField(form, ["email"])}
|
|
debounce="200 millis"
|
|
/>
|
|
|
|
<TextFieldFormInputFC
|
|
field={Form.useField(form, ["password"])}
|
|
debounce="200 millis"
|
|
/>
|
|
|
|
<Button disabled={!canSubmit}>Submit</Button>
|
|
</Flex>
|
|
</form>
|
|
</Container>
|
|
)
|
|
}) {}
|
|
|
|
|
|
export const Route = createFileRoute("/form")({
|
|
component: Component.makeUntraced("RegisterRoute")(function*() {
|
|
const RegisterRouteFC = yield* Effect.provide(
|
|
RegisterPage,
|
|
yield* Hooks.useContext(RegisterForm.Default, { finalizerExecutionMode: "fork" }),
|
|
)
|
|
|
|
return <RegisterRouteFC />
|
|
}).pipe(
|
|
Component.withRuntime(runtime.context)
|
|
)
|
|
})
|