@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.
- package/.out/core/mobx/form_model.d.ts +5 -1
- package/.out/core/mobx/form_model.js +51 -14
- package/.out/core/mobx/hooks.js +7 -4
- package/.out/core/mobx/specs/form_model.tests.js +2 -2
- 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 +74 -16
- package/core/mobx/hooks.tsx +7 -4
- package/core/mobx/specs/form_model.tests.ts +2 -2
- package/dist/index.cjs +66 -17
- package/dist/index.d.cts +5 -1
- package/dist/index.d.ts +5 -1
- package/dist/index.js +66 -17
- package/package.json +1 -1
|
@@ -15,6 +15,7 @@ type FlattenedFieldOverrides<ValuePathsToAdapters extends Readonly<Record<string
|
|
|
15
15
|
-readonly [K in keyof ValuePathsToAdapters]?: FieldOverride<ToOfFieldAdapter<ValuePathsToAdapters[K]>>;
|
|
16
16
|
};
|
|
17
17
|
export declare enum Validation {
|
|
18
|
+
None = 0,
|
|
18
19
|
Changed = 1,
|
|
19
20
|
Always = 2
|
|
20
21
|
}
|
|
@@ -43,13 +44,14 @@ export declare abstract class FormModel<T extends Type, ValueToTypePaths extends
|
|
|
43
44
|
get fields(): SimplifyDeep<FlattenedConvertedFieldsOf<ValuePathsToAdapters>>;
|
|
44
45
|
private get knownFields();
|
|
45
46
|
private maybeSynthesizeFieldByValuePath;
|
|
47
|
+
private getField;
|
|
46
48
|
private synthesizeFieldByPaths;
|
|
47
49
|
getAccessorForValuePath(valuePath: keyof ValuePathsToAdapters): Accessor | undefined;
|
|
48
50
|
get accessors(): Readonly<Record<string, Accessor>>;
|
|
49
51
|
private maybeGetAdapterForValuePath;
|
|
50
52
|
private getAdapterForValuePath;
|
|
51
53
|
typePath<K extends keyof ValueToTypePaths>(valuePath: K): ValueToTypePaths[K];
|
|
52
|
-
setFieldValue<K extends keyof ValuePathsToAdapters>(valuePath: K, value: ToOfFieldAdapter<ValuePathsToAdapters[K]>, validation?: Validation
|
|
54
|
+
setFieldValue<K extends keyof ValuePathsToAdapters>(valuePath: K, value: ToOfFieldAdapter<ValuePathsToAdapters[K]>, validation?: Validation): boolean;
|
|
53
55
|
addListItem<K extends keyof FlattenedListTypesOfType<T>>(valuePath: K, elementValue?: Maybe<ElementOfArray<FlattenedValuesOfType<T>[K]>>, index?: number): void;
|
|
54
56
|
removeListItem<K extends keyof FlattenedListTypesOfType<T>>(...elementValuePaths: readonly `${K}.${number}`[]): void;
|
|
55
57
|
private internalSetFieldValue;
|
|
@@ -57,6 +59,8 @@ export declare abstract class FormModel<T extends Type, ValueToTypePaths extends
|
|
|
57
59
|
clearFieldValue<K extends StringKeyOf<ValuePathsToAdapters>>(valuePath: K): void;
|
|
58
60
|
clearAll(value: ValueOfType<T>): void;
|
|
59
61
|
isValuePathActive<K extends keyof ValuePathsToAdapters>(valuePath: K): boolean;
|
|
62
|
+
getValidation<K extends keyof ValuePathsToAdapters>(valuePath: K): Validation;
|
|
63
|
+
isDirty<K extends keyof ValuePathsToAdapters>(valuePath: K): boolean;
|
|
60
64
|
validateField<K extends keyof ValuePathsToAdapters>(valuePath: K, validation?: Validation): boolean;
|
|
61
65
|
validateAll(validation?: Validation): boolean;
|
|
62
66
|
}
|
|
@@ -49,6 +49,7 @@ import { computed, observable, runInAction, } from 'mobx';
|
|
|
49
49
|
import { UnreliableFieldConversionType, } from 'types/field_converters';
|
|
50
50
|
export var Validation;
|
|
51
51
|
(function (Validation) {
|
|
52
|
+
Validation[Validation["None"] = 0] = "None";
|
|
52
53
|
Validation[Validation["Changed"] = 1] = "Changed";
|
|
53
54
|
Validation[Validation["Always"] = 2] = "Always";
|
|
54
55
|
})(Validation || (Validation = {}));
|
|
@@ -144,7 +145,7 @@ let FormModel = (() => {
|
|
|
144
145
|
case 'edit':
|
|
145
146
|
return false;
|
|
146
147
|
default:
|
|
147
|
-
|
|
148
|
+
throw new UnreachableError(this.mode);
|
|
148
149
|
}
|
|
149
150
|
}
|
|
150
151
|
get fields() {
|
|
@@ -189,7 +190,7 @@ let FormModel = (() => {
|
|
|
189
190
|
}
|
|
190
191
|
return this.synthesizeFieldByPaths(valuePath, typePath);
|
|
191
192
|
}
|
|
192
|
-
|
|
193
|
+
getField(valuePath, typePath) {
|
|
193
194
|
const adapter = this.adapters[typePath];
|
|
194
195
|
if (adapter == null) {
|
|
195
196
|
// invalid path, which can happen
|
|
@@ -211,12 +212,30 @@ let FormModel = (() => {
|
|
|
211
212
|
: defaultValue,
|
|
212
213
|
// eslint-disable-next-line @typescript-eslint/consistent-type-assertions
|
|
213
214
|
valuePath, context);
|
|
214
|
-
// const error = this.errors[valuePath]
|
|
215
|
-
let error = undefined;
|
|
216
215
|
const displayedValue = fieldOverride != null ? fieldOverride[0] : value;
|
|
217
|
-
|
|
216
|
+
return {
|
|
217
|
+
context,
|
|
218
|
+
convert,
|
|
219
|
+
create,
|
|
220
|
+
revert,
|
|
221
|
+
displayedValue,
|
|
222
|
+
// value,
|
|
223
|
+
required,
|
|
224
|
+
readonly,
|
|
225
|
+
defaultValue,
|
|
226
|
+
};
|
|
227
|
+
}
|
|
228
|
+
synthesizeFieldByPaths(valuePath, typePath) {
|
|
229
|
+
var _b;
|
|
230
|
+
const field = this.getField(valuePath, typePath);
|
|
231
|
+
if (field == null) {
|
|
232
|
+
return;
|
|
233
|
+
}
|
|
234
|
+
const { context, convert, revert, displayedValue, required, readonly, defaultValue, } = field;
|
|
235
|
+
const validation = (_b = this.validation[valuePath]) !== null && _b !== void 0 ? _b : Validation.None;
|
|
236
|
+
let error;
|
|
218
237
|
switch (validation) {
|
|
219
|
-
case
|
|
238
|
+
case Validation.None:
|
|
220
239
|
// skip validation
|
|
221
240
|
break;
|
|
222
241
|
case Validation.Changed:
|
|
@@ -250,7 +269,7 @@ let FormModel = (() => {
|
|
|
250
269
|
throw new UnreachableError(validation);
|
|
251
270
|
}
|
|
252
271
|
return {
|
|
253
|
-
value:
|
|
272
|
+
value: displayedValue,
|
|
254
273
|
error,
|
|
255
274
|
readonly: readonly && !this.forceMutableFields,
|
|
256
275
|
required,
|
|
@@ -277,7 +296,7 @@ let FormModel = (() => {
|
|
|
277
296
|
typePath(valuePath) {
|
|
278
297
|
return valuePathToTypePath(this.type, valuePath, true);
|
|
279
298
|
}
|
|
280
|
-
setFieldValue(valuePath, value, validation
|
|
299
|
+
setFieldValue(valuePath, value, validation) {
|
|
281
300
|
return this.internalSetFieldValue(valuePath, value, validation);
|
|
282
301
|
}
|
|
283
302
|
addListItem(valuePath,
|
|
@@ -417,9 +436,6 @@ let FormModel = (() => {
|
|
|
417
436
|
if (validation != null) {
|
|
418
437
|
this.validation[valuePath] = validation;
|
|
419
438
|
}
|
|
420
|
-
else {
|
|
421
|
-
delete this.validation[valuePath];
|
|
422
|
-
}
|
|
423
439
|
switch (conversion.type) {
|
|
424
440
|
case UnreliableFieldConversionType.Failure:
|
|
425
441
|
if (conversion.value != null && accessor != null) {
|
|
@@ -475,15 +491,36 @@ let FormModel = (() => {
|
|
|
475
491
|
// eslint-disable-next-line @typescript-eslint/consistent-type-assertions
|
|
476
492
|
return keys.has(valuePath);
|
|
477
493
|
}
|
|
478
|
-
|
|
494
|
+
getValidation(valuePath) {
|
|
479
495
|
var _b;
|
|
480
|
-
|
|
496
|
+
return (_b = this.validation[valuePath]) !== null && _b !== void 0 ? _b : Validation.None;
|
|
497
|
+
}
|
|
498
|
+
isDirty(valuePath) {
|
|
499
|
+
// eslint-disable-next-line @typescript-eslint/consistent-type-assertions
|
|
500
|
+
const typePath = valuePathToTypePath(this.type,
|
|
501
|
+
// eslint-disable-next-line @typescript-eslint/consistent-type-assertions
|
|
502
|
+
valuePath, true);
|
|
503
|
+
const field = this.getField(valuePath, typePath);
|
|
504
|
+
if (field == null) {
|
|
505
|
+
return false;
|
|
506
|
+
}
|
|
507
|
+
const { displayedValue, convert, context, defaultValue, } = field;
|
|
508
|
+
const originalValue = valuePath in this.originalValues
|
|
509
|
+
// eslint-disable-next-line @typescript-eslint/consistent-type-assertions
|
|
510
|
+
? this.originalValues[valuePath]
|
|
511
|
+
: defaultValue;
|
|
512
|
+
// eslint-disable-next-line @typescript-eslint/consistent-type-assertions
|
|
513
|
+
const { value: originalDisplayedValue } = convert(originalValue, valuePath, context);
|
|
514
|
+
// TODO better comparisons, displayed values can still be complex
|
|
515
|
+
return (displayedValue !== originalDisplayedValue);
|
|
516
|
+
}
|
|
517
|
+
validateField(valuePath, validation = Validation.Always) {
|
|
481
518
|
runInAction(() => {
|
|
482
519
|
this.validation[valuePath] = validation;
|
|
483
520
|
});
|
|
484
521
|
return this.fields[valuePath].error == null;
|
|
485
522
|
}
|
|
486
|
-
validateAll(validation =
|
|
523
|
+
validateAll(validation = Validation.Always) {
|
|
487
524
|
const accessors = toArray(this.accessors);
|
|
488
525
|
runInAction(() => {
|
|
489
526
|
accessors.forEach(([valuePath]) => {
|
package/.out/core/mobx/hooks.js
CHANGED
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
import { useCallback, } from 'react';
|
|
2
|
+
import { Validation, } from './form_model';
|
|
2
3
|
export function useDefaultMobxFormHooks(model, { onValidFieldSubmit, onValidFormSubmit, } = {}) {
|
|
3
4
|
const onFieldValueChange = useCallback(function (path, value) {
|
|
4
|
-
|
|
5
|
-
model.setFieldValue(path, value,
|
|
5
|
+
const validation = Math.min(model.getValidation(path), Validation.Changed);
|
|
6
|
+
model.setFieldValue(path, value, validation);
|
|
6
7
|
}, [model]);
|
|
7
8
|
const onFieldSubmit = useCallback(function (valuePath) {
|
|
8
9
|
if (model.validateField(valuePath)) {
|
|
@@ -18,8 +19,10 @@ export function useDefaultMobxFormHooks(model, { onValidFieldSubmit, onValidForm
|
|
|
18
19
|
// (e.g. changing a discriminator)
|
|
19
20
|
// TODO debounce?
|
|
20
21
|
setTimeout(function () {
|
|
21
|
-
if
|
|
22
|
-
|
|
22
|
+
// only start validation if the user has changed the field
|
|
23
|
+
if (model.isValuePathActive(path) && model.isDirty(path)) {
|
|
24
|
+
// further workaround to make sure we don't downgrade the existing validation
|
|
25
|
+
model.validateField(path, Math.max(Validation.Changed, model.getValidation(path)));
|
|
23
26
|
}
|
|
24
27
|
}, 100);
|
|
25
28
|
}, [model]);
|
|
@@ -863,8 +863,8 @@ describe('all', function () {
|
|
|
863
863
|
it('respects the field being readonly', () => {
|
|
864
864
|
expect(model.fields['$.n'].readonly).toBeTruthy();
|
|
865
865
|
});
|
|
866
|
-
it('
|
|
867
|
-
expect(model.validateAll()).
|
|
866
|
+
it('fails validation with invalid, clean data', () => {
|
|
867
|
+
expect(model.validateAll()).toBeFalsy();
|
|
868
868
|
});
|
|
869
869
|
it('fails validation with invalid, dirty data', () => {
|
|
870
870
|
model.setFieldValue('$.n', '2');
|