@strictly/react-form 0.0.23 → 0.0.25

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
- ESM dist/index.js 56.70 KB
11
- ESM ⚡️ Build success in 139ms
12
- CJS dist/index.cjs 60.71 KB
13
- CJS ⚡️ Build success in 141ms
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
14
14
  DTS Build start
15
- DTS ⚡️ Build success in 31420ms
16
- DTS dist/index.d.cts 37.20 KB
17
- DTS dist/index.d.ts 37.20 KB
18
- Done in 32.57s.
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.
@@ -1,3 +1,3 @@
1
1
  yarn run v1.22.22
2
2
  $ tsc -b
3
- Done in 0.37s.
3
+ Done in 0.35s.
@@ -89,6 +89,7 @@ type FlattenedFieldOverrides<
89
89
  }
90
90
 
91
91
  export enum Validation {
92
+ None = 0,
92
93
  Changed = 1,
93
94
  Always = 2,
94
95
  }
@@ -211,7 +212,7 @@ export abstract class FormModel<
211
212
  case 'edit':
212
213
  return false
213
214
  default:
214
- return this.mode satisfies never
215
+ throw new UnreachableError(this.mode)
215
216
  }
216
217
  }
217
218
 
@@ -272,7 +273,7 @@ export abstract class FormModel<
272
273
  return this.synthesizeFieldByPaths(valuePath, typePath)
273
274
  }
274
275
 
275
- private synthesizeFieldByPaths(valuePath: keyof ValuePathsToAdapters, typePath: keyof TypePathsToAdapters) {
276
+ private getField(valuePath: keyof ValuePathsToAdapters, typePath: keyof TypePathsToAdapters) {
276
277
  const adapter = this.adapters[typePath]
277
278
  if (adapter == null) {
278
279
  // invalid path, which can happen
@@ -310,12 +311,39 @@ export abstract class FormModel<
310
311
  valuePath as string,
311
312
  context,
312
313
  )
313
- // const error = this.errors[valuePath]
314
- let error: unknown = undefined
315
314
  const displayedValue = fieldOverride != null ? fieldOverride[0] : value
316
- const validation = this.validation[valuePath]
315
+
316
+ return {
317
+ context,
318
+ convert,
319
+ create,
320
+ revert,
321
+ displayedValue,
322
+ // value,
323
+ required,
324
+ readonly,
325
+ defaultValue,
326
+ }
327
+ }
328
+
329
+ private synthesizeFieldByPaths(valuePath: keyof ValuePathsToAdapters, typePath: keyof TypePathsToAdapters) {
330
+ const field = this.getField(valuePath, typePath)
331
+ if (field == null) {
332
+ return
333
+ }
334
+ const {
335
+ context,
336
+ convert,
337
+ revert,
338
+ displayedValue,
339
+ required,
340
+ readonly,
341
+ defaultValue,
342
+ } = field
343
+ const validation = this.validation[valuePath] ?? Validation.None
344
+ let error: unknown
317
345
  switch (validation) {
318
- case undefined:
346
+ case Validation.None:
319
347
  // skip validation
320
348
  break
321
349
  case Validation.Changed:
@@ -347,7 +375,7 @@ export abstract class FormModel<
347
375
  throw new UnreachableError(validation)
348
376
  }
349
377
  return {
350
- value: fieldOverride != null ? fieldOverride[0] : value,
378
+ value: displayedValue,
351
379
  error,
352
380
  readonly: readonly && !this.forceMutableFields,
353
381
  required,
@@ -393,7 +421,7 @@ export abstract class FormModel<
393
421
  setFieldValue<K extends keyof ValuePathsToAdapters>(
394
422
  valuePath: K,
395
423
  value: ToOfFieldAdapter<ValuePathsToAdapters[K]>,
396
- validation: Validation | undefined | null = this.validation[valuePath],
424
+ validation?: Validation,
397
425
  ): boolean {
398
426
  return this.internalSetFieldValue(valuePath, value, validation)
399
427
  }
@@ -563,7 +591,7 @@ export abstract class FormModel<
563
591
  private internalSetFieldValue<K extends keyof ValuePathsToAdapters>(
564
592
  valuePath: K,
565
593
  value: ToOfFieldAdapter<ValuePathsToAdapters[K]>,
566
- validation: Validation | undefined | null,
594
+ validation: Validation | undefined,
567
595
  ): boolean {
568
596
  const { revert } = this.getAdapterForValuePath(valuePath)
569
597
 
@@ -576,8 +604,6 @@ export abstract class FormModel<
576
604
  this.fieldOverrides[valuePath] = [value]
577
605
  if (validation != null) {
578
606
  this.validation[valuePath] = validation
579
- } else {
580
- delete this.validation[valuePath]
581
607
  }
582
608
  switch (conversion.type) {
583
609
  case UnreliableFieldConversionType.Failure:
@@ -644,12 +670,44 @@ export abstract class FormModel<
644
670
  return keys.has(valuePath as string)
645
671
  }
646
672
 
673
+ getValidation<K extends keyof ValuePathsToAdapters>(valuePath: K): Validation {
674
+ return this.validation[valuePath] ?? Validation.None
675
+ }
676
+
677
+ isDirty<K extends keyof ValuePathsToAdapters>(valuePath: K): boolean {
678
+ // eslint-disable-next-line @typescript-eslint/consistent-type-assertions
679
+ const typePath = valuePathToTypePath<ValueToTypePaths, keyof ValueToTypePaths>(
680
+ this.type,
681
+ // eslint-disable-next-line @typescript-eslint/consistent-type-assertions
682
+ valuePath as keyof ValueToTypePaths,
683
+ true,
684
+ ) as keyof TypePathsToAdapters
685
+ const field = this.getField(valuePath, typePath)
686
+
687
+ if (field == null) {
688
+ return false
689
+ }
690
+
691
+ const {
692
+ displayedValue,
693
+ convert,
694
+ context,
695
+ defaultValue,
696
+ } = field
697
+
698
+ const originalValue = valuePath in this.originalValues
699
+ // eslint-disable-next-line @typescript-eslint/consistent-type-assertions
700
+ ? this.originalValues[valuePath as string]
701
+ : defaultValue
702
+ // eslint-disable-next-line @typescript-eslint/consistent-type-assertions
703
+ const { value: originalDisplayedValue } = convert(originalValue, valuePath as string, context)
704
+ // TODO better comparisons, displayed values can still be complex
705
+ return (displayedValue !== originalDisplayedValue)
706
+ }
707
+
647
708
  validateField<K extends keyof ValuePathsToAdapters>(
648
709
  valuePath: K,
649
- validation: Validation = Math.max(
650
- this.mode === 'create' ? Validation.Always : Validation.Changed,
651
- this.validation[valuePath] ?? Validation.Changed,
652
- ),
710
+ validation: Validation = Validation.Always,
653
711
  ): boolean {
654
712
  runInAction(() => {
655
713
  this.validation[valuePath] = validation
@@ -657,7 +715,7 @@ export abstract class FormModel<
657
715
  return this.fields[valuePath].error == null
658
716
  }
659
717
 
660
- validateAll(validation: Validation = this.mode === 'create' ? Validation.Always : Validation.Changed): boolean {
718
+ validateAll(validation: Validation = Validation.Always): boolean {
661
719
  const accessors = toArray(this.accessors)
662
720
 
663
721
  runInAction(() => {
@@ -8,6 +8,7 @@ import {
8
8
  import type { ValueTypeOfField } from 'types/value_type_of_field'
9
9
  import {
10
10
  type FormModel,
11
+ Validation,
11
12
  } from './form_model'
12
13
 
13
14
  // eslint-disable-next-line @typescript-eslint/no-explicit-any
@@ -40,8 +41,8 @@ export function useDefaultMobxFormHooks<
40
41
  path: Path,
41
42
  value: ValueTypeOfField<F[Path]>,
42
43
  ) {
43
- // clear any validation
44
- model.setFieldValue<Path>(path, value, null)
44
+ const validation = Math.min(model.getValidation(path), Validation.Changed)
45
+ model.setFieldValue<Path>(path, value, validation)
45
46
  },
46
47
  [model],
47
48
  )
@@ -65,8 +66,10 @@ export function useDefaultMobxFormHooks<
65
66
  // (e.g. changing a discriminator)
66
67
  // TODO debounce?
67
68
  setTimeout(function () {
68
- if (model.isValuePathActive(path)) {
69
- model.validateField(path)
69
+ // only start validation if the user has changed the field
70
+ if (model.isValuePathActive(path) && model.isDirty(path)) {
71
+ // further workaround to make sure we don't downgrade the existing validation
72
+ model.validateField(path, Math.max(Validation.Changed, model.getValidation(path)))
70
73
  }
71
74
  }, 100)
72
75
  },
@@ -1267,8 +1267,8 @@ describe('all', function () {
1267
1267
  expect(model.fields['$.n'].readonly).toBeTruthy()
1268
1268
  })
1269
1269
 
1270
- it('validates successfully with clean, but invalid data', () => {
1271
- expect(model.validateAll()).toBeTruthy()
1270
+ it('fails validation with invalid, clean data', () => {
1271
+ expect(model.validateAll()).toBeFalsy()
1272
1272
  })
1273
1273
 
1274
1274
  it('fails validation with invalid, dirty data', () => {
package/dist/index.cjs CHANGED
@@ -358,6 +358,7 @@ var import_base2 = require("@strictly/base");
358
358
  var import_define = require("@strictly/define");
359
359
  var import_mobx = require("mobx");
360
360
  var Validation = /* @__PURE__ */ ((Validation2) => {
361
+ Validation2[Validation2["None"] = 0] = "None";
361
362
  Validation2[Validation2["Changed"] = 1] = "Changed";
362
363
  Validation2[Validation2["Always"] = 2] = "Always";
363
364
  return Validation2;
@@ -412,7 +413,7 @@ var FormModel = class {
412
413
  case "edit":
413
414
  return false;
414
415
  default:
415
- return this.mode;
416
+ throw new import_base2.UnreachableError(this.mode);
416
417
  }
417
418
  }
418
419
  get fields() {
@@ -462,7 +463,7 @@ var FormModel = class {
462
463
  }
463
464
  return this.synthesizeFieldByPaths(valuePath, typePath);
464
465
  }
465
- synthesizeFieldByPaths(valuePath, typePath) {
466
+ getField(valuePath, typePath) {
466
467
  const adapter2 = this.adapters[typePath];
467
468
  if (adapter2 == null) {
468
469
  return;
@@ -490,11 +491,38 @@ var FormModel = class {
490
491
  valuePath,
491
492
  context
492
493
  );
493
- let error = void 0;
494
494
  const displayedValue = fieldOverride != null ? fieldOverride[0] : value;
495
- const validation = this.validation[valuePath];
495
+ return {
496
+ context,
497
+ convert,
498
+ create,
499
+ revert,
500
+ displayedValue,
501
+ // value,
502
+ required,
503
+ readonly,
504
+ defaultValue
505
+ };
506
+ }
507
+ synthesizeFieldByPaths(valuePath, typePath) {
508
+ var _a;
509
+ const field = this.getField(valuePath, typePath);
510
+ if (field == null) {
511
+ return;
512
+ }
513
+ const {
514
+ context,
515
+ convert,
516
+ revert,
517
+ displayedValue,
518
+ required,
519
+ readonly,
520
+ defaultValue
521
+ } = field;
522
+ const validation = (_a = this.validation[valuePath]) != null ? _a : 0 /* None */;
523
+ let error;
496
524
  switch (validation) {
497
- case void 0:
525
+ case 0 /* None */:
498
526
  break;
499
527
  case 1 /* Changed */:
500
528
  if (revert != null) {
@@ -522,7 +550,7 @@ var FormModel = class {
522
550
  throw new import_base2.UnreachableError(validation);
523
551
  }
524
552
  return {
525
- value: fieldOverride != null ? fieldOverride[0] : value,
553
+ value: displayedValue,
526
554
  error,
527
555
  readonly: readonly && !this.forceMutableFields,
528
556
  required
@@ -555,7 +583,7 @@ var FormModel = class {
555
583
  typePath(valuePath) {
556
584
  return (0, import_define.valuePathToTypePath)(this.type, valuePath, true);
557
585
  }
558
- setFieldValue(valuePath, value, validation = this.validation[valuePath]) {
586
+ setFieldValue(valuePath, value, validation) {
559
587
  return this.internalSetFieldValue(valuePath, value, validation);
560
588
  }
561
589
  addListItem(valuePath, elementValue = null, index) {
@@ -699,8 +727,6 @@ var FormModel = class {
699
727
  this.fieldOverrides[valuePath] = [value];
700
728
  if (validation != null) {
701
729
  this.validation[valuePath] = validation;
702
- } else {
703
- delete this.validation[valuePath];
704
730
  }
705
731
  switch (conversion.type) {
706
732
  case 1 /* Failure */:
@@ -757,16 +783,38 @@ var FormModel = class {
757
783
  const keys = new Set(Object.keys(values));
758
784
  return keys.has(valuePath);
759
785
  }
760
- validateField(valuePath, validation = Math.max(
761
- this.mode === "create" ? 2 /* Always */ : 1 /* Changed */,
762
- ((_a) => (_a = this.validation[valuePath]) != null ? _a : 1 /* Changed */)()
763
- )) {
786
+ getValidation(valuePath) {
787
+ var _a;
788
+ return (_a = this.validation[valuePath]) != null ? _a : 0 /* None */;
789
+ }
790
+ isDirty(valuePath) {
791
+ const typePath = (0, import_define.valuePathToTypePath)(
792
+ this.type,
793
+ // eslint-disable-next-line @typescript-eslint/consistent-type-assertions
794
+ valuePath,
795
+ true
796
+ );
797
+ const field = this.getField(valuePath, typePath);
798
+ if (field == null) {
799
+ return false;
800
+ }
801
+ const {
802
+ displayedValue,
803
+ convert,
804
+ context,
805
+ defaultValue
806
+ } = field;
807
+ const originalValue = valuePath in this.originalValues ? this.originalValues[valuePath] : defaultValue;
808
+ const { value: originalDisplayedValue } = convert(originalValue, valuePath, context);
809
+ return displayedValue !== originalDisplayedValue;
810
+ }
811
+ validateField(valuePath, validation = 2 /* Always */) {
764
812
  (0, import_mobx.runInAction)(() => {
765
813
  this.validation[valuePath] = validation;
766
814
  });
767
815
  return this.fields[valuePath].error == null;
768
816
  }
769
- validateAll(validation = this.mode === "create" ? 2 /* Always */ : 1 /* Changed */) {
817
+ validateAll(validation = 2 /* Always */) {
770
818
  const accessors = (0, import_base2.toArray)(this.accessors);
771
819
  (0, import_mobx.runInAction)(() => {
772
820
  accessors.forEach(([valuePath]) => {
@@ -801,7 +849,8 @@ function useDefaultMobxFormHooks(model, {
801
849
  } = {}) {
802
850
  const onFieldValueChange = (0, import_react.useCallback)(
803
851
  function(path, value) {
804
- model.setFieldValue(path, value, null);
852
+ const validation = Math.min(model.getValidation(path), 1 /* Changed */);
853
+ model.setFieldValue(path, value, validation);
805
854
  },
806
855
  [model]
807
856
  );
@@ -820,8 +869,8 @@ function useDefaultMobxFormHooks(model, {
820
869
  const onFieldBlur = (0, import_react.useCallback)(
821
870
  function(path) {
822
871
  setTimeout(function() {
823
- if (model.isValuePathActive(path)) {
824
- model.validateField(path);
872
+ if (model.isValuePathActive(path) && model.isDirty(path)) {
873
+ model.validateField(path, Math.max(1 /* Changed */, model.getValidation(path)));
825
874
  }
826
875
  }, 100);
827
876
  },
package/dist/index.d.cts CHANGED
@@ -113,6 +113,7 @@ type FlattenedFieldOverrides<ValuePathsToAdapters extends Readonly<Record<string
113
113
  -readonly [K in keyof ValuePathsToAdapters]?: FieldOverride<ToOfFieldAdapter<ValuePathsToAdapters[K]>>;
114
114
  };
115
115
  declare enum Validation {
116
+ None = 0,
116
117
  Changed = 1,
117
118
  Always = 2
118
119
  }
@@ -141,13 +142,14 @@ declare abstract class FormModel<T extends Type, ValueToTypePaths extends Readon
141
142
  get fields(): SimplifyDeep<FlattenedConvertedFieldsOf<ValuePathsToAdapters>>;
142
143
  private get knownFields();
143
144
  private maybeSynthesizeFieldByValuePath;
145
+ private getField;
144
146
  private synthesizeFieldByPaths;
145
147
  getAccessorForValuePath(valuePath: keyof ValuePathsToAdapters): Accessor | undefined;
146
148
  get accessors(): Readonly<Record<string, Accessor>>;
147
149
  private maybeGetAdapterForValuePath;
148
150
  private getAdapterForValuePath;
149
151
  typePath<K extends keyof ValueToTypePaths>(valuePath: K): ValueToTypePaths[K];
150
- setFieldValue<K extends keyof ValuePathsToAdapters>(valuePath: K, value: ToOfFieldAdapter<ValuePathsToAdapters[K]>, validation?: Validation | undefined | null): boolean;
152
+ setFieldValue<K extends keyof ValuePathsToAdapters>(valuePath: K, value: ToOfFieldAdapter<ValuePathsToAdapters[K]>, validation?: Validation): boolean;
151
153
  addListItem<K extends keyof FlattenedListTypesOfType<T>>(valuePath: K, elementValue?: Maybe<ElementOfArray<FlattenedValuesOfType<T>[K]>>, index?: number): void;
152
154
  removeListItem<K extends keyof FlattenedListTypesOfType<T>>(...elementValuePaths: readonly `${K}.${number}`[]): void;
153
155
  private internalSetFieldValue;
@@ -155,6 +157,8 @@ declare abstract class FormModel<T extends Type, ValueToTypePaths extends Readon
155
157
  clearFieldValue<K extends StringKeyOf<ValuePathsToAdapters>>(valuePath: K): void;
156
158
  clearAll(value: ValueOfType<T>): void;
157
159
  isValuePathActive<K extends keyof ValuePathsToAdapters>(valuePath: K): boolean;
160
+ getValidation<K extends keyof ValuePathsToAdapters>(valuePath: K): Validation;
161
+ isDirty<K extends keyof ValuePathsToAdapters>(valuePath: K): boolean;
158
162
  validateField<K extends keyof ValuePathsToAdapters>(valuePath: K, validation?: Validation): boolean;
159
163
  validateAll(validation?: Validation): boolean;
160
164
  }
package/dist/index.d.ts CHANGED
@@ -113,6 +113,7 @@ type FlattenedFieldOverrides<ValuePathsToAdapters extends Readonly<Record<string
113
113
  -readonly [K in keyof ValuePathsToAdapters]?: FieldOverride<ToOfFieldAdapter<ValuePathsToAdapters[K]>>;
114
114
  };
115
115
  declare enum Validation {
116
+ None = 0,
116
117
  Changed = 1,
117
118
  Always = 2
118
119
  }
@@ -141,13 +142,14 @@ declare abstract class FormModel<T extends Type, ValueToTypePaths extends Readon
141
142
  get fields(): SimplifyDeep<FlattenedConvertedFieldsOf<ValuePathsToAdapters>>;
142
143
  private get knownFields();
143
144
  private maybeSynthesizeFieldByValuePath;
145
+ private getField;
144
146
  private synthesizeFieldByPaths;
145
147
  getAccessorForValuePath(valuePath: keyof ValuePathsToAdapters): Accessor | undefined;
146
148
  get accessors(): Readonly<Record<string, Accessor>>;
147
149
  private maybeGetAdapterForValuePath;
148
150
  private getAdapterForValuePath;
149
151
  typePath<K extends keyof ValueToTypePaths>(valuePath: K): ValueToTypePaths[K];
150
- setFieldValue<K extends keyof ValuePathsToAdapters>(valuePath: K, value: ToOfFieldAdapter<ValuePathsToAdapters[K]>, validation?: Validation | undefined | null): boolean;
152
+ setFieldValue<K extends keyof ValuePathsToAdapters>(valuePath: K, value: ToOfFieldAdapter<ValuePathsToAdapters[K]>, validation?: Validation): boolean;
151
153
  addListItem<K extends keyof FlattenedListTypesOfType<T>>(valuePath: K, elementValue?: Maybe<ElementOfArray<FlattenedValuesOfType<T>[K]>>, index?: number): void;
152
154
  removeListItem<K extends keyof FlattenedListTypesOfType<T>>(...elementValuePaths: readonly `${K}.${number}`[]): void;
153
155
  private internalSetFieldValue;
@@ -155,6 +157,8 @@ declare abstract class FormModel<T extends Type, ValueToTypePaths extends Readon
155
157
  clearFieldValue<K extends StringKeyOf<ValuePathsToAdapters>>(valuePath: K): void;
156
158
  clearAll(value: ValueOfType<T>): void;
157
159
  isValuePathActive<K extends keyof ValuePathsToAdapters>(valuePath: K): boolean;
160
+ getValidation<K extends keyof ValuePathsToAdapters>(valuePath: K): Validation;
161
+ isDirty<K extends keyof ValuePathsToAdapters>(valuePath: K): boolean;
158
162
  validateField<K extends keyof ValuePathsToAdapters>(valuePath: K, validation?: Validation): boolean;
159
163
  validateAll(validation?: Validation): boolean;
160
164
  }
package/dist/index.js CHANGED
@@ -327,6 +327,7 @@ import {
327
327
  runInAction
328
328
  } from "mobx";
329
329
  var Validation = /* @__PURE__ */ ((Validation2) => {
330
+ Validation2[Validation2["None"] = 0] = "None";
330
331
  Validation2[Validation2["Changed"] = 1] = "Changed";
331
332
  Validation2[Validation2["Always"] = 2] = "Always";
332
333
  return Validation2;
@@ -381,7 +382,7 @@ var FormModel = class {
381
382
  case "edit":
382
383
  return false;
383
384
  default:
384
- return this.mode;
385
+ throw new UnreachableError2(this.mode);
385
386
  }
386
387
  }
387
388
  get fields() {
@@ -431,7 +432,7 @@ var FormModel = class {
431
432
  }
432
433
  return this.synthesizeFieldByPaths(valuePath, typePath);
433
434
  }
434
- synthesizeFieldByPaths(valuePath, typePath) {
435
+ getField(valuePath, typePath) {
435
436
  const adapter2 = this.adapters[typePath];
436
437
  if (adapter2 == null) {
437
438
  return;
@@ -459,11 +460,38 @@ var FormModel = class {
459
460
  valuePath,
460
461
  context
461
462
  );
462
- let error = void 0;
463
463
  const displayedValue = fieldOverride != null ? fieldOverride[0] : value;
464
- const validation = this.validation[valuePath];
464
+ return {
465
+ context,
466
+ convert,
467
+ create,
468
+ revert,
469
+ displayedValue,
470
+ // value,
471
+ required,
472
+ readonly,
473
+ defaultValue
474
+ };
475
+ }
476
+ synthesizeFieldByPaths(valuePath, typePath) {
477
+ var _a;
478
+ const field = this.getField(valuePath, typePath);
479
+ if (field == null) {
480
+ return;
481
+ }
482
+ const {
483
+ context,
484
+ convert,
485
+ revert,
486
+ displayedValue,
487
+ required,
488
+ readonly,
489
+ defaultValue
490
+ } = field;
491
+ const validation = (_a = this.validation[valuePath]) != null ? _a : 0 /* None */;
492
+ let error;
465
493
  switch (validation) {
466
- case void 0:
494
+ case 0 /* None */:
467
495
  break;
468
496
  case 1 /* Changed */:
469
497
  if (revert != null) {
@@ -491,7 +519,7 @@ var FormModel = class {
491
519
  throw new UnreachableError2(validation);
492
520
  }
493
521
  return {
494
- value: fieldOverride != null ? fieldOverride[0] : value,
522
+ value: displayedValue,
495
523
  error,
496
524
  readonly: readonly && !this.forceMutableFields,
497
525
  required
@@ -524,7 +552,7 @@ var FormModel = class {
524
552
  typePath(valuePath) {
525
553
  return valuePathToTypePath(this.type, valuePath, true);
526
554
  }
527
- setFieldValue(valuePath, value, validation = this.validation[valuePath]) {
555
+ setFieldValue(valuePath, value, validation) {
528
556
  return this.internalSetFieldValue(valuePath, value, validation);
529
557
  }
530
558
  addListItem(valuePath, elementValue = null, index) {
@@ -668,8 +696,6 @@ var FormModel = class {
668
696
  this.fieldOverrides[valuePath] = [value];
669
697
  if (validation != null) {
670
698
  this.validation[valuePath] = validation;
671
- } else {
672
- delete this.validation[valuePath];
673
699
  }
674
700
  switch (conversion.type) {
675
701
  case 1 /* Failure */:
@@ -726,16 +752,38 @@ var FormModel = class {
726
752
  const keys = new Set(Object.keys(values));
727
753
  return keys.has(valuePath);
728
754
  }
729
- validateField(valuePath, validation = Math.max(
730
- this.mode === "create" ? 2 /* Always */ : 1 /* Changed */,
731
- ((_a) => (_a = this.validation[valuePath]) != null ? _a : 1 /* Changed */)()
732
- )) {
755
+ getValidation(valuePath) {
756
+ var _a;
757
+ return (_a = this.validation[valuePath]) != null ? _a : 0 /* None */;
758
+ }
759
+ isDirty(valuePath) {
760
+ const typePath = valuePathToTypePath(
761
+ this.type,
762
+ // eslint-disable-next-line @typescript-eslint/consistent-type-assertions
763
+ valuePath,
764
+ true
765
+ );
766
+ const field = this.getField(valuePath, typePath);
767
+ if (field == null) {
768
+ return false;
769
+ }
770
+ const {
771
+ displayedValue,
772
+ convert,
773
+ context,
774
+ defaultValue
775
+ } = field;
776
+ const originalValue = valuePath in this.originalValues ? this.originalValues[valuePath] : defaultValue;
777
+ const { value: originalDisplayedValue } = convert(originalValue, valuePath, context);
778
+ return displayedValue !== originalDisplayedValue;
779
+ }
780
+ validateField(valuePath, validation = 2 /* Always */) {
733
781
  runInAction(() => {
734
782
  this.validation[valuePath] = validation;
735
783
  });
736
784
  return this.fields[valuePath].error == null;
737
785
  }
738
- validateAll(validation = this.mode === "create" ? 2 /* Always */ : 1 /* Changed */) {
786
+ validateAll(validation = 2 /* Always */) {
739
787
  const accessors = toArray(this.accessors);
740
788
  runInAction(() => {
741
789
  accessors.forEach(([valuePath]) => {
@@ -772,7 +820,8 @@ function useDefaultMobxFormHooks(model, {
772
820
  } = {}) {
773
821
  const onFieldValueChange = useCallback(
774
822
  function(path, value) {
775
- model.setFieldValue(path, value, null);
823
+ const validation = Math.min(model.getValidation(path), 1 /* Changed */);
824
+ model.setFieldValue(path, value, validation);
776
825
  },
777
826
  [model]
778
827
  );
@@ -791,8 +840,8 @@ function useDefaultMobxFormHooks(model, {
791
840
  const onFieldBlur = useCallback(
792
841
  function(path) {
793
842
  setTimeout(function() {
794
- if (model.isValuePathActive(path)) {
795
- model.validateField(path);
843
+ if (model.isValuePathActive(path) && model.isDirty(path)) {
844
+ model.validateField(path, Math.max(1 /* Changed */, model.getValidation(path)));
796
845
  }
797
846
  }, 100);
798
847
  },
package/package.json CHANGED
@@ -72,7 +72,7 @@
72
72
  "test:watch": "vitest"
73
73
  },
74
74
  "type": "module",
75
- "version": "0.0.23",
75
+ "version": "0.0.25",
76
76
  "exports": {
77
77
  ".": {
78
78
  "import": {