0.2.2 #31

Merged
Thilawyn merged 184 commits from next into master 2026-01-16 17:05:31 +01:00
3 changed files with 20 additions and 18 deletions
Showing only changes of commit cf4ba5805f - Show all commits

View File

@@ -55,7 +55,7 @@ const ComponentProto = Object.freeze({
this: Component<P, A, E, R> this: Component<P, A, E, R>
) { ) {
const self = this const self = this
// biome-ignore lint/style/noNonNullAssertion: context initialization // biome-ignore lint/style/noNonNullAssertion: React ref initialization
const runtimeRef = React.useRef<Runtime.Runtime<Exclude<R, Scope.Scope>>>(null!) const runtimeRef = React.useRef<Runtime.Runtime<Exclude<R, Scope.Scope>>>(null!)
runtimeRef.current = yield* Effect.runtime<Exclude<R, Scope.Scope>>() runtimeRef.current = yield* Effect.runtime<Exclude<R, Scope.Scope>>()

View File

@@ -16,7 +16,7 @@ extends Pipeable.Pipeable {
readonly [FormTypeId]: FormTypeId readonly [FormTypeId]: FormTypeId
readonly schema: Schema.Schema<A, I, R> readonly schema: Schema.Schema<A, I, R>
readonly submitFn: (value: NoInfer<A>) => Effect.Effect<SA, SE, SR> readonly onSubmit: (value: NoInfer<A>) => Effect.Effect<SA, SE, SR>
readonly debounce: Option.Option<Duration.DurationInput> readonly debounce: Option.Option<Duration.DurationInput>
readonly valueRef: SubscriptionRef.SubscriptionRef<Option.Option<A>> readonly valueRef: SubscriptionRef.SubscriptionRef<Option.Option<A>>
@@ -34,7 +34,7 @@ extends Pipeable.Class() implements Form<A, I, R, SA, SE, SR> {
constructor( constructor(
readonly schema: Schema.Schema<A, I, R>, readonly schema: Schema.Schema<A, I, R>,
readonly submitFn: (value: NoInfer<A>) => Effect.Effect<SA, SE, SR>, readonly onSubmit: (value: NoInfer<A>) => Effect.Effect<SA, SE, SR>,
readonly debounce: Option.Option<Duration.DurationInput>, readonly debounce: Option.Option<Duration.DurationInput>,
readonly valueRef: SubscriptionRef.SubscriptionRef<Option.Option<A>>, readonly valueRef: SubscriptionRef.SubscriptionRef<Option.Option<A>>,
@@ -55,7 +55,7 @@ export namespace make {
export interface Options<in out A, in out I, out R, in out SA = void, in out SE = A, out SR = never> { export interface Options<in out A, in out I, out R, in out SA = void, in out SE = A, out SR = never> {
readonly schema: Schema.Schema<A, I, R> readonly schema: Schema.Schema<A, I, R>
readonly initialEncodedValue: NoInfer<I> readonly initialEncodedValue: NoInfer<I>
readonly submitFn: (value: NoInfer<A>) => Effect.Effect<SA, SE, SR>, readonly onSubmit: (value: NoInfer<A>) => Effect.Effect<SA, SE, SR>,
readonly debounce?: Duration.DurationInput, readonly debounce?: Duration.DurationInput,
} }
} }
@@ -74,7 +74,7 @@ export const make: {
return new FormImpl( return new FormImpl(
options.schema, options.schema,
options.submitFn, options.onSubmit,
Option.fromNullable(options.debounce), Option.fromNullable(options.debounce),
valueRef, valueRef,
@@ -142,7 +142,7 @@ export const submit = <A, I, R, SA, SE, SR>(
Effect.andThen(identity), Effect.andThen(identity),
Effect.tap(Ref.set(self.submitStateRef, AsyncData.loading())), Effect.tap(Ref.set(self.submitStateRef, AsyncData.loading())),
Effect.andThen(flow( Effect.andThen(flow(
self.submitFn as (value: NoInfer<A>) => Effect.Effect<SA, SE | ParseResult.ParseError, SR>, self.onSubmit as (value: NoInfer<A>) => Effect.Effect<SA, SE | ParseResult.ParseError, SR>,
Effect.tapErrorTag("ParseError", e => Ref.set(self.errorRef, Option.some(e as ParseResult.ParseError))), Effect.tapErrorTag("ParseError", e => Ref.set(self.errorRef, Option.some(e as ParseResult.ParseError))),
Effect.exit, Effect.exit,
Effect.map(Exit.match({ Effect.map(Exit.match({

View File

@@ -39,7 +39,7 @@ class RegisterForm extends Effect.Service<RegisterForm>()("RegisterForm", {
), ),
initialEncodedValue: { email: "", password: "", birth: Option.none() }, initialEncodedValue: { email: "", password: "", birth: Option.none() },
submitFn: v => Effect.sleep("500 millis").pipe( onSubmit: v => Effect.sleep("500 millis").pipe(
Effect.andThen(Console.log(v)), Effect.andThen(Console.log(v)),
Effect.andThen(Effect.sync(() => alert("Done!"))), Effect.andThen(Effect.sync(() => alert("Done!"))),
), ),
@@ -47,7 +47,7 @@ class RegisterForm extends Effect.Service<RegisterForm>()("RegisterForm", {
}) })
}) {} }) {}
class RegisterPage extends Component.makeUntraced("RegisterPage")(function*() { class RegisterFormView extends Component.makeUntraced("RegisterFormView")(function*() {
const form = yield* RegisterForm const form = yield* RegisterForm
const submit = yield* Form.useSubmit(form) const submit = yield* Form.useSubmit(form)
const [canSubmit] = yield* Hooks.useSubscribables(form.canSubmitSubscribable) const [canSubmit] = yield* Hooks.useSubscribables(form.canSubmitSubscribable)
@@ -84,16 +84,18 @@ class RegisterPage extends Component.makeUntraced("RegisterPage")(function*() {
) )
}) {} }) {}
const RegisterPage = Component.makeUntraced("RegisterPage")(function*() {
export const Route = createFileRoute("/form")({ const RegisterFormViewFC = yield* Effect.provide(
component: Component.makeUntraced("RegisterRoute")(function*() { RegisterFormView,
const RegisterRouteFC = yield* Effect.provide(
RegisterPage,
yield* Hooks.useContext(RegisterForm.Default, { finalizerExecutionMode: "fork" }), yield* Hooks.useContext(RegisterForm.Default, { finalizerExecutionMode: "fork" }),
) )
return <RegisterRouteFC /> return <RegisterFormViewFC />
}).pipe( }).pipe(
Component.withRuntime(runtime.context) Component.withRuntime(runtime.context)
) )
export const Route = createFileRoute("/form")({
component: RegisterPage
}) })