@strictly/react-form 0.0.25 → 0.0.27

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.
@@ -7,12 +7,12 @@ $ tsup
7
7
  CLI Target: es6
8
8
  CJS Build start
9
9
  ESM Build start
10
- CJS dist/index.cjs 61.91 KB
11
- CJS ⚡️ Build success in 125ms
12
- ESM dist/index.js 57.87 KB
13
- ESM ⚡️ Build success in 146ms
10
+ CJS dist/index.cjs 63.79 KB
11
+ CJS ⚡️ Build success in 126ms
12
+ ESM dist/index.js 59.70 KB
13
+ ESM ⚡️ Build success in 132ms
14
14
  DTS Build start
15
- DTS ⚡️ Build success in 31902ms
16
- DTS dist/index.d.cts 37.37 KB
17
- DTS dist/index.d.ts 37.37 KB
18
- Done in 33.10s.
15
+ DTS ⚡️ Build success in 32334ms
16
+ DTS dist/index.d.cts 38.14 KB
17
+ DTS dist/index.d.ts 38.14 KB
18
+ Done in 33.47s.
@@ -1,3 +1,3 @@
1
1
  yarn run v1.22.22
2
2
  $ tsc -b
3
- Done in 0.35s.
3
+ Done in 0.40s.
@@ -12,6 +12,7 @@ import {
12
12
  import {
13
13
  type Accessor,
14
14
  type AnyValueType,
15
+ equals,
15
16
  flattenAccessorsOfType,
16
17
  type FlattenedValuesOfType,
17
18
  flattenTypesOfType,
@@ -88,6 +89,12 @@ type FlattenedFieldOverrides<
88
89
  >
89
90
  }
90
91
 
92
+ type FlattenedErrorOverrides<
93
+ ValuePathsToAdapters extends Readonly<Record<string, FieldAdapter>>,
94
+ > = {
95
+ -readonly [K in keyof ValuePathsToAdapters]?: ErrorOfFieldAdapter<ValuePathsToAdapters[K]>
96
+ }
97
+
91
98
  export enum Validation {
92
99
  None = 0,
93
100
  Changed = 1,
@@ -144,6 +151,8 @@ export abstract class FormModel<
144
151
  @observable.shallow
145
152
  accessor fieldOverrides: FlattenedFieldOverrides<ValuePathsToAdapters>
146
153
  @observable.shallow
154
+ accessor errorOverrides: FlattenedErrorOverrides<ValuePathsToAdapters> = {}
155
+ @observable.shallow
147
156
  accessor validation: FlattenedValidation<ValuePathsToAdapters> = {}
148
157
 
149
158
  private readonly flattenedTypeDefs: Readonly<Record<string, Type>>
@@ -341,39 +350,42 @@ export abstract class FormModel<
341
350
  defaultValue,
342
351
  } = field
343
352
  const validation = this.validation[valuePath] ?? Validation.None
344
- let error: unknown
345
- switch (validation) {
346
- case Validation.None:
347
- // skip validation
348
- break
349
- case Validation.Changed:
350
- if (revert != null) {
351
- const originalValue = valuePath in this.originalValues
353
+ let error: unknown = this.errorOverrides[valuePath]
354
+ if (error == null) {
355
+ switch (validation) {
356
+ case Validation.None:
357
+ // skip validation
358
+ break
359
+ case Validation.Changed:
360
+ if (revert != null) {
361
+ const originalValue = valuePath in this.originalValues
362
+ // eslint-disable-next-line @typescript-eslint/consistent-type-assertions
363
+ ? this.originalValues[valuePath as string]
364
+ : defaultValue
352
365
  // eslint-disable-next-line @typescript-eslint/consistent-type-assertions
353
- ? this.originalValues[valuePath as string]
354
- : defaultValue
355
- // eslint-disable-next-line @typescript-eslint/consistent-type-assertions
356
- const { value: originalDisplayedValue } = convert(originalValue, valuePath as string, context)
357
- // TODO better comparisons, displayed values can still be complex
358
- if (displayedValue !== originalDisplayedValue) {
359
- const revertResult = revert(displayedValue, valuePath, context)
366
+ const { value: originalDisplayedValue } = convert(originalValue, valuePath as string, context)
367
+ // TODO better comparisons, displayed values can still be complex
368
+ if (displayedValue !== originalDisplayedValue) {
369
+ const revertResult = revert(displayedValue, valuePath, context)
370
+ if (revertResult?.type === UnreliableFieldConversionType.Failure) {
371
+ ;({ error } = revertResult)
372
+ }
373
+ }
374
+ }
375
+ break
376
+ case Validation.Always:
377
+ {
378
+ const revertResult = revert?.(displayedValue, valuePath, context)
360
379
  if (revertResult?.type === UnreliableFieldConversionType.Failure) {
361
380
  ;({ error } = revertResult)
362
381
  }
363
382
  }
364
- }
365
- break
366
- case Validation.Always:
367
- {
368
- const revertResult = revert?.(displayedValue, valuePath, context)
369
- if (revertResult?.type === UnreliableFieldConversionType.Failure) {
370
- ;({ error } = revertResult)
371
- }
372
- }
373
- break
374
- default:
375
- throw new UnreachableError(validation)
383
+ break
384
+ default:
385
+ throw new UnreachableError(validation)
386
+ }
376
387
  }
388
+
377
389
  return {
378
390
  value: displayedValue,
379
391
  error,
@@ -414,6 +426,14 @@ export abstract class FormModel<
414
426
  )
415
427
  }
416
428
 
429
+ @computed
430
+ get dirty() {
431
+ return Object.keys(this.accessors).some((valuePath) => {
432
+ // eslint-disable-next-line @typescript-eslint/consistent-type-assertions
433
+ return this.isFieldDirty(valuePath as keyof ValuePathsToAdapters)
434
+ })
435
+ }
436
+
417
437
  typePath<K extends keyof ValueToTypePaths>(valuePath: K): ValueToTypePaths[K] {
418
438
  return valuePathToTypePath<ValueToTypePaths, K>(this.type, valuePath, true)
419
439
  }
@@ -579,6 +599,8 @@ export abstract class FormModel<
579
599
  const validation = this.validation[fromJsonPath]
580
600
  delete this.validation[fromJsonPath]
581
601
  this.validation[toJsonPath] = validation
602
+ // eslint-disable-next-line @typescript-eslint/consistent-type-assertions, @typescript-eslint/no-explicit-any
603
+ this.moveListItem(fromJsonPath as any, toJsonPath as any)
582
604
  })
583
605
  accessor.set(newList)
584
606
  // delete any value overrides so the new list isn't shadowed
@@ -588,6 +610,13 @@ export abstract class FormModel<
588
610
  })
589
611
  }
590
612
 
613
+ protected moveListItem<K extends keyof FlattenedListTypesOfType<T>>(fromValuePath: K, toValuePath: K) {
614
+ // do nothing, this is for subclasses to override
615
+ // put in some nonsense so TS doesn't complain about the parameters not being used
616
+ fromValuePath satisfies K
617
+ toValuePath satisfies K
618
+ }
619
+
591
620
  private internalSetFieldValue<K extends keyof ValuePathsToAdapters>(
592
621
  valuePath: K,
593
622
  value: ToOfFieldAdapter<ValuePathsToAdapters[K]>,
@@ -602,6 +631,7 @@ export abstract class FormModel<
602
631
  const accessor = this.getAccessorForValuePath(valuePath)
603
632
  return runInAction(() => {
604
633
  this.fieldOverrides[valuePath] = [value]
634
+ delete this.errorOverrides[valuePath]
605
635
  if (validation != null) {
606
636
  this.validation[valuePath] = validation
607
637
  }
@@ -620,11 +650,30 @@ export abstract class FormModel<
620
650
  })
621
651
  }
622
652
 
653
+ /**
654
+ * Forces an error onto a field. Error will be removed if the field value changes
655
+ * @param valuePath the field to display an error for
656
+ * @param error the error to display
657
+ */
658
+ overrideFieldError<K extends keyof ValuePathsToAdapters>(
659
+ valuePath: K,
660
+ error?: ErrorOfFieldAdapter<ValuePathsToAdapters[K]>,
661
+ ) {
662
+ runInAction(() => {
663
+ if (error) {
664
+ this.errorOverrides[valuePath] = error
665
+ } else {
666
+ delete this.errorOverrides[valuePath]
667
+ }
668
+ })
669
+ }
670
+
623
671
  clearFieldError<K extends keyof ValuePathsToAdapters>(valuePath: K) {
624
672
  const fieldOverride = this.fieldOverrides[valuePath]
625
673
  if (fieldOverride != null) {
626
674
  runInAction(() => {
627
675
  delete this.validation[valuePath]
676
+ delete this.errorOverrides[valuePath]
628
677
  })
629
678
  }
630
679
  }
@@ -651,6 +700,7 @@ export abstract class FormModel<
651
700
  runInAction(() => {
652
701
  this.fieldOverrides[key] = [displayValue]
653
702
  delete this.validation[key]
703
+ delete this.errorOverrides[key]
654
704
  })
655
705
  }
656
706
 
@@ -659,6 +709,7 @@ export abstract class FormModel<
659
709
  this.validation = {}
660
710
  // TODO this isn't correct, should reload from value
661
711
  this.fieldOverrides = {}
712
+ this.errorOverrides = {}
662
713
  this.value = mobxCopy(this.type, value)
663
714
  })
664
715
  }
@@ -674,7 +725,7 @@ export abstract class FormModel<
674
725
  return this.validation[valuePath] ?? Validation.None
675
726
  }
676
727
 
677
- isDirty<K extends keyof ValuePathsToAdapters>(valuePath: K): boolean {
728
+ isFieldDirty<K extends keyof ValuePathsToAdapters>(valuePath: K): boolean {
678
729
  // eslint-disable-next-line @typescript-eslint/consistent-type-assertions
679
730
  const typePath = valuePathToTypePath<ValueToTypePaths, keyof ValueToTypePaths>(
680
731
  this.type,
@@ -691,18 +742,33 @@ export abstract class FormModel<
691
742
  const {
692
743
  displayedValue,
693
744
  convert,
745
+ revert,
694
746
  context,
695
747
  defaultValue,
696
748
  } = field
697
749
 
750
+ // if either the display value, or the stored value, match the original, then assume it's not dirty
698
751
  const originalValue = valuePath in this.originalValues
699
752
  // eslint-disable-next-line @typescript-eslint/consistent-type-assertions
700
753
  ? this.originalValues[valuePath as string]
701
754
  : defaultValue
755
+ if (revert != null) {
756
+ // eslint-disable-next-line @typescript-eslint/consistent-type-assertions
757
+ const typeDef = this.flattenedTypeDefs[typePath as string]
758
+ const {
759
+ value,
760
+ type,
761
+ } = revert(displayedValue, valuePath, context)
762
+ if (type === UnreliableFieldConversionType.Success) {
763
+ if (equals(typeDef, originalValue, value)) {
764
+ return false
765
+ }
766
+ }
767
+ }
702
768
  // eslint-disable-next-line @typescript-eslint/consistent-type-assertions
703
769
  const { value: originalDisplayedValue } = convert(originalValue, valuePath as string, context)
704
- // TODO better comparisons, displayed values can still be complex
705
- return (displayedValue !== originalDisplayedValue)
770
+ // try to compare the displayed values directly if we can't revert the displayed value
771
+ return displayedValue !== originalDisplayedValue
706
772
  }
707
773
 
708
774
  validateField<K extends keyof ValuePathsToAdapters>(
@@ -711,6 +777,7 @@ export abstract class FormModel<
711
777
  ): boolean {
712
778
  runInAction(() => {
713
779
  this.validation[valuePath] = validation
780
+ delete this.errorOverrides[valuePath]
714
781
  })
715
782
  return this.fields[valuePath].error == null
716
783
  }
@@ -732,4 +799,8 @@ export abstract class FormModel<
732
799
  },
733
800
  )
734
801
  }
802
+
803
+ validateSubmit() {
804
+ return this.validateAll()
805
+ }
735
806
  }
@@ -66,8 +66,8 @@ export function useDefaultMobxFormHooks<
66
66
  // (e.g. changing a discriminator)
67
67
  // TODO debounce?
68
68
  setTimeout(function () {
69
- // only start validation if the user has changed the field
70
- if (model.isValuePathActive(path) && model.isDirty(path)) {
69
+ // only start validation if the user has changed the field and there isn't already an error visible
70
+ if (model.isValuePathActive(path) && model.isFieldDirty(path) && model.fields[path].error == null) {
71
71
  // further workaround to make sure we don't downgrade the existing validation
72
72
  model.validateField(path, Math.max(Validation.Changed, model.getValidation(path)))
73
73
  }
@@ -78,7 +78,7 @@ export function useDefaultMobxFormHooks<
78
78
 
79
79
  const onFormSubmit = useCallback(
80
80
  function () {
81
- if (model.validateAll()) {
81
+ if (model.validateSubmit()) {
82
82
  onValidFormSubmit?.(model.value)
83
83
  }
84
84
  },
package/dist/index.cjs CHANGED
@@ -363,8 +363,8 @@ var Validation = /* @__PURE__ */ ((Validation2) => {
363
363
  Validation2[Validation2["Always"] = 2] = "Always";
364
364
  return Validation2;
365
365
  })(Validation || {});
366
- var _accessors_dec, _knownFields_dec, _fields_dec, _validation_dec, _fieldOverrides_dec, _value_dec, _init, _value, _fieldOverrides, _validation;
367
- _value_dec = [import_mobx.observable.ref], _fieldOverrides_dec = [import_mobx.observable.shallow], _validation_dec = [import_mobx.observable.shallow], _fields_dec = [import_mobx.computed], _knownFields_dec = [import_mobx.computed], _accessors_dec = [import_mobx.computed];
366
+ var _dirty_dec, _accessors_dec, _knownFields_dec, _fields_dec, _validation_dec, _errorOverrides_dec, _fieldOverrides_dec, _value_dec, _init, _value, _fieldOverrides, _errorOverrides, _validation;
367
+ _value_dec = [import_mobx.observable.ref], _fieldOverrides_dec = [import_mobx.observable.shallow], _errorOverrides_dec = [import_mobx.observable.shallow], _validation_dec = [import_mobx.observable.shallow], _fields_dec = [import_mobx.computed], _knownFields_dec = [import_mobx.computed], _accessors_dec = [import_mobx.computed], _dirty_dec = [import_mobx.computed];
368
368
  var FormModel = class {
369
369
  constructor(type, originalValue, adapters, mode) {
370
370
  this.type = type;
@@ -373,7 +373,8 @@ var FormModel = class {
373
373
  __runInitializers(_init, 5, this);
374
374
  __privateAdd(this, _value, __runInitializers(_init, 8, this)), __runInitializers(_init, 11, this);
375
375
  __privateAdd(this, _fieldOverrides, __runInitializers(_init, 12, this)), __runInitializers(_init, 15, this);
376
- __privateAdd(this, _validation, __runInitializers(_init, 16, this, {})), __runInitializers(_init, 19, this);
376
+ __privateAdd(this, _errorOverrides, __runInitializers(_init, 16, this, {})), __runInitializers(_init, 19, this);
377
+ __privateAdd(this, _validation, __runInitializers(_init, 20, this, {})), __runInitializers(_init, 23, this);
377
378
  __publicField(this, "flattenedTypeDefs");
378
379
  // cannot be type safe
379
380
  // eslint-disable-next-line @typescript-eslint/no-explicit-any
@@ -520,34 +521,36 @@ var FormModel = class {
520
521
  defaultValue
521
522
  } = field;
522
523
  const validation = (_a = this.validation[valuePath]) != null ? _a : 0 /* None */;
523
- let error;
524
- switch (validation) {
525
- case 0 /* None */:
526
- break;
527
- case 1 /* Changed */:
528
- if (revert != null) {
529
- const originalValue = valuePath in this.originalValues ? this.originalValues[valuePath] : defaultValue;
530
- const { value: originalDisplayedValue } = convert(originalValue, valuePath, context);
531
- if (displayedValue !== originalDisplayedValue) {
532
- const revertResult = revert(displayedValue, valuePath, context);
524
+ let error = this.errorOverrides[valuePath];
525
+ if (error == null) {
526
+ switch (validation) {
527
+ case 0 /* None */:
528
+ break;
529
+ case 1 /* Changed */:
530
+ if (revert != null) {
531
+ const originalValue = valuePath in this.originalValues ? this.originalValues[valuePath] : defaultValue;
532
+ const { value: originalDisplayedValue } = convert(originalValue, valuePath, context);
533
+ if (displayedValue !== originalDisplayedValue) {
534
+ const revertResult = revert(displayedValue, valuePath, context);
535
+ if ((revertResult == null ? void 0 : revertResult.type) === 1 /* Failure */) {
536
+ ;
537
+ ({ error } = revertResult);
538
+ }
539
+ }
540
+ }
541
+ break;
542
+ case 2 /* Always */:
543
+ {
544
+ const revertResult = revert == null ? void 0 : revert(displayedValue, valuePath, context);
533
545
  if ((revertResult == null ? void 0 : revertResult.type) === 1 /* Failure */) {
534
546
  ;
535
547
  ({ error } = revertResult);
536
548
  }
537
549
  }
538
- }
539
- break;
540
- case 2 /* Always */:
541
- {
542
- const revertResult = revert == null ? void 0 : revert(displayedValue, valuePath, context);
543
- if ((revertResult == null ? void 0 : revertResult.type) === 1 /* Failure */) {
544
- ;
545
- ({ error } = revertResult);
546
- }
547
- }
548
- break;
549
- default:
550
- throw new import_base2.UnreachableError(validation);
550
+ break;
551
+ default:
552
+ throw new import_base2.UnreachableError(validation);
553
+ }
551
554
  }
552
555
  return {
553
556
  value: displayedValue,
@@ -580,6 +583,11 @@ var FormModel = class {
580
583
  valuePath
581
584
  );
582
585
  }
586
+ get dirty() {
587
+ return Object.keys(this.accessors).some((valuePath) => {
588
+ return this.isFieldDirty(valuePath);
589
+ });
590
+ }
583
591
  typePath(valuePath) {
584
592
  return (0, import_define.valuePathToTypePath)(this.type, valuePath, true);
585
593
  }
@@ -712,12 +720,17 @@ var FormModel = class {
712
720
  const validation = this.validation[fromJsonPath];
713
721
  delete this.validation[fromJsonPath];
714
722
  this.validation[toJsonPath] = validation;
723
+ this.moveListItem(fromJsonPath, toJsonPath);
715
724
  });
716
725
  accessor.set(newList);
717
726
  delete this.fieldOverrides[listValuePath];
718
727
  });
719
728
  });
720
729
  }
730
+ moveListItem(fromValuePath, toValuePath) {
731
+ fromValuePath;
732
+ toValuePath;
733
+ }
721
734
  internalSetFieldValue(valuePath, value, validation) {
722
735
  const { revert } = this.getAdapterForValuePath(valuePath);
723
736
  (0, import_base2.assertExists)(revert, "setting value not supported {}", valuePath);
@@ -725,6 +738,7 @@ var FormModel = class {
725
738
  const accessor = this.getAccessorForValuePath(valuePath);
726
739
  return (0, import_mobx.runInAction)(() => {
727
740
  this.fieldOverrides[valuePath] = [value];
741
+ delete this.errorOverrides[valuePath];
728
742
  if (validation != null) {
729
743
  this.validation[valuePath] = validation;
730
744
  }
@@ -742,11 +756,26 @@ var FormModel = class {
742
756
  }
743
757
  });
744
758
  }
759
+ /**
760
+ * Forces an error onto a field. Error will be removed if the field value changes
761
+ * @param valuePath the field to display an error for
762
+ * @param error the error to display
763
+ */
764
+ overrideFieldError(valuePath, error) {
765
+ (0, import_mobx.runInAction)(() => {
766
+ if (error) {
767
+ this.errorOverrides[valuePath] = error;
768
+ } else {
769
+ delete this.errorOverrides[valuePath];
770
+ }
771
+ });
772
+ }
745
773
  clearFieldError(valuePath) {
746
774
  const fieldOverride = this.fieldOverrides[valuePath];
747
775
  if (fieldOverride != null) {
748
776
  (0, import_mobx.runInAction)(() => {
749
777
  delete this.validation[valuePath];
778
+ delete this.errorOverrides[valuePath];
750
779
  });
751
780
  }
752
781
  }
@@ -769,12 +798,14 @@ var FormModel = class {
769
798
  (0, import_mobx.runInAction)(() => {
770
799
  this.fieldOverrides[key] = [displayValue];
771
800
  delete this.validation[key];
801
+ delete this.errorOverrides[key];
772
802
  });
773
803
  }
774
804
  clearAll(value) {
775
805
  (0, import_mobx.runInAction)(() => {
776
806
  this.validation = {};
777
807
  this.fieldOverrides = {};
808
+ this.errorOverrides = {};
778
809
  this.value = (0, import_define.mobxCopy)(this.type, value);
779
810
  });
780
811
  }
@@ -787,7 +818,7 @@ var FormModel = class {
787
818
  var _a;
788
819
  return (_a = this.validation[valuePath]) != null ? _a : 0 /* None */;
789
820
  }
790
- isDirty(valuePath) {
821
+ isFieldDirty(valuePath) {
791
822
  const typePath = (0, import_define.valuePathToTypePath)(
792
823
  this.type,
793
824
  // eslint-disable-next-line @typescript-eslint/consistent-type-assertions
@@ -801,16 +832,30 @@ var FormModel = class {
801
832
  const {
802
833
  displayedValue,
803
834
  convert,
835
+ revert,
804
836
  context,
805
837
  defaultValue
806
838
  } = field;
807
839
  const originalValue = valuePath in this.originalValues ? this.originalValues[valuePath] : defaultValue;
840
+ if (revert != null) {
841
+ const typeDef = this.flattenedTypeDefs[typePath];
842
+ const {
843
+ value,
844
+ type
845
+ } = revert(displayedValue, valuePath, context);
846
+ if (type === 0 /* Success */) {
847
+ if ((0, import_define.equals)(typeDef, originalValue, value)) {
848
+ return false;
849
+ }
850
+ }
851
+ }
808
852
  const { value: originalDisplayedValue } = convert(originalValue, valuePath, context);
809
853
  return displayedValue !== originalDisplayedValue;
810
854
  }
811
855
  validateField(valuePath, validation = 2 /* Always */) {
812
856
  (0, import_mobx.runInAction)(() => {
813
857
  this.validation[valuePath] = validation;
858
+ delete this.errorOverrides[valuePath];
814
859
  });
815
860
  return this.fields[valuePath].error == null;
816
861
  }
@@ -828,17 +873,23 @@ var FormModel = class {
828
873
  }
829
874
  );
830
875
  }
876
+ validateSubmit() {
877
+ return this.validateAll();
878
+ }
831
879
  };
832
880
  _init = __decoratorStart(null);
833
881
  _value = new WeakMap();
834
882
  _fieldOverrides = new WeakMap();
883
+ _errorOverrides = new WeakMap();
835
884
  _validation = new WeakMap();
836
885
  __decorateElement(_init, 4, "value", _value_dec, FormModel, _value);
837
886
  __decorateElement(_init, 4, "fieldOverrides", _fieldOverrides_dec, FormModel, _fieldOverrides);
887
+ __decorateElement(_init, 4, "errorOverrides", _errorOverrides_dec, FormModel, _errorOverrides);
838
888
  __decorateElement(_init, 4, "validation", _validation_dec, FormModel, _validation);
839
889
  __decorateElement(_init, 2, "fields", _fields_dec, FormModel);
840
890
  __decorateElement(_init, 2, "knownFields", _knownFields_dec, FormModel);
841
891
  __decorateElement(_init, 2, "accessors", _accessors_dec, FormModel);
892
+ __decorateElement(_init, 2, "dirty", _dirty_dec, FormModel);
842
893
  __decoratorMetadata(_init, FormModel);
843
894
 
844
895
  // core/mobx/hooks.tsx
@@ -869,7 +920,7 @@ function useDefaultMobxFormHooks(model, {
869
920
  const onFieldBlur = (0, import_react.useCallback)(
870
921
  function(path) {
871
922
  setTimeout(function() {
872
- if (model.isValuePathActive(path) && model.isDirty(path)) {
923
+ if (model.isValuePathActive(path) && model.isFieldDirty(path) && model.fields[path].error == null) {
873
924
  model.validateField(path, Math.max(1 /* Changed */, model.getValidation(path)));
874
925
  }
875
926
  }, 100);
@@ -878,7 +929,7 @@ function useDefaultMobxFormHooks(model, {
878
929
  );
879
930
  const onFormSubmit = (0, import_react.useCallback)(
880
931
  function() {
881
- if (model.validateAll()) {
932
+ if (model.validateSubmit()) {
882
933
  onValidFormSubmit == null ? void 0 : onValidFormSubmit(model.value);
883
934
  }
884
935
  },
package/dist/index.d.cts CHANGED
@@ -112,6 +112,9 @@ type FieldOverride<V = any> = Maybe<V>;
112
112
  type FlattenedFieldOverrides<ValuePathsToAdapters extends Readonly<Record<string, FieldAdapter>>> = {
113
113
  -readonly [K in keyof ValuePathsToAdapters]?: FieldOverride<ToOfFieldAdapter<ValuePathsToAdapters[K]>>;
114
114
  };
115
+ type FlattenedErrorOverrides<ValuePathsToAdapters extends Readonly<Record<string, FieldAdapter>>> = {
116
+ -readonly [K in keyof ValuePathsToAdapters]?: ErrorOfFieldAdapter<ValuePathsToAdapters[K]>;
117
+ };
115
118
  declare enum Validation {
116
119
  None = 0,
117
120
  Changed = 1,
@@ -133,6 +136,7 @@ declare abstract class FormModel<T extends Type, ValueToTypePaths extends Readon
133
136
  protected readonly mode: FormMode;
134
137
  accessor value: MobxValueOfType<T>;
135
138
  accessor fieldOverrides: FlattenedFieldOverrides<ValuePathsToAdapters>;
139
+ accessor errorOverrides: FlattenedErrorOverrides<ValuePathsToAdapters>;
136
140
  accessor validation: FlattenedValidation<ValuePathsToAdapters>;
137
141
  private readonly flattenedTypeDefs;
138
142
  private readonly originalValues;
@@ -148,19 +152,28 @@ declare abstract class FormModel<T extends Type, ValueToTypePaths extends Readon
148
152
  get accessors(): Readonly<Record<string, Accessor>>;
149
153
  private maybeGetAdapterForValuePath;
150
154
  private getAdapterForValuePath;
155
+ get dirty(): boolean;
151
156
  typePath<K extends keyof ValueToTypePaths>(valuePath: K): ValueToTypePaths[K];
152
157
  setFieldValue<K extends keyof ValuePathsToAdapters>(valuePath: K, value: ToOfFieldAdapter<ValuePathsToAdapters[K]>, validation?: Validation): boolean;
153
158
  addListItem<K extends keyof FlattenedListTypesOfType<T>>(valuePath: K, elementValue?: Maybe<ElementOfArray<FlattenedValuesOfType<T>[K]>>, index?: number): void;
154
159
  removeListItem<K extends keyof FlattenedListTypesOfType<T>>(...elementValuePaths: readonly `${K}.${number}`[]): void;
160
+ protected moveListItem<K extends keyof FlattenedListTypesOfType<T>>(fromValuePath: K, toValuePath: K): void;
155
161
  private internalSetFieldValue;
162
+ /**
163
+ * Forces an error onto a field. Error will be removed if the field value changes
164
+ * @param valuePath the field to display an error for
165
+ * @param error the error to display
166
+ */
167
+ overrideFieldError<K extends keyof ValuePathsToAdapters>(valuePath: K, error?: ErrorOfFieldAdapter<ValuePathsToAdapters[K]>): void;
156
168
  clearFieldError<K extends keyof ValuePathsToAdapters>(valuePath: K): void;
157
169
  clearFieldValue<K extends StringKeyOf<ValuePathsToAdapters>>(valuePath: K): void;
158
170
  clearAll(value: ValueOfType<T>): void;
159
171
  isValuePathActive<K extends keyof ValuePathsToAdapters>(valuePath: K): boolean;
160
172
  getValidation<K extends keyof ValuePathsToAdapters>(valuePath: K): Validation;
161
- isDirty<K extends keyof ValuePathsToAdapters>(valuePath: K): boolean;
173
+ isFieldDirty<K extends keyof ValuePathsToAdapters>(valuePath: K): boolean;
162
174
  validateField<K extends keyof ValuePathsToAdapters>(valuePath: K, validation?: Validation): boolean;
163
175
  validateAll(validation?: Validation): boolean;
176
+ validateSubmit(): boolean;
164
177
  }
165
178
 
166
179
  type ValueOfModel<M extends FormModel<any, any, any, any, any>> = M extends FormModel<infer T, any, any, any, any> ? ValueOfType<ReadonlyTypeOfType<T>> : never;
package/dist/index.d.ts CHANGED
@@ -112,6 +112,9 @@ type FieldOverride<V = any> = Maybe<V>;
112
112
  type FlattenedFieldOverrides<ValuePathsToAdapters extends Readonly<Record<string, FieldAdapter>>> = {
113
113
  -readonly [K in keyof ValuePathsToAdapters]?: FieldOverride<ToOfFieldAdapter<ValuePathsToAdapters[K]>>;
114
114
  };
115
+ type FlattenedErrorOverrides<ValuePathsToAdapters extends Readonly<Record<string, FieldAdapter>>> = {
116
+ -readonly [K in keyof ValuePathsToAdapters]?: ErrorOfFieldAdapter<ValuePathsToAdapters[K]>;
117
+ };
115
118
  declare enum Validation {
116
119
  None = 0,
117
120
  Changed = 1,
@@ -133,6 +136,7 @@ declare abstract class FormModel<T extends Type, ValueToTypePaths extends Readon
133
136
  protected readonly mode: FormMode;
134
137
  accessor value: MobxValueOfType<T>;
135
138
  accessor fieldOverrides: FlattenedFieldOverrides<ValuePathsToAdapters>;
139
+ accessor errorOverrides: FlattenedErrorOverrides<ValuePathsToAdapters>;
136
140
  accessor validation: FlattenedValidation<ValuePathsToAdapters>;
137
141
  private readonly flattenedTypeDefs;
138
142
  private readonly originalValues;
@@ -148,19 +152,28 @@ declare abstract class FormModel<T extends Type, ValueToTypePaths extends Readon
148
152
  get accessors(): Readonly<Record<string, Accessor>>;
149
153
  private maybeGetAdapterForValuePath;
150
154
  private getAdapterForValuePath;
155
+ get dirty(): boolean;
151
156
  typePath<K extends keyof ValueToTypePaths>(valuePath: K): ValueToTypePaths[K];
152
157
  setFieldValue<K extends keyof ValuePathsToAdapters>(valuePath: K, value: ToOfFieldAdapter<ValuePathsToAdapters[K]>, validation?: Validation): boolean;
153
158
  addListItem<K extends keyof FlattenedListTypesOfType<T>>(valuePath: K, elementValue?: Maybe<ElementOfArray<FlattenedValuesOfType<T>[K]>>, index?: number): void;
154
159
  removeListItem<K extends keyof FlattenedListTypesOfType<T>>(...elementValuePaths: readonly `${K}.${number}`[]): void;
160
+ protected moveListItem<K extends keyof FlattenedListTypesOfType<T>>(fromValuePath: K, toValuePath: K): void;
155
161
  private internalSetFieldValue;
162
+ /**
163
+ * Forces an error onto a field. Error will be removed if the field value changes
164
+ * @param valuePath the field to display an error for
165
+ * @param error the error to display
166
+ */
167
+ overrideFieldError<K extends keyof ValuePathsToAdapters>(valuePath: K, error?: ErrorOfFieldAdapter<ValuePathsToAdapters[K]>): void;
156
168
  clearFieldError<K extends keyof ValuePathsToAdapters>(valuePath: K): void;
157
169
  clearFieldValue<K extends StringKeyOf<ValuePathsToAdapters>>(valuePath: K): void;
158
170
  clearAll(value: ValueOfType<T>): void;
159
171
  isValuePathActive<K extends keyof ValuePathsToAdapters>(valuePath: K): boolean;
160
172
  getValidation<K extends keyof ValuePathsToAdapters>(valuePath: K): Validation;
161
- isDirty<K extends keyof ValuePathsToAdapters>(valuePath: K): boolean;
173
+ isFieldDirty<K extends keyof ValuePathsToAdapters>(valuePath: K): boolean;
162
174
  validateField<K extends keyof ValuePathsToAdapters>(valuePath: K, validation?: Validation): boolean;
163
175
  validateAll(validation?: Validation): boolean;
176
+ validateSubmit(): boolean;
164
177
  }
165
178
 
166
179
  type ValueOfModel<M extends FormModel<any, any, any, any, any>> = M extends FormModel<infer T, any, any, any, any> ? ValueOfType<ReadonlyTypeOfType<T>> : never;