Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion docs/config.json
Original file line number Diff line number Diff line change
Expand Up @@ -497,7 +497,7 @@
},
{
"label": "FieldGroupHelper",
"to": "framework/react/reference/interfaces/FieldGroupHelper"
"to": "reference/interfaces/FieldGroupHelper"
}
]
}
Expand Down
2 changes: 1 addition & 1 deletion packages/form-core/src/FieldApi/FieldApi.lib.ts
Original file line number Diff line number Diff line change
Expand Up @@ -232,7 +232,7 @@ export function getOrCreateFieldApi(
return getOrCreateFieldApi(childNode, segments, form, options, scope)
}

childNode = new InternalFieldApi(
childNode = new form._FieldApi(
{
segment,
parent: node,
Expand Down
5 changes: 5 additions & 0 deletions packages/form-core/src/FormApi/FormApi.lib.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { batch, createAtom } from '@tanstack/store'
import {
InternalFieldApi,
getDefaultValueCacheResult,
getOrCreateFieldApi,
shouldCacheDefaultValue,
Expand Down Expand Up @@ -212,6 +213,10 @@ export class InternalFormApi<
*/
static majorVersion = 2

// Allows adapters to control which InternalFieldApi subclass creates fields.
get _FieldApi(): typeof InternalFieldApi {
return InternalFieldApi
}
atom: ReadonlyAtom<
FormState<TFormData, ToFormErrorTypes<TFormValidators, TSubmitReturn>>
>
Expand Down
59 changes: 7 additions & 52 deletions packages/react-form/src/AppForm/Components.lib.tsx
Original file line number Diff line number Diff line change
@@ -1,64 +1,20 @@
import React from 'react'

import { InternalFormGroupApi } from '@tanstack/form-core/internals'
import {
attachReactFormComponents,
createArrayFieldComponent,
} from '../ReactForm/Components.lib'
import { useField } from '../ReactForm/useField.lib'
import { useValueFieldSubscription } from '../ReactForm/fieldSubscriptions.lib'
import { Subscribe } from '../Subscribe.public'
import { FieldContext, FormContext } from './contexts.lib'
import type {
AnyInternalFormApi,
FieldOptionsScope,
} from '@tanstack/form-core/internals'
import type { FieldOptionsScope } from '@tanstack/form-core/internals'
import type { FunctionComponent, ReactNode } from 'react'
import type {
AppFormComponent,
ReactAppFormApi,
} from './ReactAppFormApi.public'
import type { AnyReactFormComponentMap } from './componentMap.public'
import type { AppFormComponent } from './ReactAppFormApi.public'
import type {
ReactFormFieldProps,
ReactFormGroupProps,
} from '../ReactForm/Components.public'
import type { InternalReactFormApi } from '../ReactForm/ReactFormApi.lib'

type AnyReactAppFormApi = ReactAppFormApi<any, any, AnyReactFormComponentMap>

export function attachReactAppFormComponents(
form: AnyInternalFormApi,
formComponents: Record<string, FunctionComponent<any>>,
fieldComponents: Record<string, FunctionComponent<any>>,
): AnyReactAppFormApi {
const resultForm = attachReactFormComponents(
form,
fieldComponents,
) as never as AnyReactAppFormApi
const field = createFieldWithContext(form, fieldComponents, 'field')
const arrayField = resultForm.ArrayField as FunctionComponent<any>
const groupField = createFieldWithContext(form, fieldComponents, 'field')
const groupArrayField = createArrayFieldComponent(
form,
fieldComponents,
'field',
) as FunctionComponent<any>
const formGroup = createFormGroupWithContext(
resultForm as any,
groupField,
groupArrayField,
)

resultForm.AppForm = createAppForm(form)
resultForm.Field = field as AnyReactAppFormApi['Field']
resultForm.ArrayField = arrayField as AnyReactAppFormApi['ArrayField']
resultForm.FormGroup = formGroup as AnyReactAppFormApi['FormGroup']

return Object.assign(resultForm, formComponents)
}

function createAppForm(form: AnyInternalFormApi): AppFormComponent {
export function createAppForm(form: InternalReactFormApi): AppFormComponent {
const AppForm: FunctionComponent<{
children: Exclude<ReactNode, Promise<any>>
}> = function AppFormComponent(props) {
Expand All @@ -75,13 +31,12 @@ type AnyFieldComponent = FunctionComponent<
ReactFormFieldProps<any, any, any, any, never, any, any, any>
>

function createFieldWithContext(
form: AnyInternalFormApi,
fieldComponents: Record<string, FunctionComponent<any>>,
export function createFieldWithContext(
form: InternalReactFormApi,
scope: FieldOptionsScope,
) {
const TanStackFormField: AnyFieldComponent = (props) => {
const fieldApi = useField({ ...props, form }, fieldComponents, scope)
const fieldApi = useField({ ...props, form }, scope)
const field = useValueFieldSubscription(fieldApi)

return (
Expand All @@ -101,7 +56,7 @@ type AnyFormGroupComponent = FunctionComponent<
ReactFormGroupProps<any, any, any, any, any, any>
>

function createFormGroupWithContext(
export function createFormGroupWithContext(
form: InternalReactFormApi,
groupField: FunctionComponent<any>,
groupArrayField: FunctionComponent<any>,
Expand Down
14 changes: 14 additions & 0 deletions packages/react-form/src/AppForm/ReactAppFieldApi.lib.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
import { InternalFieldApi } from '@tanstack/form-core/internals'
import type { FunctionComponent } from 'react'

type FieldComponents = Record<string, FunctionComponent<any>>

export function createInternalReactAppFieldApiClass(
fieldComponents: FieldComponents,
): typeof InternalFieldApi {
class InternalReactAppFieldApi extends InternalFieldApi<any, any, any> {}

Object.assign(InternalReactAppFieldApi.prototype, fieldComponents)

return InternalReactAppFieldApi as typeof InternalFieldApi
}
71 changes: 71 additions & 0 deletions packages/react-form/src/AppForm/ReactAppFormApi.lib.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
import { createArrayFieldComponent } from '../ReactForm/Components.lib'
import { InternalReactFormApi } from '../ReactForm/ReactFormApi.lib'
import { createInternalReactAppFieldApiClass } from './ReactAppFieldApi.lib'
import {
createAppForm,
createFieldWithContext,
createFormGroupWithContext,
} from './Components.lib'
import type { DefaultOptions, FormOptions } from '@tanstack/form-core'
import type { FunctionComponent } from 'react'
import type { AppFormComponent } from './ReactAppFormApi.public'

type FormComponents = Record<string, FunctionComponent<any>>

interface InternalReactAppFormApiInstance extends InternalReactFormApi {
AppForm: AppFormComponent
}

type InternalReactAppFormApiConstructor<
TFormComponents extends FormComponents,
> = new (
options: FormOptions<any, any, any, unknown>,
defaultOptions?: DefaultOptions,
) => InternalReactAppFormApiInstance & TFormComponents

export function createInternalReactAppFormApiClass<
const TFormComponents extends FormComponents,
>(
formComponents: TFormComponents,
fieldComponents: FormComponents,
): InternalReactAppFormApiConstructor<TFormComponents> {
const InternalReactAppFieldApi =
createInternalReactAppFieldApiClass(fieldComponents)

class InternalReactAppFormApi
extends InternalReactFormApi
implements InternalReactAppFormApiInstance
{
AppForm: AppFormComponent

override get _FieldApi(): typeof InternalReactAppFieldApi {
return InternalReactAppFieldApi
}

constructor(
options: FormOptions<any, any, any, unknown>,
defaultOptions?: DefaultOptions,
) {
super(options, defaultOptions)

const field = createFieldWithContext(this, 'field')
const groupField = createFieldWithContext(this, 'field')
const groupArrayField = createArrayFieldComponent(
this,
'field',
) as FunctionComponent<any>

this.AppForm = createAppForm(this)
this.Field = field as InternalReactAppFormApi['Field']
this.FormGroup = createFormGroupWithContext(
this,
groupField,
groupArrayField,
) as InternalReactAppFormApi['FormGroup']
}
}

Object.assign(InternalReactAppFormApi.prototype, formComponents)

return InternalReactAppFormApi as unknown as InternalReactAppFormApiConstructor<TFormComponents>
}
18 changes: 7 additions & 11 deletions packages/react-form/src/AppForm/initializeAppForm.lib.ts
Original file line number Diff line number Diff line change
@@ -1,13 +1,12 @@
import { InternalFormApi } from '@tanstack/form-core/internals'
import { attachReactAppFormComponents } from './Components.lib'
import { createInternalReactAppFormApiClass } from './ReactAppFormApi.lib'
import type { InternalReactFormApi } from '../ReactForm/ReactFormApi.lib'
import type {
DefaultFieldOptions,
DefaultFormGroupOptions,
DefaultFormOptions,
DefaultOptions,
FormOptions,
} from '@tanstack/form-core'
import type { InternalReactFormApi } from '../ReactForm/ReactFormApi.lib'
import type { FunctionComponent } from 'react'

interface AnyCreateFormHookOptions {
Expand All @@ -21,6 +20,10 @@ interface AnyCreateFormHookOptions {
export function createAppFormInitializer(
createOptions: AnyCreateFormHookOptions,
): (options: FormOptions<any, any, any, unknown>) => InternalReactFormApi {
const InternalReactAppFormApi = createInternalReactAppFormApiClass(
createOptions.formComponents,
createOptions.fieldComponents,
)
const hasDefaultOptions =
createOptions.defaultFormOptions ||
createOptions.defaultFieldOptions ||
Expand All @@ -35,13 +38,6 @@ export function createAppFormInitializer(
: undefined

return (options) => {
const form = new InternalFormApi(options, defaultOptions)
const extendedForm = attachReactAppFormComponents(
form,
createOptions.formComponents,
createOptions.fieldComponents,
)

return extendedForm as never
return new InternalReactAppFormApi(options, defaultOptions)
}
}
58 changes: 11 additions & 47 deletions packages/react-form/src/ReactForm/Components.lib.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,7 @@ import {
useValueFieldSubscription,
} from './fieldSubscriptions.lib'
import { useField } from './useField.lib'
import type {
AnyInternalFormApi,
FieldOptionsScope,
} from '@tanstack/form-core/internals'
import type { FieldOptionsScope } from '@tanstack/form-core/internals'
import type { InternalReactFormApi } from './ReactFormApi.lib'
import type { FunctionComponent, ReactNode } from 'react'
import type {
Expand All @@ -18,41 +15,16 @@ import type {
ReactFormSubscribeProps,
} from './Components.public'

export function attachReactFormComponents(
form: AnyInternalFormApi,
fieldComponents: Record<string, FunctionComponent<any>> | null,
): InternalReactFormApi {
const resultForm = form as InternalReactFormApi
resultForm.Field = createFieldComponent(
form,
fieldComponents,
'field',
) as InternalReactFormApi['Field']
resultForm.ArrayField = createArrayFieldComponent(
form,
fieldComponents,
'field',
)
resultForm.Subscribe = createSubscribeComponent(form)
resultForm.FormGroup = createFormGroupComponent(
resultForm,
fieldComponents,
) as InternalReactFormApi['FormGroup']

return resultForm
}

type AnyFieldComponent = FunctionComponent<
ReactFormFieldProps<any, any, any, any, never, any, any, any>
>

function createFieldComponent(
form: AnyInternalFormApi,
fieldComponents: Record<string, FunctionComponent<any>> | null,
export function createFieldComponent(
form: InternalReactFormApi,
scope: FieldOptionsScope,
): AnyFieldComponent {
const TanStackFormField: AnyFieldComponent = (props) => {
const fieldApi = useField({ ...props, form }, fieldComponents, scope)
const fieldApi = useField({ ...props, form }, scope)
const field = useValueFieldSubscription(fieldApi)

return props.children(field)
Expand All @@ -69,12 +41,11 @@ type AnyArrayFieldComponent = {
}

export function createArrayFieldComponent(
form: AnyInternalFormApi,
fieldComponents: Record<string, FunctionComponent<any>> | null,
form: InternalReactFormApi,
scope: FieldOptionsScope,
): AnyArrayFieldComponent {
const TanStackFormArrayField: AnyArrayFieldComponent = (props) => {
const fieldApi = useField({ ...props, form }, fieldComponents, scope)
const fieldApi = useField({ ...props, form }, scope)
const field = useArrayFieldSubscription(fieldApi)

return props.children(field)
Expand All @@ -90,8 +61,8 @@ type AnySubscribeComponent = {
displayName?: string
}

function createSubscribeComponent(
form: AnyInternalFormApi,
export function createSubscribeComponent(
form: InternalReactFormApi,
): AnySubscribeComponent {
const TanStackFormSubscribe: AnySubscribeComponent = (props) => {
return <Subscribe source={form.atom} {...props} />
Expand All @@ -106,9 +77,8 @@ type AnyFormGroupComponent = FunctionComponent<
ReactFormGroupProps<any, any, any, any, any, any>
>

function createFormGroupComponent(
export function createFormGroupComponent(
form: InternalReactFormApi,
fieldComponents: Record<string, FunctionComponent<any>> | null,
): AnyFormGroupComponent {
const TanStackFormGroup: AnyFormGroupComponent = (props) => {
const groupRef =
Expand All @@ -118,7 +88,6 @@ function createFormGroupComponent(
groupRef.current = attachReactFormGroupComponents(
new InternalFormGroupApi({ ...props, form } as never),
form,
fieldComponents,
)
}

Expand All @@ -141,7 +110,6 @@ function createFormGroupComponent(
function attachReactFormGroupComponents(
group: InternalFormGroupApi<any, any, any, any, any>,
form: InternalReactFormApi,
fieldComponents: Record<string, FunctionComponent<any>> | null,
) {
type FormGroupComponents = InternalFormGroupApi<any, any, any, any, any> & {
Field: FunctionComponent<any>
Expand All @@ -150,12 +118,8 @@ function attachReactFormGroupComponents(
}

const resultGroup: FormGroupComponents = group as never
const GroupField = createFieldComponent(form, fieldComponents, 'field')
const GroupArrayField = createArrayFieldComponent(
form,
fieldComponents,
'field',
)
const GroupField = createFieldComponent(form, 'field')
const GroupArrayField = createArrayFieldComponent(form, 'field')

resultGroup.Field = function Field(props) {
return (
Expand Down
Loading
Loading