@tanstack/vue-form 1.33.4 → 2.0.0-alpha.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (92) hide show
  1. package/dist/AppForm/Components.lib.js +16 -0
  2. package/dist/AppForm/VueAppFormApi.public.d.ts +14 -0
  3. package/dist/AppForm/appFormOptions.public.d.ts +14 -0
  4. package/dist/AppForm/componentMap.public.d.ts +10 -0
  5. package/dist/AppForm/contexts.lib.js +18 -0
  6. package/dist/AppForm/createFormHook.public.d.ts +6 -0
  7. package/dist/AppForm/createFormHook.public.js +24 -0
  8. package/dist/AppForm/createFormHookTypes.public.d.ts +15 -0
  9. package/dist/AppForm/fieldComponentHelpers.lib.js +22 -0
  10. package/dist/AppForm/getFormHookHelpers.public.d.ts +37 -0
  11. package/dist/AppForm/getFormHookHelpers.public.js +18 -0
  12. package/dist/AppForm/initializeAppForm.lib.js +10 -0
  13. package/dist/FieldGroup/FieldGroupApi.public.d.ts +30 -0
  14. package/dist/FieldGroup/withFields.lib.js +62 -0
  15. package/dist/FieldGroup/withFields.public.d.ts +57 -0
  16. package/dist/FieldGroup/withFields.public.js +8 -0
  17. package/dist/Subscribe.public.d.ts +15 -0
  18. package/dist/Subscribe.public.js +22 -0
  19. package/dist/VueForm/Components.lib.js +94 -0
  20. package/dist/VueForm/Components.public.d.ts +71 -0
  21. package/dist/VueForm/VueFormApi.lib.js +26 -0
  22. package/dist/VueForm/fieldSubscriptions.lib.js +34 -0
  23. package/dist/VueForm/formApiTypes.public.d.ts +10 -0
  24. package/dist/VueForm/formType.public.d.ts +9 -0
  25. package/dist/VueForm/useField.lib.js +44 -0
  26. package/dist/VueForm/useForm.public.d.ts +8 -0
  27. package/dist/VueForm/useForm.public.js +10 -0
  28. package/dist/index.d.ts +16 -0
  29. package/dist/index.js +11 -0
  30. package/dist/vueTypes.lib.d.ts +6 -0
  31. package/package.json +13 -29
  32. package/src/AppForm/Components.lib.ts +31 -0
  33. package/src/AppForm/VueAppFormApi.public.ts +17 -0
  34. package/src/AppForm/appFormOptions.public.ts +55 -0
  35. package/src/AppForm/componentMap.public.ts +19 -0
  36. package/src/AppForm/contexts.lib.ts +31 -0
  37. package/src/AppForm/createFormHook.public.ts +32 -0
  38. package/src/AppForm/createFormHookTypes.public.ts +28 -0
  39. package/src/AppForm/fieldComponentHelpers.lib.ts +21 -0
  40. package/src/AppForm/getFormHookHelpers.public.ts +109 -0
  41. package/src/AppForm/initializeAppForm.lib.ts +18 -0
  42. package/src/FieldGroup/FieldGroupApi.public.ts +148 -0
  43. package/src/FieldGroup/withFields.lib.ts +103 -0
  44. package/src/FieldGroup/withFields.public.ts +191 -0
  45. package/src/Subscribe.public.ts +46 -0
  46. package/src/VueForm/Components.lib.ts +182 -0
  47. package/src/VueForm/Components.public.ts +451 -0
  48. package/src/VueForm/VueFormApi.lib.ts +35 -0
  49. package/src/VueForm/fieldSubscriptions.lib.ts +47 -0
  50. package/src/VueForm/formApiTypes.public.ts +32 -0
  51. package/src/VueForm/formType.public.ts +46 -0
  52. package/src/VueForm/useField.lib.ts +64 -0
  53. package/src/VueForm/useForm.public.ts +30 -0
  54. package/src/index.ts +15 -4
  55. package/src/vueTypes.lib.ts +31 -0
  56. package/dist/cjs/index.cjs +0 -27
  57. package/dist/cjs/index.cjs.map +0 -1
  58. package/dist/cjs/index.d.cts +0 -5
  59. package/dist/cjs/types.d.cts +0 -9
  60. package/dist/cjs/useField.cjs +0 -106
  61. package/dist/cjs/useField.cjs.map +0 -1
  62. package/dist/cjs/useField.d.cts +0 -54
  63. package/dist/cjs/useForm.cjs +0 -62
  64. package/dist/cjs/useForm.cjs.map +0 -1
  65. package/dist/cjs/useForm.d.cts +0 -23
  66. package/dist/cjs/useFormGroup.cjs +0 -41
  67. package/dist/cjs/useFormGroup.cjs.map +0 -1
  68. package/dist/cjs/useFormGroup.d.cts +0 -37
  69. package/dist/cjs/useFormId.cjs +0 -31
  70. package/dist/cjs/useFormId.cjs.map +0 -1
  71. package/dist/cjs/useFormId.d.cts +0 -7
  72. package/dist/esm/index.d.ts +0 -5
  73. package/dist/esm/index.js +0 -15
  74. package/dist/esm/index.js.map +0 -1
  75. package/dist/esm/types.d.ts +0 -9
  76. package/dist/esm/useField.d.ts +0 -54
  77. package/dist/esm/useField.js +0 -106
  78. package/dist/esm/useField.js.map +0 -1
  79. package/dist/esm/useForm.d.ts +0 -23
  80. package/dist/esm/useForm.js +0 -62
  81. package/dist/esm/useForm.js.map +0 -1
  82. package/dist/esm/useFormGroup.d.ts +0 -37
  83. package/dist/esm/useFormGroup.js +0 -41
  84. package/dist/esm/useFormGroup.js.map +0 -1
  85. package/dist/esm/useFormId.d.ts +0 -7
  86. package/dist/esm/useFormId.js +0 -14
  87. package/dist/esm/useFormId.js.map +0 -1
  88. package/src/types.ts +0 -114
  89. package/src/useField.tsx +0 -550
  90. package/src/useForm.tsx +0 -360
  91. package/src/useFormGroup.tsx +0 -532
  92. package/src/useFormId.ts +0 -24
@@ -0,0 +1,148 @@
1
+ import type {
2
+ DeepKeys,
3
+ DeepKeysWhereValueIncludes,
4
+ DeepValue,
5
+ FieldValidators,
6
+ FormApiArrayMethods,
7
+ FormApiFieldMethods,
8
+ FormErrorTypes,
9
+ FormState,
10
+ ToFieldError,
11
+ ValidationIssue,
12
+ } from '@tanstack/form-core'
13
+ import type { Component, PublicProps } from 'vue'
14
+ import type { ReadonlyAtom } from '@tanstack/vue-store'
15
+ import type {
16
+ VueFieldApi,
17
+ VueFormFieldProps,
18
+ VueFormSubscribeProps,
19
+ } from '../VueForm/Components.public'
20
+ import type { VueComponentInstance } from '../vueTypes.lib'
21
+
22
+ export type FieldGroupFieldComponent<
23
+ in out TFieldData,
24
+ in out TFieldComponents extends Record<string, Component>,
25
+ > = new <const TFieldName extends DeepKeys<TFieldData>>(
26
+ props: VueFormFieldProps<
27
+ TFieldData,
28
+ TFieldName,
29
+ DeepValue<TFieldData, TFieldName>,
30
+ FieldValidators<TFieldData, TFieldName, DeepValue<TFieldData, TFieldName>>,
31
+ ValidationIssue,
32
+ unknown,
33
+ FormErrorTypes,
34
+ TFieldComponents
35
+ > &
36
+ PublicProps,
37
+ ) => VueComponentInstance<
38
+ VueFormFieldProps<
39
+ TFieldData,
40
+ TFieldName,
41
+ DeepValue<TFieldData, TFieldName>,
42
+ FieldValidators<TFieldData, TFieldName, DeepValue<TFieldData, TFieldName>>,
43
+ ValidationIssue,
44
+ unknown,
45
+ FormErrorTypes,
46
+ TFieldComponents
47
+ >,
48
+ {
49
+ default: {
50
+ field: VueFieldApi<
51
+ TFieldName,
52
+ DeepValue<TFieldData, TFieldName>,
53
+ ToFieldError<
54
+ FieldValidators<
55
+ TFieldData,
56
+ TFieldName,
57
+ DeepValue<TFieldData, TFieldName>
58
+ >,
59
+ ValidationIssue,
60
+ FormErrorTypes
61
+ >,
62
+ unknown,
63
+ FormErrorTypes,
64
+ TFieldComponents
65
+ >
66
+ }
67
+ }
68
+ >
69
+
70
+ export type FieldGroupArrayFieldComponent<
71
+ in out TFieldData,
72
+ in out TFieldComponents extends Record<string, Component>,
73
+ > = new <
74
+ const TFieldName extends DeepKeysWhereValueIncludes<TFieldData, Array<any>>,
75
+ >(
76
+ props: VueFormFieldProps<
77
+ TFieldData,
78
+ TFieldName,
79
+ DeepValue<TFieldData, TFieldName>,
80
+ FieldValidators<TFieldData, TFieldName, DeepValue<TFieldData, TFieldName>>,
81
+ ValidationIssue,
82
+ unknown,
83
+ FormErrorTypes,
84
+ TFieldComponents
85
+ > &
86
+ PublicProps,
87
+ ) => VueComponentInstance<
88
+ VueFormFieldProps<
89
+ TFieldData,
90
+ TFieldName,
91
+ DeepValue<TFieldData, TFieldName>,
92
+ FieldValidators<TFieldData, TFieldName, DeepValue<TFieldData, TFieldName>>,
93
+ ValidationIssue,
94
+ unknown,
95
+ FormErrorTypes,
96
+ TFieldComponents
97
+ >,
98
+ {
99
+ default: {
100
+ field: VueFieldApi<
101
+ TFieldName,
102
+ DeepValue<TFieldData, TFieldName>,
103
+ ToFieldError<
104
+ FieldValidators<
105
+ TFieldData,
106
+ TFieldName,
107
+ DeepValue<TFieldData, TFieldName>
108
+ >,
109
+ ValidationIssue,
110
+ FormErrorTypes
111
+ >,
112
+ unknown,
113
+ FormErrorTypes,
114
+ TFieldComponents
115
+ >
116
+ }
117
+ }
118
+ >
119
+
120
+ export type FieldGroupSubscribeProps<TSelected> = VueFormSubscribeProps<
121
+ unknown,
122
+ FormErrorTypes,
123
+ TSelected
124
+ >
125
+
126
+ export type FieldGroupSubscribeComponent = new <TSelected>(
127
+ props: FieldGroupSubscribeProps<TSelected> & PublicProps,
128
+ ) => VueComponentInstance<
129
+ FieldGroupSubscribeProps<TSelected>,
130
+ { default: NoInfer<TSelected> }
131
+ >
132
+
133
+ export interface FieldGroupApi<
134
+ in out TFieldData,
135
+ in out TFieldComponents extends Record<string, Component> = Record<
136
+ never,
137
+ never
138
+ >,
139
+ >
140
+ extends FormApiFieldMethods<TFieldData>, FormApiArrayMethods<TFieldData> {
141
+ atom: ReadonlyAtom<TFieldData>
142
+ Field: FieldGroupFieldComponent<TFieldData, TFieldComponents>
143
+ ArrayField: FieldGroupArrayFieldComponent<TFieldData, TFieldComponents>
144
+ Subscribe: FieldGroupSubscribeComponent
145
+ }
146
+
147
+ export type AnyFieldGroupApi = FieldGroupApi<any, Record<string, Component>>
148
+ export type FieldGroupFormState = FormState<unknown, FormErrorTypes>
@@ -0,0 +1,103 @@
1
+ import {
2
+ InternalFieldGroupApi,
3
+ defineFieldGroupFieldsRuntime,
4
+ fieldGroupHelperRuntime,
5
+ } from '@tanstack/form-core/internals'
6
+ import { computed, defineComponent, h } from 'vue'
7
+ import type { Component } from 'vue'
8
+ import type {
9
+ AnyFieldApiOptions,
10
+ InternalFieldGroupBindings,
11
+ } from '@tanstack/form-core/internals'
12
+ import type { AnyFieldGroupApi } from './FieldGroupApi.public'
13
+
14
+ function attachVueFieldGroupComponents(
15
+ group: InternalFieldGroupApi,
16
+ form: any,
17
+ ): AnyFieldGroupApi {
18
+ const forwardField = (component: Component, name: string) =>
19
+ defineComponent(
20
+ (_props, context) => {
21
+ const resolved = computed(() => {
22
+ const options = {
23
+ ...context.attrs,
24
+ } as unknown as AnyFieldApiOptions
25
+ return group._getFormFieldOptions(options, (base, overrides) => ({
26
+ ...base,
27
+ ...overrides,
28
+ }))
29
+ })
30
+ return () => h(component, resolved.value as never, context.slots)
31
+ },
32
+ { name, inheritAttrs: false },
33
+ )
34
+
35
+ const Field = forwardField(form.Field, 'TanStackForm.FieldGroup.Field')
36
+ const ArrayField = forwardField(
37
+ form.ArrayField,
38
+ 'TanStackForm.FieldGroup.ArrayField',
39
+ )
40
+ const Subscribe = defineComponent(
41
+ (_props, context) => () =>
42
+ h(form.Subscribe, { ...context.attrs } as never, context.slots),
43
+ { name: 'TanStackForm.FieldGroup.Subscribe', inheritAttrs: false },
44
+ )
45
+
46
+ return Object.assign(group, {
47
+ Field,
48
+ ArrayField,
49
+ Subscribe,
50
+ }) as never
51
+ }
52
+
53
+ export function defineFieldGroupRuntime<
54
+ TFields extends Record<string, unknown>,
55
+ >(defineFieldGroupFn: (helperRuntime: any) => TFields) {
56
+ const fields = defineFieldGroupFieldsRuntime(
57
+ defineFieldGroupFn(fieldGroupHelperRuntime),
58
+ )
59
+
60
+ return {
61
+ fields,
62
+ bindComponent: (component: Component, fieldsPropName: string) =>
63
+ withFieldsRuntime(fields, component, fieldsPropName),
64
+ }
65
+ }
66
+
67
+ function withFieldsRuntime(
68
+ fields: Record<string, unknown>,
69
+ component: Component,
70
+ fieldsPropName: string,
71
+ ) {
72
+ const fieldNames = Object.keys(fields)
73
+
74
+ return defineComponent(
75
+ (_props, context) => {
76
+ const form = context.attrs.form as any
77
+ if (!form) {
78
+ throw new Error(
79
+ 'TanStack Form: Field groups must receive a `form` prop.',
80
+ )
81
+ }
82
+
83
+ const fieldGroupApi = attachVueFieldGroupComponents(
84
+ new InternalFieldGroupApi({
85
+ form,
86
+ fieldNames,
87
+ getBindings: () =>
88
+ context.attrs[fieldsPropName] as InternalFieldGroupBindings,
89
+ }),
90
+ form,
91
+ )
92
+
93
+ return () => {
94
+ const componentProps = { ...context.attrs }
95
+ delete componentProps.form
96
+ delete componentProps[fieldsPropName]
97
+ componentProps[fieldsPropName] = fieldGroupApi
98
+ return h(component, componentProps, context.slots)
99
+ }
100
+ },
101
+ { name: 'TanStackForm.FieldGroup', inheritAttrs: false },
102
+ )
103
+ }
@@ -0,0 +1,191 @@
1
+ import { defineFieldGroupRuntime } from './withFields.lib'
2
+ import type {
3
+ DeepKeys,
4
+ DeepKeysWhereValueIncludes,
5
+ DeepValue,
6
+ FormApi,
7
+ } from '@tanstack/form-core'
8
+ import type { Component, PublicProps } from 'vue'
9
+ import type { VueTanStackFormComponents } from '../VueForm/Components.public'
10
+ import type { FieldGroupApi } from './FieldGroupApi.public'
11
+ import type { VueComponentInstance } from '../vueTypes.lib'
12
+
13
+ declare const fieldGroupFieldSlotValueSymbol: unique symbol
14
+ declare const fieldGroupFieldsSymbol: unique symbol
15
+
16
+ export type FieldGroupFieldSlotMode = 'strict' | 'loose'
17
+ export interface FieldGroupFieldSlot<
18
+ out TValue,
19
+ out TMode extends FieldGroupFieldSlotMode = FieldGroupFieldSlotMode,
20
+ > {
21
+ readonly mode: TMode
22
+ readonly [fieldGroupFieldSlotValueSymbol]: TValue
23
+ }
24
+ export type AnyFieldGroupFieldSlot = FieldGroupFieldSlot<any>
25
+ export type StrictFieldGroupFieldSlot<TValue> = FieldGroupFieldSlot<
26
+ TValue,
27
+ 'strict'
28
+ >
29
+ export type LooseFieldGroupFieldSlot<TValue> = FieldGroupFieldSlot<
30
+ TValue,
31
+ 'loose'
32
+ >
33
+ export type FieldGroupFieldSlotValue<TSlot> =
34
+ TSlot extends FieldGroupFieldSlot<infer TValue> ? TValue : never
35
+ export type FieldGroupFieldSlotModeOf<TSlot> =
36
+ TSlot extends FieldGroupFieldSlot<any, infer TMode> ? TMode : never
37
+
38
+ type IsSame<TTypeA, TTypeB> = [TTypeA] extends [TTypeB]
39
+ ? [TTypeB] extends [TTypeA]
40
+ ? true
41
+ : false
42
+ : false
43
+
44
+ export type FieldGroupFieldSlotAllows<TSlot, TValue> =
45
+ TSlot extends FieldGroupFieldSlot<infer TAcceptedValue, infer TMode>
46
+ ? TMode extends 'strict'
47
+ ? IsSame<TValue, TAcceptedValue>
48
+ : [TValue] extends [TAcceptedValue]
49
+ ? true
50
+ : false
51
+ : false
52
+
53
+ export type FieldGroupFieldNameForSlot<
54
+ TFieldData,
55
+ TSlot extends AnyFieldGroupFieldSlot,
56
+ > = {
57
+ [TFieldName in DeepKeys<TFieldData>]: FieldGroupFieldSlotAllows<
58
+ TSlot,
59
+ DeepValue<TFieldData, TFieldName>
60
+ > extends true
61
+ ? TFieldName
62
+ : never
63
+ }[DeepKeys<TFieldData>]
64
+
65
+ export type FieldGroupFields = Record<string, AnyFieldGroupFieldSlot>
66
+ export type FieldGroupFieldNames<
67
+ TFieldData,
68
+ TFields extends FieldGroupFields,
69
+ > = {
70
+ [TFieldName in keyof TFields]: FieldGroupFieldNameForSlot<
71
+ TFieldData,
72
+ TFields[TFieldName]
73
+ >
74
+ }
75
+ export type FieldGroupFieldData<TFields extends FieldGroupFields> = {
76
+ [
77
+ TFieldName in keyof TFields
78
+ ]: TFields[TFieldName] extends FieldGroupFieldSlot<infer TValue, any>
79
+ ? TValue
80
+ : never
81
+ }
82
+ export type FieldGroupFieldsOf<TFieldGroup> = TFieldGroup extends {
83
+ readonly [fieldGroupFieldsSymbol]: infer TFields
84
+ }
85
+ ? TFields
86
+ : never
87
+
88
+ export type VueFieldGroup<
89
+ TFields extends FieldGroupFields,
90
+ TFieldComponents extends Record<string, Component> = Record<never, never>,
91
+ > = FieldGroupApi<FieldGroupFieldData<TFields>, TFieldComponents> & {
92
+ readonly [fieldGroupFieldsSymbol]: TFields
93
+ }
94
+ export type FieldGroupFieldComponentsOf<TFieldGroup> =
95
+ TFieldGroup extends VueFieldGroup<any, infer TFieldComponents>
96
+ ? TFieldComponents
97
+ : never
98
+ export type FieldGroupForm<
99
+ TFieldComponents extends Record<string, Component> = Record<never, never>,
100
+ TFormData = any,
101
+ > = FormApi<TFormData, any> &
102
+ VueTanStackFormComponents<TFormData, any, TFieldComponents>
103
+
104
+ export type FieldGroupFieldBindingForSlot<
105
+ TFormData,
106
+ TSlot extends AnyFieldGroupFieldSlot,
107
+ > =
108
+ TSlot extends FieldGroupFieldSlot<infer TValue, infer TMode>
109
+ ? TMode extends 'strict'
110
+ ? FieldGroupFieldNameForSlot<TFormData, TSlot>
111
+ : DeepKeysWhereValueIncludes<TFormData, TValue>
112
+ : never
113
+ export type FieldGroupFieldBindings<
114
+ TFields extends FieldGroupFields,
115
+ TFormData = any,
116
+ > = {
117
+ [TFieldName in keyof TFields]: FieldGroupFieldBindingForSlot<
118
+ TFormData,
119
+ TFields[TFieldName]
120
+ >
121
+ }
122
+ export type FieldGroupFieldBindingsOf<TFieldGroup, TFormData> =
123
+ FieldGroupFieldsOf<TFieldGroup> extends FieldGroupFields
124
+ ? FieldGroupFieldBindings<FieldGroupFieldsOf<TFieldGroup>, TFormData>
125
+ : never
126
+ export type FieldGroupFieldsPropName<
127
+ TProps,
128
+ TFieldGroup extends VueFieldGroup<any, any>,
129
+ > = {
130
+ [TPropName in keyof TProps]-?: IsSame<
131
+ TProps[TPropName],
132
+ TFieldGroup
133
+ > extends true
134
+ ? TPropName
135
+ : never
136
+ }[keyof TProps]
137
+
138
+ export type FieldGroupWithFieldsFn<
139
+ TFieldGroup extends VueFieldGroup<any, any>,
140
+ > = <TProps extends object, const TFieldsPropName extends PropertyKey>(
141
+ component: Component & (new (props: TProps & PublicProps) => any),
142
+ fieldsPropName: TFieldsPropName &
143
+ FieldGroupFieldsPropName<TProps, TFieldGroup>,
144
+ ) => new <TFormData>(
145
+ props: Omit<TProps, TFieldsPropName | 'form'> & {
146
+ form: FieldGroupForm<FieldGroupFieldComponentsOf<TFieldGroup>, TFormData>
147
+ } & {
148
+ [TPropName in TFieldsPropName]: FieldGroupFieldBindingsOf<
149
+ TFieldGroup,
150
+ TFormData
151
+ >
152
+ } & PublicProps,
153
+ ) => VueComponentInstance<
154
+ Omit<TProps, TFieldsPropName | 'form'> & {
155
+ form: FieldGroupForm<FieldGroupFieldComponentsOf<TFieldGroup>, TFormData>
156
+ } & {
157
+ [TPropName in TFieldsPropName]: FieldGroupFieldBindingsOf<
158
+ TFieldGroup,
159
+ TFormData
160
+ >
161
+ } & Record<string, any>,
162
+ {}
163
+ >
164
+
165
+ export interface FieldGroupHelper {
166
+ strict: <TValue>() => StrictFieldGroupFieldSlot<TValue>
167
+ loose: <TValue>() => LooseFieldGroupFieldSlot<TValue>
168
+ }
169
+
170
+ export interface FieldGroupDefinition<
171
+ TFields extends FieldGroupFields,
172
+ TFieldComponents extends Record<string, Component>,
173
+ > {
174
+ /** The virtual field-group API injected into the bound component. */
175
+ fields: VueFieldGroup<TFields, TFieldComponents>
176
+ /** Binds a component's virtual field API to concrete paths in a form. */
177
+ bindComponent: FieldGroupWithFieldsFn<
178
+ VueFieldGroup<TFields, TFieldComponents>
179
+ >
180
+ }
181
+
182
+ /** Signature shared by `defineFieldGroup` and app-form field-group definers. */
183
+ export type DefineFieldGroupFn<
184
+ TFieldComponents extends Record<string, Component>,
185
+ > = <const TFields extends FieldGroupFields>(
186
+ defineFn: (helper: FieldGroupHelper) => TFields,
187
+ ) => FieldGroupDefinition<TFields, TFieldComponents>
188
+
189
+ /** Defines a reusable group of virtual fields. */
190
+ export const defineFieldGroup: DefineFieldGroupFn<Record<never, never>> =
191
+ defineFieldGroupRuntime as never
@@ -0,0 +1,46 @@
1
+ import { shallow, useSelector } from '@tanstack/vue-store'
2
+ import { defineComponent } from 'vue'
3
+ import type {
4
+ Atom,
5
+ ReadonlyAtom,
6
+ ReadonlyStore,
7
+ Store,
8
+ } from '@tanstack/vue-store'
9
+ import type { VueComponentWithSlots } from './vueTypes.lib'
10
+
11
+ export type SubscribeSource<TValue> =
12
+ Atom<TValue> | ReadonlyAtom<TValue> | Store<TValue> | ReadonlyStore<TValue>
13
+
14
+ export interface SubscribeProps<in out TSourceData, in out TSelected> {
15
+ source: SubscribeSource<TSourceData>
16
+ selector: (state: TSourceData) => TSelected
17
+ when?: (selected: NoInfer<TSelected>) => boolean
18
+ }
19
+
20
+ export type SubscribeComponent = new <TSourceData, const TSelected>(
21
+ props: SubscribeProps<TSourceData, TSelected>,
22
+ ) => InstanceType<
23
+ VueComponentWithSlots<
24
+ SubscribeProps<TSourceData, TSelected>,
25
+ { default: NoInfer<TSelected> }
26
+ >
27
+ >
28
+
29
+ const SubscribeImpl = defineComponent<SubscribeProps<any, any>>(
30
+ (props, { slots }) => {
31
+ const selected = useSelector(props.source, props.selector, {
32
+ compare: shallow,
33
+ })
34
+
35
+ return () => {
36
+ if (props.when?.(selected.value) === false) return null
37
+ return slots.default?.(selected.value)
38
+ }
39
+ },
40
+ {
41
+ name: 'TanStackForm.Subscribe',
42
+ props: ['source', 'selector', 'when'],
43
+ },
44
+ )
45
+
46
+ export const Subscribe = SubscribeImpl as never as SubscribeComponent
@@ -0,0 +1,182 @@
1
+ import { shallow, useSelector } from '@tanstack/vue-store'
2
+ import { InternalFormGroupApi } from '@tanstack/form-core/internals'
3
+ import {
4
+ defineComponent,
5
+ h,
6
+ onMounted,
7
+ onUnmounted,
8
+ provide,
9
+ watchEffect,
10
+ } from 'vue'
11
+ import { Subscribe } from '../Subscribe.public'
12
+ import {
13
+ createArrayFieldSubscription,
14
+ createValueFieldSubscription,
15
+ } from './fieldSubscriptions.lib'
16
+ import { useField } from './useField.lib'
17
+ import type { Component, InjectionKey, Slots } from 'vue'
18
+ import type {
19
+ AnyFieldApiOptions,
20
+ AnyInternalFieldApi,
21
+ AnyInternalFormApi,
22
+ InternalFormGroupApi as InternalFormGroupApiType,
23
+ } from '@tanstack/form-core/internals'
24
+ import type { InternalVueFormApi } from './VueFormApi.lib'
25
+
26
+ export function attachVueFormComponents(
27
+ form: AnyInternalFormApi,
28
+ fieldComponents: Record<string, Component> | null,
29
+ fieldContext?: InjectionKey<AnyInternalFieldApi>,
30
+ ): InternalVueFormApi {
31
+ const resultForm = form as InternalVueFormApi
32
+ resultForm.Field = createFieldComponent(
33
+ form,
34
+ fieldComponents,
35
+ false,
36
+ fieldContext,
37
+ ) as never
38
+ resultForm.ArrayField = createFieldComponent(
39
+ form,
40
+ fieldComponents,
41
+ true,
42
+ fieldContext,
43
+ ) as never
44
+ resultForm.Subscribe = createSubscribeComponent(form) as never
45
+ resultForm.FormGroup = createFormGroupComponent(resultForm) as never
46
+ return resultForm
47
+ }
48
+
49
+ function createFieldComponent(
50
+ form: AnyInternalFormApi,
51
+ fieldComponents: Record<string, Component> | null,
52
+ array: boolean,
53
+ fieldContext?: InjectionKey<AnyInternalFieldApi>,
54
+ ) {
55
+ return defineComponent(
56
+ (_props, context) => {
57
+ const options = () => ({ ...context.attrs, form }) as never
58
+ const fieldApi = useField(options, fieldComponents)
59
+ const selection = array
60
+ ? createArrayFieldSubscription(fieldApi)
61
+ : createValueFieldSubscription(fieldApi)
62
+
63
+ if (fieldContext) {
64
+ // Field APIs are stable for a mounted name. Supplying the current API
65
+ // mirrors Vue v1 composition components while the parent subscription
66
+ // handles state-driven renders.
67
+ provide(fieldContext, fieldApi.value)
68
+ }
69
+
70
+ return () => {
71
+ void selection.value
72
+ return context.slots.default?.({ field: fieldApi.value })
73
+ }
74
+ },
75
+ {
76
+ name: array ? 'TanStackForm.ArrayField' : 'TanStackForm.Field',
77
+ inheritAttrs: false,
78
+ },
79
+ )
80
+ }
81
+
82
+ function createSubscribeComponent(form: AnyInternalFormApi) {
83
+ return defineComponent(
84
+ (_props, context) => () =>
85
+ h(
86
+ Subscribe as never,
87
+ { ...context.attrs, source: form.atom } as never,
88
+ context.slots,
89
+ ),
90
+ {
91
+ name: 'TanStackForm.Subscribe',
92
+ inheritAttrs: false,
93
+ },
94
+ )
95
+ }
96
+
97
+ function createFormGroupComponent(form: InternalVueFormApi) {
98
+ return defineComponent(
99
+ (_props, context) => {
100
+ const options = () => ({ ...context.attrs, form })
101
+ const group = attachVueFormGroupComponents(
102
+ new InternalFormGroupApi(options() as never),
103
+ form,
104
+ )
105
+
106
+ watchEffect(() => group.update(options() as never))
107
+
108
+ let mounted = false
109
+ onMounted(() => {
110
+ mounted = true
111
+ group.mount()
112
+ })
113
+ onUnmounted(() => {
114
+ if (mounted) group._cleanup()
115
+ })
116
+
117
+ const state = useSelector(group.atom, (value) => value, {
118
+ compare: shallow,
119
+ })
120
+
121
+ return () => {
122
+ void state.value
123
+ return context.slots.default?.({ group })
124
+ }
125
+ },
126
+ {
127
+ name: 'TanStackForm.FormGroup',
128
+ inheritAttrs: false,
129
+ },
130
+ )
131
+ }
132
+
133
+ function forwardToField(
134
+ component: Component,
135
+ group: InternalFormGroupApiType<any, any, any, any, any>,
136
+ ) {
137
+ return defineComponent(
138
+ (_props, context) => () => {
139
+ const options = { ...context.attrs } as unknown as AnyFieldApiOptions
140
+ return h(
141
+ component,
142
+ group._getFormFieldOptions(options, (base, overrides) => ({
143
+ ...base,
144
+ ...overrides,
145
+ })) as never,
146
+ context.slots,
147
+ )
148
+ },
149
+ { inheritAttrs: false },
150
+ )
151
+ }
152
+
153
+ function attachVueFormGroupComponents(
154
+ group: InternalFormGroupApiType<any, any, any, any, any>,
155
+ form: InternalVueFormApi,
156
+ ) {
157
+ type GroupWithComponents = InternalFormGroupApiType<
158
+ any,
159
+ any,
160
+ any,
161
+ any,
162
+ any
163
+ > & {
164
+ Field: Component
165
+ ArrayField: Component
166
+ Subscribe: Component
167
+ }
168
+
169
+ const resultGroup = group as GroupWithComponents
170
+ resultGroup.Field = forwardToField(form.Field as never, group)
171
+ resultGroup.ArrayField = forwardToField(form.ArrayField as never, group)
172
+ resultGroup.Subscribe = defineComponent(
173
+ (_props, context) => () =>
174
+ h(
175
+ Subscribe as never,
176
+ { ...context.attrs, source: group.atom } as never,
177
+ context.slots as Slots,
178
+ ),
179
+ { name: 'TanStackForm.FormGroup.Subscribe', inheritAttrs: false },
180
+ )
181
+ return resultGroup
182
+ }