@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.
- package/.out/core/mobx/form_model.d.ts +14 -1
- package/.out/core/mobx/form_model.js +99 -37
- package/.out/core/mobx/hooks.js +3 -3
- package/.out/tsconfig.tsbuildinfo +1 -1
- package/.turbo/turbo-build.log +8 -8
- package/.turbo/turbo-check-types.log +1 -1
- package/core/mobx/form_model.ts +101 -30
- package/core/mobx/hooks.tsx +3 -3
- package/dist/index.cjs +80 -29
- package/dist/index.d.cts +14 -1
- package/dist/index.d.ts +14 -1
- package/dist/index.js +81 -29
- package/package.json +1 -1
package/.turbo/turbo-build.log
CHANGED
|
@@ -7,12 +7,12 @@ $ tsup
|
|
|
7
7
|
[34mCLI[39m Target: es6
|
|
8
8
|
[34mCJS[39m Build start
|
|
9
9
|
[34mESM[39m Build start
|
|
10
|
-
[32mCJS[39m [1mdist/index.cjs [22m[
|
|
11
|
-
[32mCJS[39m ⚡️ Build success in
|
|
12
|
-
[32mESM[39m [1mdist/index.js [22m[
|
|
13
|
-
[32mESM[39m ⚡️ Build success in
|
|
10
|
+
[32mCJS[39m [1mdist/index.cjs [22m[32m63.79 KB[39m
|
|
11
|
+
[32mCJS[39m ⚡️ Build success in 126ms
|
|
12
|
+
[32mESM[39m [1mdist/index.js [22m[32m59.70 KB[39m
|
|
13
|
+
[32mESM[39m ⚡️ Build success in 132ms
|
|
14
14
|
[34mDTS[39m Build start
|
|
15
|
-
[32mDTS[39m ⚡️ Build success in
|
|
16
|
-
[32mDTS[39m [1mdist/index.d.cts [22m[
|
|
17
|
-
[32mDTS[39m [1mdist/index.d.ts [22m[
|
|
18
|
-
Done in 33.
|
|
15
|
+
[32mDTS[39m ⚡️ Build success in 32334ms
|
|
16
|
+
[32mDTS[39m [1mdist/index.d.cts [22m[32m38.14 KB[39m
|
|
17
|
+
[32mDTS[39m [1mdist/index.d.ts [22m[32m38.14 KB[39m
|
|
18
|
+
Done in 33.47s.
|
package/core/mobx/form_model.ts
CHANGED
|
@@ -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
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
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
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
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
|
-
|
|
366
|
-
|
|
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
|
-
|
|
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
|
-
//
|
|
705
|
-
return
|
|
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
|
}
|
package/core/mobx/hooks.tsx
CHANGED
|
@@ -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.
|
|
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.
|
|
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,
|
|
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
|
-
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
|
|
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
|
-
|
|
540
|
-
|
|
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
|
-
|
|
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.
|
|
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.
|
|
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
|
-
|
|
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
|
-
|
|
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;
|