@tanstack/form-core 0.3.4 → 0.3.5
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/FieldApi.cjs.map +1 -1
- package/build/legacy/FieldApi.d.cts +1 -1
- package/build/legacy/FieldApi.d.ts +1 -1
- package/build/legacy/FieldApi.js.map +1 -1
- package/build/legacy/FormApi.cjs.map +1 -1
- package/build/legacy/FormApi.js.map +1 -1
- package/build/legacy/index.d.cts +43 -42
- package/build/legacy/index.d.ts +43 -42
- package/build/legacy/utils.cjs.map +1 -1
- package/build/legacy/utils.d.cts +2 -2
- package/build/legacy/utils.d.ts +2 -2
- package/build/legacy/utils.js.map +1 -1
- package/build/modern/FieldApi.cjs.map +1 -1
- package/build/modern/FieldApi.d.cts +1 -1
- package/build/modern/FieldApi.d.ts +1 -1
- package/build/modern/FieldApi.js.map +1 -1
- package/build/modern/FormApi.cjs.map +1 -1
- package/build/modern/FormApi.js.map +1 -1
- package/build/modern/index.d.cts +43 -42
- package/build/modern/index.d.ts +43 -42
- package/build/modern/utils.cjs.map +1 -1
- package/build/modern/utils.d.cts +2 -2
- package/build/modern/utils.d.ts +2 -2
- package/build/modern/utils.js.map +1 -1
- package/package.json +1 -1
- package/src/FieldApi.ts +78 -67
- package/src/FormApi.ts +7 -5
- package/src/utils.ts +13 -7
package/src/FieldApi.ts
CHANGED
|
@@ -4,59 +4,67 @@ import { Store } from '@tanstack/store'
|
|
|
4
4
|
|
|
5
5
|
export type ValidationCause = 'change' | 'blur' | 'submit' | 'mount'
|
|
6
6
|
|
|
7
|
-
type ValidateFn<TData,
|
|
7
|
+
type ValidateFn<TData, TParentData, TName extends DeepKeys<TParentData>> = (
|
|
8
8
|
value: TData,
|
|
9
|
-
fieldApi: FieldApi<TData,
|
|
9
|
+
fieldApi: FieldApi<TData, TParentData, TName>,
|
|
10
10
|
) => ValidationError
|
|
11
11
|
|
|
12
|
-
type ValidateAsyncFn<
|
|
12
|
+
type ValidateAsyncFn<
|
|
13
|
+
TData,
|
|
14
|
+
TParentData,
|
|
15
|
+
TName extends DeepKeys<TParentData>,
|
|
16
|
+
> = (
|
|
13
17
|
value: TData,
|
|
14
|
-
fieldApi: FieldApi<TData,
|
|
18
|
+
fieldApi: FieldApi<TData, TParentData, TName>,
|
|
15
19
|
) => ValidationError | Promise<ValidationError>
|
|
16
20
|
|
|
17
21
|
export interface FieldOptions<
|
|
18
|
-
|
|
19
|
-
|
|
22
|
+
TData,
|
|
23
|
+
TParentData,
|
|
20
24
|
/**
|
|
21
25
|
* This allows us to restrict the name to only be a valid field name while
|
|
22
26
|
* also assigning it to a generic
|
|
23
27
|
*/
|
|
24
|
-
TName
|
|
28
|
+
TName extends DeepKeys<TParentData>,
|
|
25
29
|
/**
|
|
26
30
|
* If TData is unknown, we can use the TName generic to determine the type
|
|
27
31
|
*/
|
|
28
|
-
|
|
32
|
+
TResolvedData = unknown extends TData ? DeepValue<TParentData, TName> : TData,
|
|
29
33
|
> {
|
|
30
|
-
name:
|
|
31
|
-
index?:
|
|
32
|
-
defaultValue?:
|
|
34
|
+
name: DeepKeys<TParentData>
|
|
35
|
+
index?: TResolvedData extends any[] ? number : never
|
|
36
|
+
defaultValue?: TResolvedData
|
|
33
37
|
asyncDebounceMs?: number
|
|
34
38
|
asyncAlways?: boolean
|
|
35
|
-
onMount?: (formApi: FieldApi<
|
|
36
|
-
onChange?: ValidateFn<
|
|
37
|
-
onChangeAsync?: ValidateAsyncFn<
|
|
39
|
+
onMount?: (formApi: FieldApi<TResolvedData, TParentData, TName>) => void
|
|
40
|
+
onChange?: ValidateFn<TResolvedData, TParentData, TName>
|
|
41
|
+
onChangeAsync?: ValidateAsyncFn<TResolvedData, TParentData, TName>
|
|
38
42
|
onChangeAsyncDebounceMs?: number
|
|
39
|
-
onBlur?: ValidateFn<
|
|
40
|
-
onBlurAsync?: ValidateAsyncFn<
|
|
43
|
+
onBlur?: ValidateFn<TResolvedData, TParentData, TName>
|
|
44
|
+
onBlurAsync?: ValidateAsyncFn<TResolvedData, TParentData, TName>
|
|
41
45
|
onBlurAsyncDebounceMs?: number
|
|
42
|
-
onSubmitAsync?: ValidateAsyncFn<
|
|
46
|
+
onSubmitAsync?: ValidateAsyncFn<TResolvedData, TParentData, TName>
|
|
43
47
|
defaultMeta?: Partial<FieldMeta>
|
|
44
48
|
}
|
|
45
49
|
|
|
46
50
|
export interface FieldApiOptions<
|
|
47
|
-
|
|
48
|
-
|
|
51
|
+
TData,
|
|
52
|
+
TParentData,
|
|
49
53
|
/**
|
|
50
54
|
* This allows us to restrict the name to only be a valid field name while
|
|
51
55
|
* also assigning it to a generic
|
|
52
56
|
*/
|
|
53
|
-
TName
|
|
57
|
+
TName extends DeepKeys<TParentData>,
|
|
54
58
|
/**
|
|
55
59
|
* If TData is unknown, we can use the TName generic to determine the type
|
|
56
60
|
*/
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
61
|
+
TResolvedData extends ResolveData<TData, TParentData, TName> = ResolveData<
|
|
62
|
+
TData,
|
|
63
|
+
TParentData,
|
|
64
|
+
TName
|
|
65
|
+
>,
|
|
66
|
+
> extends FieldOptions<TData, TParentData, TName, TResolvedData> {
|
|
67
|
+
form: FormApi<TParentData>
|
|
60
68
|
}
|
|
61
69
|
|
|
62
70
|
export type FieldMeta = {
|
|
@@ -74,43 +82,35 @@ export type FieldState<TData> = {
|
|
|
74
82
|
meta: FieldMeta
|
|
75
83
|
}
|
|
76
84
|
|
|
77
|
-
type
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
> =
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
infer _TName,
|
|
85
|
-
infer RealTData
|
|
86
|
-
>
|
|
87
|
-
? RealTData
|
|
88
|
-
: never
|
|
85
|
+
export type ResolveData<TData, TParentData, TName> = unknown extends TData
|
|
86
|
+
? DeepValue<TParentData, TName>
|
|
87
|
+
: TData
|
|
88
|
+
|
|
89
|
+
export type ResolveName<TParentData> = unknown extends TParentData
|
|
90
|
+
? string
|
|
91
|
+
: DeepKeys<TParentData>
|
|
89
92
|
|
|
90
93
|
export class FieldApi<
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
_TData,
|
|
99
|
-
TFormData,
|
|
100
|
-
Opts
|
|
94
|
+
TData,
|
|
95
|
+
TParentData,
|
|
96
|
+
TName extends DeepKeys<TParentData>,
|
|
97
|
+
TResolvedData extends ResolveData<TData, TParentData, TName> = ResolveData<
|
|
98
|
+
TData,
|
|
99
|
+
TParentData,
|
|
100
|
+
TName
|
|
101
101
|
>,
|
|
102
102
|
> {
|
|
103
103
|
uid: number
|
|
104
|
-
form:
|
|
105
|
-
name!: DeepKeys<
|
|
106
|
-
options:
|
|
107
|
-
store!: Store<FieldState<
|
|
108
|
-
state!: FieldState<
|
|
109
|
-
prevState!: FieldState<
|
|
104
|
+
form: FieldApiOptions<TData, TParentData, TName, TResolvedData>['form']
|
|
105
|
+
name!: DeepKeys<TParentData>
|
|
106
|
+
options: FieldApiOptions<TData, TParentData, TName> = {} as any
|
|
107
|
+
store!: Store<FieldState<TResolvedData>>
|
|
108
|
+
state!: FieldState<TResolvedData>
|
|
109
|
+
prevState!: FieldState<TResolvedData>
|
|
110
110
|
|
|
111
111
|
constructor(
|
|
112
|
-
opts:
|
|
113
|
-
form: FormApi<
|
|
112
|
+
opts: FieldApiOptions<TData, TParentData, TName, TResolvedData> & {
|
|
113
|
+
form: FormApi<TParentData>
|
|
114
114
|
},
|
|
115
115
|
) {
|
|
116
116
|
this.form = opts.form
|
|
@@ -123,7 +123,7 @@ export class FieldApi<
|
|
|
123
123
|
|
|
124
124
|
this.name = opts.name as any
|
|
125
125
|
|
|
126
|
-
this.store = new Store<FieldState<
|
|
126
|
+
this.store = new Store<FieldState<TResolvedData>>(
|
|
127
127
|
{
|
|
128
128
|
value: this.getValue(),
|
|
129
129
|
// eslint-disable-next-line @typescript-eslint/no-unnecessary-condition
|
|
@@ -190,12 +190,12 @@ export class FieldApi<
|
|
|
190
190
|
}
|
|
191
191
|
}
|
|
192
192
|
|
|
193
|
-
update = (opts: FieldApiOptions<
|
|
193
|
+
update = (opts: FieldApiOptions<TResolvedData, TParentData, TName>) => {
|
|
194
194
|
// Default Value
|
|
195
195
|
// eslint-disable-next-line @typescript-eslint/no-unnecessary-condition
|
|
196
196
|
if (this.state.value === undefined) {
|
|
197
197
|
const formDefault =
|
|
198
|
-
opts.form.options.defaultValues?.[opts.name as keyof
|
|
198
|
+
opts.form.options.defaultValues?.[opts.name as keyof TParentData]
|
|
199
199
|
|
|
200
200
|
if (opts.defaultValue !== undefined) {
|
|
201
201
|
this.setValue(opts.defaultValue as never)
|
|
@@ -212,12 +212,12 @@ export class FieldApi<
|
|
|
212
212
|
this.options = opts as never
|
|
213
213
|
}
|
|
214
214
|
|
|
215
|
-
getValue = ():
|
|
216
|
-
return this.form.getFieldValue(this.name)
|
|
215
|
+
getValue = (): TResolvedData => {
|
|
216
|
+
return this.form.getFieldValue(this.name) as any
|
|
217
217
|
}
|
|
218
218
|
|
|
219
219
|
setValue = (
|
|
220
|
-
updater: Updater<
|
|
220
|
+
updater: Updater<TResolvedData>,
|
|
221
221
|
options?: { touch?: boolean; notify?: boolean },
|
|
222
222
|
) => {
|
|
223
223
|
this.form.setFieldValue(this.name, updater as never, options)
|
|
@@ -241,12 +241,13 @@ export class FieldApi<
|
|
|
241
241
|
|
|
242
242
|
getInfo = () => this.form.getFieldInfo(this.name)
|
|
243
243
|
|
|
244
|
-
pushValue = (
|
|
245
|
-
|
|
244
|
+
pushValue = (
|
|
245
|
+
value: TResolvedData extends any[] ? TResolvedData[number] : never,
|
|
246
|
+
) => this.form.pushFieldValue(this.name, value as any)
|
|
246
247
|
|
|
247
248
|
insertValue = (
|
|
248
249
|
index: number,
|
|
249
|
-
value:
|
|
250
|
+
value: TResolvedData extends any[] ? TResolvedData[number] : never,
|
|
250
251
|
) => this.form.insertFieldValue(this.name, index, value as any)
|
|
251
252
|
|
|
252
253
|
removeValue = (index: number) => this.form.removeFieldValue(this.name, index)
|
|
@@ -254,11 +255,21 @@ export class FieldApi<
|
|
|
254
255
|
swapValues = (aIndex: number, bIndex: number) =>
|
|
255
256
|
this.form.swapFieldValues(this.name, aIndex, bIndex)
|
|
256
257
|
|
|
257
|
-
getSubField = <
|
|
258
|
-
|
|
258
|
+
getSubField = <
|
|
259
|
+
TSubData,
|
|
260
|
+
TSubName extends DeepKeys<TResolvedData>,
|
|
261
|
+
TSubResolvedData extends ResolveData<
|
|
262
|
+
DeepValue<TResolvedData, TSubName>,
|
|
263
|
+
TResolvedData,
|
|
264
|
+
TSubName
|
|
265
|
+
>,
|
|
266
|
+
>(
|
|
267
|
+
name: TSubName,
|
|
268
|
+
): FieldApi<TSubData, TResolvedData, TSubName, TSubResolvedData> =>
|
|
269
|
+
new FieldApi({
|
|
259
270
|
name: `${this.name}.${name}` as never,
|
|
260
271
|
form: this.form,
|
|
261
|
-
})
|
|
272
|
+
}) as any
|
|
262
273
|
|
|
263
274
|
validateSync = (value = this.state.value, cause: ValidationCause) => {
|
|
264
275
|
const { onChange, onBlur } = this.options
|
|
@@ -387,7 +398,7 @@ export class FieldApi<
|
|
|
387
398
|
|
|
388
399
|
validate = (
|
|
389
400
|
cause: ValidationCause,
|
|
390
|
-
value?:
|
|
401
|
+
value?: TResolvedData,
|
|
391
402
|
): ValidationError[] | Promise<ValidationError[]> => {
|
|
392
403
|
// If the field is pristine and validatePristine is false, do not validate
|
|
393
404
|
if (!this.state.meta.isTouched) return []
|
|
@@ -405,7 +416,7 @@ export class FieldApi<
|
|
|
405
416
|
return this.validateAsync(value, cause)
|
|
406
417
|
}
|
|
407
418
|
|
|
408
|
-
handleChange = (updater: Updater<
|
|
419
|
+
handleChange = (updater: Updater<TResolvedData>) => {
|
|
409
420
|
this.setValue(updater, { touch: true })
|
|
410
421
|
}
|
|
411
422
|
|
package/src/FormApi.ts
CHANGED
|
@@ -31,7 +31,7 @@ export type FormOptions<TData> = {
|
|
|
31
31
|
}
|
|
32
32
|
|
|
33
33
|
export type FieldInfo<TFormData> = {
|
|
34
|
-
instances: Record<string, FieldApi<any, TFormData>>
|
|
34
|
+
instances: Record<string, FieldApi<any, TFormData, any>>
|
|
35
35
|
} & ValidationMeta
|
|
36
36
|
|
|
37
37
|
export type ValidationMeta = {
|
|
@@ -106,7 +106,7 @@ export class FormApi<TFormData> {
|
|
|
106
106
|
constructor(opts?: FormOptions<TFormData>) {
|
|
107
107
|
this.store = new Store<FormState<TFormData>>(
|
|
108
108
|
getDefaultFormState({
|
|
109
|
-
...opts?.defaultState,
|
|
109
|
+
...(opts?.defaultState as any),
|
|
110
110
|
values: opts?.defaultValues ?? opts?.defaultState?.values,
|
|
111
111
|
isFormValid: true,
|
|
112
112
|
}),
|
|
@@ -174,7 +174,7 @@ export class FormApi<TFormData> {
|
|
|
174
174
|
getDefaultFormState(
|
|
175
175
|
Object.assign(
|
|
176
176
|
{},
|
|
177
|
-
this.state,
|
|
177
|
+
this.state as any,
|
|
178
178
|
// eslint-disable-next-line @typescript-eslint/no-unnecessary-condition
|
|
179
179
|
shouldUpdateState ? options.defaultState : {},
|
|
180
180
|
// eslint-disable-next-line @typescript-eslint/no-unnecessary-condition
|
|
@@ -194,7 +194,7 @@ export class FormApi<TFormData> {
|
|
|
194
194
|
reset = () =>
|
|
195
195
|
this.store.setState(() =>
|
|
196
196
|
getDefaultFormState({
|
|
197
|
-
...this.options.defaultState,
|
|
197
|
+
...(this.options.defaultState as any),
|
|
198
198
|
values: this.options.defaultValues ?? this.options.defaultState?.values,
|
|
199
199
|
}),
|
|
200
200
|
)
|
|
@@ -288,7 +288,9 @@ export class FormApi<TFormData> {
|
|
|
288
288
|
return this.state.fieldMeta[field]
|
|
289
289
|
}
|
|
290
290
|
|
|
291
|
-
getFieldInfo = <TField extends DeepKeys<TFormData>>(
|
|
291
|
+
getFieldInfo = <TField extends DeepKeys<TFormData>>(
|
|
292
|
+
field: TField,
|
|
293
|
+
): FieldInfo<TFormData> => {
|
|
292
294
|
// eslint-disable-next-line @typescript-eslint/no-unnecessary-condition
|
|
293
295
|
return (this.fieldInfo[field] ||= {
|
|
294
296
|
instances: {},
|
package/src/utils.ts
CHANGED
|
@@ -133,22 +133,28 @@ type AllowedIndexes<
|
|
|
133
133
|
? AllowedIndexes<Tail, Keys | Tail['length']>
|
|
134
134
|
: Keys
|
|
135
135
|
|
|
136
|
-
export type DeepKeys<T> =
|
|
137
|
-
?
|
|
136
|
+
export type DeepKeys<T, TDepth extends any[] = []> = TDepth['length'] extends 5
|
|
137
|
+
? never
|
|
138
|
+
: unknown extends T
|
|
139
|
+
? string
|
|
138
140
|
: object extends T
|
|
139
141
|
? string
|
|
140
142
|
: T extends readonly any[] & IsTuple<T>
|
|
141
|
-
? AllowedIndexes<T> | DeepKeysPrefix<T, AllowedIndexes<T
|
|
143
|
+
? AllowedIndexes<T> | DeepKeysPrefix<T, AllowedIndexes<T>, TDepth>
|
|
142
144
|
: T extends any[]
|
|
143
|
-
? DeepKeys<T[number]>
|
|
145
|
+
? DeepKeys<T[number], [...TDepth, any]>
|
|
144
146
|
: T extends Date
|
|
145
147
|
? never
|
|
146
148
|
: T extends object
|
|
147
|
-
? (keyof T & string) | DeepKeysPrefix<T, keyof T>
|
|
149
|
+
? (keyof T & string) | DeepKeysPrefix<T, keyof T, TDepth>
|
|
148
150
|
: never
|
|
149
151
|
|
|
150
|
-
type DeepKeysPrefix<
|
|
151
|
-
|
|
152
|
+
type DeepKeysPrefix<
|
|
153
|
+
T,
|
|
154
|
+
TPrefix,
|
|
155
|
+
TDepth extends any[],
|
|
156
|
+
> = TPrefix extends keyof T & (number | string)
|
|
157
|
+
? `${TPrefix}.${DeepKeys<T[TPrefix], [...TDepth, any]> & string}`
|
|
152
158
|
: never
|
|
153
159
|
|
|
154
160
|
export type DeepValue<T, TProp> = T extends Record<string | number, any>
|