import type { Schema } from "effect" import { Context, Layer, Option } from "effect" import type React from "react" import { FormFramework } from "./FormFramework.js" import type { Path } from "./Path.js" export const NoDefaultValue = Symbol.for("FormField/NoDefaultValue") type NoDefaultValue = typeof NoDefaultValue class FormFieldClass< Self, A extends React.FC, S extends Schema.Schema.AnyNoContext > { private constructor( readonly tag: Context.Tag>, readonly schema: S, readonly defaultValue: S["Encoded"] | NoDefaultValue ) {} static withDefaultValue = < Self, A extends React.FC, S extends Schema.Schema.AnyNoContext >( tag: Context.Tag>, schema: S, defaultValue: S["Encoded"] | NoDefaultValue ) => new FormFieldClass(tag, schema, defaultValue) static withoutDefaultValue = < Self, A extends React.FC, S extends Schema.Schema.AnyNoContext >( tag: Context.Tag>, schema: S ) => new FormFieldClass(tag, schema, NoDefaultValue) decorate(): FormFieldClass { // @ts-expect-error "casting this to another ReactFC type" return this } getDefaultValue(): Option.Option { return Option.liftPredicate( this.defaultValue, (value) => value !== NoDefaultValue ) } matchDefaultValue({ withDefaultValue, withoutDefaultValue }: { withDefaultValue: (value: S["Encoded"]) => void withoutDefaultValue?: () => void }): void { if (this.defaultValue === NoDefaultValue) { withoutDefaultValue?.() } else { withDefaultValue(this.defaultValue) } } } export interface ComponentBuilder> { (_: { path: Path }): A } export type OfProps = FormFieldClass< any, React.FC, Schema.Schema.AnyNoContext > export type Any = FormFieldClass< any, React.FC, Schema.Schema.AnyNoContext > export const FormField = (id: Id) => < Self, A extends React.FC = React.FC, S_ extends Schema.Schema.AnyNoContext = Schema.Schema.AnyNoContext >() => { const tag = Context.Tag(id)>() return Object.assign(tag, { make: (props: { schema: S defaultValue: S["Encoded"] }): FormFieldClass => FormFieldClass.withDefaultValue(tag, props.schema, props.defaultValue), makeRequired: (props: { schema: S }): FormFieldClass => FormFieldClass.withoutDefaultValue( tag, // @ts-expect-error "schema.annotations looses the type" props.schema.annotations({ message: () => ({ message: "This field is required", override: true }) }) ), layerUncontrolled: (component: A) => Layer.effect( tag, FormFramework.use(({ registerUncontrolled: register }) => ({ path }) => register(component, path) as A) ), layerControlled: (component: A) => Layer.effect( tag, FormFramework.use(({ registerControlled }) => ({ path }) => registerControlled(component, path) as A) ) }) }