0.1.4 (#5)
All checks were successful
Publish / publish (push) Successful in 21s
Lint / lint (push) Successful in 12s

Co-authored-by: Julien Valverdé <julien.valverde@mailo.com>
Reviewed-on: #5
This commit was merged in pull request #5.
This commit is contained in:
2025-10-02 18:18:23 +02:00
parent 831a808568
commit 9a3c91b50b
66 changed files with 1157 additions and 634 deletions

View File

@@ -0,0 +1,77 @@
import { Callout, Flex, Spinner, Switch, TextField } from "@radix-ui/themes"
import { Array, Option } from "effect"
import { Component, Form, Hooks } from "effect-fc"
interface Props
extends TextField.RootProps, Form.useInput.Options {
readonly optional?: false
readonly field: Form.FormField<any, string>
}
interface OptionalProps
extends Omit<TextField.RootProps, "defaultValue">, Form.useOptionalInput.Options<string> {
readonly optional: true
readonly field: Form.FormField<any, Option.Option<string>>
}
export type TextFieldFormInputProps = Props | OptionalProps
export class TextFieldFormInput extends Component.makeUntraced("TextFieldFormInput")(
function*(props: TextFieldFormInputProps) {
const input: (
| { readonly optional: true } & Form.useOptionalInput.Result<string>
| { readonly optional: false } & Form.useInput.Result<string>
) = props.optional
// biome-ignore lint/correctness/useHookAtTopLevel: "optional" reactivity not supported
? { optional: true, ...yield* Form.useOptionalInput(props.field, props) }
// biome-ignore lint/correctness/useHookAtTopLevel: "optional" reactivity not supported
: { optional: false, ...yield* Form.useInput(props.field, props) }
const [issues, isValidating, isSubmitting] = yield* Hooks.useSubscribables(
props.field.issuesSubscribable,
props.field.isValidatingSubscribable,
props.field.isSubmittingSubscribable,
)
return (
<Flex direction="column" gap="1">
<TextField.Root
value={input.value}
onChange={e => input.setValue(e.target.value)}
disabled={(input.optional && !input.enabled) || isSubmitting}
{...props}
>
{input.optional &&
<TextField.Slot side="left">
<Switch
size="1"
checked={input.enabled}
onCheckedChange={input.setEnabled}
/>
</TextField.Slot>
}
{isValidating &&
<TextField.Slot side="right">
<Spinner />
</TextField.Slot>
}
{props.children}
</TextField.Root>
{Option.match(Array.head(issues), {
onSome: issue => (
<Callout.Root>
<Callout.Text>{issue.message}</Callout.Text>
</Callout.Root>
),
onNone: () => <></>,
})}
</Flex>
)
}
) {}

View File

@@ -1,7 +1,8 @@
import { Callout, Flex, TextArea, TextAreaProps } from "@radix-ui/themes"
import { Array, Equivalence, Option, ParseResult, Schema, Struct } from "effect"
/** biome-ignore-all lint/correctness/useHookAtTopLevel: effect-fc HOC */
import { Callout, Flex, TextArea, type TextAreaProps } from "@radix-ui/themes"
import { Array, type Equivalence, Option, ParseResult, type Schema, Struct } from "effect"
import { Component } from "effect-fc"
import { useInput } from "effect-fc/hooks"
import { useInput } from "effect-fc/Hooks"
import * as React from "react"
@@ -15,7 +16,7 @@ export const TextAreaInput = <A, R>(options: {
React.JSX.Element,
ParseResult.ParseError,
R
> => Component.makeUntraced(function* TextFieldInput(props) {
> => Component.makeUntraced("TextFieldInput")(function*(props) {
const input = yield* useInput({ ...options, ...props })
const issue = React.useMemo(() => input.error.pipe(
Option.map(ParseResult.ArrayFormatter.formatErrorSync),

View File

@@ -1,7 +1,8 @@
/** biome-ignore-all lint/correctness/useHookAtTopLevel: effect-fc HOC */
import { Callout, Checkbox, Flex, TextField } from "@radix-ui/themes"
import { Array, Equivalence, Option, ParseResult, Schema, Struct } from "effect"
import { Component, Memo } from "effect-fc"
import { useInput, useOptionalInput } from "effect-fc/hooks"
import { Array, type Equivalence, Option, ParseResult, type Schema, Struct } from "effect"
import { Component } from "effect-fc"
import { useInput, useOptionalInput } from "effect-fc/Hooks"
import * as React from "react"
@@ -18,7 +19,7 @@ export const TextFieldInput = <A, R, O extends boolean = false>(options: {
readonly optional?: O
readonly schema: Schema.Schema<A, string, R>
readonly equivalence?: Equivalence.Equivalence<A>
}) => Component.makeUntraced(function* TextFieldInput(props: O extends true
}) => Component.makeUntraced("TextFieldInput")(function*(props: O extends true
? TextFieldOptionalInputProps<A, R>
: TextFieldInputProps<A, R>
) {
@@ -28,12 +29,10 @@ export const TextFieldInput = <A, R, O extends boolean = false>(options: {
) = options.optional
? {
optional: true,
// eslint-disable-next-line react-hooks/rules-of-hooks
...yield* useOptionalInput({ ...options, ...props as TextFieldOptionalInputProps<A, R> }),
}
: {
optional: false,
// eslint-disable-next-line react-hooks/rules-of-hooks
...yield* useInput({ ...options, ...props as TextFieldInputProps<A, R> }),
}
@@ -67,4 +66,4 @@ export const TextFieldInput = <A, R, O extends boolean = false>(options: {
}
</Flex>
)
}).pipe(Memo.memo)
})