import { FormGroupApi, functionalUpdate } from '@tanstack/form-core' import { createComponent, createComputed, createSignal, onCleanup, onMount, } from 'solid-js' import { useStore } from '@tanstack/solid-store' import type { DeepKeys, DeepValue, FormAsyncValidateOrFn, FormGroupApiOptions, FormGroupAsyncValidateOrFn, FormGroupOptions, FormGroupValidateOrFn, FormValidateOrFn, } from '@tanstack/form-core' import type { Accessor, JSX, JSXElement } from 'solid-js' // ugly way to trick solid into triggering updates for changes on the formGroupApi function makeFormGroupReactive< TParentData, TName extends DeepKeys, TData extends DeepValue, TOnMount extends undefined | FormGroupValidateOrFn, TOnChange extends | undefined | FormGroupValidateOrFn, TOnChangeAsync extends | undefined | FormGroupAsyncValidateOrFn, TOnBlur extends undefined | FormGroupValidateOrFn, TOnBlurAsync extends | undefined | FormGroupAsyncValidateOrFn, TOnSubmit extends | undefined | FormGroupValidateOrFn, TOnSubmitAsync extends | undefined | FormGroupAsyncValidateOrFn, TOnDynamic extends | undefined | FormGroupValidateOrFn, TOnDynamicAsync extends | undefined | FormGroupAsyncValidateOrFn, TSubmitMeta, TFormOnMount extends undefined | FormValidateOrFn, TFormOnChange extends undefined | FormValidateOrFn, TFormOnChangeAsync extends undefined | FormAsyncValidateOrFn, TFormOnBlur extends undefined | FormValidateOrFn, TFormOnBlurAsync extends undefined | FormAsyncValidateOrFn, TFormOnSubmit extends undefined | FormValidateOrFn, TFormOnSubmitAsync extends undefined | FormAsyncValidateOrFn, TFormOnDynamic extends undefined | FormValidateOrFn, TFormOnDynamicAsync extends undefined | FormAsyncValidateOrFn, TFormOnServer extends undefined | FormAsyncValidateOrFn, TParentSubmitMeta, >( formGroupApi: FormGroupApi< TParentData, TName, TData, TOnMount, TOnChange, TOnChangeAsync, TOnBlur, TOnBlurAsync, TOnSubmit, TOnSubmitAsync, TOnDynamic, TOnDynamicAsync, TSubmitMeta, TFormOnMount, TFormOnChange, TFormOnChangeAsync, TFormOnBlur, TFormOnBlurAsync, TFormOnSubmit, TFormOnSubmitAsync, TFormOnDynamic, TFormOnDynamicAsync, TFormOnServer, TParentSubmitMeta >, ): () => FormGroupApi< TParentData, TName, TData, TOnMount, TOnChange, TOnChangeAsync, TOnBlur, TOnBlurAsync, TOnSubmit, TOnSubmitAsync, TOnDynamic, TOnDynamicAsync, TSubmitMeta, TFormOnMount, TFormOnChange, TFormOnChangeAsync, TFormOnBlur, TFormOnBlurAsync, TFormOnSubmit, TFormOnSubmitAsync, TFormOnDynamic, TFormOnDynamicAsync, TFormOnServer, TParentSubmitMeta > { const [group, setGroup] = createSignal(formGroupApi, { equals: false }) // Handle shallow comparison to make sure that Derived doesn't create a new setGroup call every time const store = useStore(formGroupApi.store, (store) => store) // Run before initial render createComputed(() => { // Use the store to track dependencies store() setGroup(formGroupApi) }) return group } export function createFormGroup< TParentData, TName extends DeepKeys, TData extends DeepValue, TOnMount extends undefined | FormGroupValidateOrFn, TOnChange extends | undefined | FormGroupValidateOrFn, TOnChangeAsync extends | undefined | FormGroupAsyncValidateOrFn, TOnBlur extends undefined | FormGroupValidateOrFn, TOnBlurAsync extends | undefined | FormGroupAsyncValidateOrFn, TOnSubmit extends | undefined | FormGroupValidateOrFn, TOnSubmitAsync extends | undefined | FormGroupAsyncValidateOrFn, TOnDynamic extends | undefined | FormGroupValidateOrFn, TOnDynamicAsync extends | undefined | FormGroupAsyncValidateOrFn, TSubmitMeta, TFormOnMount extends undefined | FormValidateOrFn, TFormOnChange extends undefined | FormValidateOrFn, TFormOnChangeAsync extends undefined | FormAsyncValidateOrFn, TFormOnBlur extends undefined | FormValidateOrFn, TFormOnBlurAsync extends undefined | FormAsyncValidateOrFn, TFormOnSubmit extends undefined | FormValidateOrFn, TFormOnSubmitAsync extends undefined | FormAsyncValidateOrFn, TFormOnDynamic extends undefined | FormValidateOrFn, TFormOnDynamicAsync extends undefined | FormAsyncValidateOrFn, TFormOnServer extends undefined | FormAsyncValidateOrFn, TParentSubmitMeta, >( opts: () => FormGroupApiOptions< TParentData, TName, TData, TOnMount, TOnChange, TOnChangeAsync, TOnBlur, TOnBlurAsync, TOnSubmit, TOnSubmitAsync, TOnDynamic, TOnDynamicAsync, TSubmitMeta, TFormOnMount, TFormOnChange, TFormOnChangeAsync, TFormOnBlur, TFormOnBlurAsync, TFormOnSubmit, TFormOnSubmitAsync, TFormOnDynamic, TFormOnDynamicAsync, TFormOnServer, TParentSubmitMeta >, ) { const options = opts() const api = new FormGroupApi(options) let mounted = false // Instantiates form group meta and removes it when unrendered onMount(() => { const cleanupFn = api.mount() mounted = true onCleanup(() => { cleanupFn() mounted = false }) }) /** * formGroupApi.update should not have any side effects. Think of it like a `useRef` * that we need to keep updated every render with the most up-to-date information. * * createComputed to make sure this effect runs before render effects */ createComputed(() => { if (!mounted) return api.update(opts()) }) return makeFormGroupReactive< TParentData, TName, TData, TOnMount, TOnChange, TOnChangeAsync, TOnBlur, TOnBlurAsync, TOnSubmit, TOnSubmitAsync, TOnDynamic, TOnDynamicAsync, TSubmitMeta, TFormOnMount, TFormOnChange, TFormOnChangeAsync, TFormOnBlur, TFormOnBlurAsync, TFormOnSubmit, TFormOnSubmitAsync, TFormOnDynamic, TFormOnDynamicAsync, TFormOnServer, TParentSubmitMeta >(api) } interface FormGroupComponentBoundProps< TParentData, TName extends DeepKeys, TData extends DeepValue, TOnMount extends undefined | FormGroupValidateOrFn, TOnChange extends | undefined | FormGroupValidateOrFn, TOnChangeAsync extends | undefined | FormGroupAsyncValidateOrFn, TOnBlur extends undefined | FormGroupValidateOrFn, TOnBlurAsync extends | undefined | FormGroupAsyncValidateOrFn, TOnSubmit extends | undefined | FormGroupValidateOrFn, TOnSubmitAsync extends | undefined | FormGroupAsyncValidateOrFn, TOnDynamic extends | undefined | FormGroupValidateOrFn, TOnDynamicAsync extends | undefined | FormGroupAsyncValidateOrFn, TSubmitMeta, TFormOnMount extends undefined | FormValidateOrFn, TFormOnChange extends undefined | FormValidateOrFn, TFormOnChangeAsync extends undefined | FormAsyncValidateOrFn, TFormOnBlur extends undefined | FormValidateOrFn, TFormOnBlurAsync extends undefined | FormAsyncValidateOrFn, TFormOnSubmit extends undefined | FormValidateOrFn, TFormOnSubmitAsync extends undefined | FormAsyncValidateOrFn, TFormOnDynamic extends undefined | FormValidateOrFn, TFormOnDynamicAsync extends undefined | FormAsyncValidateOrFn, TFormOnServer extends undefined | FormAsyncValidateOrFn, TParentSubmitMeta, ExtendedApi = {}, > extends FormGroupOptions< TParentData, TName, TData, TOnMount, TOnChange, TOnChangeAsync, TOnBlur, TOnBlurAsync, TOnSubmit, TOnSubmitAsync, TOnDynamic, TOnDynamicAsync, TSubmitMeta, TFormOnMount, TFormOnChange, TFormOnChangeAsync, TFormOnBlur, TFormOnBlurAsync, TFormOnSubmit, TFormOnSubmitAsync, TFormOnDynamic, TFormOnDynamicAsync, TFormOnServer, TParentSubmitMeta > { children: ( formGroupApi: Accessor< FormGroupApi< TParentData, TName, TData, TOnMount, TOnChange, TOnChangeAsync, TOnBlur, TOnBlurAsync, TOnSubmit, TOnSubmitAsync, TOnDynamic, TOnDynamicAsync, TSubmitMeta, TFormOnMount, TFormOnChange, TFormOnChangeAsync, TFormOnBlur, TFormOnBlurAsync, TFormOnSubmit, TFormOnSubmitAsync, TFormOnDynamic, TFormOnDynamicAsync, TFormOnServer, TParentSubmitMeta > > & ExtendedApi, ) => JSX.Element } /** * A type alias representing a form group component for a specific form data type. */ export type FormGroupComponent< in out TParentData, in out TFormOnMount extends undefined | FormValidateOrFn, in out TFormOnChange extends undefined | FormValidateOrFn, in out TFormOnChangeAsync extends | undefined | FormAsyncValidateOrFn, in out TFormOnBlur extends undefined | FormValidateOrFn, in out TFormOnBlurAsync extends | undefined | FormAsyncValidateOrFn, in out TFormOnSubmit extends undefined | FormValidateOrFn, in out TFormOnSubmitAsync extends | undefined | FormAsyncValidateOrFn, in out TFormOnDynamic extends undefined | FormValidateOrFn, in out TFormOnDynamicAsync extends | undefined | FormAsyncValidateOrFn, in out TFormOnServer extends undefined | FormAsyncValidateOrFn, in out TParentSubmitMeta, in out ExtendedApi = {}, > = < const TName extends DeepKeys, TData extends DeepValue, TOnMount extends undefined | FormGroupValidateOrFn, TOnChange extends | undefined | FormGroupValidateOrFn, TOnChangeAsync extends | undefined | FormGroupAsyncValidateOrFn, TOnBlur extends undefined | FormGroupValidateOrFn, TOnBlurAsync extends | undefined | FormGroupAsyncValidateOrFn, TOnSubmit extends | undefined | FormGroupValidateOrFn, TOnSubmitAsync extends | undefined | FormGroupAsyncValidateOrFn, TOnDynamic extends | undefined | FormGroupValidateOrFn, TOnDynamicAsync extends | undefined | FormGroupAsyncValidateOrFn, TSubmitMeta, >( props: FormGroupComponentBoundProps< TParentData, TName, TData, TOnMount, TOnChange, TOnChangeAsync, TOnBlur, TOnBlurAsync, TOnSubmit, TOnSubmitAsync, TOnDynamic, TOnDynamicAsync, TSubmitMeta, TFormOnMount, TFormOnChange, TFormOnChangeAsync, TFormOnBlur, TFormOnBlurAsync, TFormOnSubmit, TFormOnSubmitAsync, TFormOnDynamic, TFormOnDynamicAsync, TFormOnServer, TParentSubmitMeta, ExtendedApi >, ) => JSXElement interface FormGroupComponentProps< TParentData, TName extends DeepKeys, TData extends DeepValue, TOnMount extends undefined | FormGroupValidateOrFn, TOnChange extends | undefined | FormGroupValidateOrFn, TOnChangeAsync extends | undefined | FormGroupAsyncValidateOrFn, TOnBlur extends undefined | FormGroupValidateOrFn, TOnBlurAsync extends | undefined | FormGroupAsyncValidateOrFn, TOnSubmit extends | undefined | FormGroupValidateOrFn, TOnSubmitAsync extends | undefined | FormGroupAsyncValidateOrFn, TOnDynamic extends | undefined | FormGroupValidateOrFn, TOnDynamicAsync extends | undefined | FormGroupAsyncValidateOrFn, TSubmitMeta, TFormOnMount extends undefined | FormValidateOrFn, TFormOnChange extends undefined | FormValidateOrFn, TFormOnChangeAsync extends undefined | FormAsyncValidateOrFn, TFormOnBlur extends undefined | FormValidateOrFn, TFormOnBlurAsync extends undefined | FormAsyncValidateOrFn, TFormOnSubmit extends undefined | FormValidateOrFn, TFormOnSubmitAsync extends undefined | FormAsyncValidateOrFn, TFormOnDynamic extends undefined | FormValidateOrFn, TFormOnDynamicAsync extends undefined | FormAsyncValidateOrFn, TFormOnServer extends undefined | FormAsyncValidateOrFn, TParentSubmitMeta, > extends FormGroupApiOptions< TParentData, TName, TData, TOnMount, TOnChange, TOnChangeAsync, TOnBlur, TOnBlurAsync, TOnSubmit, TOnSubmitAsync, TOnDynamic, TOnDynamicAsync, TSubmitMeta, TFormOnMount, TFormOnChange, TFormOnChangeAsync, TFormOnBlur, TFormOnBlurAsync, TFormOnSubmit, TFormOnSubmitAsync, TFormOnDynamic, TFormOnDynamicAsync, TFormOnServer, TParentSubmitMeta > { children: ( formGroupApi: () => FormGroupApi< TParentData, TName, TData, TOnMount, TOnChange, TOnChangeAsync, TOnBlur, TOnBlurAsync, TOnSubmit, TOnSubmitAsync, TOnDynamic, TOnDynamicAsync, TSubmitMeta, TFormOnMount, TFormOnChange, TFormOnChangeAsync, TFormOnBlur, TFormOnBlurAsync, TFormOnSubmit, TFormOnSubmitAsync, TFormOnDynamic, TFormOnDynamicAsync, TFormOnServer, TParentSubmitMeta >, ) => JSXElement } export function FormGroup< TParentData, TName extends DeepKeys, TData extends DeepValue, TOnMount extends undefined | FormGroupValidateOrFn, TOnChange extends | undefined | FormGroupValidateOrFn, TOnChangeAsync extends | undefined | FormGroupAsyncValidateOrFn, TOnBlur extends undefined | FormGroupValidateOrFn, TOnBlurAsync extends | undefined | FormGroupAsyncValidateOrFn, TOnSubmit extends | undefined | FormGroupValidateOrFn, TOnSubmitAsync extends | undefined | FormGroupAsyncValidateOrFn, TOnDynamic extends | undefined | FormGroupValidateOrFn, TOnDynamicAsync extends | undefined | FormGroupAsyncValidateOrFn, TSubmitMeta, TFormOnMount extends undefined | FormValidateOrFn, TFormOnChange extends undefined | FormValidateOrFn, TFormOnChangeAsync extends undefined | FormAsyncValidateOrFn, TFormOnBlur extends undefined | FormValidateOrFn, TFormOnBlurAsync extends undefined | FormAsyncValidateOrFn, TFormOnSubmit extends undefined | FormValidateOrFn, TFormOnSubmitAsync extends undefined | FormAsyncValidateOrFn, TFormOnDynamic extends undefined | FormValidateOrFn, TFormOnDynamicAsync extends undefined | FormAsyncValidateOrFn, TFormOnServer extends undefined | FormAsyncValidateOrFn, TParentSubmitMeta, >( props: FormGroupComponentProps< TParentData, TName, TData, TOnMount, TOnChange, TOnChangeAsync, TOnBlur, TOnBlurAsync, TOnSubmit, TOnSubmitAsync, TOnDynamic, TOnDynamicAsync, TSubmitMeta, TFormOnMount, TFormOnChange, TFormOnChangeAsync, TFormOnBlur, TFormOnBlurAsync, TFormOnSubmit, TFormOnSubmitAsync, TFormOnDynamic, TFormOnDynamicAsync, TFormOnServer, TParentSubmitMeta >, ) { const formGroupApi = createFormGroup< TParentData, TName, TData, TOnMount, TOnChange, TOnChangeAsync, TOnBlur, TOnBlurAsync, TOnSubmit, TOnSubmitAsync, TOnDynamic, TOnDynamicAsync, TSubmitMeta, TFormOnMount, TFormOnChange, TFormOnChangeAsync, TFormOnBlur, TFormOnBlurAsync, TFormOnSubmit, TFormOnSubmitAsync, TFormOnDynamic, TFormOnDynamicAsync, TFormOnServer, TParentSubmitMeta >(() => { const { children, ...formGroupOptions } = props return formGroupOptions }) return <>{createComponent(() => props.children(formGroupApi), {})} }