@strictly/react-form 0.0.24 → 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.
- package/.out/core/mobx/form_model.d.ts +14 -0
- package/.out/core/mobx/form_model.js +127 -36
- package/.out/core/mobx/hooks.js +5 -4
- package/.out/tsconfig.tsbuildinfo +1 -1
- package/.turbo/turbo-build.log +8 -8
- package/.turbo/turbo-check-types.log +1 -1
- package/.turbo/turbo-release$colon$exports.log +1 -1
- package/core/mobx/form_model.ts +150 -29
- package/core/mobx/hooks.tsx +5 -4
- package/dist/index.cjs +126 -32
- package/dist/index.d.cts +14 -0
- package/dist/index.d.ts +14 -0
- package/dist/index.js +127 -32
- 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
|
-
[
|
|
11
|
-
[
|
|
12
|
-
[
|
|
13
|
-
[
|
|
10
|
+
[32mESM[39m [1mdist/index.js [22m[32m59.53 KB[39m
|
|
11
|
+
[32mESM[39m ⚡️ Build success in 105ms
|
|
12
|
+
[32mCJS[39m [1mdist/index.cjs [22m[32m63.62 KB[39m
|
|
13
|
+
[32mCJS[39m ⚡️ Build success in 110ms
|
|
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 32.
|
|
15
|
+
[32mDTS[39m ⚡️ Build success in 30914ms
|
|
16
|
+
[32mDTS[39m [1mdist/index.d.cts [22m[32m38.03 KB[39m
|
|
17
|
+
[32mDTS[39m [1mdist/index.d.ts [22m[32m38.03 KB[39m
|
|
18
|
+
Done in 32.12s.
|
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>>
|
|
@@ -273,7 +282,7 @@ export abstract class FormModel<
|
|
|
273
282
|
return this.synthesizeFieldByPaths(valuePath, typePath)
|
|
274
283
|
}
|
|
275
284
|
|
|
276
|
-
private
|
|
285
|
+
private getField(valuePath: keyof ValuePathsToAdapters, typePath: keyof TypePathsToAdapters) {
|
|
277
286
|
const adapter = this.adapters[typePath]
|
|
278
287
|
if (adapter == null) {
|
|
279
288
|
// invalid path, which can happen
|
|
@@ -311,43 +320,74 @@ export abstract class FormModel<
|
|
|
311
320
|
valuePath as string,
|
|
312
321
|
context,
|
|
313
322
|
)
|
|
314
|
-
let error: unknown = undefined
|
|
315
323
|
const displayedValue = fieldOverride != null ? fieldOverride[0] : value
|
|
324
|
+
|
|
325
|
+
return {
|
|
326
|
+
context,
|
|
327
|
+
convert,
|
|
328
|
+
create,
|
|
329
|
+
revert,
|
|
330
|
+
displayedValue,
|
|
331
|
+
// value,
|
|
332
|
+
required,
|
|
333
|
+
readonly,
|
|
334
|
+
defaultValue,
|
|
335
|
+
}
|
|
336
|
+
}
|
|
337
|
+
|
|
338
|
+
private synthesizeFieldByPaths(valuePath: keyof ValuePathsToAdapters, typePath: keyof TypePathsToAdapters) {
|
|
339
|
+
const field = this.getField(valuePath, typePath)
|
|
340
|
+
if (field == null) {
|
|
341
|
+
return
|
|
342
|
+
}
|
|
343
|
+
const {
|
|
344
|
+
context,
|
|
345
|
+
convert,
|
|
346
|
+
revert,
|
|
347
|
+
displayedValue,
|
|
348
|
+
required,
|
|
349
|
+
readonly,
|
|
350
|
+
defaultValue,
|
|
351
|
+
} = field
|
|
316
352
|
const validation = this.validation[valuePath] ?? Validation.None
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
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
|
|
324
365
|
// eslint-disable-next-line @typescript-eslint/consistent-type-assertions
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
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)
|
|
332
379
|
if (revertResult?.type === UnreliableFieldConversionType.Failure) {
|
|
333
380
|
;({ error } = revertResult)
|
|
334
381
|
}
|
|
335
382
|
}
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
const revertResult = revert?.(displayedValue, valuePath, context)
|
|
341
|
-
if (revertResult?.type === UnreliableFieldConversionType.Failure) {
|
|
342
|
-
;({ error } = revertResult)
|
|
343
|
-
}
|
|
344
|
-
}
|
|
345
|
-
break
|
|
346
|
-
default:
|
|
347
|
-
throw new UnreachableError(validation)
|
|
383
|
+
break
|
|
384
|
+
default:
|
|
385
|
+
throw new UnreachableError(validation)
|
|
386
|
+
}
|
|
348
387
|
}
|
|
388
|
+
|
|
349
389
|
return {
|
|
350
|
-
value:
|
|
390
|
+
value: displayedValue,
|
|
351
391
|
error,
|
|
352
392
|
readonly: readonly && !this.forceMutableFields,
|
|
353
393
|
required,
|
|
@@ -386,6 +426,14 @@ export abstract class FormModel<
|
|
|
386
426
|
)
|
|
387
427
|
}
|
|
388
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
|
+
|
|
389
437
|
typePath<K extends keyof ValueToTypePaths>(valuePath: K): ValueToTypePaths[K] {
|
|
390
438
|
return valuePathToTypePath<ValueToTypePaths, K>(this.type, valuePath, true)
|
|
391
439
|
}
|
|
@@ -574,6 +622,7 @@ export abstract class FormModel<
|
|
|
574
622
|
const accessor = this.getAccessorForValuePath(valuePath)
|
|
575
623
|
return runInAction(() => {
|
|
576
624
|
this.fieldOverrides[valuePath] = [value]
|
|
625
|
+
delete this.errorOverrides[valuePath]
|
|
577
626
|
if (validation != null) {
|
|
578
627
|
this.validation[valuePath] = validation
|
|
579
628
|
}
|
|
@@ -592,11 +641,30 @@ export abstract class FormModel<
|
|
|
592
641
|
})
|
|
593
642
|
}
|
|
594
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
|
+
|
|
595
662
|
clearFieldError<K extends keyof ValuePathsToAdapters>(valuePath: K) {
|
|
596
663
|
const fieldOverride = this.fieldOverrides[valuePath]
|
|
597
664
|
if (fieldOverride != null) {
|
|
598
665
|
runInAction(() => {
|
|
599
666
|
delete this.validation[valuePath]
|
|
667
|
+
delete this.errorOverrides[valuePath]
|
|
600
668
|
})
|
|
601
669
|
}
|
|
602
670
|
}
|
|
@@ -623,6 +691,7 @@ export abstract class FormModel<
|
|
|
623
691
|
runInAction(() => {
|
|
624
692
|
this.fieldOverrides[key] = [displayValue]
|
|
625
693
|
delete this.validation[key]
|
|
694
|
+
delete this.errorOverrides[key]
|
|
626
695
|
})
|
|
627
696
|
}
|
|
628
697
|
|
|
@@ -631,6 +700,7 @@ export abstract class FormModel<
|
|
|
631
700
|
this.validation = {}
|
|
632
701
|
// TODO this isn't correct, should reload from value
|
|
633
702
|
this.fieldOverrides = {}
|
|
703
|
+
this.errorOverrides = {}
|
|
634
704
|
this.value = mobxCopy(this.type, value)
|
|
635
705
|
})
|
|
636
706
|
}
|
|
@@ -646,12 +716,59 @@ export abstract class FormModel<
|
|
|
646
716
|
return this.validation[valuePath] ?? Validation.None
|
|
647
717
|
}
|
|
648
718
|
|
|
719
|
+
isFieldDirty<K extends keyof ValuePathsToAdapters>(valuePath: K): boolean {
|
|
720
|
+
// eslint-disable-next-line @typescript-eslint/consistent-type-assertions
|
|
721
|
+
const typePath = valuePathToTypePath<ValueToTypePaths, keyof ValueToTypePaths>(
|
|
722
|
+
this.type,
|
|
723
|
+
// eslint-disable-next-line @typescript-eslint/consistent-type-assertions
|
|
724
|
+
valuePath as keyof ValueToTypePaths,
|
|
725
|
+
true,
|
|
726
|
+
) as keyof TypePathsToAdapters
|
|
727
|
+
const field = this.getField(valuePath, typePath)
|
|
728
|
+
|
|
729
|
+
if (field == null) {
|
|
730
|
+
return false
|
|
731
|
+
}
|
|
732
|
+
|
|
733
|
+
const {
|
|
734
|
+
displayedValue,
|
|
735
|
+
convert,
|
|
736
|
+
revert,
|
|
737
|
+
context,
|
|
738
|
+
defaultValue,
|
|
739
|
+
} = field
|
|
740
|
+
|
|
741
|
+
// if either the display value, or the stored value, match the original, then assume it's not dirty
|
|
742
|
+
const originalValue = valuePath in this.originalValues
|
|
743
|
+
// eslint-disable-next-line @typescript-eslint/consistent-type-assertions
|
|
744
|
+
? this.originalValues[valuePath as string]
|
|
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
|
+
}
|
|
759
|
+
// eslint-disable-next-line @typescript-eslint/consistent-type-assertions
|
|
760
|
+
const { value: originalDisplayedValue } = convert(originalValue, valuePath as string, context)
|
|
761
|
+
// try to compare the displayed values directly if we can't revert the displayed value
|
|
762
|
+
return displayedValue !== originalDisplayedValue
|
|
763
|
+
}
|
|
764
|
+
|
|
649
765
|
validateField<K extends keyof ValuePathsToAdapters>(
|
|
650
766
|
valuePath: K,
|
|
651
767
|
validation: Validation = Validation.Always,
|
|
652
768
|
): boolean {
|
|
653
769
|
runInAction(() => {
|
|
654
770
|
this.validation[valuePath] = validation
|
|
771
|
+
delete this.errorOverrides[valuePath]
|
|
655
772
|
})
|
|
656
773
|
return this.fields[valuePath].error == null
|
|
657
774
|
}
|
|
@@ -673,4 +790,8 @@ export abstract class FormModel<
|
|
|
673
790
|
},
|
|
674
791
|
)
|
|
675
792
|
}
|
|
793
|
+
|
|
794
|
+
validateSubmit() {
|
|
795
|
+
return this.validateAll()
|
|
796
|
+
}
|
|
676
797
|
}
|
package/core/mobx/hooks.tsx
CHANGED
|
@@ -41,8 +41,8 @@ export function useDefaultMobxFormHooks<
|
|
|
41
41
|
path: Path,
|
|
42
42
|
value: ValueTypeOfField<F[Path]>,
|
|
43
43
|
) {
|
|
44
|
-
|
|
45
|
-
model.setFieldValue<Path>(path, value)
|
|
44
|
+
const validation = Math.min(model.getValidation(path), Validation.Changed)
|
|
45
|
+
model.setFieldValue<Path>(path, value, validation)
|
|
46
46
|
},
|
|
47
47
|
[model],
|
|
48
48
|
)
|
|
@@ -66,7 +66,8 @@ export function useDefaultMobxFormHooks<
|
|
|
66
66
|
// (e.g. changing a discriminator)
|
|
67
67
|
// TODO debounce?
|
|
68
68
|
setTimeout(function () {
|
|
69
|
-
if
|
|
69
|
+
// only start validation if the user has changed the field
|
|
70
|
+
if (model.isValuePathActive(path) && model.isFieldDirty(path)) {
|
|
70
71
|
// further workaround to make sure we don't downgrade the existing validation
|
|
71
72
|
model.validateField(path, Math.max(Validation.Changed, model.getValidation(path)))
|
|
72
73
|
}
|
|
@@ -77,7 +78,7 @@ export function useDefaultMobxFormHooks<
|
|
|
77
78
|
|
|
78
79
|
const onFormSubmit = useCallback(
|
|
79
80
|
function () {
|
|
80
|
-
if (model.
|
|
81
|
+
if (model.validateSubmit()) {
|
|
81
82
|
onValidFormSubmit?.(model.value)
|
|
82
83
|
}
|
|
83
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
|
|
@@ -463,8 +464,7 @@ var FormModel = class {
|
|
|
463
464
|
}
|
|
464
465
|
return this.synthesizeFieldByPaths(valuePath, typePath);
|
|
465
466
|
}
|
|
466
|
-
|
|
467
|
-
var _a;
|
|
467
|
+
getField(valuePath, typePath) {
|
|
468
468
|
const adapter2 = this.adapters[typePath];
|
|
469
469
|
if (adapter2 == null) {
|
|
470
470
|
return;
|
|
@@ -492,39 +492,68 @@ var FormModel = class {
|
|
|
492
492
|
valuePath,
|
|
493
493
|
context
|
|
494
494
|
);
|
|
495
|
-
let error = void 0;
|
|
496
495
|
const displayedValue = fieldOverride != null ? fieldOverride[0] : value;
|
|
496
|
+
return {
|
|
497
|
+
context,
|
|
498
|
+
convert,
|
|
499
|
+
create,
|
|
500
|
+
revert,
|
|
501
|
+
displayedValue,
|
|
502
|
+
// value,
|
|
503
|
+
required,
|
|
504
|
+
readonly,
|
|
505
|
+
defaultValue
|
|
506
|
+
};
|
|
507
|
+
}
|
|
508
|
+
synthesizeFieldByPaths(valuePath, typePath) {
|
|
509
|
+
var _a;
|
|
510
|
+
const field = this.getField(valuePath, typePath);
|
|
511
|
+
if (field == null) {
|
|
512
|
+
return;
|
|
513
|
+
}
|
|
514
|
+
const {
|
|
515
|
+
context,
|
|
516
|
+
convert,
|
|
517
|
+
revert,
|
|
518
|
+
displayedValue,
|
|
519
|
+
required,
|
|
520
|
+
readonly,
|
|
521
|
+
defaultValue
|
|
522
|
+
} = field;
|
|
497
523
|
const validation = (_a = this.validation[valuePath]) != null ? _a : 0 /* None */;
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
const
|
|
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);
|
|
507
545
|
if ((revertResult == null ? void 0 : revertResult.type) === 1 /* Failure */) {
|
|
508
546
|
;
|
|
509
547
|
({ error } = revertResult);
|
|
510
548
|
}
|
|
511
549
|
}
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
const revertResult = revert == null ? void 0 : revert(displayedValue, valuePath, context);
|
|
517
|
-
if ((revertResult == null ? void 0 : revertResult.type) === 1 /* Failure */) {
|
|
518
|
-
;
|
|
519
|
-
({ error } = revertResult);
|
|
520
|
-
}
|
|
521
|
-
}
|
|
522
|
-
break;
|
|
523
|
-
default:
|
|
524
|
-
throw new import_base2.UnreachableError(validation);
|
|
550
|
+
break;
|
|
551
|
+
default:
|
|
552
|
+
throw new import_base2.UnreachableError(validation);
|
|
553
|
+
}
|
|
525
554
|
}
|
|
526
555
|
return {
|
|
527
|
-
value:
|
|
556
|
+
value: displayedValue,
|
|
528
557
|
error,
|
|
529
558
|
readonly: readonly && !this.forceMutableFields,
|
|
530
559
|
required
|
|
@@ -554,6 +583,11 @@ var FormModel = class {
|
|
|
554
583
|
valuePath
|
|
555
584
|
);
|
|
556
585
|
}
|
|
586
|
+
get dirty() {
|
|
587
|
+
return Object.keys(this.accessors).some((valuePath) => {
|
|
588
|
+
return this.isFieldDirty(valuePath);
|
|
589
|
+
});
|
|
590
|
+
}
|
|
557
591
|
typePath(valuePath) {
|
|
558
592
|
return (0, import_define.valuePathToTypePath)(this.type, valuePath, true);
|
|
559
593
|
}
|
|
@@ -699,6 +733,7 @@ var FormModel = class {
|
|
|
699
733
|
const accessor = this.getAccessorForValuePath(valuePath);
|
|
700
734
|
return (0, import_mobx.runInAction)(() => {
|
|
701
735
|
this.fieldOverrides[valuePath] = [value];
|
|
736
|
+
delete this.errorOverrides[valuePath];
|
|
702
737
|
if (validation != null) {
|
|
703
738
|
this.validation[valuePath] = validation;
|
|
704
739
|
}
|
|
@@ -716,11 +751,26 @@ var FormModel = class {
|
|
|
716
751
|
}
|
|
717
752
|
});
|
|
718
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
|
+
}
|
|
719
768
|
clearFieldError(valuePath) {
|
|
720
769
|
const fieldOverride = this.fieldOverrides[valuePath];
|
|
721
770
|
if (fieldOverride != null) {
|
|
722
771
|
(0, import_mobx.runInAction)(() => {
|
|
723
772
|
delete this.validation[valuePath];
|
|
773
|
+
delete this.errorOverrides[valuePath];
|
|
724
774
|
});
|
|
725
775
|
}
|
|
726
776
|
}
|
|
@@ -743,12 +793,14 @@ var FormModel = class {
|
|
|
743
793
|
(0, import_mobx.runInAction)(() => {
|
|
744
794
|
this.fieldOverrides[key] = [displayValue];
|
|
745
795
|
delete this.validation[key];
|
|
796
|
+
delete this.errorOverrides[key];
|
|
746
797
|
});
|
|
747
798
|
}
|
|
748
799
|
clearAll(value) {
|
|
749
800
|
(0, import_mobx.runInAction)(() => {
|
|
750
801
|
this.validation = {};
|
|
751
802
|
this.fieldOverrides = {};
|
|
803
|
+
this.errorOverrides = {};
|
|
752
804
|
this.value = (0, import_define.mobxCopy)(this.type, value);
|
|
753
805
|
});
|
|
754
806
|
}
|
|
@@ -761,9 +813,44 @@ var FormModel = class {
|
|
|
761
813
|
var _a;
|
|
762
814
|
return (_a = this.validation[valuePath]) != null ? _a : 0 /* None */;
|
|
763
815
|
}
|
|
816
|
+
isFieldDirty(valuePath) {
|
|
817
|
+
const typePath = (0, import_define.valuePathToTypePath)(
|
|
818
|
+
this.type,
|
|
819
|
+
// eslint-disable-next-line @typescript-eslint/consistent-type-assertions
|
|
820
|
+
valuePath,
|
|
821
|
+
true
|
|
822
|
+
);
|
|
823
|
+
const field = this.getField(valuePath, typePath);
|
|
824
|
+
if (field == null) {
|
|
825
|
+
return false;
|
|
826
|
+
}
|
|
827
|
+
const {
|
|
828
|
+
displayedValue,
|
|
829
|
+
convert,
|
|
830
|
+
revert,
|
|
831
|
+
context,
|
|
832
|
+
defaultValue
|
|
833
|
+
} = field;
|
|
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
|
+
}
|
|
847
|
+
const { value: originalDisplayedValue } = convert(originalValue, valuePath, context);
|
|
848
|
+
return displayedValue !== originalDisplayedValue;
|
|
849
|
+
}
|
|
764
850
|
validateField(valuePath, validation = 2 /* Always */) {
|
|
765
851
|
(0, import_mobx.runInAction)(() => {
|
|
766
852
|
this.validation[valuePath] = validation;
|
|
853
|
+
delete this.errorOverrides[valuePath];
|
|
767
854
|
});
|
|
768
855
|
return this.fields[valuePath].error == null;
|
|
769
856
|
}
|
|
@@ -781,17 +868,23 @@ var FormModel = class {
|
|
|
781
868
|
}
|
|
782
869
|
);
|
|
783
870
|
}
|
|
871
|
+
validateSubmit() {
|
|
872
|
+
return this.validateAll();
|
|
873
|
+
}
|
|
784
874
|
};
|
|
785
875
|
_init = __decoratorStart(null);
|
|
786
876
|
_value = new WeakMap();
|
|
787
877
|
_fieldOverrides = new WeakMap();
|
|
878
|
+
_errorOverrides = new WeakMap();
|
|
788
879
|
_validation = new WeakMap();
|
|
789
880
|
__decorateElement(_init, 4, "value", _value_dec, FormModel, _value);
|
|
790
881
|
__decorateElement(_init, 4, "fieldOverrides", _fieldOverrides_dec, FormModel, _fieldOverrides);
|
|
882
|
+
__decorateElement(_init, 4, "errorOverrides", _errorOverrides_dec, FormModel, _errorOverrides);
|
|
791
883
|
__decorateElement(_init, 4, "validation", _validation_dec, FormModel, _validation);
|
|
792
884
|
__decorateElement(_init, 2, "fields", _fields_dec, FormModel);
|
|
793
885
|
__decorateElement(_init, 2, "knownFields", _knownFields_dec, FormModel);
|
|
794
886
|
__decorateElement(_init, 2, "accessors", _accessors_dec, FormModel);
|
|
887
|
+
__decorateElement(_init, 2, "dirty", _dirty_dec, FormModel);
|
|
795
888
|
__decoratorMetadata(_init, FormModel);
|
|
796
889
|
|
|
797
890
|
// core/mobx/hooks.tsx
|
|
@@ -802,7 +895,8 @@ function useDefaultMobxFormHooks(model, {
|
|
|
802
895
|
} = {}) {
|
|
803
896
|
const onFieldValueChange = (0, import_react.useCallback)(
|
|
804
897
|
function(path, value) {
|
|
805
|
-
model.
|
|
898
|
+
const validation = Math.min(model.getValidation(path), 1 /* Changed */);
|
|
899
|
+
model.setFieldValue(path, value, validation);
|
|
806
900
|
},
|
|
807
901
|
[model]
|
|
808
902
|
);
|
|
@@ -821,7 +915,7 @@ function useDefaultMobxFormHooks(model, {
|
|
|
821
915
|
const onFieldBlur = (0, import_react.useCallback)(
|
|
822
916
|
function(path) {
|
|
823
917
|
setTimeout(function() {
|
|
824
|
-
if (model.isValuePathActive(path)) {
|
|
918
|
+
if (model.isValuePathActive(path) && model.isFieldDirty(path)) {
|
|
825
919
|
model.validateField(path, Math.max(1 /* Changed */, model.getValidation(path)));
|
|
826
920
|
}
|
|
827
921
|
}, 100);
|
|
@@ -830,7 +924,7 @@ function useDefaultMobxFormHooks(model, {
|
|
|
830
924
|
);
|
|
831
925
|
const onFormSubmit = (0, import_react.useCallback)(
|
|
832
926
|
function() {
|
|
833
|
-
if (model.
|
|
927
|
+
if (model.validateSubmit()) {
|
|
834
928
|
onValidFormSubmit == null ? void 0 : onValidFormSubmit(model.value);
|
|
835
929
|
}
|
|
836
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;
|
|
@@ -142,23 +146,33 @@ declare abstract class FormModel<T extends Type, ValueToTypePaths extends Readon
|
|
|
142
146
|
get fields(): SimplifyDeep<FlattenedConvertedFieldsOf<ValuePathsToAdapters>>;
|
|
143
147
|
private get knownFields();
|
|
144
148
|
private maybeSynthesizeFieldByValuePath;
|
|
149
|
+
private getField;
|
|
145
150
|
private synthesizeFieldByPaths;
|
|
146
151
|
getAccessorForValuePath(valuePath: keyof ValuePathsToAdapters): Accessor | undefined;
|
|
147
152
|
get accessors(): Readonly<Record<string, Accessor>>;
|
|
148
153
|
private maybeGetAdapterForValuePath;
|
|
149
154
|
private getAdapterForValuePath;
|
|
155
|
+
get dirty(): boolean;
|
|
150
156
|
typePath<K extends keyof ValueToTypePaths>(valuePath: K): ValueToTypePaths[K];
|
|
151
157
|
setFieldValue<K extends keyof ValuePathsToAdapters>(valuePath: K, value: ToOfFieldAdapter<ValuePathsToAdapters[K]>, validation?: Validation): boolean;
|
|
152
158
|
addListItem<K extends keyof FlattenedListTypesOfType<T>>(valuePath: K, elementValue?: Maybe<ElementOfArray<FlattenedValuesOfType<T>[K]>>, index?: number): void;
|
|
153
159
|
removeListItem<K extends keyof FlattenedListTypesOfType<T>>(...elementValuePaths: readonly `${K}.${number}`[]): void;
|
|
154
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;
|
|
155
167
|
clearFieldError<K extends keyof ValuePathsToAdapters>(valuePath: K): void;
|
|
156
168
|
clearFieldValue<K extends StringKeyOf<ValuePathsToAdapters>>(valuePath: K): void;
|
|
157
169
|
clearAll(value: ValueOfType<T>): void;
|
|
158
170
|
isValuePathActive<K extends keyof ValuePathsToAdapters>(valuePath: K): boolean;
|
|
159
171
|
getValidation<K extends keyof ValuePathsToAdapters>(valuePath: K): Validation;
|
|
172
|
+
isFieldDirty<K extends keyof ValuePathsToAdapters>(valuePath: K): boolean;
|
|
160
173
|
validateField<K extends keyof ValuePathsToAdapters>(valuePath: K, validation?: Validation): boolean;
|
|
161
174
|
validateAll(validation?: Validation): boolean;
|
|
175
|
+
validateSubmit(): boolean;
|
|
162
176
|
}
|
|
163
177
|
|
|
164
178
|
type ValueOfModel<M extends FormModel<any, any, any, any, any>> = M extends FormModel<infer T, any, any, any, any> ? ValueOfType<ReadonlyTypeOfType<T>> : never;
|