mirror of
https://github.com/langgenius/dify.git
synced 2026-09-06 09:14:25 +08:00
211 lines
5.5 KiB
TypeScript
211 lines
5.5 KiB
TypeScript
import type {
|
|
AnyFieldLikeMeta,
|
|
DeepKeys,
|
|
DeepValue,
|
|
FormAsyncValidateOrFn,
|
|
FormValidateOrFn,
|
|
UpdateMetaOptions,
|
|
Updater,
|
|
ValidationCause,
|
|
} from '@tanstack/form-core'
|
|
import type { Atom, WritableAtom } from 'jotai'
|
|
import { FormApi } from '@tanstack/form-core'
|
|
import { atom } from 'jotai'
|
|
import { atomWithLazy } from 'jotai/vanilla/utils'
|
|
|
|
type FormValidator<TValues> = FormValidateOrFn<TValues> | undefined
|
|
type FormAsyncValidator<TValues> = FormAsyncValidateOrFn<TValues> | undefined
|
|
|
|
export type TanStackFormApi<TValues, TSubmitMeta = never> = FormApi<
|
|
TValues,
|
|
FormValidator<TValues>,
|
|
FormValidator<TValues>,
|
|
FormAsyncValidator<TValues>,
|
|
FormValidator<TValues>,
|
|
FormAsyncValidator<TValues>,
|
|
FormValidator<TValues>,
|
|
FormAsyncValidator<TValues>,
|
|
FormValidator<TValues>,
|
|
FormAsyncValidator<TValues>,
|
|
FormAsyncValidator<TValues>,
|
|
TSubmitMeta
|
|
>
|
|
|
|
export type FormState<TValues, TSubmitMeta = never> = TanStackFormApi<TValues, TSubmitMeta>['state']
|
|
|
|
type FormOptionsInput<TValues, TSubmitMeta = never> = TanStackFormApi<
|
|
TValues,
|
|
TSubmitMeta
|
|
>['options']
|
|
|
|
export type FormFieldAtomValue<TValue> = {
|
|
value: TValue
|
|
meta: AnyFieldLikeMeta | undefined
|
|
}
|
|
|
|
export type FormFieldUpdate<TValues, TField extends DeepKeys<TValues> = DeepKeys<TValues>> =
|
|
TField extends DeepKeys<TValues>
|
|
? {
|
|
name: TField
|
|
value: Updater<DeepValue<TValues, TField>>
|
|
options?: UpdateMetaOptions
|
|
}
|
|
: never
|
|
|
|
export type FormAtomInstance<TValues, TSubmitMeta = never> = {
|
|
api: TanStackFormApi<TValues, TSubmitMeta>
|
|
stateAtom: Atom<FormState<TValues, TSubmitMeta>>
|
|
}
|
|
|
|
export type FormAtom<TValues, TSubmitMeta = never> = Atom<FormAtomInstance<TValues, TSubmitMeta>>
|
|
|
|
export type FormAtoms<TValues, TSubmitMeta = never> = {
|
|
formAtom: FormAtom<TValues, TSubmitMeta>
|
|
stateAtom: Atom<FormState<TValues, TSubmitMeta>>
|
|
valuesAtom: Atom<TValues>
|
|
isSubmittingAtom: Atom<boolean>
|
|
setFieldAtom: WritableAtom<null, [FormFieldUpdate<TValues>], void>
|
|
fieldAtom: <TField extends DeepKeys<TValues>>(
|
|
name: TField,
|
|
) => WritableAtom<
|
|
FormFieldAtomValue<DeepValue<TValues, TField>>,
|
|
[Updater<DeepValue<TValues, TField>>, UpdateMetaOptions?],
|
|
void
|
|
>
|
|
validateAtom: WritableAtom<null, [ValidationCause], unknown | Promise<unknown>>
|
|
submitAtom: WritableAtom<null, [], Promise<void>>
|
|
}
|
|
|
|
function createFormInstance<TValues, TSubmitMeta = never>(
|
|
api: TanStackFormApi<TValues, TSubmitMeta>,
|
|
): FormAtomInstance<TValues, TSubmitMeta> {
|
|
const stateAtom = atom(api.state)
|
|
|
|
stateAtom.onMount = (setFormState) => {
|
|
const mountCleanup = api.mount()
|
|
setFormState(api.state)
|
|
|
|
const subscription = api.store.subscribe(() => {
|
|
setFormState(api.state)
|
|
})
|
|
|
|
return () => {
|
|
subscription.unsubscribe()
|
|
mountCleanup()
|
|
}
|
|
}
|
|
|
|
return {
|
|
api,
|
|
stateAtom,
|
|
}
|
|
}
|
|
|
|
function setFormFieldValue<TValues, TSubmitMeta, TField extends DeepKeys<TValues>>(
|
|
form: FormAtomInstance<TValues, TSubmitMeta>,
|
|
name: TField,
|
|
value: Updater<DeepValue<TValues, TField>>,
|
|
options?: UpdateMetaOptions,
|
|
) {
|
|
const shouldValidate =
|
|
!options?.dontValidate && !(options?.dontUpdateMeta && !form.api.getFieldMeta(name)?.isTouched)
|
|
|
|
form.api.setFieldValue(
|
|
name,
|
|
value,
|
|
shouldValidate ? options : { ...(options ?? {}), dontValidate: true },
|
|
)
|
|
|
|
if (!shouldValidate) return
|
|
|
|
const fieldMeta = form.api.getFieldMeta(name)
|
|
if (!fieldMeta?.errorMap.onSubmit) return
|
|
|
|
if (fieldMeta.errorMap.onChange || fieldMeta.errorMap.onDynamic) return
|
|
|
|
form.api.setFieldMeta(name, (prev) => ({
|
|
...prev,
|
|
errorMap: {
|
|
...prev.errorMap,
|
|
onSubmit: undefined,
|
|
},
|
|
errorSourceMap: {
|
|
...prev.errorSourceMap,
|
|
onSubmit: undefined,
|
|
},
|
|
}))
|
|
}
|
|
|
|
export function atomWithForm<TValues, TSubmitMeta = never>(
|
|
options: FormOptionsInput<TValues, TSubmitMeta>,
|
|
): FormAtom<TValues, TSubmitMeta> {
|
|
return atomWithLazy(() => createFormInstance(new FormApi(options)))
|
|
}
|
|
|
|
export function createFormAtoms<TValues, TSubmitMeta = never>(
|
|
formAtom: FormAtom<TValues, TSubmitMeta>,
|
|
): FormAtoms<TValues, TSubmitMeta> {
|
|
const stateAtom = atom((get) => {
|
|
const form = get(formAtom)
|
|
return get(form.stateAtom)
|
|
})
|
|
|
|
const valuesAtom = atom((get) => {
|
|
return get(stateAtom).values
|
|
})
|
|
|
|
const isSubmittingAtom = atom((get) => {
|
|
return get(stateAtom).isSubmitting
|
|
})
|
|
|
|
const setFieldAtom = atom<null, [FormFieldUpdate<TValues>], void>(null, (get, _set, update) => {
|
|
setFormFieldValue(get(formAtom), update.name, update.value, update.options)
|
|
})
|
|
|
|
function fieldAtom<TField extends DeepKeys<TValues>>(
|
|
name: TField,
|
|
): WritableAtom<
|
|
FormFieldAtomValue<DeepValue<TValues, TField>>,
|
|
[Updater<DeepValue<TValues, TField>>, UpdateMetaOptions?],
|
|
void
|
|
> {
|
|
return atom(
|
|
(get) => {
|
|
const form = get(formAtom)
|
|
const state = get(form.stateAtom)
|
|
const api = form.api
|
|
|
|
return {
|
|
value: api.getFieldValue(name),
|
|
meta: state.fieldMeta[name],
|
|
}
|
|
},
|
|
(get, _set, value, options) => {
|
|
setFormFieldValue(get(formAtom), name, value, options)
|
|
},
|
|
)
|
|
}
|
|
|
|
const validateAtom = atom<null, [ValidationCause], unknown | Promise<unknown>>(
|
|
null,
|
|
(get, _set, cause) => {
|
|
return get(formAtom).api.validate(cause)
|
|
},
|
|
)
|
|
|
|
const submitAtom = atom<null, [], Promise<void>>(null, (get) => {
|
|
return get(formAtom).api.handleSubmit()
|
|
})
|
|
|
|
return {
|
|
formAtom,
|
|
stateAtom,
|
|
valuesAtom,
|
|
isSubmittingAtom,
|
|
setFieldAtom,
|
|
fieldAtom,
|
|
validateAtom,
|
|
submitAtom,
|
|
}
|
|
}
|