@tanstack/form-core 0.40.3 → 0.41.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.
@@ -6,21 +6,10 @@ const standardSchemaValidator = require("./standardSchemaValidator.cjs");
6
6
  function getDefaultFormState(defaultState) {
7
7
  return {
8
8
  values: defaultState.values ?? {},
9
- errors: defaultState.errors ?? [],
10
9
  errorMap: defaultState.errorMap ?? {},
11
- fieldMeta: defaultState.fieldMeta ?? {},
12
- canSubmit: defaultState.canSubmit ?? true,
13
- isFieldsValid: defaultState.isFieldsValid ?? false,
14
- isFieldsValidating: defaultState.isFieldsValidating ?? false,
15
- isFormValid: defaultState.isFormValid ?? false,
16
- isFormValidating: defaultState.isFormValidating ?? false,
10
+ fieldMetaBase: defaultState.fieldMetaBase ?? {},
17
11
  isSubmitted: defaultState.isSubmitted ?? false,
18
12
  isSubmitting: defaultState.isSubmitting ?? false,
19
- isTouched: defaultState.isTouched ?? false,
20
- isBlurred: defaultState.isBlurred ?? false,
21
- isPristine: defaultState.isPristine ?? true,
22
- isDirty: defaultState.isDirty ?? false,
23
- isValid: defaultState.isValid ?? false,
24
13
  isValidating: defaultState.isValidating ?? false,
25
14
  submissionAttempts: defaultState.submissionAttempts ?? 0,
26
15
  validationMetaMap: defaultState.validationMetaMap ?? {
@@ -45,18 +34,25 @@ class FormApi {
45
34
  this.fieldInfo = {};
46
35
  this.prevTransformArray = [];
47
36
  this.mount = () => {
37
+ const cleanupFieldMetaDerived = this.fieldMetaDerived.mount();
38
+ const cleanupStoreDerived = this.store.mount();
39
+ const cleanup = () => {
40
+ cleanupFieldMetaDerived();
41
+ cleanupStoreDerived();
42
+ };
48
43
  const { onMount } = this.options.validators || {};
49
- if (!onMount) return;
44
+ if (!onMount) return cleanup;
50
45
  this.validateSync("mount");
46
+ return cleanup;
51
47
  };
52
48
  this.update = (options) => {
53
49
  if (!options) return;
54
50
  const oldOptions = this.options;
55
51
  this.options = options;
56
- this.store.batch(() => {
52
+ store.batch(() => {
57
53
  const shouldUpdateValues = options.defaultValues && options.defaultValues !== oldOptions.defaultValues && !this.state.isTouched;
58
54
  const shouldUpdateState = options.defaultState !== oldOptions.defaultState && !this.state.isTouched;
59
- this.store.setState(
55
+ this.baseStore.setState(
60
56
  () => getDefaultFormState(
61
57
  Object.assign(
62
58
  {},
@@ -72,27 +68,27 @@ class FormApi {
72
68
  };
73
69
  this.reset = (values, opts2) => {
74
70
  const { fieldMeta: currentFieldMeta } = this.state;
75
- const fieldMeta = this.resetFieldMeta(currentFieldMeta);
71
+ const fieldMetaBase = this.resetFieldMeta(currentFieldMeta);
76
72
  if (values && !(opts2 == null ? void 0 : opts2.keepDefaultValues)) {
77
73
  this.options = {
78
74
  ...this.options,
79
75
  defaultValues: values
80
76
  };
81
77
  }
82
- this.store.setState(
78
+ this.baseStore.setState(
83
79
  () => {
84
80
  var _a2;
85
81
  return getDefaultFormState({
86
82
  ...this.options.defaultState,
87
83
  values: values ?? this.options.defaultValues ?? ((_a2 = this.options.defaultState) == null ? void 0 : _a2.values),
88
- fieldMeta
84
+ fieldMetaBase
89
85
  });
90
86
  }
91
87
  );
92
88
  };
93
89
  this.validateAllFields = async (cause) => {
94
90
  const fieldValidationPromises = [];
95
- this.store.batch(() => {
91
+ store.batch(() => {
96
92
  void Object.values(this.fieldInfo).forEach((field) => {
97
93
  if (!field.instance) return;
98
94
  const fieldInstance = field.instance;
@@ -103,9 +99,6 @@ class FormApi {
103
99
  if (!field.instance.state.meta.isTouched) {
104
100
  field.instance.setMeta((prev) => ({ ...prev, isTouched: true }));
105
101
  }
106
- if (!field.instance.state.meta.isBlurred) {
107
- field.instance.setMeta((prev) => ({ ...prev, isBlurred: true }));
108
- }
109
102
  });
110
103
  });
111
104
  const fieldErrorMapMap = await Promise.all(fieldValidationPromises);
@@ -122,7 +115,7 @@ class FormApi {
122
115
  (fieldKey) => fieldKeysToValidate.some((key) => fieldKey.startsWith(key))
123
116
  );
124
117
  const fieldValidationPromises = [];
125
- this.store.batch(() => {
118
+ store.batch(() => {
126
119
  fieldsToValidate.forEach((nestedField) => {
127
120
  fieldValidationPromises.push(
128
121
  Promise.resolve().then(() => this.validateField(nestedField, cause))
@@ -139,16 +132,13 @@ class FormApi {
139
132
  if (!fieldInstance.state.meta.isTouched) {
140
133
  fieldInstance.setMeta((prev) => ({ ...prev, isTouched: true }));
141
134
  }
142
- if (!fieldInstance.state.meta.isBlurred) {
143
- fieldInstance.setMeta((prev) => ({ ...prev, isBlurred: true }));
144
- }
145
135
  return fieldInstance.validate(cause);
146
136
  };
147
137
  this.validateSync = (cause) => {
148
138
  const validates = utils.getSyncValidatorArray(cause, this.options);
149
139
  let hasErrored = false;
150
140
  const fieldsErrorMap = {};
151
- this.store.batch(() => {
141
+ store.batch(() => {
152
142
  for (const validateObj of validates) {
153
143
  if (!validateObj.validate) continue;
154
144
  const rawError = this.runValidator({
@@ -183,7 +173,7 @@ class FormApi {
183
173
  }
184
174
  }
185
175
  if (this.state.errorMap[errorMapKey] !== formError) {
186
- this.store.setState((prev) => ({
176
+ this.baseStore.setState((prev) => ({
187
177
  ...prev,
188
178
  errorMap: {
189
179
  ...prev.errorMap,
@@ -198,7 +188,7 @@ class FormApi {
198
188
  });
199
189
  const submitErrKey = getErrorMapKey("submit");
200
190
  if (this.state.errorMap[submitErrKey] && cause !== "submit" && !hasErrored) {
201
- this.store.setState((prev) => ({
191
+ this.baseStore.setState((prev) => ({
202
192
  ...prev,
203
193
  errorMap: {
204
194
  ...prev.errorMap,
@@ -211,7 +201,7 @@ class FormApi {
211
201
  this.validateAsync = async (cause) => {
212
202
  const validates = utils.getAsyncValidatorArray(cause, this.options);
213
203
  if (!this.state.isFormValidating) {
214
- this.store.setState((prev) => ({ ...prev, isFormValidating: true }));
204
+ this.baseStore.setState((prev) => ({ ...prev, isFormValidating: true }));
215
205
  }
216
206
  const promises = [];
217
207
  let fieldErrors;
@@ -271,7 +261,7 @@ class FormApi {
271
261
  }
272
262
  }
273
263
  }
274
- this.store.setState((prev) => ({
264
+ this.baseStore.setState((prev) => ({
275
265
  ...prev,
276
266
  errorMap: {
277
267
  ...prev.errorMap,
@@ -302,7 +292,7 @@ class FormApi {
302
292
  }
303
293
  }
304
294
  }
305
- this.store.setState((prev) => ({
295
+ this.baseStore.setState((prev) => ({
306
296
  ...prev,
307
297
  isFormValidating: false
308
298
  }));
@@ -317,7 +307,7 @@ class FormApi {
317
307
  };
318
308
  this.handleSubmit = async () => {
319
309
  var _a2, _b, _c, _d;
320
- this.store.setState((old) => ({
310
+ this.baseStore.setState((old) => ({
321
311
  ...old,
322
312
  // Submission attempts mark the form as not submitted
323
313
  isSubmitted: false,
@@ -325,9 +315,9 @@ class FormApi {
325
315
  submissionAttempts: old.submissionAttempts + 1
326
316
  }));
327
317
  if (!this.state.canSubmit) return;
328
- this.store.setState((d) => ({ ...d, isSubmitting: true }));
318
+ this.baseStore.setState((d) => ({ ...d, isSubmitting: true }));
329
319
  const done = () => {
330
- this.store.setState((prev) => ({ ...prev, isSubmitting: false }));
320
+ this.baseStore.setState((prev) => ({ ...prev, isSubmitting: false }));
331
321
  };
332
322
  await this.validateAllFields("submit");
333
323
  if (!this.state.isValid) {
@@ -338,7 +328,7 @@ class FormApi {
338
328
  });
339
329
  return;
340
330
  }
341
- this.store.batch(() => {
331
+ store.batch(() => {
342
332
  void Object.values(this.fieldInfo).forEach((field) => {
343
333
  var _a3, _b2, _c2;
344
334
  (_c2 = (_b2 = (_a3 = field.instance) == null ? void 0 : _a3.options.listeners) == null ? void 0 : _b2.onSubmit) == null ? void 0 : _c2.call(_b2, {
@@ -349,8 +339,8 @@ class FormApi {
349
339
  });
350
340
  try {
351
341
  await ((_d = (_c = this.options).onSubmit) == null ? void 0 : _d.call(_c, { value: this.state.values, formApi: this }));
352
- this.store.batch(() => {
353
- this.store.setState((prev) => ({ ...prev, isSubmitted: true }));
342
+ store.batch(() => {
343
+ this.baseStore.setState((prev) => ({ ...prev, isSubmitted: true }));
354
344
  done();
355
345
  });
356
346
  } catch (err) {
@@ -376,12 +366,15 @@ class FormApi {
376
366
  });
377
367
  };
378
368
  this.setFieldMeta = (field, updater) => {
379
- this.store.setState((prev) => {
369
+ this.baseStore.setState((prev) => {
380
370
  return {
381
371
  ...prev,
382
- fieldMeta: {
383
- ...prev.fieldMeta,
384
- [field]: utils.functionalUpdate(updater, prev.fieldMeta[field])
372
+ fieldMetaBase: {
373
+ ...prev.fieldMetaBase,
374
+ [field]: utils.functionalUpdate(
375
+ updater,
376
+ prev.fieldMetaBase[field]
377
+ )
385
378
  }
386
379
  };
387
380
  });
@@ -406,12 +399,11 @@ class FormApi {
406
399
  };
407
400
  this.setFieldValue = (field, updater, opts2) => {
408
401
  const dontUpdateMeta = (opts2 == null ? void 0 : opts2.dontUpdateMeta) ?? false;
409
- this.store.batch(() => {
402
+ store.batch(() => {
410
403
  if (!dontUpdateMeta) {
411
404
  this.setFieldMeta(field, (prev) => ({
412
405
  ...prev,
413
406
  isTouched: true,
414
- isBlurred: true,
415
407
  isDirty: true,
416
408
  errorMap: {
417
409
  // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition
@@ -420,7 +412,7 @@ class FormApi {
420
412
  }
421
413
  }));
422
414
  }
423
- this.store.setState((prev) => {
415
+ this.baseStore.setState((prev) => {
424
416
  return {
425
417
  ...prev,
426
418
  values: utils.setBy(prev.values, field, updater)
@@ -429,10 +421,10 @@ class FormApi {
429
421
  });
430
422
  };
431
423
  this.deleteField = (field) => {
432
- this.store.setState((prev) => {
424
+ this.baseStore.setState((prev) => {
433
425
  const newState = { ...prev };
434
426
  newState.values = utils.deleteBy(newState.values, field);
435
- delete newState.fieldMeta[field];
427
+ delete newState.fieldMetaBase[field];
436
428
  return newState;
437
429
  });
438
430
  delete this.fieldInfo[field];
@@ -521,80 +513,131 @@ class FormApi {
521
513
  this.validateField(`${field}[${index1}]`, "change");
522
514
  this.validateField(`${field}[${index2}]`, "change");
523
515
  };
524
- this.store = new store.Store(
516
+ this.baseStore = new store.Store(
525
517
  getDefaultFormState({
526
518
  ...opts == null ? void 0 : opts.defaultState,
527
519
  values: (opts == null ? void 0 : opts.defaultValues) ?? ((_a = opts == null ? void 0 : opts.defaultState) == null ? void 0 : _a.values),
528
520
  isFormValid: true
529
- }),
530
- {
531
- onUpdate: () => {
532
- var _a2, _b, _c, _d;
533
- let { state } = this.store;
534
- const fieldMetaValues = Object.values(state.fieldMeta);
535
- const isFieldsValidating = fieldMetaValues.some(
536
- (field) => field == null ? void 0 : field.isValidating
537
- );
538
- const isFieldsValid = !fieldMetaValues.some(
539
- (field) => (field == null ? void 0 : field.errorMap) && utils.isNonEmptyArray(Object.values(field.errorMap).filter(Boolean))
540
- );
541
- const isTouched = fieldMetaValues.some((field) => field == null ? void 0 : field.isTouched);
542
- const isBlurred = fieldMetaValues.some((field) => field == null ? void 0 : field.isBlurred);
543
- if (isTouched && ((_a2 = state == null ? void 0 : state.errorMap) == null ? void 0 : _a2.onMount)) {
544
- state.errorMap.onMount = void 0;
521
+ })
522
+ );
523
+ this.fieldMetaDerived = new store.Derived({
524
+ deps: [this.baseStore],
525
+ fn: ({ prevDepVals, currDepVals, prevVal: _prevVal }) => {
526
+ var _a2;
527
+ const prevVal = _prevVal;
528
+ const prevBaseStore = prevDepVals == null ? void 0 : prevDepVals[0];
529
+ const currBaseStore = currDepVals[0];
530
+ const fieldMeta = {};
531
+ for (const fieldName of Object.keys(
532
+ currBaseStore.fieldMetaBase
533
+ )) {
534
+ const currBaseVal = currBaseStore.fieldMetaBase[fieldName];
535
+ const prevBaseVal = prevBaseStore == null ? void 0 : prevBaseStore.fieldMetaBase[fieldName];
536
+ let fieldErrors = (_a2 = prevVal == null ? void 0 : prevVal[fieldName]) == null ? void 0 : _a2.errors;
537
+ if (!prevBaseVal || currBaseVal.errorMap !== prevBaseVal.errorMap) {
538
+ fieldErrors = Object.values(currBaseVal.errorMap ?? {}).filter(
539
+ (val) => val !== void 0
540
+ );
545
541
  }
546
- const isDirty = fieldMetaValues.some((field) => field == null ? void 0 : field.isDirty);
547
- const isPristine = !isDirty;
548
- const hasOnMountError = Boolean(
549
- // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition
550
- ((_b = state.errorMap) == null ? void 0 : _b.onMount) || // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition
551
- fieldMetaValues.some((f) => {
552
- var _a3;
553
- return (_a3 = f == null ? void 0 : f.errorMap) == null ? void 0 : _a3.onMount;
554
- })
555
- );
556
- const isValidating = isFieldsValidating || state.isFormValidating;
557
- state.errors = Object.values(state.errorMap).reduce((prev, curr) => {
558
- if (curr === void 0) return prev;
559
- if (typeof curr === "string") {
560
- prev.push(curr);
561
- return prev;
562
- } else if (curr && isFormValidationError(curr)) {
563
- prev.push(curr.form);
564
- return prev;
565
- }
566
- return prev;
567
- }, []);
568
- const isFormValid = state.errors.length === 0;
569
- const isValid = isFieldsValid && isFormValid;
570
- const canSubmit = state.submissionAttempts === 0 && !isTouched && !hasOnMountError || !isValidating && !state.isSubmitting && isValid;
571
- state = {
572
- ...state,
573
- isFieldsValidating,
574
- isFieldsValid,
575
- isFormValid,
576
- isValid,
577
- canSubmit,
578
- isTouched,
579
- isBlurred,
580
- isPristine,
581
- isDirty
542
+ const isFieldPristine = !currBaseVal.isDirty;
543
+ fieldMeta[fieldName] = {
544
+ ...currBaseVal,
545
+ errors: fieldErrors,
546
+ isPristine: isFieldPristine
582
547
  };
583
- this.state = state;
584
- this.store.state = this.state;
585
- const transformArray = ((_c = this.options.transform) == null ? void 0 : _c.deps) ?? [];
586
- const shouldTransform = transformArray.length !== this.prevTransformArray.length || transformArray.some((val, i) => val !== this.prevTransformArray[i]);
587
- if (shouldTransform) {
588
- (_d = this.options.transform) == null ? void 0 : _d.fn(this);
589
- this.store.state = this.state;
590
- this.prevTransformArray = transformArray;
591
- }
592
548
  }
549
+ return fieldMeta;
593
550
  }
594
- );
595
- this.state = this.store.state;
551
+ });
552
+ this.store = new store.Derived({
553
+ deps: [this.baseStore, this.fieldMetaDerived],
554
+ fn: ({ prevDepVals, currDepVals, prevVal: _prevVal }) => {
555
+ var _a2, _b, _c, _d;
556
+ const prevVal = _prevVal;
557
+ const prevBaseStore = prevDepVals == null ? void 0 : prevDepVals[0];
558
+ const currBaseStore = currDepVals[0];
559
+ const fieldMetaValues = Object.values(currBaseStore.fieldMetaBase);
560
+ const isFieldsValidating = fieldMetaValues.some(
561
+ (field) => field == null ? void 0 : field.isValidating
562
+ );
563
+ const isFieldsValid = !fieldMetaValues.some(
564
+ (field) => (field == null ? void 0 : field.errorMap) && utils.isNonEmptyArray(Object.values(field.errorMap).filter(Boolean))
565
+ );
566
+ const isTouched = fieldMetaValues.some((field) => field == null ? void 0 : field.isTouched);
567
+ const isBlurred = fieldMetaValues.some((field) => field == null ? void 0 : field.isBlurred);
568
+ const shouldInvalidateOnMount = (
569
+ // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition
570
+ isTouched && ((_a2 = currBaseStore == null ? void 0 : currBaseStore.errorMap) == null ? void 0 : _a2.onMount)
571
+ );
572
+ const isDirty = fieldMetaValues.some((field) => field == null ? void 0 : field.isDirty);
573
+ const isPristine = !isDirty;
574
+ const hasOnMountError = Boolean(
575
+ // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition
576
+ ((_b = currBaseStore.errorMap) == null ? void 0 : _b.onMount) || // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition
577
+ fieldMetaValues.some((f) => {
578
+ var _a3;
579
+ return (_a3 = f == null ? void 0 : f.errorMap) == null ? void 0 : _a3.onMount;
580
+ })
581
+ );
582
+ const isValidating = !!isFieldsValidating;
583
+ let errors = (prevVal == null ? void 0 : prevVal.errors) ?? [];
584
+ if (!prevBaseStore || currBaseStore.errorMap !== prevBaseStore.errorMap) {
585
+ errors = Object.values(currBaseStore.errorMap).reduce(
586
+ (prev, curr) => {
587
+ if (curr === void 0) return prev;
588
+ if (typeof curr === "string") {
589
+ prev.push(curr);
590
+ return prev;
591
+ } else if (curr && isFormValidationError(curr)) {
592
+ prev.push(curr.form);
593
+ return prev;
594
+ }
595
+ return prev;
596
+ },
597
+ []
598
+ );
599
+ }
600
+ const isFormValid = errors.length === 0;
601
+ const isValid = isFieldsValid && isFormValid;
602
+ const canSubmit = currBaseStore.submissionAttempts === 0 && !isTouched && !hasOnMountError || !isValidating && !currBaseStore.isSubmitting && isValid;
603
+ let errorMap = currBaseStore.errorMap;
604
+ if (shouldInvalidateOnMount) {
605
+ errors = errors.filter(
606
+ (err) => err !== currBaseStore.errorMap.onMount
607
+ );
608
+ errorMap = Object.assign(errorMap, { onMount: void 0 });
609
+ }
610
+ let state = {
611
+ ...currBaseStore,
612
+ errorMap,
613
+ fieldMeta: this.fieldMetaDerived.state,
614
+ errors,
615
+ isFieldsValidating,
616
+ isFieldsValid,
617
+ isFormValid,
618
+ isValid,
619
+ canSubmit,
620
+ isTouched,
621
+ isBlurred,
622
+ isPristine,
623
+ isDirty
624
+ };
625
+ const transformArray = ((_c = this.options.transform) == null ? void 0 : _c.deps) ?? [];
626
+ const shouldTransform = transformArray.length !== this.prevTransformArray.length || transformArray.some((val, i) => val !== this.prevTransformArray[i]);
627
+ if (shouldTransform) {
628
+ const newObj = Object.assign({}, this, { state });
629
+ (_d = this.options.transform) == null ? void 0 : _d.fn(newObj);
630
+ state = newObj.state;
631
+ this.prevTransformArray = transformArray;
632
+ }
633
+ return state;
634
+ }
635
+ });
596
636
  this.update(opts || {});
597
637
  }
638
+ get state() {
639
+ return this.store.state;
640
+ }
598
641
  /**
599
642
  * @private
600
643
  */
@@ -615,7 +658,7 @@ class FormApi {
615
658
  * Updates the form's errorMap
616
659
  */
617
660
  setErrorMap(errorMap) {
618
- this.store.setState((prev) => ({
661
+ this.baseStore.setState((prev) => ({
619
662
  ...prev,
620
663
  errorMap: {
621
664
  ...prev.errorMap,