@tanstack/solid-form 1.12.4 → 1.14.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.
@@ -2,7 +2,6 @@ import { FieldApi } from '@tanstack/form-core'
2
2
  import {
3
3
  createComponent,
4
4
  createComputed,
5
- createMemo,
6
5
  createSignal,
7
6
  onCleanup,
8
7
  onMount,
@@ -17,7 +16,7 @@ import type {
17
16
  Narrow,
18
17
  } from '@tanstack/form-core'
19
18
 
20
- import type { JSXElement } from 'solid-js'
19
+ import type { Accessor, Component, JSX, JSXElement } from 'solid-js'
21
20
  import type { CreateFieldOptions, CreateFieldOptionsBound } from './types'
22
21
 
23
22
  interface SolidFieldApi<
@@ -359,7 +358,8 @@ interface FieldComponentBoundProps<
359
358
  TFormOnSubmit extends undefined | FormValidateOrFn<TParentData>,
360
359
  TFormOnSubmitAsync extends undefined | FormAsyncValidateOrFn<TParentData>,
361
360
  TFormOnServer extends undefined | FormAsyncValidateOrFn<TParentData>,
362
- TParentSubmitMeta,
361
+ TPatentSubmitMeta,
362
+ ExtendedApi = {},
363
363
  > extends CreateFieldOptionsBound<
364
364
  TParentData,
365
365
  TName,
@@ -373,43 +373,56 @@ interface FieldComponentBoundProps<
373
373
  TOnSubmitAsync
374
374
  > {
375
375
  children: (
376
- fieldApi: () => FieldApi<
377
- TParentData,
378
- TName,
379
- TData,
380
- TOnMount,
381
- TOnChange,
382
- TOnChangeAsync,
383
- TOnBlur,
384
- TOnBlurAsync,
385
- TOnSubmit,
386
- TOnSubmitAsync,
387
- TFormOnMount,
388
- TFormOnChange,
389
- TFormOnChangeAsync,
390
- TFormOnBlur,
391
- TFormOnBlurAsync,
392
- TFormOnSubmit,
393
- TFormOnSubmitAsync,
394
- TFormOnServer,
395
- TParentSubmitMeta
396
- >,
397
- ) => JSXElement
376
+ fieldApi: Accessor<
377
+ FieldApi<
378
+ TParentData,
379
+ TName,
380
+ TData,
381
+ TOnMount,
382
+ TOnChange,
383
+ TOnChangeAsync,
384
+ TOnBlur,
385
+ TOnBlurAsync,
386
+ TOnSubmit,
387
+ TOnSubmitAsync,
388
+ TFormOnMount,
389
+ TFormOnChange,
390
+ TFormOnChangeAsync,
391
+ TFormOnBlur,
392
+ TFormOnBlurAsync,
393
+ TFormOnSubmit,
394
+ TFormOnSubmitAsync,
395
+ TFormOnServer,
396
+ TPatentSubmitMeta
397
+ >
398
+ > &
399
+ ExtendedApi,
400
+ ) => JSX.Element
398
401
  }
399
402
 
403
+ /**
404
+ * A type alias representing a field component for a specific form data type.
405
+ */
400
406
  export type FieldComponent<
401
- TParentData,
402
- TFormOnMount extends undefined | FormValidateOrFn<TParentData>,
403
- TFormOnChange extends undefined | FormValidateOrFn<TParentData>,
404
- TFormOnChangeAsync extends undefined | FormAsyncValidateOrFn<TParentData>,
405
- TFormOnBlur extends undefined | FormValidateOrFn<TParentData>,
406
- TFormOnBlurAsync extends undefined | FormAsyncValidateOrFn<TParentData>,
407
- TFormOnSubmit extends undefined | FormValidateOrFn<TParentData>,
408
- TFormOnSubmitAsync extends undefined | FormAsyncValidateOrFn<TParentData>,
409
- TFormOnServer extends undefined | FormAsyncValidateOrFn<TParentData>,
410
- TParentSubmitMeta,
407
+ in out TParentData,
408
+ in out TFormOnMount extends undefined | FormValidateOrFn<TParentData>,
409
+ in out TFormOnChange extends undefined | FormValidateOrFn<TParentData>,
410
+ in out TFormOnChangeAsync extends
411
+ | undefined
412
+ | FormAsyncValidateOrFn<TParentData>,
413
+ in out TFormOnBlur extends undefined | FormValidateOrFn<TParentData>,
414
+ in out TFormOnBlurAsync extends
415
+ | undefined
416
+ | FormAsyncValidateOrFn<TParentData>,
417
+ in out TFormOnSubmit extends undefined | FormValidateOrFn<TParentData>,
418
+ in out TFormOnSubmitAsync extends
419
+ | undefined
420
+ | FormAsyncValidateOrFn<TParentData>,
421
+ in out TFormOnServer extends undefined | FormAsyncValidateOrFn<TParentData>,
422
+ in out TPatentSubmitMeta,
423
+ in out ExtendedApi = {},
411
424
  > = <
412
- TName extends DeepKeys<TParentData>,
425
+ const TName extends DeepKeys<TParentData>,
413
426
  TData extends DeepValue<TParentData, TName>,
414
427
  TOnMount extends undefined | FieldValidateOrFn<TParentData, TName, TData>,
415
428
  TOnChange extends undefined | FieldValidateOrFn<TParentData, TName, TData>,
@@ -446,8 +459,9 @@ export type FieldComponent<
446
459
  TFormOnSubmit,
447
460
  TFormOnSubmitAsync,
448
461
  TFormOnServer,
449
- TParentSubmitMeta
450
- >) => JSXElement
462
+ TPatentSubmitMeta,
463
+ ExtendedApi
464
+ >) => JSX.Element
451
465
 
452
466
  interface FieldComponentProps<
453
467
  TParentData,
@@ -112,6 +112,44 @@ export interface SolidFormApi<
112
112
  }) => JSXElement
113
113
  }
114
114
 
115
+ /**
116
+ * An extended version of the `FormApi` class that includes React-specific functionalities from `ReactFormApi`
117
+ */
118
+ export type SolidFormExtendedApi<
119
+ TFormData,
120
+ TOnMount extends undefined | FormValidateOrFn<TFormData>,
121
+ TOnChange extends undefined | FormValidateOrFn<TFormData>,
122
+ TOnChangeAsync extends undefined | FormAsyncValidateOrFn<TFormData>,
123
+ TOnBlur extends undefined | FormValidateOrFn<TFormData>,
124
+ TOnBlurAsync extends undefined | FormAsyncValidateOrFn<TFormData>,
125
+ TOnSubmit extends undefined | FormValidateOrFn<TFormData>,
126
+ TOnSubmitAsync extends undefined | FormAsyncValidateOrFn<TFormData>,
127
+ TOnServer extends undefined | FormAsyncValidateOrFn<TFormData>,
128
+ TSubmitMeta,
129
+ > = FormApi<
130
+ TFormData,
131
+ TOnMount,
132
+ TOnChange,
133
+ TOnChangeAsync,
134
+ TOnBlur,
135
+ TOnBlurAsync,
136
+ TOnSubmit,
137
+ TOnSubmitAsync,
138
+ TOnServer,
139
+ TSubmitMeta
140
+ > &
141
+ SolidFormApi<
142
+ TFormData,
143
+ TOnMount,
144
+ TOnChange,
145
+ TOnChangeAsync,
146
+ TOnBlur,
147
+ TOnBlurAsync,
148
+ TOnSubmit,
149
+ TOnSubmitAsync,
150
+ TOnServer,
151
+ TSubmitMeta
152
+ >
115
153
  export function createForm<
116
154
  TParentData,
117
155
  TFormOnMount extends undefined | FormValidateOrFn<TParentData>,
@@ -0,0 +1,386 @@
1
+ import { createContext, splitProps, useContext } from 'solid-js'
2
+ import { createForm } from './createForm'
3
+ import type {
4
+ AnyFieldApi,
5
+ AnyFormApi,
6
+ FieldApi,
7
+ FormAsyncValidateOrFn,
8
+ FormOptions,
9
+ FormValidateOrFn,
10
+ } from '@tanstack/form-core'
11
+ import type {
12
+ Accessor,
13
+ Component,
14
+ Context,
15
+ JSXElement,
16
+ ParentProps,
17
+ } from 'solid-js'
18
+ import type { FieldComponent } from './createField'
19
+ import type { SolidFormExtendedApi } from './createForm'
20
+
21
+ /**
22
+ * TypeScript inferencing is weird.
23
+ *
24
+ * If you have:
25
+ *
26
+ * @example
27
+ *
28
+ * interface Args<T> {
29
+ * arg?: T
30
+ * }
31
+ *
32
+ * function test<T>(arg?: Partial<Args<T>>): T {
33
+ * return 0 as any;
34
+ * }
35
+ *
36
+ * const a = test({});
37
+ *
38
+ * Then `T` will default to `unknown`.
39
+ *
40
+ * However, if we change `test` to be:
41
+ *
42
+ * @example
43
+ *
44
+ * function test<T extends undefined>(arg?: Partial<Args<T>>): T;
45
+ *
46
+ * Then `T` becomes `undefined`.
47
+ *
48
+ * Here, we are checking if the passed type `T` extends `DefaultT` and **only**
49
+ * `DefaultT`, as if that's the case we assume that inferencing has not occured.
50
+ */
51
+ type UnwrapOrAny<T> = [unknown] extends [T] ? any : T
52
+ type UnwrapDefaultOrAny<DefaultT, T> = [DefaultT] extends [T]
53
+ ? [T] extends [DefaultT]
54
+ ? any
55
+ : T
56
+ : T
57
+
58
+ export function createFormHookContexts() {
59
+ // We should never hit the `null` case here
60
+ const fieldContext = createContext<Accessor<AnyFieldApi>>(
61
+ null as unknown as Accessor<AnyFieldApi>,
62
+ )
63
+
64
+ function useFieldContext<TData>() {
65
+ const field = useContext(fieldContext)
66
+
67
+ // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition
68
+ if (!field) {
69
+ throw new Error(
70
+ '`fieldContext` only works when within a `fieldComponent` passed to `createFormHook`',
71
+ )
72
+ }
73
+
74
+ return field as Accessor<
75
+ FieldApi<
76
+ any,
77
+ string,
78
+ TData,
79
+ any,
80
+ any,
81
+ any,
82
+ any,
83
+ any,
84
+ any,
85
+ any,
86
+ any,
87
+ any,
88
+ any,
89
+ any,
90
+ any,
91
+ any,
92
+ any,
93
+ any,
94
+ any
95
+ >
96
+ >
97
+ }
98
+
99
+ // We should never hit the `null` case here
100
+ const formContext = createContext<AnyFormApi>(null as unknown as AnyFormApi)
101
+
102
+ function useFormContext() {
103
+ const form = useContext(formContext)
104
+
105
+ // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition
106
+ if (!form) {
107
+ throw new Error(
108
+ '`formContext` only works when within a `formComponent` passed to `createFormHook`',
109
+ )
110
+ }
111
+
112
+ return form as SolidFormExtendedApi<
113
+ // If you need access to the form data, you need to use `withForm` instead
114
+ Record<string, never>,
115
+ any,
116
+ any,
117
+ any,
118
+ any,
119
+ any,
120
+ any,
121
+ any,
122
+ any,
123
+ any
124
+ >
125
+ }
126
+
127
+ return { fieldContext, useFieldContext, useFormContext, formContext }
128
+ }
129
+
130
+ interface CreateFormHookProps<
131
+ TFieldComponents extends Record<string, Component<any>>,
132
+ TFormComponents extends Record<string, Component<any>>,
133
+ > {
134
+ fieldComponents: TFieldComponents
135
+ fieldContext: Context<Accessor<AnyFieldApi>>
136
+ formComponents: TFormComponents
137
+ formContext: Context<AnyFormApi>
138
+ }
139
+
140
+ type AppFieldExtendedSolidFormApi<
141
+ TFormData,
142
+ TOnMount extends undefined | FormValidateOrFn<TFormData>,
143
+ TOnChange extends undefined | FormValidateOrFn<TFormData>,
144
+ TOnChangeAsync extends undefined | FormAsyncValidateOrFn<TFormData>,
145
+ TOnBlur extends undefined | FormValidateOrFn<TFormData>,
146
+ TOnBlurAsync extends undefined | FormAsyncValidateOrFn<TFormData>,
147
+ TOnSubmit extends undefined | FormValidateOrFn<TFormData>,
148
+ TOnSubmitAsync extends undefined | FormAsyncValidateOrFn<TFormData>,
149
+ TOnServer extends undefined | FormAsyncValidateOrFn<TFormData>,
150
+ TSubmitMeta,
151
+ TFieldComponents extends Record<string, Component<any>>,
152
+ TFormComponents extends Record<string, Component<any>>,
153
+ > = SolidFormExtendedApi<
154
+ TFormData,
155
+ TOnMount,
156
+ TOnChange,
157
+ TOnChangeAsync,
158
+ TOnBlur,
159
+ TOnBlurAsync,
160
+ TOnSubmit,
161
+ TOnSubmitAsync,
162
+ TOnServer,
163
+ TSubmitMeta
164
+ > &
165
+ NoInfer<TFormComponents> & {
166
+ AppField: FieldComponent<
167
+ TFormData,
168
+ TOnMount,
169
+ TOnChange,
170
+ TOnChangeAsync,
171
+ TOnBlur,
172
+ TOnBlurAsync,
173
+ TOnSubmit,
174
+ TOnSubmitAsync,
175
+ TOnServer,
176
+ TSubmitMeta,
177
+ NoInfer<TFieldComponents>
178
+ >
179
+ AppForm: Component<ParentProps>
180
+ }
181
+
182
+ export interface WithFormProps<
183
+ TFormData,
184
+ TOnMount extends undefined | FormValidateOrFn<TFormData>,
185
+ TOnChange extends undefined | FormValidateOrFn<TFormData>,
186
+ TOnChangeAsync extends undefined | FormAsyncValidateOrFn<TFormData>,
187
+ TOnBlur extends undefined | FormValidateOrFn<TFormData>,
188
+ TOnBlurAsync extends undefined | FormAsyncValidateOrFn<TFormData>,
189
+ TOnSubmit extends undefined | FormValidateOrFn<TFormData>,
190
+ TOnSubmitAsync extends undefined | FormAsyncValidateOrFn<TFormData>,
191
+ TOnServer extends undefined | FormAsyncValidateOrFn<TFormData>,
192
+ TSubmitMeta,
193
+ TFieldComponents extends Record<string, Component<any>>,
194
+ TFormComponents extends Record<string, Component<any>>,
195
+ TRenderProps extends Record<string, unknown> = Record<string, never>,
196
+ > extends FormOptions<
197
+ TFormData,
198
+ TOnMount,
199
+ TOnChange,
200
+ TOnChangeAsync,
201
+ TOnBlur,
202
+ TOnBlurAsync,
203
+ TOnSubmit,
204
+ TOnSubmitAsync,
205
+ TOnServer,
206
+ TSubmitMeta
207
+ > {
208
+ // Optional, but adds props to the `render` function outside of `form`
209
+ props?: TRenderProps
210
+ render: (
211
+ props: ParentProps<
212
+ NoInfer<TRenderProps> & {
213
+ form: AppFieldExtendedSolidFormApi<
214
+ TFormData,
215
+ TOnMount,
216
+ TOnChange,
217
+ TOnChangeAsync,
218
+ TOnBlur,
219
+ TOnBlurAsync,
220
+ TOnSubmit,
221
+ TOnSubmitAsync,
222
+ TOnServer,
223
+ TSubmitMeta,
224
+ TFieldComponents,
225
+ TFormComponents
226
+ >
227
+ }
228
+ >,
229
+ ) => JSXElement
230
+ }
231
+
232
+ export function createFormHook<
233
+ const TComponents extends Record<string, Component<any>>,
234
+ const TFormComponents extends Record<string, Component<any>>,
235
+ >(opts: CreateFormHookProps<TComponents, TFormComponents>) {
236
+ function useAppForm<
237
+ TFormData,
238
+ TOnMount extends undefined | FormValidateOrFn<TFormData>,
239
+ TOnChange extends undefined | FormValidateOrFn<TFormData>,
240
+ TOnChangeAsync extends undefined | FormAsyncValidateOrFn<TFormData>,
241
+ TOnBlur extends undefined | FormValidateOrFn<TFormData>,
242
+ TOnBlurAsync extends undefined | FormAsyncValidateOrFn<TFormData>,
243
+ TOnSubmit extends undefined | FormValidateOrFn<TFormData>,
244
+ TOnSubmitAsync extends undefined | FormAsyncValidateOrFn<TFormData>,
245
+ TOnServer extends undefined | FormAsyncValidateOrFn<TFormData>,
246
+ TSubmitMeta,
247
+ >(
248
+ props: Accessor<
249
+ FormOptions<
250
+ TFormData,
251
+ TOnMount,
252
+ TOnChange,
253
+ TOnChangeAsync,
254
+ TOnBlur,
255
+ TOnBlurAsync,
256
+ TOnSubmit,
257
+ TOnSubmitAsync,
258
+ TOnServer,
259
+ TSubmitMeta
260
+ >
261
+ >,
262
+ ): AppFieldExtendedSolidFormApi<
263
+ TFormData,
264
+ TOnMount,
265
+ TOnChange,
266
+ TOnChangeAsync,
267
+ TOnBlur,
268
+ TOnBlurAsync,
269
+ TOnSubmit,
270
+ TOnSubmitAsync,
271
+ TOnServer,
272
+ TSubmitMeta,
273
+ TComponents,
274
+ TFormComponents
275
+ > {
276
+ const form = createForm(props)
277
+
278
+ const AppForm = ((formProps) => {
279
+ return (
280
+ <opts.formContext.Provider value={form}>
281
+ {formProps.children}
282
+ </opts.formContext.Provider>
283
+ )
284
+ }) as Component<ParentProps>
285
+
286
+ const AppField = ((_props) => {
287
+ const [childProps, fieldProps] = splitProps(_props, ['children'])
288
+ return (
289
+ <form.Field {...fieldProps}>
290
+ {(field) => (
291
+ <opts.fieldContext.Provider value={field}>
292
+ {childProps.children(Object.assign(field, opts.fieldComponents))}
293
+ </opts.fieldContext.Provider>
294
+ )}
295
+ </form.Field>
296
+ )
297
+ }) as FieldComponent<
298
+ TFormData,
299
+ TOnMount,
300
+ TOnChange,
301
+ TOnChangeAsync,
302
+ TOnBlur,
303
+ TOnBlurAsync,
304
+ TOnSubmit,
305
+ TOnSubmitAsync,
306
+ TOnServer,
307
+ TSubmitMeta,
308
+ TComponents
309
+ >
310
+
311
+ const extendedForm: AppFieldExtendedSolidFormApi<
312
+ TFormData,
313
+ TOnMount,
314
+ TOnChange,
315
+ TOnChangeAsync,
316
+ TOnBlur,
317
+ TOnBlurAsync,
318
+ TOnSubmit,
319
+ TOnSubmitAsync,
320
+ TOnServer,
321
+ TSubmitMeta,
322
+ TComponents,
323
+ TFormComponents
324
+ > = form as never
325
+ extendedForm.AppField = AppField
326
+ extendedForm.AppForm = AppForm
327
+ for (const [key, value] of Object.entries(opts.formComponents)) {
328
+ // Since it's a generic I need to cast it to an object
329
+ ;(extendedForm as Record<string, any>)[key] = value
330
+ }
331
+
332
+ return extendedForm
333
+ }
334
+
335
+ function withForm<
336
+ TFormData,
337
+ TOnMount extends undefined | FormValidateOrFn<TFormData>,
338
+ TOnChange extends undefined | FormValidateOrFn<TFormData>,
339
+ TOnChangeAsync extends undefined | FormAsyncValidateOrFn<TFormData>,
340
+ TOnBlur extends undefined | FormValidateOrFn<TFormData>,
341
+ TOnBlurAsync extends undefined | FormAsyncValidateOrFn<TFormData>,
342
+ TOnSubmit extends undefined | FormValidateOrFn<TFormData>,
343
+ TOnSubmitAsync extends undefined | FormAsyncValidateOrFn<TFormData>,
344
+ TOnServer extends undefined | FormAsyncValidateOrFn<TFormData>,
345
+ TSubmitMeta,
346
+ TRenderProps extends Record<string, unknown> = {},
347
+ >({
348
+ render,
349
+ props,
350
+ }: WithFormProps<
351
+ TFormData,
352
+ TOnMount,
353
+ TOnChange,
354
+ TOnChangeAsync,
355
+ TOnBlur,
356
+ TOnBlurAsync,
357
+ TOnSubmit,
358
+ TOnSubmitAsync,
359
+ TOnServer,
360
+ TSubmitMeta,
361
+ TComponents,
362
+ TFormComponents,
363
+ TRenderProps
364
+ >): WithFormProps<
365
+ UnwrapOrAny<TFormData>,
366
+ UnwrapDefaultOrAny<undefined | FormValidateOrFn<TFormData>, TOnMount>,
367
+ UnwrapDefaultOrAny<undefined | FormValidateOrFn<TFormData>, TOnChange>,
368
+ UnwrapDefaultOrAny<undefined | FormValidateOrFn<TFormData>, TOnChangeAsync>,
369
+ UnwrapDefaultOrAny<undefined | FormValidateOrFn<TFormData>, TOnBlur>,
370
+ UnwrapDefaultOrAny<undefined | FormValidateOrFn<TFormData>, TOnBlurAsync>,
371
+ UnwrapDefaultOrAny<undefined | FormValidateOrFn<TFormData>, TOnSubmit>,
372
+ UnwrapDefaultOrAny<undefined | FormValidateOrFn<TFormData>, TOnSubmitAsync>,
373
+ UnwrapDefaultOrAny<undefined | FormValidateOrFn<TFormData>, TOnServer>,
374
+ UnwrapOrAny<TSubmitMeta>,
375
+ UnwrapOrAny<TComponents>,
376
+ UnwrapOrAny<TFormComponents>,
377
+ UnwrapOrAny<TRenderProps>
378
+ >['render'] {
379
+ return (innerProps) => render({ ...props, ...innerProps })
380
+ }
381
+
382
+ return {
383
+ useAppForm,
384
+ withForm,
385
+ }
386
+ }
package/src/index.tsx CHANGED
@@ -6,3 +6,6 @@ export { createForm, type SolidFormApi } from './createForm'
6
6
 
7
7
  export type { CreateField, FieldComponent } from './createField'
8
8
  export { createField, Field } from './createField'
9
+
10
+ export type { WithFormProps } from './createFormHook'
11
+ export { createFormHook, createFormHookContexts } from './createFormHook'