From 4d5c188599f902829ae0335a4b97a45a507df900 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julien=20Valverd=C3=A9?= Date: Wed, 12 Nov 2025 07:39:47 +0100 Subject: [PATCH] Refactor Form --- packages/effect-fc/src/Form.ts | 41 +++++++++++++++++++++++++++++++--- 1 file changed, 38 insertions(+), 3 deletions(-) diff --git a/packages/effect-fc/src/Form.ts b/packages/effect-fc/src/Form.ts index 15ca682..3f8fa6a 100644 --- a/packages/effect-fc/src/Form.ts +++ b/packages/effect-fc/src/Form.ts @@ -1,4 +1,4 @@ -import { Array, Cause, Chunk, type Duration, Effect, Equal, Exit, Fiber, flow, identity, Option, ParseResult, Pipeable, Predicate, Ref, Schema, type Scope, Stream } from "effect" +import { Array, Cause, Chunk, type Duration, Effect, Equal, Exit, Fiber, flow, Hash, identity, Option, ParseResult, Pipeable, Predicate, Ref, Schema, type Scope, Stream } from "effect" import type { NoSuchElementException } from "effect/Cause" import type * as React from "react" import * as Component from "./Component.js" @@ -54,6 +54,21 @@ extends Pipeable.Class() implements Form { } } +const FormFieldKeySymbol = Symbol.for("@effect-fc/Form/FormFieldKeySymbol") +class FormFieldKey implements Equal.Equal { + [FormFieldKeySymbol] = FormFieldKeySymbol + constructor(readonly a: PropertyPath.PropertyPath) {} + + [Equal.symbol](that: Equal.Equal) { + return Predicate.hasProperty(that, FormFieldKeySymbol) + ? PropertyPath.equivalence(this.a, (that as unknown as FormFieldKey).a) + : false + } + [Hash.symbol]() { + return 0 + } +} + export const isForm = (u: unknown): u is Form => Predicate.hasProperty(u, FormTypeId) export namespace make { @@ -204,8 +219,8 @@ export const service = Effect.forkScoped(run(form)), ) -export const field = >>( - self: Form, +export const field = >>( + self: Form, path: P, ): FormField, PropertyPath.ValueFromPath> => new FormFieldImpl( Subscribable.mapEffect(self.valueRef, Option.match({ @@ -256,6 +271,26 @@ extends Pipeable.Class() implements FormField { export const isFormField = (u: unknown): u is FormField => Predicate.hasProperty(u, FormFieldTypeId) +export const makeFormField = >>( + self: Form, + path: P, +): FormField, PropertyPath.ValueFromPath> => new FormFieldImpl( + Subscribable.mapEffect(self.valueRef, Option.match({ + onSome: v => Option.map(PropertyPath.get(v, path), Option.some), + onNone: () => Option.some(Option.none()), + })), + SubscriptionSubRef.makeFromPath(self.encodedValueRef, path), + Subscribable.mapEffect(self.errorRef, Option.match({ + onSome: flow( + ParseResult.ArrayFormatter.formatError, + Effect.map(Array.filter(issue => PropertyPath.equivalence(issue.path, path))), + ), + onNone: () => Effect.succeed([]), + })), + Subscribable.map(self.validationFiberRef, Option.isSome), + Subscribable.map(self.submitResultRef, result => Result.isRunning(result) || Result.isRefreshing(result)), +) + export namespace useInput { export interface Options {