@tanstack/vue-form 0.3.4 → 0.3.6
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.
- package/build/legacy/createFormFactory.cjs.map +1 -1
- package/build/legacy/createFormFactory.d.cts +1 -1
- package/build/legacy/createFormFactory.d.ts +1 -1
- package/build/legacy/createFormFactory.js.map +1 -1
- package/build/legacy/types.cjs.map +1 -1
- package/build/legacy/types.d.cts +2 -2
- package/build/legacy/types.d.ts +2 -2
- package/build/legacy/useField.cjs.map +1 -1
- package/build/legacy/useField.d.cts +16 -20
- package/build/legacy/useField.d.ts +16 -20
- package/build/legacy/useField.js +1 -3
- package/build/legacy/useField.js.map +1 -1
- package/build/legacy/useForm.cjs.map +1 -1
- package/build/legacy/useForm.d.cts +1 -1
- package/build/legacy/useForm.d.ts +1 -1
- package/build/legacy/useForm.js.map +1 -1
- package/build/modern/createFormFactory.cjs.map +1 -1
- package/build/modern/createFormFactory.d.cts +1 -1
- package/build/modern/createFormFactory.d.ts +1 -1
- package/build/modern/createFormFactory.js.map +1 -1
- package/build/modern/types.cjs.map +1 -1
- package/build/modern/types.d.cts +2 -2
- package/build/modern/types.d.ts +2 -2
- package/build/modern/useField.cjs.map +1 -1
- package/build/modern/useField.d.cts +16 -20
- package/build/modern/useField.d.ts +16 -20
- package/build/modern/useField.js +1 -3
- package/build/modern/useField.js.map +1 -1
- package/build/modern/useForm.cjs.map +1 -1
- package/build/modern/useForm.d.cts +1 -1
- package/build/modern/useForm.d.ts +1 -1
- package/build/modern/useForm.js.map +1 -1
- package/package.json +2 -2
- package/src/createFormFactory.ts +1 -1
- package/src/tests/useField.test.tsx +5 -5
- package/src/tests/useForm.test.tsx +3 -3
- package/src/types.ts +5 -5
- package/src/useField.tsx +44 -58
- package/src/useForm.tsx +1 -1
- package/build/lib/createFormFactory.d.ts +0 -8
- package/build/lib/createFormFactory.js +0 -12
- package/build/lib/formContext.d.ts +0 -11
- package/build/lib/formContext.js +0 -12
- package/build/lib/index.d.ts +0 -5
- package/build/lib/index.js +0 -5
- package/build/lib/tests/useField.test.d.ts +0 -2
- package/build/lib/tests/useField.test.jsx +0 -109
- package/build/lib/tests/useForm.test.d.ts +0 -2
- package/build/lib/tests/useForm.test.jsx +0 -71
- package/build/lib/tests/utils.d.ts +0 -1
- package/build/lib/tests/utils.js +0 -5
- package/build/lib/types.d.ts +0 -4
- package/build/lib/types.js +0 -1
- package/build/lib/useField.d.ts +0 -42
- package/build/lib/useField.jsx +0 -41
- package/build/lib/useForm.d.ts +0 -19
- package/build/lib/useForm.jsx +0 -35
@@ -1 +1 @@
|
|
1
|
-
{"version":3,"sources":["../../src/useForm.tsx"],"sourcesContent":["import { FormApi, type FormState, type FormOptions } from '@tanstack/form-core'\nimport { type NoInfer, useStore } from '@tanstack/vue-store'\nimport { type UseField, type FieldComponent, Field, useField } from './useField'\nimport { provideFormContext } from './formContext'\nimport {\n type EmitsOptions,\n type SlotsType,\n type SetupContext,\n defineComponent,\n} from 'vue-demi'\n\ndeclare module '@tanstack/form-core' {\n // eslint-disable-next-line no-shadow\n interface FormApi<TFormData> {\n Provider: (props: Record<string, any> & {}) => any\n provideFormContext: () => void\n Field: FieldComponent<TFormData
|
1
|
+
{"version":3,"sources":["../../src/useForm.tsx"],"sourcesContent":["import { FormApi, type FormState, type FormOptions } from '@tanstack/form-core'\nimport { type NoInfer, useStore } from '@tanstack/vue-store'\nimport { type UseField, type FieldComponent, Field, useField } from './useField'\nimport { provideFormContext } from './formContext'\nimport {\n type EmitsOptions,\n type SlotsType,\n type SetupContext,\n defineComponent,\n} from 'vue-demi'\n\ndeclare module '@tanstack/form-core' {\n // eslint-disable-next-line no-shadow\n interface FormApi<TFormData> {\n Provider: (props: Record<string, any> & {}) => any\n provideFormContext: () => void\n Field: FieldComponent<TFormData>\n useField: UseField<TFormData>\n useStore: <TSelected = NoInfer<FormState<TFormData>>>(\n selector?: (state: NoInfer<FormState<TFormData>>) => TSelected,\n ) => TSelected\n Subscribe: <TSelected = NoInfer<FormState<TFormData>>>(\n props: {\n selector?: (state: NoInfer<FormState<TFormData>>) => TSelected\n },\n context: SetupContext<\n EmitsOptions,\n SlotsType<{ default: NoInfer<FormState<TFormData>> }>\n >,\n ) => any\n }\n}\n\nexport function useForm<TData>(opts?: FormOptions<TData>): FormApi<TData> {\n const formApi = (() => {\n const api = new FormApi<TData>(opts)\n\n api.Provider = defineComponent(\n (_, context) => {\n provideFormContext({ formApi })\n return () => context.slots.default!()\n },\n { name: 'Provider' },\n )\n api.provideFormContext = () => {\n provideFormContext({ formApi })\n }\n api.Field = Field as never\n api.useField = useField as never\n api.useStore = (selector) => {\n return useStore(api.store as never, selector as never) as never\n }\n api.Subscribe = defineComponent(\n (props, context) => {\n const allProps = { ...props, ...context.attrs }\n const selector = allProps.selector ?? ((state) => state)\n const data = useStore(api.store as never, selector as never)\n return () => context.slots.default!(data.value)\n },\n {\n name: 'Subscribe',\n inheritAttrs: false,\n },\n )\n\n return api\n })()\n\n // formApi.useStore((state) => state.isSubmitting)\n formApi.update(opts)\n\n return formApi as never\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,uBAA0D;AAC1D,uBAAuC;AACvC,sBAAoE;AACpE,yBAAmC;AACnC,sBAKO;AAwBA,SAAS,QAAe,MAA2C;AACxE,QAAM,WAAW,MAAM;AACrB,UAAM,MAAM,IAAI,yBAAe,IAAI;AAEnC,QAAI,eAAW;AAAA,MACb,CAAC,GAAG,YAAY;AACd,mDAAmB,EAAE,QAAQ,CAAC;AAC9B,eAAO,MAAM,QAAQ,MAAM,QAAS;AAAA,MACtC;AAAA,MACA,EAAE,MAAM,WAAW;AAAA,IACrB;AACA,QAAI,qBAAqB,MAAM;AAC7B,iDAAmB,EAAE,QAAQ,CAAC;AAAA,IAChC;AACA,QAAI,QAAQ;AACZ,QAAI,WAAW;AACf,QAAI,WAAW,CAAC,aAAa;AAC3B,iBAAO,2BAAS,IAAI,OAAgB,QAAiB;AAAA,IACvD;AACA,QAAI,gBAAY;AAAA,MACd,CAAC,OAAO,YAAY;AAClB,cAAM,WAAW,EAAE,GAAG,OAAO,GAAG,QAAQ,MAAM;AAC9C,cAAM,WAAW,SAAS,aAAa,CAAC,UAAU;AAClD,cAAM,WAAO,2BAAS,IAAI,OAAgB,QAAiB;AAC3D,eAAO,MAAM,QAAQ,MAAM,QAAS,KAAK,KAAK;AAAA,MAChD;AAAA,MACA;AAAA,QACE,MAAM;AAAA,QACN,cAAc;AAAA,MAChB;AAAA,IACF;AAEA,WAAO;AAAA,EACT,GAAG;AAGH,UAAQ,OAAO,IAAI;AAEnB,SAAO;AACT;","names":[]}
|
@@ -8,7 +8,7 @@ declare module '@tanstack/form-core' {
|
|
8
8
|
interface FormApi<TFormData> {
|
9
9
|
Provider: (props: Record<string, any> & {}) => any;
|
10
10
|
provideFormContext: () => void;
|
11
|
-
Field: FieldComponent<TFormData
|
11
|
+
Field: FieldComponent<TFormData>;
|
12
12
|
useField: UseField<TFormData>;
|
13
13
|
useStore: <TSelected = NoInfer<FormState<TFormData>>>(selector?: (state: NoInfer<FormState<TFormData>>) => TSelected) => TSelected;
|
14
14
|
Subscribe: <TSelected = NoInfer<FormState<TFormData>>>(props: {
|
@@ -8,7 +8,7 @@ declare module '@tanstack/form-core' {
|
|
8
8
|
interface FormApi<TFormData> {
|
9
9
|
Provider: (props: Record<string, any> & {}) => any;
|
10
10
|
provideFormContext: () => void;
|
11
|
-
Field: FieldComponent<TFormData
|
11
|
+
Field: FieldComponent<TFormData>;
|
12
12
|
useField: UseField<TFormData>;
|
13
13
|
useStore: <TSelected = NoInfer<FormState<TFormData>>>(selector?: (state: NoInfer<FormState<TFormData>>) => TSelected) => TSelected;
|
14
14
|
Subscribe: <TSelected = NoInfer<FormState<TFormData>>>(props: {
|
@@ -1 +1 @@
|
|
1
|
-
{"version":3,"sources":["../../src/useForm.tsx"],"sourcesContent":["import { FormApi, type FormState, type FormOptions } from '@tanstack/form-core'\nimport { type NoInfer, useStore } from '@tanstack/vue-store'\nimport { type UseField, type FieldComponent, Field, useField } from './useField'\nimport { provideFormContext } from './formContext'\nimport {\n type EmitsOptions,\n type SlotsType,\n type SetupContext,\n defineComponent,\n} from 'vue-demi'\n\ndeclare module '@tanstack/form-core' {\n // eslint-disable-next-line no-shadow\n interface FormApi<TFormData> {\n Provider: (props: Record<string, any> & {}) => any\n provideFormContext: () => void\n Field: FieldComponent<TFormData
|
1
|
+
{"version":3,"sources":["../../src/useForm.tsx"],"sourcesContent":["import { FormApi, type FormState, type FormOptions } from '@tanstack/form-core'\nimport { type NoInfer, useStore } from '@tanstack/vue-store'\nimport { type UseField, type FieldComponent, Field, useField } from './useField'\nimport { provideFormContext } from './formContext'\nimport {\n type EmitsOptions,\n type SlotsType,\n type SetupContext,\n defineComponent,\n} from 'vue-demi'\n\ndeclare module '@tanstack/form-core' {\n // eslint-disable-next-line no-shadow\n interface FormApi<TFormData> {\n Provider: (props: Record<string, any> & {}) => any\n provideFormContext: () => void\n Field: FieldComponent<TFormData>\n useField: UseField<TFormData>\n useStore: <TSelected = NoInfer<FormState<TFormData>>>(\n selector?: (state: NoInfer<FormState<TFormData>>) => TSelected,\n ) => TSelected\n Subscribe: <TSelected = NoInfer<FormState<TFormData>>>(\n props: {\n selector?: (state: NoInfer<FormState<TFormData>>) => TSelected\n },\n context: SetupContext<\n EmitsOptions,\n SlotsType<{ default: NoInfer<FormState<TFormData>> }>\n >,\n ) => any\n }\n}\n\nexport function useForm<TData>(opts?: FormOptions<TData>): FormApi<TData> {\n const formApi = (() => {\n const api = new FormApi<TData>(opts)\n\n api.Provider = defineComponent(\n (_, context) => {\n provideFormContext({ formApi })\n return () => context.slots.default!()\n },\n { name: 'Provider' },\n )\n api.provideFormContext = () => {\n provideFormContext({ formApi })\n }\n api.Field = Field as never\n api.useField = useField as never\n api.useStore = (selector) => {\n return useStore(api.store as never, selector as never) as never\n }\n api.Subscribe = defineComponent(\n (props, context) => {\n const allProps = { ...props, ...context.attrs }\n const selector = allProps.selector ?? ((state) => state)\n const data = useStore(api.store as never, selector as never)\n return () => context.slots.default!(data.value)\n },\n {\n name: 'Subscribe',\n inheritAttrs: false,\n },\n )\n\n return api\n })()\n\n // formApi.useStore((state) => state.isSubmitting)\n formApi.update(opts)\n\n return formApi as never\n}\n"],"mappings":";AAAA,SAAS,eAAiD;AAC1D,SAAuB,gBAAgB;AACvC,SAA6C,OAAO,gBAAgB;AACpE,SAAS,0BAA0B;AACnC;AAAA,EAIE;AAAA,OACK;AAwBA,SAAS,QAAe,MAA2C;AACxE,QAAM,WAAW,MAAM;AACrB,UAAM,MAAM,IAAI,QAAe,IAAI;AAEnC,QAAI,WAAW;AAAA,MACb,CAAC,GAAG,YAAY;AACd,2BAAmB,EAAE,QAAQ,CAAC;AAC9B,eAAO,MAAM,QAAQ,MAAM,QAAS;AAAA,MACtC;AAAA,MACA,EAAE,MAAM,WAAW;AAAA,IACrB;AACA,QAAI,qBAAqB,MAAM;AAC7B,yBAAmB,EAAE,QAAQ,CAAC;AAAA,IAChC;AACA,QAAI,QAAQ;AACZ,QAAI,WAAW;AACf,QAAI,WAAW,CAAC,aAAa;AAC3B,aAAO,SAAS,IAAI,OAAgB,QAAiB;AAAA,IACvD;AACA,QAAI,YAAY;AAAA,MACd,CAAC,OAAO,YAAY;AAClB,cAAM,WAAW,EAAE,GAAG,OAAO,GAAG,QAAQ,MAAM;AAC9C,cAAM,WAAW,SAAS,aAAa,CAAC,UAAU;AAClD,cAAM,OAAO,SAAS,IAAI,OAAgB,QAAiB;AAC3D,eAAO,MAAM,QAAQ,MAAM,QAAS,KAAK,KAAK;AAAA,MAChD;AAAA,MACA;AAAA,QACE,MAAM;AAAA,QACN,cAAc;AAAA,MAChB;AAAA,IACF;AAEA,WAAO;AAAA,EACT,GAAG;AAGH,UAAQ,OAAO,IAAI;AAEnB,SAAO;AACT;","names":[]}
|
package/package.json
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
{
|
2
2
|
"name": "@tanstack/vue-form",
|
3
|
-
"version": "0.3.
|
3
|
+
"version": "0.3.6",
|
4
4
|
"description": "Powerful, type-safe forms for Vue.",
|
5
5
|
"author": "tannerlinsley",
|
6
6
|
"license": "MIT",
|
@@ -45,7 +45,7 @@
|
|
45
45
|
"@tanstack/store": "0.1.3",
|
46
46
|
"@tanstack/vue-store": "0.1.3",
|
47
47
|
"vue-demi": "^0.14.6",
|
48
|
-
"@tanstack/form-core": "0.3.
|
48
|
+
"@tanstack/form-core": "0.3.6"
|
49
49
|
},
|
50
50
|
"devDependencies": {
|
51
51
|
"@vue/composition-api": "1.7.2",
|
package/src/createFormFactory.ts
CHANGED
@@ -6,7 +6,7 @@ import { useForm } from './useForm'
|
|
6
6
|
export type FormFactory<TFormData> = {
|
7
7
|
useForm: (opts?: FormOptions<TFormData>) => FormApi<TFormData>
|
8
8
|
useField: UseField<TFormData>
|
9
|
-
Field: FieldComponent<TFormData
|
9
|
+
Field: FieldComponent<TFormData>
|
10
10
|
}
|
11
11
|
|
12
12
|
export function createFormFactory<TFormData>(
|
@@ -30,7 +30,7 @@ describe('useField', () => {
|
|
30
30
|
|
31
31
|
return () => (
|
32
32
|
<form.Field name="firstName" defaultValue="FirstName">
|
33
|
-
{({ field }: { field: FieldApi<
|
33
|
+
{({ field }: { field: FieldApi<Person, 'firstName'> }) => (
|
34
34
|
<input
|
35
35
|
data-testid={'fieldinput'}
|
36
36
|
value={field.state.value}
|
@@ -68,7 +68,7 @@ describe('useField', () => {
|
|
68
68
|
name="firstName"
|
69
69
|
onChange={(value) => (value === 'other' ? error : undefined)}
|
70
70
|
>
|
71
|
-
{({ field }: { field: FieldApi<
|
71
|
+
{({ field }: { field: FieldApi<Person, 'firstName'> }) => (
|
72
72
|
<div>
|
73
73
|
<input
|
74
74
|
data-testid="fieldinput"
|
@@ -111,7 +111,7 @@ describe('useField', () => {
|
|
111
111
|
name="firstName"
|
112
112
|
onChange={(value) => (value === 'other' ? error : undefined)}
|
113
113
|
>
|
114
|
-
{({ field }: { field: FieldApi<
|
114
|
+
{({ field }: { field: FieldApi<Person, 'firstName'> }) => (
|
115
115
|
<div>
|
116
116
|
<input
|
117
117
|
data-testid="fieldinput"
|
@@ -159,7 +159,7 @@ describe('useField', () => {
|
|
159
159
|
return error
|
160
160
|
}}
|
161
161
|
>
|
162
|
-
{({ field }: { field: FieldApi<
|
162
|
+
{({ field }: { field: FieldApi<Person, 'firstName'> }) => (
|
163
163
|
<div>
|
164
164
|
<input
|
165
165
|
data-testid="fieldinput"
|
@@ -211,7 +211,7 @@ describe('useField', () => {
|
|
211
211
|
return error
|
212
212
|
}}
|
213
213
|
>
|
214
|
-
{({ field }: { field: FieldApi<
|
214
|
+
{({ field }: { field: FieldApi<Person, 'firstName'> }) => (
|
215
215
|
<div>
|
216
216
|
<input
|
217
217
|
data-testid="fieldinput"
|
@@ -29,7 +29,7 @@ describe('useForm', () => {
|
|
29
29
|
|
30
30
|
return () => (
|
31
31
|
<form.Field name="firstName" defaultValue="">
|
32
|
-
{({ field }: { field: FieldApi<
|
32
|
+
{({ field }: { field: FieldApi<Person, 'firstName'> }) => (
|
33
33
|
<input
|
34
34
|
data-testid={'fieldinput'}
|
35
35
|
value={field.state.value}
|
@@ -69,7 +69,7 @@ describe('useForm', () => {
|
|
69
69
|
|
70
70
|
return () => (
|
71
71
|
<form.Field name="firstName" defaultValue="">
|
72
|
-
{({ field }: { field: FieldApi<
|
72
|
+
{({ field }: { field: FieldApi<Person, 'firstName'> }) => (
|
73
73
|
<p>{field.state.value}</p>
|
74
74
|
)}
|
75
75
|
</form.Field>
|
@@ -101,7 +101,7 @@ describe('useForm', () => {
|
|
101
101
|
{({
|
102
102
|
field,
|
103
103
|
}: {
|
104
|
-
field: FieldApi<
|
104
|
+
field: FieldApi<{ firstName: string }, 'firstName'>
|
105
105
|
}) => {
|
106
106
|
return (
|
107
107
|
<input
|
package/src/types.ts
CHANGED
@@ -1,9 +1,9 @@
|
|
1
|
-
import type { FieldOptions, DeepKeys } from '@tanstack/form-core'
|
1
|
+
import type { FieldOptions, DeepKeys, DeepValue } from '@tanstack/form-core'
|
2
2
|
|
3
3
|
export type UseFieldOptions<
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
> = FieldOptions<
|
4
|
+
TParentData,
|
5
|
+
TName extends DeepKeys<TParentData>,
|
6
|
+
TData = DeepValue<TParentData, TName>,
|
7
|
+
> = FieldOptions<TParentData, TName, TData> & {
|
8
8
|
mode?: 'value' | 'array'
|
9
9
|
}
|
package/src/useField.tsx
CHANGED
@@ -1,8 +1,4 @@
|
|
1
|
-
import {
|
2
|
-
FieldApi,
|
3
|
-
type FieldApiOptions,
|
4
|
-
type FormApi,
|
5
|
-
} from '@tanstack/form-core'
|
1
|
+
import { FieldApi } from '@tanstack/form-core'
|
6
2
|
import type { DeepKeys, DeepValue, Narrow } from '@tanstack/form-core'
|
7
3
|
import { useStore } from '@tanstack/vue-store'
|
8
4
|
import { defineComponent, onMounted, onUnmounted, watch } from 'vue-demi'
|
@@ -12,44 +8,46 @@ import type { UseFieldOptions } from './types'
|
|
12
8
|
|
13
9
|
declare module '@tanstack/form-core' {
|
14
10
|
// eslint-disable-next-line no-shadow
|
15
|
-
interface FieldApi<
|
16
|
-
|
11
|
+
interface FieldApi<
|
12
|
+
TParentData,
|
13
|
+
TName extends DeepKeys<TParentData>,
|
14
|
+
TData = DeepValue<TParentData, TName>,
|
15
|
+
> {
|
16
|
+
Field: FieldComponent<TData>
|
17
17
|
}
|
18
18
|
}
|
19
19
|
|
20
|
-
export type UseField<
|
21
|
-
opts?: { name: Narrow<
|
22
|
-
|
23
|
-
|
20
|
+
export type UseField<TParentData> = <TName extends DeepKeys<TParentData>>(
|
21
|
+
opts?: { name: Narrow<TName> } & UseFieldOptions<
|
22
|
+
TParentData,
|
23
|
+
TName,
|
24
|
+
DeepValue<TParentData, TName>
|
24
25
|
>,
|
25
|
-
) => FieldApi<DeepValue<
|
26
|
+
) => FieldApi<TParentData, TName, DeepValue<TParentData, TName>>
|
26
27
|
|
27
28
|
export function useField<
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
? string
|
32
|
-
: DeepKeys<TFormData> = unknown extends TFormData
|
33
|
-
? string
|
34
|
-
: DeepKeys<TFormData>,
|
29
|
+
TParentData,
|
30
|
+
TName extends DeepKeys<TParentData>,
|
31
|
+
TData = DeepValue<TParentData, TName>,
|
35
32
|
>(
|
36
|
-
opts: UseFieldOptions<
|
33
|
+
opts: UseFieldOptions<TParentData, TName>,
|
37
34
|
): {
|
38
35
|
api: FieldApi<
|
39
|
-
|
40
|
-
|
41
|
-
Omit<typeof opts, 'onMount'> & {
|
42
|
-
|
43
|
-
}
|
36
|
+
TParentData,
|
37
|
+
TName
|
38
|
+
// Omit<typeof opts, 'onMount'> & {
|
39
|
+
// form: FormApi<TParentData>
|
40
|
+
// }
|
44
41
|
>
|
45
42
|
state: Readonly<
|
46
43
|
Ref<
|
47
44
|
FieldApi<
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
45
|
+
TParentData,
|
46
|
+
TName,
|
47
|
+
TData
|
48
|
+
// Omit<typeof opts, 'onMount'> & {
|
49
|
+
// form: FormApi<TParentData>
|
50
|
+
// }
|
53
51
|
>['state']
|
54
52
|
>
|
55
53
|
>
|
@@ -91,17 +89,15 @@ export function useField<
|
|
91
89
|
return { api: fieldApi, state: fieldState } as never
|
92
90
|
}
|
93
91
|
|
94
|
-
export type FieldValue<
|
95
|
-
? unknown extends
|
96
|
-
?
|
97
|
-
: DeepValue<
|
98
|
-
: DeepValue<
|
92
|
+
export type FieldValue<TParentData, TName> = TParentData extends any[]
|
93
|
+
? unknown extends TName
|
94
|
+
? TParentData[number]
|
95
|
+
: DeepValue<TParentData[number], TName>
|
96
|
+
: DeepValue<TParentData, TName>
|
99
97
|
|
100
98
|
type FieldComponentProps<
|
101
99
|
TParentData,
|
102
|
-
|
103
|
-
TField,
|
104
|
-
TName extends unknown extends TFormData ? string : DeepKeys<TFormData>,
|
100
|
+
TName extends DeepKeys<TParentData>,
|
105
101
|
> = (TParentData extends any[]
|
106
102
|
? {
|
107
103
|
name?: TName
|
@@ -111,40 +107,30 @@ type FieldComponentProps<
|
|
111
107
|
name: TName
|
112
108
|
index?: never
|
113
109
|
}) &
|
114
|
-
Omit<UseFieldOptions<
|
110
|
+
Omit<UseFieldOptions<TParentData, TName>, 'name' | 'index'>
|
115
111
|
|
116
|
-
export type FieldComponent<TParentData
|
117
|
-
|
118
|
-
|
119
|
-
// Name of the field
|
120
|
-
TName extends unknown extends TFormData ? string : DeepKeys<TFormData>,
|
112
|
+
export type FieldComponent<TParentData> = <
|
113
|
+
TName extends DeepKeys<TParentData>,
|
114
|
+
TData = DeepValue<TParentData, TName>,
|
121
115
|
>(
|
122
|
-
fieldOptions: FieldComponentProps<TParentData,
|
116
|
+
fieldOptions: FieldComponentProps<TParentData, TName>,
|
123
117
|
context: SetupContext<
|
124
118
|
{},
|
125
119
|
SlotsType<{
|
126
120
|
default: {
|
127
|
-
field: FieldApi<
|
128
|
-
|
129
|
-
TFormData,
|
130
|
-
FieldApiOptions<TField, TFormData, TName>
|
131
|
-
>
|
132
|
-
state: FieldApi<
|
133
|
-
TField,
|
134
|
-
TFormData,
|
135
|
-
FieldApiOptions<TField, TFormData, TName>
|
136
|
-
>['state']
|
121
|
+
field: FieldApi<TParentData, TName, TData>
|
122
|
+
state: FieldApi<TParentData, TName, TData>['state']
|
137
123
|
}
|
138
124
|
}>
|
139
125
|
>,
|
140
126
|
) => any
|
141
127
|
|
142
128
|
export const Field = defineComponent(
|
143
|
-
<
|
144
|
-
fieldOptions: UseFieldOptions<
|
129
|
+
<TParentData, TName extends DeepKeys<TParentData>>(
|
130
|
+
fieldOptions: UseFieldOptions<TParentData, TName>,
|
145
131
|
context: SetupContext,
|
146
132
|
) => {
|
147
|
-
const fieldApi = useField({ ...fieldOptions, ...context.attrs })
|
133
|
+
const fieldApi = useField({ ...fieldOptions, ...context.attrs } as any)
|
148
134
|
|
149
135
|
provideFormContext({
|
150
136
|
formApi: fieldApi.api.form,
|
package/src/useForm.tsx
CHANGED
@@ -14,7 +14,7 @@ declare module '@tanstack/form-core' {
|
|
14
14
|
interface FormApi<TFormData> {
|
15
15
|
Provider: (props: Record<string, any> & {}) => any
|
16
16
|
provideFormContext: () => void
|
17
|
-
Field: FieldComponent<TFormData
|
17
|
+
Field: FieldComponent<TFormData>
|
18
18
|
useField: UseField<TFormData>
|
19
19
|
useStore: <TSelected = NoInfer<FormState<TFormData>>>(
|
20
20
|
selector?: (state: NoInfer<FormState<TFormData>>) => TSelected,
|
@@ -1,8 +0,0 @@
|
|
1
|
-
import type { FormApi, FormOptions } from '@tanstack/form-core';
|
2
|
-
import { type UseField, type FieldComponent } from './useField';
|
3
|
-
export type FormFactory<TFormData> = {
|
4
|
-
useForm: (opts?: FormOptions<TFormData>) => FormApi<TFormData>;
|
5
|
-
useField: UseField<TFormData>;
|
6
|
-
Field: FieldComponent<TFormData, TFormData>;
|
7
|
-
};
|
8
|
-
export declare function createFormFactory<TFormData>(defaultOpts?: FormOptions<TFormData>): FormFactory<TFormData>;
|
@@ -1,12 +0,0 @@
|
|
1
|
-
import { Field, useField } from './useField';
|
2
|
-
import { useForm } from './useForm';
|
3
|
-
export function createFormFactory(defaultOpts) {
|
4
|
-
return {
|
5
|
-
useForm: (opts) => {
|
6
|
-
const formOptions = Object.assign({}, defaultOpts, opts);
|
7
|
-
return useForm(formOptions);
|
8
|
-
},
|
9
|
-
useField: useField,
|
10
|
-
Field: Field,
|
11
|
-
};
|
12
|
-
}
|
@@ -1,11 +0,0 @@
|
|
1
|
-
import type { FormApi } from '@tanstack/form-core';
|
2
|
-
export type FormContext = {
|
3
|
-
formApi: FormApi<any>;
|
4
|
-
parentFieldName?: string;
|
5
|
-
} | null;
|
6
|
-
export declare const formContext: unique symbol;
|
7
|
-
export declare function provideFormContext(val: FormContext): void;
|
8
|
-
export declare function useFormContext(): {
|
9
|
-
formApi: FormApi<any>;
|
10
|
-
parentFieldName?: string | undefined;
|
11
|
-
};
|
package/build/lib/formContext.js
DELETED
@@ -1,12 +0,0 @@
|
|
1
|
-
import { inject, provide } from 'vue-demi';
|
2
|
-
export const formContext = Symbol('FormContext');
|
3
|
-
export function provideFormContext(val) {
|
4
|
-
provide(formContext, val);
|
5
|
-
}
|
6
|
-
export function useFormContext() {
|
7
|
-
const formApi = inject(formContext);
|
8
|
-
if (!formApi) {
|
9
|
-
throw new Error(`You are trying to use the form API outside of a form!`);
|
10
|
-
}
|
11
|
-
return formApi;
|
12
|
-
}
|
package/build/lib/index.d.ts
DELETED
package/build/lib/index.js
DELETED
@@ -1,109 +0,0 @@
|
|
1
|
-
/// <reference lib="dom" />
|
2
|
-
import { defineComponent } from 'vue-demi';
|
3
|
-
import { render, waitFor } from '@testing-library/vue';
|
4
|
-
import '@testing-library/jest-dom';
|
5
|
-
import { createFormFactory, provideFormContext, } from '../index';
|
6
|
-
import userEvent from '@testing-library/user-event';
|
7
|
-
import { sleep } from './utils';
|
8
|
-
const user = userEvent.setup();
|
9
|
-
describe('useField', () => {
|
10
|
-
it('should allow to set default value', async () => {
|
11
|
-
const formFactory = createFormFactory();
|
12
|
-
const Comp = defineComponent(() => {
|
13
|
-
const form = formFactory.useForm();
|
14
|
-
provideFormContext({ formApi: form });
|
15
|
-
return () => (<form.Field name="firstName" defaultValue="FirstName">
|
16
|
-
{({ field }) => (<input data-testid={'fieldinput'} value={field.state.value} onBlur={field.handleBlur} onInput={(e) => field.handleChange(e.target.value)}/>)}
|
17
|
-
</form.Field>);
|
18
|
-
});
|
19
|
-
const { getByTestId } = render(Comp);
|
20
|
-
const input = getByTestId('fieldinput');
|
21
|
-
await waitFor(() => expect(input).toHaveValue('FirstName'));
|
22
|
-
});
|
23
|
-
it('should not validate on change if isTouched is false', async () => {
|
24
|
-
const error = 'Please enter a different value';
|
25
|
-
const formFactory = createFormFactory();
|
26
|
-
const Comp = defineComponent(() => {
|
27
|
-
const form = formFactory.useForm();
|
28
|
-
provideFormContext({ formApi: form });
|
29
|
-
return () => (<form.Field name="firstName" onChange={(value) => (value === 'other' ? error : undefined)}>
|
30
|
-
{({ field }) => (<div>
|
31
|
-
<input data-testid="fieldinput" name={field.name} value={field.state.value} onBlur={field.handleBlur} onInput={(e) => field.setValue(e.target.value)}/>
|
32
|
-
<p>{field.getMeta().errors}</p>
|
33
|
-
</div>)}
|
34
|
-
</form.Field>);
|
35
|
-
});
|
36
|
-
const { getByTestId, queryByText } = render(Comp);
|
37
|
-
const input = getByTestId('fieldinput');
|
38
|
-
await user.type(input, 'other');
|
39
|
-
expect(queryByText(error)).not.toBeInTheDocument();
|
40
|
-
});
|
41
|
-
it('should validate on change if isTouched is true', async () => {
|
42
|
-
const error = 'Please enter a different value';
|
43
|
-
const formFactory = createFormFactory();
|
44
|
-
const Comp = defineComponent(() => {
|
45
|
-
const form = formFactory.useForm();
|
46
|
-
provideFormContext({ formApi: form });
|
47
|
-
return () => (<form.Field name="firstName" onChange={(value) => (value === 'other' ? error : undefined)}>
|
48
|
-
{({ field }) => (<div>
|
49
|
-
<input data-testid="fieldinput" name={field.name} value={field.state.value} onBlur={field.handleBlur} onInput={(e) => field.handleChange(e.target.value)}/>
|
50
|
-
<p>{field.getMeta().errors}</p>
|
51
|
-
</div>)}
|
52
|
-
</form.Field>);
|
53
|
-
});
|
54
|
-
const { getByTestId, getByText, queryByText } = render(Comp);
|
55
|
-
const input = getByTestId('fieldinput');
|
56
|
-
expect(queryByText(error)).not.toBeInTheDocument();
|
57
|
-
await user.type(input, 'other');
|
58
|
-
expect(getByText(error)).toBeInTheDocument();
|
59
|
-
});
|
60
|
-
it('should validate async on change', async () => {
|
61
|
-
const error = 'Please enter a different value';
|
62
|
-
const formFactory = createFormFactory();
|
63
|
-
const Comp = defineComponent(() => {
|
64
|
-
const form = formFactory.useForm();
|
65
|
-
provideFormContext({ formApi: form });
|
66
|
-
return () => (<form.Field name="firstName" defaultMeta={{ isTouched: true }} onChangeAsync={async () => {
|
67
|
-
await sleep(10);
|
68
|
-
return error;
|
69
|
-
}}>
|
70
|
-
{({ field }) => (<div>
|
71
|
-
<input data-testid="fieldinput" name={field.name} value={field.state.value} onBlur={field.handleBlur} onInput={(e) => field.handleChange(e.target.value)}/>
|
72
|
-
<p>{field.getMeta().errors}</p>
|
73
|
-
</div>)}
|
74
|
-
</form.Field>);
|
75
|
-
});
|
76
|
-
const { getByTestId, getByText, queryByText } = render(Comp);
|
77
|
-
const input = getByTestId('fieldinput');
|
78
|
-
expect(queryByText(error)).not.toBeInTheDocument();
|
79
|
-
await user.type(input, 'other');
|
80
|
-
await waitFor(() => getByText(error));
|
81
|
-
expect(getByText(error)).toBeInTheDocument();
|
82
|
-
});
|
83
|
-
it('should validate async on change with debounce', async () => {
|
84
|
-
const mockFn = vi.fn();
|
85
|
-
const error = 'Please enter a different value';
|
86
|
-
const formFactory = createFormFactory();
|
87
|
-
const Comp = defineComponent(() => {
|
88
|
-
const form = formFactory.useForm();
|
89
|
-
provideFormContext({ formApi: form });
|
90
|
-
return () => (<form.Field name="firstName" defaultMeta={{ isTouched: true }} onChangeAsyncDebounceMs={100} onChangeAsync={async () => {
|
91
|
-
mockFn();
|
92
|
-
await sleep(10);
|
93
|
-
return error;
|
94
|
-
}}>
|
95
|
-
{({ field }) => (<div>
|
96
|
-
<input data-testid="fieldinput" name={field.name} value={field.state.value} onBlur={field.handleBlur} onInput={(e) => field.handleChange(e.target.value)}/>
|
97
|
-
<p>{field.getMeta().errors}</p>
|
98
|
-
</div>)}
|
99
|
-
</form.Field>);
|
100
|
-
});
|
101
|
-
const { getByTestId, getByText } = render(<Comp />);
|
102
|
-
const input = getByTestId('fieldinput');
|
103
|
-
await user.type(input, 'other');
|
104
|
-
// mockFn will have been called 5 times without onChangeAsyncDebounceMs
|
105
|
-
expect(mockFn).toHaveBeenCalledTimes(0);
|
106
|
-
await waitFor(() => getByText(error));
|
107
|
-
expect(getByText(error)).toBeInTheDocument();
|
108
|
-
});
|
109
|
-
});
|
@@ -1,71 +0,0 @@
|
|
1
|
-
/// <reference lib="dom" />
|
2
|
-
import { defineComponent, ref } from 'vue-demi';
|
3
|
-
import { render, waitFor } from '@testing-library/vue';
|
4
|
-
import '@testing-library/jest-dom';
|
5
|
-
import { createFormFactory, provideFormContext, useForm, } from '../index';
|
6
|
-
import userEvent from '@testing-library/user-event';
|
7
|
-
const user = userEvent.setup();
|
8
|
-
describe('useForm', () => {
|
9
|
-
it('preserved field state', async () => {
|
10
|
-
const formFactory = createFormFactory();
|
11
|
-
const Comp = defineComponent(() => {
|
12
|
-
const form = formFactory.useForm();
|
13
|
-
provideFormContext({ formApi: form });
|
14
|
-
return () => (<form.Field name="firstName" defaultValue="">
|
15
|
-
{({ field }) => (<input data-testid={'fieldinput'} value={field.state.value} onBlur={field.handleBlur} onInput={(e) => field.handleChange(e.target.value)}/>)}
|
16
|
-
</form.Field>);
|
17
|
-
});
|
18
|
-
const { getByTestId, queryByText } = render(Comp);
|
19
|
-
const input = getByTestId('fieldinput');
|
20
|
-
expect(queryByText('FirstName')).not.toBeInTheDocument();
|
21
|
-
await user.type(input, 'FirstName');
|
22
|
-
expect(input).toHaveValue('FirstName');
|
23
|
-
});
|
24
|
-
it('should allow default values to be set', async () => {
|
25
|
-
const formFactory = createFormFactory();
|
26
|
-
const Comp = defineComponent(() => {
|
27
|
-
const form = formFactory.useForm({
|
28
|
-
defaultValues: {
|
29
|
-
firstName: 'FirstName',
|
30
|
-
lastName: 'LastName',
|
31
|
-
},
|
32
|
-
});
|
33
|
-
form.provideFormContext();
|
34
|
-
return () => (<form.Field name="firstName" defaultValue="">
|
35
|
-
{({ field }) => (<p>{field.state.value}</p>)}
|
36
|
-
</form.Field>);
|
37
|
-
});
|
38
|
-
const { findByText, queryByText } = render(Comp);
|
39
|
-
expect(await findByText('FirstName')).toBeInTheDocument();
|
40
|
-
expect(queryByText('LastName')).not.toBeInTheDocument();
|
41
|
-
});
|
42
|
-
it('should handle submitting properly', async () => {
|
43
|
-
const Comp = defineComponent(() => {
|
44
|
-
const submittedData = ref();
|
45
|
-
const form = useForm({
|
46
|
-
defaultValues: {
|
47
|
-
firstName: 'FirstName',
|
48
|
-
},
|
49
|
-
onSubmit: (data) => {
|
50
|
-
submittedData.value = data;
|
51
|
-
},
|
52
|
-
});
|
53
|
-
form.provideFormContext();
|
54
|
-
return () => (<form.Provider>
|
55
|
-
<form.Field name="firstName">
|
56
|
-
{({ field, }) => {
|
57
|
-
return (<input value={field.state.value} onBlur={field.handleBlur} onChange={(e) => field.handleChange(e.target.value)} placeholder={'First name'}/>);
|
58
|
-
}}
|
59
|
-
</form.Field>
|
60
|
-
<button onClick={form.handleSubmit}>Submit</button>
|
61
|
-
{submittedData.value && (<p>Submitted data: {submittedData.value.firstName}</p>)}
|
62
|
-
</form.Provider>);
|
63
|
-
});
|
64
|
-
const { findByPlaceholderText, getByText } = render(Comp);
|
65
|
-
const input = await findByPlaceholderText('First name');
|
66
|
-
await user.clear(input);
|
67
|
-
await user.type(input, 'OtherName');
|
68
|
-
await user.click(getByText('Submit'));
|
69
|
-
await waitFor(() => expect(getByText('Submitted data: OtherName')).toBeInTheDocument());
|
70
|
-
});
|
71
|
-
});
|
@@ -1 +0,0 @@
|
|
1
|
-
export declare function sleep(timeout: number): Promise<void>;
|
package/build/lib/tests/utils.js
DELETED
package/build/lib/types.d.ts
DELETED
package/build/lib/types.js
DELETED
@@ -1 +0,0 @@
|
|
1
|
-
export {};
|