@strictly/react-form 0.0.25 → 0.0.26

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
+ ESM dist/index.js 59.53 KB
11
+ ESM ⚡️ Build success in 105ms
12
+ CJS dist/index.cjs 63.62 KB
13
+ CJS ⚡️ Build success in 110ms
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 30914ms
16
+ DTS dist/index.d.cts 38.03 KB
17
+ DTS dist/index.d.ts 38.03 KB
18
+ Done in 32.12s.
@@ -1,3 +1,3 @@
1
1
  yarn run v1.22.22
2
2
  $ tsc -b
3
- Done in 0.35s.
3
+ Done in 0.43s.
@@ -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
  }
@@ -602,6 +622,7 @@ export abstract class FormModel<
602
622
  const accessor = this.getAccessorForValuePath(valuePath)
603
623
  return runInAction(() => {
604
624
  this.fieldOverrides[valuePath] = [value]
625
+ delete this.errorOverrides[valuePath]
605
626
  if (validation != null) {
606
627
  this.validation[valuePath] = validation
607
628
  }
@@ -620,11 +641,30 @@ export abstract class FormModel<
620
641
  })
621
642
  }
622
643
 
644
+ /**
645
+ * Forces an error onto a field. Error will be removed if the field value changes
646
+ * @param valuePath the field to display an error for
647
+ * @param error the error to display
648
+ */
649
+ overrideFieldError<K extends keyof ValuePathsToAdapters>(
650
+ valuePath: K,
651
+ error?: ErrorOfFieldAdapter<ValuePathsToAdapters[K]>,
652
+ ) {
653
+ runInAction(() => {
654
+ if (error) {
655
+ this.errorOverrides[valuePath] = error
656
+ } else {
657
+ delete this.errorOverrides[valuePath]
658
+ }
659
+ })
660
+ }
661
+
623
662
  clearFieldError<K extends keyof ValuePathsToAdapters>(valuePath: K) {
624
663
  const fieldOverride = this.fieldOverrides[valuePath]
625
664
  if (fieldOverride != null) {
626
665
  runInAction(() => {
627
666
  delete this.validation[valuePath]
667
+ delete this.errorOverrides[valuePath]
628
668
  })
629
669
  }
630
670
  }
@@ -651,6 +691,7 @@ export abstract class FormModel<
651
691
  runInAction(() => {
652
692
  this.fieldOverrides[key] = [displayValue]
653
693
  delete this.validation[key]
694
+ delete this.errorOverrides[key]
654
695
  })
655
696
  }
656
697
 
@@ -659,6 +700,7 @@ export abstract class FormModel<
659
700
  this.validation = {}
660
701
  // TODO this isn't correct, should reload from value
661
702
  this.fieldOverrides = {}
703
+ this.errorOverrides = {}
662
704
  this.value = mobxCopy(this.type, value)
663
705
  })
664
706
  }
@@ -674,7 +716,7 @@ export abstract class FormModel<
674
716
  return this.validation[valuePath] ?? Validation.None
675
717
  }
676
718
 
677
- isDirty<K extends keyof ValuePathsToAdapters>(valuePath: K): boolean {
719
+ isFieldDirty<K extends keyof ValuePathsToAdapters>(valuePath: K): boolean {
678
720
  // eslint-disable-next-line @typescript-eslint/consistent-type-assertions
679
721
  const typePath = valuePathToTypePath<ValueToTypePaths, keyof ValueToTypePaths>(
680
722
  this.type,
@@ -691,18 +733,33 @@ export abstract class FormModel<
691
733
  const {
692
734
  displayedValue,
693
735
  convert,
736
+ revert,
694
737
  context,
695
738
  defaultValue,
696
739
  } = field
697
740
 
741
+ // if either the display value, or the stored value, match the original, then assume it's not dirty
698
742
  const originalValue = valuePath in this.originalValues
699
743
  // eslint-disable-next-line @typescript-eslint/consistent-type-assertions
700
744
  ? this.originalValues[valuePath as string]
701
745
  : defaultValue
746
+ if (revert != null) {
747
+ // eslint-disable-next-line @typescript-eslint/consistent-type-assertions
748
+ const typeDef = this.flattenedTypeDefs[typePath as string]
749
+ const {
750
+ value,
751
+ type,
752
+ } = revert(displayedValue, valuePath, context)
753
+ if (type === UnreliableFieldConversionType.Success) {
754
+ if (equals(typeDef, originalValue, value)) {
755
+ return false
756
+ }
757
+ }
758
+ }
702
759
  // eslint-disable-next-line @typescript-eslint/consistent-type-assertions
703
760
  const { value: originalDisplayedValue } = convert(originalValue, valuePath as string, context)
704
- // TODO better comparisons, displayed values can still be complex
705
- return (displayedValue !== originalDisplayedValue)
761
+ // try to compare the displayed values directly if we can't revert the displayed value
762
+ return displayedValue !== originalDisplayedValue
706
763
  }
707
764
 
708
765
  validateField<K extends keyof ValuePathsToAdapters>(
@@ -711,6 +768,7 @@ export abstract class FormModel<
711
768
  ): boolean {
712
769
  runInAction(() => {
713
770
  this.validation[valuePath] = validation
771
+ delete this.errorOverrides[valuePath]
714
772
  })
715
773
  return this.fields[valuePath].error == null
716
774
  }
@@ -732,4 +790,8 @@ export abstract class FormModel<
732
790
  },
733
791
  )
734
792
  }
793
+
794
+ validateSubmit() {
795
+ return this.validateAll()
796
+ }
735
797
  }
@@ -67,7 +67,7 @@ export function useDefaultMobxFormHooks<
67
67
  // TODO debounce?
68
68
  setTimeout(function () {
69
69
  // only start validation if the user has changed the field
70
- if (model.isValuePathActive(path) && model.isDirty(path)) {
70
+ if (model.isValuePathActive(path) && model.isFieldDirty(path)) {
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
  }
@@ -725,6 +733,7 @@ var FormModel = class {
725
733
  const accessor = this.getAccessorForValuePath(valuePath);
726
734
  return (0, import_mobx.runInAction)(() => {
727
735
  this.fieldOverrides[valuePath] = [value];
736
+ delete this.errorOverrides[valuePath];
728
737
  if (validation != null) {
729
738
  this.validation[valuePath] = validation;
730
739
  }
@@ -742,11 +751,26 @@ var FormModel = class {
742
751
  }
743
752
  });
744
753
  }
754
+ /**
755
+ * Forces an error onto a field. Error will be removed if the field value changes
756
+ * @param valuePath the field to display an error for
757
+ * @param error the error to display
758
+ */
759
+ overrideFieldError(valuePath, error) {
760
+ (0, import_mobx.runInAction)(() => {
761
+ if (error) {
762
+ this.errorOverrides[valuePath] = error;
763
+ } else {
764
+ delete this.errorOverrides[valuePath];
765
+ }
766
+ });
767
+ }
745
768
  clearFieldError(valuePath) {
746
769
  const fieldOverride = this.fieldOverrides[valuePath];
747
770
  if (fieldOverride != null) {
748
771
  (0, import_mobx.runInAction)(() => {
749
772
  delete this.validation[valuePath];
773
+ delete this.errorOverrides[valuePath];
750
774
  });
751
775
  }
752
776
  }
@@ -769,12 +793,14 @@ var FormModel = class {
769
793
  (0, import_mobx.runInAction)(() => {
770
794
  this.fieldOverrides[key] = [displayValue];
771
795
  delete this.validation[key];
796
+ delete this.errorOverrides[key];
772
797
  });
773
798
  }
774
799
  clearAll(value) {
775
800
  (0, import_mobx.runInAction)(() => {
776
801
  this.validation = {};
777
802
  this.fieldOverrides = {};
803
+ this.errorOverrides = {};
778
804
  this.value = (0, import_define.mobxCopy)(this.type, value);
779
805
  });
780
806
  }
@@ -787,7 +813,7 @@ var FormModel = class {
787
813
  var _a;
788
814
  return (_a = this.validation[valuePath]) != null ? _a : 0 /* None */;
789
815
  }
790
- isDirty(valuePath) {
816
+ isFieldDirty(valuePath) {
791
817
  const typePath = (0, import_define.valuePathToTypePath)(
792
818
  this.type,
793
819
  // eslint-disable-next-line @typescript-eslint/consistent-type-assertions
@@ -801,16 +827,30 @@ var FormModel = class {
801
827
  const {
802
828
  displayedValue,
803
829
  convert,
830
+ revert,
804
831
  context,
805
832
  defaultValue
806
833
  } = field;
807
834
  const originalValue = valuePath in this.originalValues ? this.originalValues[valuePath] : defaultValue;
835
+ if (revert != null) {
836
+ const typeDef = this.flattenedTypeDefs[typePath];
837
+ const {
838
+ value,
839
+ type
840
+ } = revert(displayedValue, valuePath, context);
841
+ if (type === 0 /* Success */) {
842
+ if ((0, import_define.equals)(typeDef, originalValue, value)) {
843
+ return false;
844
+ }
845
+ }
846
+ }
808
847
  const { value: originalDisplayedValue } = convert(originalValue, valuePath, context);
809
848
  return displayedValue !== originalDisplayedValue;
810
849
  }
811
850
  validateField(valuePath, validation = 2 /* Always */) {
812
851
  (0, import_mobx.runInAction)(() => {
813
852
  this.validation[valuePath] = validation;
853
+ delete this.errorOverrides[valuePath];
814
854
  });
815
855
  return this.fields[valuePath].error == null;
816
856
  }
@@ -828,17 +868,23 @@ var FormModel = class {
828
868
  }
829
869
  );
830
870
  }
871
+ validateSubmit() {
872
+ return this.validateAll();
873
+ }
831
874
  };
832
875
  _init = __decoratorStart(null);
833
876
  _value = new WeakMap();
834
877
  _fieldOverrides = new WeakMap();
878
+ _errorOverrides = new WeakMap();
835
879
  _validation = new WeakMap();
836
880
  __decorateElement(_init, 4, "value", _value_dec, FormModel, _value);
837
881
  __decorateElement(_init, 4, "fieldOverrides", _fieldOverrides_dec, FormModel, _fieldOverrides);
882
+ __decorateElement(_init, 4, "errorOverrides", _errorOverrides_dec, FormModel, _errorOverrides);
838
883
  __decorateElement(_init, 4, "validation", _validation_dec, FormModel, _validation);
839
884
  __decorateElement(_init, 2, "fields", _fields_dec, FormModel);
840
885
  __decorateElement(_init, 2, "knownFields", _knownFields_dec, FormModel);
841
886
  __decorateElement(_init, 2, "accessors", _accessors_dec, FormModel);
887
+ __decorateElement(_init, 2, "dirty", _dirty_dec, FormModel);
842
888
  __decoratorMetadata(_init, FormModel);
843
889
 
844
890
  // core/mobx/hooks.tsx
@@ -869,7 +915,7 @@ function useDefaultMobxFormHooks(model, {
869
915
  const onFieldBlur = (0, import_react.useCallback)(
870
916
  function(path) {
871
917
  setTimeout(function() {
872
- if (model.isValuePathActive(path) && model.isDirty(path)) {
918
+ if (model.isValuePathActive(path) && model.isFieldDirty(path)) {
873
919
  model.validateField(path, Math.max(1 /* Changed */, model.getValidation(path)));
874
920
  }
875
921
  }, 100);
@@ -878,7 +924,7 @@ function useDefaultMobxFormHooks(model, {
878
924
  );
879
925
  const onFormSubmit = (0, import_react.useCallback)(
880
926
  function() {
881
- if (model.validateAll()) {
927
+ if (model.validateSubmit()) {
882
928
  onValidFormSubmit == null ? void 0 : onValidFormSubmit(model.value);
883
929
  }
884
930
  },
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,27 @@ 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;
155
160
  private internalSetFieldValue;
161
+ /**
162
+ * Forces an error onto a field. Error will be removed if the field value changes
163
+ * @param valuePath the field to display an error for
164
+ * @param error the error to display
165
+ */
166
+ overrideFieldError<K extends keyof ValuePathsToAdapters>(valuePath: K, error?: ErrorOfFieldAdapter<ValuePathsToAdapters[K]>): void;
156
167
  clearFieldError<K extends keyof ValuePathsToAdapters>(valuePath: K): void;
157
168
  clearFieldValue<K extends StringKeyOf<ValuePathsToAdapters>>(valuePath: K): void;
158
169
  clearAll(value: ValueOfType<T>): void;
159
170
  isValuePathActive<K extends keyof ValuePathsToAdapters>(valuePath: K): boolean;
160
171
  getValidation<K extends keyof ValuePathsToAdapters>(valuePath: K): Validation;
161
- isDirty<K extends keyof ValuePathsToAdapters>(valuePath: K): boolean;
172
+ isFieldDirty<K extends keyof ValuePathsToAdapters>(valuePath: K): boolean;
162
173
  validateField<K extends keyof ValuePathsToAdapters>(valuePath: K, validation?: Validation): boolean;
163
174
  validateAll(validation?: Validation): boolean;
175
+ validateSubmit(): boolean;
164
176
  }
165
177
 
166
178
  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,27 @@ 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;
155
160
  private internalSetFieldValue;
161
+ /**
162
+ * Forces an error onto a field. Error will be removed if the field value changes
163
+ * @param valuePath the field to display an error for
164
+ * @param error the error to display
165
+ */
166
+ overrideFieldError<K extends keyof ValuePathsToAdapters>(valuePath: K, error?: ErrorOfFieldAdapter<ValuePathsToAdapters[K]>): void;
156
167
  clearFieldError<K extends keyof ValuePathsToAdapters>(valuePath: K): void;
157
168
  clearFieldValue<K extends StringKeyOf<ValuePathsToAdapters>>(valuePath: K): void;
158
169
  clearAll(value: ValueOfType<T>): void;
159
170
  isValuePathActive<K extends keyof ValuePathsToAdapters>(valuePath: K): boolean;
160
171
  getValidation<K extends keyof ValuePathsToAdapters>(valuePath: K): Validation;
161
- isDirty<K extends keyof ValuePathsToAdapters>(valuePath: K): boolean;
172
+ isFieldDirty<K extends keyof ValuePathsToAdapters>(valuePath: K): boolean;
162
173
  validateField<K extends keyof ValuePathsToAdapters>(valuePath: K, validation?: Validation): boolean;
163
174
  validateAll(validation?: Validation): boolean;
175
+ validateSubmit(): boolean;
164
176
  }
165
177
 
166
178
  type ValueOfModel<M extends FormModel<any, any, any, any, any>> = M extends FormModel<infer T, any, any, any, any> ? ValueOfType<ReadonlyTypeOfType<T>> : never;