@tanstack/form-core 1.20.0 → 1.21.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.
package/src/FormApi.ts CHANGED
@@ -19,6 +19,12 @@ import {
19
19
  standardSchemaValidators,
20
20
  } from './standardSchemaValidator'
21
21
  import { defaultFieldMeta, metaHelper } from './metaHelper'
22
+ import { formEventClient } from './EventClient'
23
+ import type {
24
+ RequestFormForceReset,
25
+ RequestFormReset,
26
+ RequestFormState,
27
+ } from './EventClient'
22
28
  import type { ValidationLogicFn } from './ValidationLogic'
23
29
  import type {
24
30
  StandardSchemaV1,
@@ -510,6 +516,21 @@ export interface FormOptions<
510
516
  >
511
517
  }
512
518
 
519
+ export type AnyFormOptions = FormOptions<
520
+ any,
521
+ any,
522
+ any,
523
+ any,
524
+ any,
525
+ any,
526
+ any,
527
+ any,
528
+ any,
529
+ any,
530
+ any,
531
+ any
532
+ >
533
+
513
534
  /**
514
535
  * An object representing the validation metadata for a field. Not intended for public usage.
515
536
  */
@@ -937,13 +958,21 @@ export class FormApi<
937
958
  prevTransformArray: unknown[] = []
938
959
 
939
960
  /**
940
- *
961
+ * @private
941
962
  */
942
963
  timeoutIds: {
943
964
  validations: Record<ValidationCause, ReturnType<typeof setTimeout> | null>
944
965
  listeners: Record<ListenerCause, ReturnType<typeof setTimeout> | null>
945
966
  formListeners: Record<ListenerCause, ReturnType<typeof setTimeout> | null>
946
967
  }
968
+ /**
969
+ * @private
970
+ */
971
+ private _formId: string
972
+ /**
973
+ * @private
974
+ */
975
+ private _devtoolsSubmissionOverride: boolean
947
976
 
948
977
  /**
949
978
  * Constructs a new `FormApi` instance with the given form options.
@@ -970,6 +999,10 @@ export class FormApi<
970
999
  formListeners: {} as Record<ListenerCause, never>,
971
1000
  }
972
1001
 
1002
+ this._formId = opts?.formId ?? crypto.randomUUID()
1003
+
1004
+ this._devtoolsSubmissionOverride = false
1005
+
973
1006
  this.baseStore = new Store(
974
1007
  getDefaultFormState({
975
1008
  ...(opts?.defaultState as any),
@@ -1261,10 +1294,42 @@ export class FormApi<
1261
1294
  this.handleSubmit = this.handleSubmit.bind(this)
1262
1295
 
1263
1296
  this.update(opts || {})
1297
+
1298
+ this.store.subscribe(() => {
1299
+ formEventClient.emit('form-state-change', {
1300
+ id: this._formId,
1301
+ state: this.store.state,
1302
+ options: this.options,
1303
+ })
1304
+ })
1305
+
1306
+ formEventClient.on('request-form-state', (e) => {
1307
+ if (e.payload.id === this._formId) {
1308
+ formEventClient.emit('form-state-change', {
1309
+ id: this._formId,
1310
+ state: this.store.state,
1311
+ options: this.options,
1312
+ })
1313
+ }
1314
+ })
1315
+
1316
+ formEventClient.on('request-form-reset', (e) => {
1317
+ if (e.payload.id === this._formId) {
1318
+ this.reset()
1319
+ }
1320
+ })
1321
+
1322
+ formEventClient.on('request-form-force-submit', (e) => {
1323
+ if (e.payload.id === this._formId) {
1324
+ this._devtoolsSubmissionOverride = true
1325
+ this.handleSubmit()
1326
+ this._devtoolsSubmissionOverride = false
1327
+ }
1328
+ })
1264
1329
  }
1265
1330
 
1266
- get formId(): string | undefined {
1267
- return this.options.formId
1331
+ formId(): string | undefined {
1332
+ return this._formId
1268
1333
  }
1269
1334
 
1270
1335
  /**
@@ -1298,14 +1363,29 @@ export class FormApi<
1298
1363
  const cleanup = () => {
1299
1364
  cleanupFieldMetaDerived()
1300
1365
  cleanupStoreDerived()
1366
+
1367
+ // broadcast form unmount for devtools
1368
+ formEventClient.emit('form-unmounted', {
1369
+ id: this._formId,
1370
+ })
1301
1371
  }
1302
1372
 
1303
1373
  this.options.listeners?.onMount?.({ formApi: this })
1304
1374
 
1305
1375
  const { onMount } = this.options.validators || {}
1376
+
1377
+ // broadcast form state for devtools on mounting
1378
+ formEventClient.emit('form-state-change', {
1379
+ id: this._formId,
1380
+ state: this.store.state,
1381
+ options: this.options,
1382
+ })
1383
+
1384
+ // if no validation skip
1306
1385
  if (!onMount) return cleanup
1307
- this.validateSync('mount')
1308
1386
 
1387
+ // validate
1388
+ this.validateSync('mount')
1309
1389
  return cleanup
1310
1390
  }
1311
1391
 
@@ -1932,7 +2012,7 @@ export class FormApi<
1932
2012
  )
1933
2013
  })
1934
2014
 
1935
- if (!this.state.canSubmit) return
2015
+ if (!this.state.canSubmit && !this._devtoolsSubmissionOverride) return
1936
2016
 
1937
2017
  const submitMetaArg =
1938
2018
  submitMeta ?? (this.options.onSubmitMeta as TSubmitMeta)
@@ -1947,11 +2027,22 @@ export class FormApi<
1947
2027
 
1948
2028
  if (!this.state.isFieldsValid) {
1949
2029
  done()
2030
+
1950
2031
  this.options.onSubmitInvalid?.({
1951
2032
  value: this.state.values,
1952
2033
  formApi: this,
1953
2034
  meta: submitMetaArg,
1954
2035
  })
2036
+
2037
+ formEventClient.emit('form-submission-state-change', {
2038
+ id: this._formId,
2039
+ submissionAttempt: this.state.submissionAttempts,
2040
+ successful: false,
2041
+ stage: 'validateAllFields',
2042
+ errors: (Object.values(this.state.fieldMeta) as AnyFieldMeta[])
2043
+ .map((meta: AnyFieldMeta) => meta.errors)
2044
+ .flat(),
2045
+ })
1955
2046
  return
1956
2047
  }
1957
2048
 
@@ -1960,11 +2051,21 @@ export class FormApi<
1960
2051
  // Fields are invalid, do not submit
1961
2052
  if (!this.state.isValid) {
1962
2053
  done()
2054
+
1963
2055
  this.options.onSubmitInvalid?.({
1964
2056
  value: this.state.values,
1965
2057
  formApi: this,
1966
2058
  meta: submitMetaArg,
1967
2059
  })
2060
+
2061
+ formEventClient.emit('form-submission-state-change', {
2062
+ id: this._formId,
2063
+ submissionAttempt: this.state.submissionAttempts,
2064
+ successful: false,
2065
+ stage: 'validate',
2066
+ errors: this.state.errors,
2067
+ })
2068
+
1968
2069
  return
1969
2070
  }
1970
2071
 
@@ -1995,6 +2096,13 @@ export class FormApi<
1995
2096
  isSubmitted: true,
1996
2097
  isSubmitSuccessful: true, // Set isSubmitSuccessful to true on successful submission
1997
2098
  }))
2099
+
2100
+ formEventClient.emit('form-submission-state-change', {
2101
+ id: this._formId,
2102
+ submissionAttempt: this.state.submissionAttempts,
2103
+ successful: true,
2104
+ })
2105
+
1998
2106
  done()
1999
2107
  })
2000
2108
  } catch (err) {
@@ -2002,7 +2110,17 @@ export class FormApi<
2002
2110
  ...prev,
2003
2111
  isSubmitSuccessful: false, // Ensure isSubmitSuccessful is false if an error occurs
2004
2112
  }))
2113
+
2114
+ formEventClient.emit('form-submission-state-change', {
2115
+ id: this._formId,
2116
+ submissionAttempt: this.state.submissionAttempts,
2117
+ successful: false,
2118
+ stage: 'inflight',
2119
+ onError: err,
2120
+ })
2121
+
2005
2122
  done()
2123
+
2006
2124
  throw err
2007
2125
  }
2008
2126
  }
package/src/index.ts CHANGED
@@ -8,3 +8,4 @@ export * from './formOptions'
8
8
  export * from './standardSchemaValidator'
9
9
  export * from './FieldGroupApi'
10
10
  export * from './ValidationLogic'
11
+ export * from './EventClient'