0.1.4 #5
@@ -28,7 +28,6 @@ extends Pipeable.Pipeable {
|
|||||||
class FormImpl<in out A, in out I = A, out R = never, in out SA = void, in out SE = A, out SR = never>
|
class FormImpl<in out A, in out I = A, out R = never, in out SA = void, in out SE = A, out SR = never>
|
||||||
extends Pipeable.Class() implements Form<A, I, R, SA, SE, SR> {
|
extends Pipeable.Class() implements Form<A, I, R, SA, SE, SR> {
|
||||||
readonly [FormTypeId]: FormTypeId = FormTypeId
|
readonly [FormTypeId]: FormTypeId = FormTypeId
|
||||||
readonly canSubmitSubscribable: Subscribable.Subscribable<boolean>
|
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
readonly schema: Schema.Schema<A, I, R>,
|
readonly schema: Schema.Schema<A, I, R>,
|
||||||
@@ -39,10 +38,44 @@ extends Pipeable.Class() implements Form<A, I, R, SA, SE, SR> {
|
|||||||
readonly errorRef: SubscriptionRef.SubscriptionRef<Option.Option<ParseResult.ParseError>>,
|
readonly errorRef: SubscriptionRef.SubscriptionRef<Option.Option<ParseResult.ParseError>>,
|
||||||
readonly isValidatingRef: SubscriptionRef.SubscriptionRef<boolean>,
|
readonly isValidatingRef: SubscriptionRef.SubscriptionRef<boolean>,
|
||||||
readonly submitStateRef: SubscriptionRef.SubscriptionRef<AsyncData.AsyncData<SA, SE>>,
|
readonly submitStateRef: SubscriptionRef.SubscriptionRef<AsyncData.AsyncData<SA, SE>>,
|
||||||
|
|
||||||
|
readonly canSubmitSubscribable: Subscribable.Subscribable<boolean>,
|
||||||
) {
|
) {
|
||||||
super()
|
super()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
this.canSubmitSubscribable = pipe(
|
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> {
|
||||||
|
readonly schema: Schema.Schema<A, I, R>
|
||||||
|
readonly initialEncodedValue: NoInfer<I>
|
||||||
|
readonly submit: (value: NoInfer<A>) => Effect.Effect<SA, SE, SR>
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export const make: {
|
||||||
|
<A, I = A, R = never, SA = void, SE = A, SR = never>(
|
||||||
|
options: make.Options<A, I, R, SA, SE, SR>
|
||||||
|
): Effect.Effect<Form<A, I, R, SA, SE, SR>>
|
||||||
|
} = Effect.fnUntraced(function* <A, I = A, R = never, SA = void, SE = A, SR = never>(
|
||||||
|
options: make.Options<A, I, R, SA, SE, SR>
|
||||||
|
) {
|
||||||
|
const valueRef = yield* SubscriptionRef.make(Option.none<A>())
|
||||||
|
const errorRef = yield* SubscriptionRef.make(Option.none<ParseResult.ParseError>())
|
||||||
|
const isValidatingRef = yield* SubscriptionRef.make(false)
|
||||||
|
const submitStateRef = yield* SubscriptionRef.make(AsyncData.noData<SA, SE>())
|
||||||
|
|
||||||
|
return new FormImpl(
|
||||||
|
options.schema,
|
||||||
|
options.submit,
|
||||||
|
|
||||||
|
valueRef,
|
||||||
|
yield* SubscriptionRef.make(options.initialEncodedValue),
|
||||||
|
errorRef,
|
||||||
|
isValidatingRef,
|
||||||
|
submitStateRef,
|
||||||
|
|
||||||
|
pipe(
|
||||||
<A>([value, error, isValidating, submitState]: readonly [
|
<A>([value, error, isValidating, submitState]: readonly [
|
||||||
Option.Option<A>,
|
Option.Option<A>,
|
||||||
Option.Option<ParseResult.ParseError>,
|
Option.Option<ParseResult.ParseError>,
|
||||||
@@ -64,34 +97,7 @@ extends Pipeable.Class() implements Form<A, I, R, SA, SE, SR> {
|
|||||||
)
|
)
|
||||||
},
|
},
|
||||||
}),
|
}),
|
||||||
)
|
),
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
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> {
|
|
||||||
readonly schema: Schema.Schema<A, I, R>
|
|
||||||
readonly initialEncodedValue: NoInfer<I>
|
|
||||||
readonly submit: (value: NoInfer<A>) => Effect.Effect<SA, SE, SR>
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export const make: {
|
|
||||||
<A, I = A, R = never, SA = void, SE = A, SR = never>(
|
|
||||||
options: make.Options<A, I, R, SA, SE, SR>
|
|
||||||
): Effect.Effect<Form<A, I, R, SA, SE, SR>>
|
|
||||||
} = Effect.fnUntraced(function* <A, I = A, R = never, SA = void, SE = A, SR = never>(
|
|
||||||
options: make.Options<A, I, R, SA, SE, SR>
|
|
||||||
) {
|
|
||||||
return new FormImpl(
|
|
||||||
options.schema,
|
|
||||||
options.submit,
|
|
||||||
|
|
||||||
yield* SubscriptionRef.make(Option.none<A>()),
|
|
||||||
yield* SubscriptionRef.make(options.initialEncodedValue),
|
|
||||||
yield* SubscriptionRef.make(Option.none<ParseResult.ParseError>()),
|
|
||||||
yield* SubscriptionRef.make(false),
|
|
||||||
yield* SubscriptionRef.make(AsyncData.noData<SA, SE>()),
|
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
|
|
||||||
@@ -292,3 +298,52 @@ export const useInput: {
|
|||||||
|
|
||||||
return { value, setValue, issues }
|
return { value, setValue, issues }
|
||||||
})
|
})
|
||||||
|
|
||||||
|
export namespace useOptionalInput {
|
||||||
|
export interface Options<I extends Option.Option<any>> extends useInput.Options {
|
||||||
|
readonly defaultValue: Option.Option.Value<I>
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface Result<T> extends useInput.Result<T> {}
|
||||||
|
}
|
||||||
|
|
||||||
|
export const useOptionalInput: {
|
||||||
|
<A, I extends Option.Option<any>>(
|
||||||
|
field: FormField<A, I>,
|
||||||
|
options: useOptionalInput.Options<I>,
|
||||||
|
): Effect.Effect<useOptionalInput.Result<Option.Option.Value<I>>, NoSuchElementException>
|
||||||
|
} = Effect.fnUntraced(function* <A, I extends Option.Option<any>>(
|
||||||
|
field: FormField<A, I>,
|
||||||
|
options: useOptionalInput.Options<I>,
|
||||||
|
) {
|
||||||
|
const internalValueRef = yield* Hooks.useMemo(() => field.encodedValueRef.pipe(
|
||||||
|
Effect.map(Option.match({
|
||||||
|
onSome: identity,
|
||||||
|
onNone: () => options.defaultValue,
|
||||||
|
})),
|
||||||
|
Effect.andThen(SubscriptionRef.make),
|
||||||
|
), [field])
|
||||||
|
const [value, setValue] = yield* Hooks.useRefState(internalValueRef)
|
||||||
|
const [issues] = yield* Hooks.useSubscribables(field.issuesSubscribable)
|
||||||
|
|
||||||
|
yield* Hooks.useFork(() => Effect.all([
|
||||||
|
Stream.runForEach(
|
||||||
|
Stream.drop(field.encodedValueRef, 1),
|
||||||
|
upstreamEncodedValue => Effect.whenEffect(
|
||||||
|
Ref.set(internalValueRef, upstreamEncodedValue),
|
||||||
|
Effect.andThen(internalValueRef, internalValue => !Equal.equals(upstreamEncodedValue, internalValue)),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
|
||||||
|
Stream.runForEach(
|
||||||
|
internalValueRef.changes.pipe(
|
||||||
|
Stream.drop(1),
|
||||||
|
Stream.changesWith(Equivalence.strict()),
|
||||||
|
options?.debounce ? Stream.debounce(options.debounce) : identity,
|
||||||
|
),
|
||||||
|
internalValue => Ref.set(field.encodedValueRef, internalValue),
|
||||||
|
),
|
||||||
|
], { concurrency: "unbounded" }), [field, internalValueRef])
|
||||||
|
|
||||||
|
return { value, setValue, issues }
|
||||||
|
})
|
||||||
|
|||||||
Reference in New Issue
Block a user